# Path: brainforge/reinforcement/experience.py
# def replay_memory_factory(limit, mode, time, downsample=0):
#     if time > 1:
#         return TimeExperience(time, limit, downsample)
#     else:
#         return Experience(limit, mode, downsample)
# 
# class Experience:
# 
#     def __init__(self, limit=40000, mode="drop", downsample=0):
#         self.limit = limit
#         self.X = []
#         self.Y = []
#         self._adder = {"drop": self._add_drop, "mix in": self._mix_in}[mode]
#         self.downsample = downsample
# 
#     @property
#     def N(self):
#         return len(self.X)
# 
#     def initialize(self, X, Y):
#         self.X = X
#         self.Y = Y
# 
#     def _mix_in(self, X, Y):
#         m = len(X)
#         N = self.N
#         narg = np.arange(N)
#         np.random.shuffle(narg)
#         marg = narg[:m]
#         self.X[marg] = X
#         self.Y[marg] = Y
# 
#     def _add_drop(self, X, Y):
#         m = len(X)
#         self.X = np.concatenate((self.X[m:], X))
#         self.Y = np.concatenate((self.Y[m:], Y))
# 
#     def _add(self, X, Y):
#         self.X = np.concatenate((self.X, X))
#         self.Y = np.concatenate((self.Y, Y))
#         self.X = self.X[-self.limit:]
#         self.Y = self.Y[-self.limit:]
# 
#     def _incorporate(self, X, Y):
#         if self.N < self.limit:
#             self._add(X, Y)
#         self._adder(X, Y)
# 
#     def remember(self, X, Y):
#         if self.downsample > 1:
#             X, Y = X[::self.downsample], Y[::self.downsample]
#         assert len(X) == len(Y)
#         if len(self.X) < 1:
#             self.initialize(X, Y)
#         else:
#             self._incorporate(X, Y)
# 
#     def replay(self, batch_size):
#         narg = np.arange(self.N)
#         np.random.shuffle(narg)
#         batch_args = narg[:batch_size]
#         if len(batch_args) == 0:
#             return [], []
#         return self.X[batch_args], self.Y[batch_args]


from .experience import replay_memory_factory, Experience


class AgentConfig:

    def __init__(self, batch_size=128,
                 discount_factor=0.99,
                 knowledge_transfer_rate=0.1,
                 epsilon_greedy_rate=0.9,
                 epsilon_decay=1.0,
                 epsilon_min=0.01,
                 replay_memory=None,
                 timestep=1):

        self.batch_size = batch_size
        self.gamma = discount_factor
        self.tau = knowledge_transfer_rate
        self.epsilon = epsilon_greedy_rate
        self.epsilon_decay = epsilon_decay
        self.epsilon_min = epsilon_min
        self.replay_memory = (replay_memory if isinstance(replay_memory, Experience)

# Path: brainforge/reinforcement/experience.py
# def replay_memory_factory(limit, mode, time, downsample=0):
#     if time > 1:
#         return TimeExperience(time, limit, downsample)
#     else:
#         return Experience(limit, mode, downsample)
# 
# class Experience:
# 
#     def __init__(self, limit=40000, mode="drop", downsample=0):
#         self.limit = limit
#         self.X = []
#         self.Y = []
#         self._adder = {"drop": self._add_drop, "mix in": self._mix_in}[mode]
#         self.downsample = downsample
# 
#     @property
#     def N(self):
#         return len(self.X)
# 
#     def initialize(self, X, Y):
#         self.X = X
#         self.Y = Y
# 
#     def _mix_in(self, X, Y):
#         m = len(X)
#         N = self.N
#         narg = np.arange(N)
#         np.random.shuffle(narg)
#         marg = narg[:m]
#         self.X[marg] = X
#         self.Y[marg] = Y
# 
#     def _add_drop(self, X, Y):
#         m = len(X)
#         self.X = np.concatenate((self.X[m:], X))
#         self.Y = np.concatenate((self.Y[m:], Y))
# 
#     def _add(self, X, Y):
#         self.X = np.concatenate((self.X, X))
#         self.Y = np.concatenate((self.Y, Y))
#         self.X = self.X[-self.limit:]
#         self.Y = self.Y[-self.limit:]
# 
#     def _incorporate(self, X, Y):
#         if self.N < self.limit:
#             self._add(X, Y)
#         self._adder(X, Y)
# 
#     def remember(self, X, Y):
#         if self.downsample > 1:
#             X, Y = X[::self.downsample], Y[::self.downsample]
#         assert len(X) == len(Y)
#         if len(self.X) < 1:
#             self.initialize(X, Y)
#         else:
#             self._incorporate(X, Y)
# 
#     def replay(self, batch_size):
#         narg = np.arange(self.N)
#         np.random.shuffle(narg)
#         batch_args = narg[:batch_size]
#         if len(batch_args) == 0:
#             return [], []
#         return self.X[batch_args], self.Y[batch_args]


# Path: brainforge/reinforcement/agentconfig.py
from .experience import replay_memory_factory, Experience



class AgentConfig:

    def __init__(self, batch_size=128,
                 discount_factor=0.99,
                 knowledge_transfer_rate=0.1,
                 epsilon_greedy_rate=0.9,
                 epsilon_decay=1.0,
                 epsilon_min=0.01,
                 replay_memory=None,
                 timestep=1):

        self.batch_size = batch_size
        self.gamma = discount_factor
        self.tau = knowledge_transfer_rate
        self.epsilon = epsilon_greedy_rate
        self.epsilon_decay = epsilon_decay
        self.epsilon_min = epsilon_min
        self.replay_memory = (replay_memory if isinstance(replay_memory, Experience)

                              else replay_memory_factory(replay_memory, mode="drop", time=timestep))