def __init__(self,
                 n_neurons,
                 dimensions,
                 invert_a=False,
                 invert_b=False,
                 radius=1,
                 encoders=nengo.Default,
                 **ens_kwargs):
        self.dimensions = dimensions
        self.invert_a = invert_a
        self.invert_b = invert_b

        self.A = nengo.Node(size_in=dimensions, label="A")
        self.B = nengo.Node(size_in=dimensions, label="B")
        self.product = Product(n_neurons,
                               self.transform_out.shape[1],
                               radius=radius,
                               encoders=encoders,
                               label="conv",
                               **ens_kwargs)
        self.output = nengo.Node(size_in=dimensions, label="output")

        nengo.Connection(self.A,
                         self.product.A,
                         transform=self.transformA,
                         synapse=None)
        nengo.Connection(self.B,
                         self.product.B,
                         transform=self.transformB,
                         synapse=None)
        nengo.Connection(self.product.output,
                         self.output,
                         transform=self.transform_out,
                         synapse=None)
    def __init__(
        self,
        n_neurons,
        dimensions,
        invert_a=False,
        invert_b=False,
        input_magnitude=1.0,
        **kwargs
    ):
        super().__init__(**kwargs)

        tr_a = transform_in(dimensions, "A", invert_a)
        tr_b = transform_in(dimensions, "B", invert_b)
        tr_out = transform_out(dimensions)

        with self:
            self.input_a = nengo.Node(size_in=dimensions, label="input_a")
            self.input_b = nengo.Node(size_in=dimensions, label="input_b")
            self.product = Product(
                n_neurons,
                tr_out.shape[1],
                input_magnitude=2 * input_magnitude / np.sqrt(2.0),
            )
            self.output = nengo.Node(size_in=dimensions, label="output")

            nengo.Connection(
                self.input_a, self.product.input_a, transform=tr_a, synapse=None
            )
            nengo.Connection(
                self.input_b, self.product.input_b, transform=tr_b, synapse=None
            )
            nengo.Connection(
                self.product.output, self.output, transform=tr_out, synapse=None
            )
Beispiel #3
0
    def __init__(self, neurons, dimensions, invert_a=False, invert_b=False):
        self.dimensions = dimensions
        self.invert_a = invert_a
        self.invert_b = invert_b

        self.A = nengo.Node(size_in=dimensions, label="A")
        self.B = nengo.Node(size_in=dimensions, label="B")
        self.product = Product(
            neurons, self.transform_out.shape[1], label="conv")
        self.output = nengo.Node(size_in=dimensions, label="output")

        nengo.Connection(self.A, self.product.A,
                         transform=self.transformA, synapse=None)
        nengo.Connection(self.B, self.product.B,
                         transform=self.transformB, synapse=None)
        nengo.Connection(
            self.product.output, self.output, transform=self.transform_out,
            synapse=None)
Beispiel #4
0
    def __init__(
            self,
            n_neurons,
            dimensions,
            invert_a=False,
            invert_b=False,
            input_magnitude=1.0,
            # fmt: off
            **kwargs
        # fmt: on
    ):
        if "net" in kwargs:
            raise ObsoleteError("The 'net' argument is no longer supported.")
        kwargs.setdefault("label", "Circular convolution")
        super().__init__(**kwargs)

        tr_a = transform_in(dimensions, "A", invert_a)
        tr_b = transform_in(dimensions, "B", invert_b)
        tr_out = transform_out(dimensions)

        with self:
            self.input_a = nengo.Node(size_in=dimensions, label="input_a")
            self.input_b = nengo.Node(size_in=dimensions, label="input_b")
            self.product = Product(n_neurons,
                                   tr_out.shape[1],
                                   input_magnitude=input_magnitude * 2)
            self.output = nengo.Node(size_in=dimensions, label="output")

            nengo.Connection(self.input_a,
                             self.product.input_a,
                             transform=tr_a,
                             synapse=None)
            nengo.Connection(self.input_b,
                             self.product.input_b,
                             transform=tr_b,
                             synapse=None)
            nengo.Connection(self.product.output,
                             self.output,
                             transform=tr_out,
                             synapse=None)
