# Path: deeppy/base.py
# class Model(ParamMixin):
#     def setup(self, *array_shapes):
#         pass
# 
#     def update(self, *arrays):
#         raise NotImplementedError()
# 
# class CollectionMixin(ParamMixin, PhaseMixin):
#     collection = []
# 
#     @property
#     def params(self):
#         params = []
#         for obj in self.collection:
#             if isinstance(obj, ParamMixin):
#                 params.extend(obj.params)
#         return params
# 
#     @params.setter
#     def params(self, params):
#         idx = 0
#         for obj in self.collection:
#             if isinstance(obj, ParamMixin):
#                 n_params = len(obj.params)
#                 obj.params = params[idx:idx+n_params]
#                 idx += n_params
# 
#     @property
#     def phase(self):
#         return self._phase
# 
#     @phase.setter
#     def phase(self, phase):
#         if self._phase == phase:
#             return
#         self._phase = phase
#         for obj in self.collection:
#             if isinstance(obj, PhaseMixin):
#                 obj.phase = phase
# 
# class ParamMixin(object):
#     @property
#     def params(self):
#         """ List of Parameter objects. """
#         raise NotImplementedError()
# 
#     @params.setter
#     def params(self, params):
#         raise NotImplementedError()
# 
# Path: deeppy/feed.py
# class Feed(object):
#     def __init__(self, x, batch_size=None, epoch_size=None):
#         self.x = x
#         self.n_samples = x.shape[0]
#         if batch_size is None:
#             batch_size = 128 if self.n_samples > 512 else self.n_samples
#         if epoch_size is None:
#             epoch_size = int(np.ceil(float(self.n_samples) / batch_size))
#         self.epoch_size = epoch_size
#         self.batch_size = batch_size
#         self.epoch_idx = 0
# 
#     @classmethod
#     def from_any(cls, arg):
#         if isinstance(arg, Feed):
#             arg.reset()
#             return arg
#         elif isinstance(arg, np.ndarray):
#             return cls(arg)
#         elif isinstance(arg, ca.ndarray):
#             return cls(np.array(arg))
#         elif isinstance(arg, tuple):
#             return SupervisedFeed(arg[0], arg[1])
#         raise ValueError('Invalid arguments.')
# 
#     def reset(self):
#         self.epoch_idx = 0
# 
#     def _batch_slices(self):
#         for b in range(self.epoch_size):
#             start = (self.epoch_idx * self.epoch_size + b) * self.batch_size
#             start = start % self.n_samples
#             stop = (start + self.batch_size) % self.n_samples
#             yield start, stop
#         self.epoch_idx += 1
# 
#     def batches(self):
#         x = ca.empty(self.x_shape, dtype=self.x.dtype)
#         for start, stop in self._batch_slices():
#             if stop > start:
#                 x_np = self.x[start:stop]
#             else:
#                 x_np = np.concatenate((self.x[start:], self.x[:stop]))
#             ca.copyto(x, x_np)
#             yield x,
# 
#     @property
#     def x_shape(self):
#         return (self.batch_size,) + self.x.shape[1:]
# 
#     @property
#     def shapes(self):
#         return self.x_shape,


from copy import copy
from ..base import Model, CollectionMixin, ParamMixin
from ..feed import Feed
import numpy as np


class SiameseNetwork(Model, CollectionMixin):
    def __init__(self, siamese_layers, loss):
        self.layers = siamese_layers
        self.loss = loss
        # Create second array of layers
        self.layers2 = [copy(layer) for layer in self.layers]
        for layer1, layer2 in zip(self.layers, self.layers2):
            if isinstance(layer1, ParamMixin):
                # Replace weights in layers2 with shared weights
                layer2.params = [p.share() for p in layer1.params]
        self.bprop_until = next((idx for idx, l in enumerate(self.layers)

# Path: deeppy/base.py
# class Model(ParamMixin):
#     def setup(self, *array_shapes):
#         pass
# 
#     def update(self, *arrays):
#         raise NotImplementedError()
# 
# class CollectionMixin(ParamMixin, PhaseMixin):
#     collection = []
# 
#     @property
#     def params(self):
#         params = []
#         for obj in self.collection:
#             if isinstance(obj, ParamMixin):
#                 params.extend(obj.params)
#         return params
# 
#     @params.setter
#     def params(self, params):
#         idx = 0
#         for obj in self.collection:
#             if isinstance(obj, ParamMixin):
#                 n_params = len(obj.params)
#                 obj.params = params[idx:idx+n_params]
#                 idx += n_params
# 
#     @property
#     def phase(self):
#         return self._phase
# 
#     @phase.setter
#     def phase(self, phase):
#         if self._phase == phase:
#             return
#         self._phase = phase
#         for obj in self.collection:
#             if isinstance(obj, PhaseMixin):
#                 obj.phase = phase
# 
# class ParamMixin(object):
#     @property
#     def params(self):
#         """ List of Parameter objects. """
#         raise NotImplementedError()
# 
#     @params.setter
#     def params(self, params):
#         raise NotImplementedError()
# 
# Path: deeppy/feed.py
# class Feed(object):
#     def __init__(self, x, batch_size=None, epoch_size=None):
#         self.x = x
#         self.n_samples = x.shape[0]
#         if batch_size is None:
#             batch_size = 128 if self.n_samples > 512 else self.n_samples
#         if epoch_size is None:
#             epoch_size = int(np.ceil(float(self.n_samples) / batch_size))
#         self.epoch_size = epoch_size
#         self.batch_size = batch_size
#         self.epoch_idx = 0
# 
#     @classmethod
#     def from_any(cls, arg):
#         if isinstance(arg, Feed):
#             arg.reset()
#             return arg
#         elif isinstance(arg, np.ndarray):
#             return cls(arg)
#         elif isinstance(arg, ca.ndarray):
#             return cls(np.array(arg))
#         elif isinstance(arg, tuple):
#             return SupervisedFeed(arg[0], arg[1])
#         raise ValueError('Invalid arguments.')
# 
#     def reset(self):
#         self.epoch_idx = 0
# 
#     def _batch_slices(self):
#         for b in range(self.epoch_size):
#             start = (self.epoch_idx * self.epoch_size + b) * self.batch_size
#             start = start % self.n_samples
#             stop = (start + self.batch_size) % self.n_samples
#             yield start, stop
#         self.epoch_idx += 1
# 
#     def batches(self):
#         x = ca.empty(self.x_shape, dtype=self.x.dtype)
#         for start, stop in self._batch_slices():
#             if stop > start:
#                 x_np = self.x[start:stop]
#             else:
#                 x_np = np.concatenate((self.x[start:], self.x[:stop]))
#             ca.copyto(x, x_np)
#             yield x,
# 
#     @property
#     def x_shape(self):
#         return (self.batch_size,) + self.x.shape[1:]
# 
#     @property
#     def shapes(self):
#         return self.x_shape,


# Path: deeppy/siamese/siamese_network.py
from copy import copy
from ..base import Model, CollectionMixin, ParamMixin
from ..feed import Feed
import numpy as np



class SiameseNetwork(Model, CollectionMixin):
    def __init__(self, siamese_layers, loss):
        self.layers = siamese_layers
        self.loss = loss
        # Create second array of layers
        self.layers2 = [copy(layer) for layer in self.layers]
        for layer1, layer2 in zip(self.layers, self.layers2):
            if isinstance(layer1, ParamMixin):
                # Replace weights in layers2 with shared weights
                layer2.params = [p.share() for p in layer1.params]
        self.bprop_until = next((idx for idx, l in enumerate(self.layers)

                                 if isinstance(l, ParamMixin)), 0)