Beispiel #5
0
def CircularConvolution(n_neurons, dimensions, invert_a=False, invert_b=False,
                        input_magnitude=1.0, net=None):
    """Compute the circular convolution of two vectors.

    The circular convolution :math:`c` of vectors :math:`a` and :math:`b`
    is given by

    .. math:: c[i] = \sum_j a[j] b[i - j]

    where negative indices on :math:`b` wrap around to the end of the vector.

    This computation can also be done in the Fourier domain,

    .. math:: c = DFT^{-1} ( DFT(a) DFT(b) )

    where :math:`DFT` is the Discrete Fourier Transform operator, and
    :math:`DFT^{-1}` is its inverse. This network uses this method.

    Parameters
    ----------
    n_neurons : int
        Number of neurons to use in each product computation
    dimensions : int
        The number of dimensions of the input and output vectors.

    invert_a, invert_b : bool, optional (Default: False, False)
        Whether to reverse the order of elements in either
        the first input (``invert_a``) or the second input (``invert_b``).
        Flipping the second input will make the network perform circular
        correlation instead of circular convolution.
    input_magnitude : float, optional (Default: 1.0)
        The expected magnitude of the vectors to be convolved.
        This value is used to determine the radius of the ensembles
        computing the element-wise product.
    net : Network, optional (Default: None)
        A network in which the network components will be built.
        This is typically used to provide a custom set of Nengo object
        defaults through modifying ``net.config``.

    Returns
    -------
    net : Network
        The newly built product network, or the provided ``net``.

    Attributes
    ----------
    net.A : Node
        The first vector to be convolved.
    net.B : Node
        The second vector to be convolved.
    net.product : Network
        Network created with `.Product` to do the element-wise product
        of the :math:`DFT` components.
    net.output : Node
        The resulting convolved vector.

    Examples
    --------

    A basic example computing the circular convolution of two 10-dimensional
    vectors represented by ensemble arrays::

        A = EnsembleArray(50, n_ensembles=10)
        B = EnsembleArray(50, n_ensembles=10)
        C = EnsembleArray(50, n_ensembles=10)
        cconv = nengo.networks.CircularConvolution(50, dimensions=10)
        nengo.Connection(A.output, cconv.A)
        nengo.Connection(B.output, cconv.B)
        nengo.Connection(cconv.output, C.input)

    Notes
    -----

    The network maps the input vectors :math:`a` and :math:`b` of length N into
    the Fourier domain and aligns them for complex multiplication.
    Letting :math:`F = DFT(a)` and :math:`G = DFT(b)`, this is given by::

        [ F[i].real ]     [ G[i].real ]     [ w[i] ]
        [ F[i].imag ]  *  [ G[i].imag ]  =  [ x[i] ]
        [ F[i].real ]     [ G[i].imag ]     [ y[i] ]
        [ F[i].imag ]     [ G[i].real ]     [ z[i] ]

    where :math:`i` only ranges over the lower half of the spectrum, since
    the upper half of the spectrum is the flipped complex conjugate of
    the lower half, and therefore redundant. The input transforms are
    used to perform the DFT on the inputs and align them correctly for
    complex multiplication.

    The complex product :math:`H = F * G` is then

    .. math:: H[i] = (w[i] - x[i]) + (y[i] + z[i]) I

    where :math:`I = \sqrt{-1}`. We can perform this addition along with the
    inverse DFT :math:`c = DFT^{-1}(H)` in a single output transform, finding
    only the real part of :math:`c` since the imaginary part
    is analytically zero.
    """
    if net is None:
        net = nengo.Network("Circular Convolution")

    tr_a = transform_in(dimensions, 'A', invert_a)
    tr_b = transform_in(dimensions, 'B', invert_b)
    tr_out = transform_out(dimensions)

    with net:
        net.A = nengo.Node(size_in=dimensions, label="A")
        net.B = nengo.Node(size_in=dimensions, label="B")
        net.product = Product(n_neurons, tr_out.shape[1],
                              input_magnitude=input_magnitude * 2)
        net.output = nengo.Node(size_in=dimensions, label="output")

        nengo.Connection(net.A, net.product.A, transform=tr_a, synapse=None)
        nengo.Connection(net.B, net.product.B, transform=tr_b, synapse=None)
        nengo.Connection(net.product.output, net.output,
                         transform=tr_out, synapse=None)

    return net
Beispiel #6
0
def CircularConvolution(n_neurons,
                        dimensions,
                        invert_a=False,
                        invert_b=False,
                        input_magnitude=1,
                        net=None):
    """Compute the circular convolution of two vectors.

    The circular convolution `c` of vectors `a` and `b` is given by

        c[i] = sum_j a[j] * b[i - j]

    where the indices on `b` are assumed to wrap around as required.

    This computation can also be done in the Fourier domain,

        c = DFT^{-1}( DFT(a) * DFT(b) )

    where `DFT` is the Discrete Fourier Transform operator, and
    `DFT^{-1}` is its inverse. This network uses this method.

    Parameters
    ----------
    n_neurons : int
        Number of neurons to be used, in total.
    dimensions : int
        The number of dimensions of the input and output vectors.
    invert_a, invert_b : bool
        Whether to reverse the order of elements in either
        the first input (`invert_a`) or the second input (`invert_b`).
        Flipping the second input will make the network perform circular
        correlation instead of circular convolution.
    input_magnitude : float
        The expected magnitude (vector norm) of the two input values.

    Examples
    --------

    >>> A = EnsembleArray(50, n_ensembles=10)
    >>> B = EnsembleArray(50, n_ensembles=10)
    >>> C = EnsembleArray(50, n_ensembles=10)
    >>> cconv = nengo.networks.CircularConvolution(50, dimensions=10)
    >>> nengo.Connection(A.output, cconv.A)
    >>> nengo.Connection(B.output, cconv.B)
    >>> nengo.Connection(cconv.output, C.input)

    Notes
    -----
    The network maps the input vectors `a` and `b` of length N into
    the Fourier domain and aligns them for complex multiplication.
    Letting `F = DFT(a)` and `G = DFT(b)`, this is given by:

        [ F[i].real ]     [ G[i].real ]     [ w[i] ]
        [ F[i].imag ]  *  [ G[i].imag ]  =  [ x[i] ]
        [ F[i].real ]     [ G[i].imag ]     [ y[i] ]
        [ F[i].imag ]     [ G[i].real ]     [ z[i] ]

    where `i` only ranges over the lower half of the spectrum, since
    the upper half of the spectrum is the flipped complex conjugate of
    the lower half, and therefore redundant. The input transforms are
    used to perform the DFT on the inputs and align them correctly for
    complex multiplication.

    The complex product `H = F * G` is then

        H[i] = (w[i] - x[i]) + (y[i] + z[i]) * I

    where `I = sqrt(-1)`. We can perform this addition along with the
    inverse DFT `c = DFT^{-1}(H)` in a single output transform, finding
    only the real part of `c` since the imaginary part is analytically zero.
    """
    if net is None:
        net = nengo.Network("Circular Convolution")

    tr_a = transform_in(dimensions, 'A', invert_a)
    tr_b = transform_in(dimensions, 'B', invert_b)
    tr_out = transform_out(dimensions)

    with net:
        net.A = nengo.Node(size_in=dimensions, label="A")
        net.B = nengo.Node(size_in=dimensions, label="B")
        net.product = Product(n_neurons,
                              tr_out.shape[1],
                              input_magnitude=input_magnitude * 2)
        net.output = nengo.Node(size_in=dimensions, label="output")

        nengo.Connection(net.A, net.product.A, transform=tr_a, synapse=None)
        nengo.Connection(net.B, net.product.B, transform=tr_b, synapse=None)
        nengo.Connection(net.product.output,
                         net.output,
                         transform=tr_out,
                         synapse=None)

    return net
def CircularConvolution(n_neurons,
                        dimensions,
                        invert_a=False,
                        invert_b=False,
                        input_magnitude=1.0,
                        **kwargs):
    r"""Compute the circular convolution of two vectors.

    The circular convolution :math:`c` of vectors :math:`a` and :math:`b`
    is given by

    .. math:: c[i] = \sum_j a[j] b[i - j]

    where negative indices on :math:`b` wrap around to the end of the vector.

    This computation can also be done in the Fourier domain,

    .. math:: c = DFT^{-1} ( DFT(a) \odot DFT(b) )

    where :math:`DFT` is the Discrete Fourier Transform operator, and
    :math:`DFT^{-1}` is its inverse. This network uses this method.

    Parameters
    ----------
    n_neurons : int
        Number of neurons to use in each product computation.
    dimensions : int
        The number of dimensions of the input and output vectors.
    invert_a : bool, optional
        Whether to reverse the order of elements in first input.
    invert_b : bool, optional
        Whether to reverse the order of elements in the second input.
        Flipping exactly one input will make the network perform circular
        correlation instead of circular convolution which can be treated as an
        approximate inverse to circular convolution.
    input_magnitude : float, optional
        The expected magnitude of the vectors to be convolved.
        This value is used to determine the radius of the ensembles
        computing the element-wise product.
    kwargs : dict
        Arguments to pass through to the `nengo.Network` constructor.

    Returns
    -------
    nengo.Network
        The newly built product network with attributes:

         * **input_a** (`nengo.Node`): The first vector to be convolved.
         * **input_b** (`nengo.Node`): The second vector to be convolved.
         * **product** (`nengo.networks.Product`): Network created to do the
           element-wise product of the :math:`DFT` components.
         * **output** (`nengo.Node`): The resulting convolved vector.

    Examples
    --------

    A basic example computing the circular convolution of two 10-dimensional
    vectors represented by ensemble arrays::

        A = EnsembleArray(50, n_ensembles=10)
        B = EnsembleArray(50, n_ensembles=10)
        C = EnsembleArray(50, n_ensembles=10)
        cconv = nengo_spa.networks.CircularConvolution(50, dimensions=10)
        nengo.Connection(A.output, cconv.input_a)
        nengo.Connection(B.output, cconv.input_b)
        nengo.Connection(cconv.output, C.input)

    Notes
    -----

    The network maps the input vectors :math:`a` and :math:`b` of length
    :math:`N` into the Fourier domain and aligns them for complex
    multiplication.
    Letting :math:`F = DFT(a)` and :math:`G = DFT(b)`, this is given by::

        [ F[i].real ]     [ G[i].real ]     [ w[i] ]
        [ F[i].imag ]  *  [ G[i].imag ]  =  [ x[i] ]
        [ F[i].real ]     [ G[i].imag ]     [ y[i] ]
        [ F[i].imag ]     [ G[i].real ]     [ z[i] ]

    where :math:`i` only ranges over the lower half of the spectrum, since
    the upper half of the spectrum is the flipped complex conjugate of
    the lower half, and therefore redundant. The input transforms are
    used to perform the DFT on the inputs and align them correctly for
    complex multiplication.

    The complex product :math:`H = F * G` is then

    .. math:: H[i] = (w[i] - x[i]) + (y[i] + z[i]) I

    where :math:`I = \sqrt{-1}`. We can perform this addition along with the
    inverse DFT :math:`c = DFT^{-1}(H)` in a single output transform, finding
    only the real part of :math:`c` since the imaginary part
    is analytically zero.
    """
    kwargs.setdefault('label', "CircularConvolution")

    tr_a = transform_in(dimensions, 'A', invert_a)
    tr_b = transform_in(dimensions, 'B', invert_b)
    tr_out = transform_out(dimensions)

    with nengo.Network(**kwargs) as net:
        net.input_a = nengo.Node(size_in=dimensions, label="input_a")
        net.input_b = nengo.Node(size_in=dimensions, label="input_b")
        net.product = Product(n_neurons,
                              tr_out.shape[1],
                              input_magnitude=2 * input_magnitude /
                              np.sqrt(2.))
        net.output = nengo.Node(size_in=dimensions, label="output")

        nengo.Connection(net.input_a,
                         net.product.input_a,
                         transform=tr_a,
                         synapse=None)
        nengo.Connection(net.input_b,
                         net.product.input_b,
                         transform=tr_b,
                         synapse=None)
        nengo.Connection(net.product.output,
                         net.output,
                         transform=tr_out,
                         synapse=None)

    return net