Esempio n. 1
0
def transform(n_images,
              n_features,
              n_components=0,
              disable_pbar=False,
              linear=False,
              config_file=""):
    opu = OPU(disable_pbar=disable_pbar,
              open_at_init=False,
              config_file=config_file)
    if n_components != 0:
        opu.n_components = n_components
    ins = np.ones((n_images, n_features), dtype=np.uint8)

    with opu:
        print(opu.version())
        begin = time.time()
        opu.fit1d(ins)
        if linear:
            opu.linear_transform(ins)
        else:
            opu.transform(ins)
        elapsed = time.time() - begin
        print(
            f"{n_images} transforms in {elapsed:.2f} s ({n_images / elapsed:.2f} Hz)"
        )
Esempio n. 2
0
class OPUMap(nn.Module):
    """Adapter of the OPU to the Pytorch interface.
    Forward method is mapped to `transform <lightonml.opu.OPU.transform>` of
    the `OPU <lightonml.opu.OPU>` class, depending on `ndims` parameter at the construction.

    .. seealso:: `lightonml.opu.OPU`

    Parameters
    ----------
    n_components: int,
        dimensionality of the target projection space.
    opu : lightonml.opu.OPU,
        optical processing unit instance
    ndims : int,
        number of dimensions of an input. Can be 1, 2 or 3.
        if ndims is 1, transform accepts 1d vector or batch of 1d vectors.
        if ndims is 2, transform accepts 2d vector or batch of 2d vectors.
    packed: bool, optional
        whether the input data is in bit-packed representation
        if packed is True and ndims is 2, each input vector is assumed
        to be a 1d array, and the "real" number of features must be provided
        using n_2d_features parameter
        defaults to False
    n_2d_features: list(int) or tuple(int) or np.ndarray (optional)
        number of 2d features if the input is packed
    simulated: bool, default False,
        use real or simulated OPU
    max_n_features: int, optional
        maximum number of binary features that the OPU will transform
        used only if simulated=True, in order to initiate the random matrix
    verbose_level: int, optional
        Levels are 0: nothing, 1: print info, 2: debug info, 3: trace info
        deprecated, use lightonml.set_verbose_level instead

    Attributes
    ----------
    opu : lightonml.opu.OPU,
        optical processing unit instance
    n_components : int,
        dimensionality of the target projection space.
    ndims : int,
        number of dimensions of an input. Can be 1, 2 or 3.
        if ndims is 1, transform accepts 1d vector or batch of 1d vectors.
        if ndims is 2, transform accepts 2d vector or batch of 2d vectors.
    packed: bool, optional
        whether the input data is in bit-packed representation
        if packed is True and ndims is 2, each input vector is assumed
        to be a 1d array, and the "real" number of features must be provided
        using n_2d_features parameter
        defaults to False
    n_2d_features: list(int) or tuple(int) or np.ndarray (optional)
        number of 2d features if the input is packed
    simulated: bool, default False,
        use real or simulated OPU
    max_n_features: int, optional
        maximum number of binary features that the OPU will transform
        used only if simulated=True, in order to initiate the random matrix
    fitted: bool
        if the OPU parameters have already been chosen.
    """
    def __init__(self, n_components, opu=None, ndims=1, n_2d_features=None, packed=False,
                 simulated=False, max_n_features=None, verbose_level=-1):
        if verbose_level >= 0:
            lightonml.set_verbose_level(verbose_level)
        self.verbose_level = lightonml.get_verbose_level()
        super(OPUMap, self).__init__()
        if opu is None:
            if simulated:
                simulated_opu = SimulatedOpuDevice()
                if max_n_features is None:
                    raise ValueError("When using simulated=True, you need to provide max_n_features.")
                self.opu = OPU(opu_device=simulated_opu, max_n_features=max_n_features,
                               n_components=n_components)
            else:
                self.opu = OPU(n_components=n_components)
        else:
            self.opu = opu
            self.opu.n_components = n_components
            if simulated and not isinstance(opu.device, SimulatedOpuDevice):
                warnings.warn("You provided a real OPU object but set simulated=True."
                              " Will use the real OPU.")
            if isinstance(opu.device, SimulatedOpuDevice) and not simulated:
                warnings.warn("You provided a simulated OPU object but set simulated=False. "
                              "Will use simulated OPU.")
        self.n_components = self.opu.n_components
        if ndims not in [1, 2]:
            raise ValueError("Number of input dimensions must be 1 or 2")
        self.ndims = ndims
        self.n_2d_features = n_2d_features
        self.packed = packed
        self.simulated = simulated
        self.max_n_features = max_n_features

        self.fitted = False
        self.online = False
        if lightonml.get_verbose_level() >= 1:
            print("OPU output is detached from the computational graph.")

    @property
    def n_components(self):
        return self.opu.n_components

    @n_components.setter
    def n_components(self, value):
        self.opu.n_components = value

    def forward(self, input):
        """Performs the nonlinear random projections.

        .. seealso:: `lightonml.opu.OPU.transform`
        """
        if not self.fitted:
            print("OPUMap was not fit to data. Performing fit on the first batch with default parameters...")
            self.fit(input)

        if self.online:
            output = torch.empty((len(input), self.n_components), dtype=torch.uint8)
            for i in range(len(input)):
                output[i] = self.opu.transform(input[i])
            return output.detach()
        else:
            output = self.opu.transform(input)
        return output.detach()

    def reset_parameters(self, input, y, n_features, packed, online):
        if online:
            self.online = True
        if self.ndims == 1:
            self.opu.fit1d(input, n_features=n_features, packed=packed, online=self.online)
        elif self.ndims == 2:
            self.opu.fit2d(input, n_features=n_features, packed=packed, online=self.online)
        else:
            assert False, "OPUMap.ndims={}; expected 1 or 2.".format(self.ndims)
        self.fitted = True
        return

    def fit(self, X=None, y=None, n_features=None, packed=False, online=False):
        """Configure OPU transform for 1d or 2d vectors

        The function can be either called with input vector, for fitting OPU
        parameters to it, or just vector dimensions, with `n_features`.

        When input is bit-packed the packed flag must be set to True.

        When input vectors must be transformed one by one, performance will
        be improved with the online flag set to True.

        Parameters
        ----------
        X: np.ndarray or torch.Tensor, optional,
            Fit will be made on this vector to optimize transform parameters
        y: np.ndarray or torch.Tensor, optional,
            For consistence with Sklearn API.
        n_features: int or tuple(int)
            Number of features for the input, necessary if X parameter isn't provided
        packed: bool
            Set to true if the input vectors will be already bit-packed
        online: bool, optional
            Set to true if the transforms will be made one vector after the other
            defaults to False

            .. seealso:: `lightonml.opu.OPU.fit1d`
            .. seealso:: `lightonml.opu.OPU.fit2d`
        """
        return self.reset_parameters(X, y, n_features, packed, online)

    def extra_repr(self):
        return 'out_features={}, n_dims={}, packed={} simulated={}'.format(
            self.n_components, self.n_dims, self.packed, self.simulated
        )

    def open(self):
        self.opu.open()

    def close(self):
        self.opu.close()

    def __enter__(self):
        self.open()
        return self

    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.close()
Esempio n. 3
0
def opu_tutorial(opu: OPU):
    # Rule is that OPU input is binary, so input vector elements should be 0 or 1
    # To get your input as binary, see the encoders from lightonml package.

    # The tutorial makes use of NumPy arrays, but you can use PyTorch tensors as input.
    # The output then is a tensor as well.

    # The dimension of output is governed by the n_components attribute:
    print("Number of components:", opu.n_components)

    # Simplest input is a batch of N 1d vectors, of arbitrary size
    inp_1d = random_bin((2000, 1000))
    out1 = opu.fit_transform1d(inp_1d)
    print("1D transform out shape", out1.shape)  # (2000, opu.n_components)

    # In this example we fit the OPU with the input and then transform or linear_transform,
    # but if we have several batches to be transformed, we must call fit first
    # on one the batches, and then transform:
    inp_1d_b = random_bin((2000, 1000))
    opu.fit1d(inp_1d)
    out1 = opu.transform(inp_1d)
    out1_b = opu.transform(inp_1d_b)
    print("transform of two separate batches", out1.shape, out1_b.shape)

    # For a linear transform, use OPU.linear_transform with the same parameters:
    out1_l = opu.linear_transform(inp_1d)
    print("linear_transform of a batch", out1.shape, out1_l.shape)

    # You can also run fit with the number of features, instead:
    opu.fit1d(n_features=inp_1d.shape[1])
    out1_alt = opu.transform(inp_1d)
    print("transform 1d", out1_alt.shape)

    # But you can transform a single vector if you wish
    single = opu.fit_transform1d(inp_1d[0])
    print("single transform out shape", single.shape)  # n_components

    # However if you have many single vectors, you will need either to use the
    # online mode (see below), or have them transformed in a single batch,
    # you will gain a lot of performance

    # if you have 2d vectors, you'll benefit from the fact that OPU input is
    # physically in 2d, so don't reshape them! Instead, use fit_transform2d:
    inp_2d = random_bin((2000, 13, 10))  # 200 vectors of shape 13x10
    out2 = opu.fit_transform2d(inp_2d)
    print("2D transform out shape", out2.shape)  # (2000, opu.n_components)

    # If you're batch is 2D-shaped, transform will recognize it and return
    # an output of the same shape:
    inp_3d = random_bin((300, 100, 3000))
    # call 1D transform on a 3D vector
    out_3d = opu.fit_transform1d(inp_3d)  # batch of 300x100 1D vectors
    print("3D transform out shape", out_3d.shape)  # (300, 100, opu.n_components)

    # You can also have bit-packed input, which will optimize memory-space,
    # as well as being a bit more efficient
    inp_1d_p = np.packbits(inp_1d, axis=1)
    print("1D packed transform in shape", inp_1d_p.shape)  # (2000, 1000/8)
    out1_p = opu.fit_transform1d(inp_1d_p, packed=True)
    print("1D packed transform out shape", out1_p.shape)  # (2000, opu.n_components)

    # If your input is 2d AND bit-packed, you must tell what shape it is
    inp_2d_p = np.packbits(inp_2d.reshape(2000, -1), axis=1)
    print("1D packed transform in shape", inp_2d_p.shape)  # (2000, 1300/8)
    out2_p = opu.fit_transform2d(inp_2d_p, packed=True, n_2d_features=(13, 10))
    print("2D packed transform out shape", out2_p.shape)  # (2000, opu.n_components)

    # Input is formatted to match OPU's input device, but you can pass directly
    # formatted input of the correct input size (should it be 1d or 2d)
    input_shape = tuple(opu.device.input_shape)
    single_raw_2d = random_bin(input_shape)
    single_raw_1d = np.reshape(single_raw_2d, -1)
    out_raw_1d = opu.fit_transform1d(single_raw_1d)
    print("1D raw out shape", out_raw_1d.shape)  # (2000, opu.n_components)
    out_raw_2d = opu.fit_transform2d(single_raw_2d)
    print("1D raw out shape", out_raw_2d.shape)  # (2000, opu.n_components)
    # Of course, the equivalent for packed input of input device's size
    # will work the same way

    # It can also be a batch of them
    many_raw_2d = random_bin((100, ) + input_shape)
    out_many_raw_2d = opu.fit_transform2d(many_raw_2d)
    print("Many raw out shape", out_many_raw_2d.shape)   # (100, opu.n_components)

    # The online mode allows you to run accelerate the run of single vectors:
    n_features1d = 1200
    opu.fit1d(n_features=n_features1d, online=True)
    for _ in range(10):
        online_out = opu.transform(random_bin(n_features1d))
    print("Online out shape", online_out.shape)

    n_features2d = (50, 50)
    opu.fit2d(n_features=n_features2d, online=True)
    for _ in range(10):
        online_out = opu.transform(random_bin(n_features2d))
    print("Online out shape", online_out.shape)
Esempio n. 4
0
class OPUMap(BaseEstimator, TransformerMixin):
    """Adapter of the OPU to scikit-learn.
    Transform method is mapped to `transform <lightonml.opu.OPU.transform>` of
    the `OPU <lightonml.opu.OPU>` class, depending on `ndims` parameter
    at the construction.

    .. seealso:: `lightonml.opu.OPU`

    Parameters
    ----------
    n_components: int,
        dimensionality of the target projection space.
    opu : lightonml.opu.OPU,
        optical processing unit instance
    ndims : int,
        number of dimensions of an input. Can be 1 or 2.
        if ndims is 1, transform accepts 1d vector or batch of 1d vectors.
        if ndims is 2, transform accepts 2d vector or batch of 2d vectors.
    packed: bool, optional
        whether the input data is in bit-packed representation
        if packed is True and ndims is 2, each input vector is assumed
        to be a 1d array, and the "real" number of features must be provided
        using n_2d_features parameter
        defaults to False
    n_2d_features: list(int) or tuple(int) or np.ndarray (optional)
        number of 2d features if the input is packed
    simulated: bool, default False,
        use real or simulated OPU
    max_n_features: int, optional
        maximum number of binary features that the OPU will transform
        used only if simulated=True, in order to initiate the random matrix
    verbose_level: int, optional
        Levels are 0: nothing, 1: print info, 2: debug info, 3: trace info
        deprecated, use lightonml.set_verbose_level instead

    Attributes
    ----------
    opu : lightonml.opu.OPU,
        optical processing unit instance
    n_components : int,
        dimensionality of the target projection space.
    ndims : int,
        number of dimensions of an input. Can be 1 or 2.
        if ndims is 1, transform accepts 1d vector or batch of 1d vectors.
        if ndims is 2, transform accepts 2d vector or batch of 2d vectors.
    packed: bool, optional
        whether the input data is in bit-packed representation
        if packed is True and ndims is 2, each input vector is assumed
        to be a 1d array, and the "real" number of features must be provided
        using n_2d_features parameter
        defaults to False
    n_2d_features: list(int) or tuple(int) or np.ndarray (optional)
        number of 2d features if the input is packed
    simulated: bool, default False,
        use real or simulated OPU
    max_n_features: int, optional
        maximum number of binary features that the OPU will transform
        used only if simulated=True, in order to initiate the random matrix
    """
    def __init__(self,
                 n_components,
                 opu=None,
                 ndims=1,
                 n_2d_features=None,
                 packed=False,
                 simulated=False,
                 max_n_features=None,
                 verbose_level=-1):
        # verbose_level shouldn't be used anymore, but put it as attributes
        # in order to comply with sklearn estimator
        if verbose_level >= 0:
            lightonml.set_verbose_level(verbose_level)
        self.verbose_level = lightonml.get_verbose_level()

        if opu is None:
            if simulated:
                simulated_opu_device = SimulatedOpuDevice()
                if max_n_features is None:
                    raise ValueError(
                        "When using simulated=True, you need to provide max_n_features."
                    )
                self.opu = OPU(opu_device=simulated_opu_device,
                               max_n_features=max_n_features,
                               n_components=n_components)
            else:
                self.opu = OPU(n_components=n_components)
        else:
            self.opu = opu
            self.opu.n_components = n_components
            if simulated and not isinstance(opu.device, SimulatedOpuDevice):
                warnings.warn(
                    "You provided a real OPU object but set simulated=True."
                    " Will use the real OPU.")
            if isinstance(opu.device, SimulatedOpuDevice) and not simulated:
                warnings.warn(
                    "You provided a simulated OPU object but set simulated=False."
                    " Will use simulated OPU.")

        if ndims not in [1, 2]:
            raise ValueError("Number of input dimensions must be 1 or 2")
        self.ndims = ndims
        self.n_2d_features = n_2d_features
        self.packed = packed
        self.simulated = simulated
        self.max_n_features = max_n_features
        self.fitted = False

    @property
    def n_components(self):
        return self.opu.n_components

    @n_components.setter
    def n_components(self, value):
        self.opu.n_components = value

    def fit(self, X=None, y=None, n_features=None, packed=False, online=False):
        """
        Configure OPU transform for 1d or 2d vectors

        The function can be either called with input vector, for fitting OPU
        parameters to it, or just vector dimensions, with `n_features`.

        When input is bit-packed the packed flag must be set to True.

        When input vectors must be transformed one by one, performance will
        be improved with the online flag set to True.

        Parameters
        ----------
        X: np.ndarray,
            Fit will be made on this vector to optimize transform parameters
        y: np.ndarray,
            For sklearn interface compatibility
        n_features: int or tuple(int),
            Number of features for the input, necessary if X parameter isn't provided
        packed: bool, optional
            Set to true if the input vectors will be already bit-packed
            defaults to False
        online: bool, optional
            Set to true if the transforms will be made one vector after the other
            defaults to False

        Returns
        -------
        self
        """
        if self.ndims == 1:
            self.opu.fit1d(X,
                           n_features=n_features,
                           packed=packed,
                           online=online)
        elif self.ndims == 2:
            self.opu.fit2d(X,
                           n_features=n_features,
                           packed=packed,
                           online=online)
        else:
            assert False, "SklearnOPU.ndims={}; expected 1 or 2.".format(
                self.ndims)
        self.fitted = True
        return self

    def transform(self, X, y=None):
        """Performs the nonlinear random projections.

            .. seealso:: `lightonml.opu.OPU.transform`
        """
        if self.opu.n_components != self.n_components:
            self.opu.n_components = self.n_components
        if not self.fitted:
            print(
                "OPUMap was not fit to data. Performing fit on the input with default parameters..."
            )
            self.fit(X)
        return np.array(self.opu.transform(X))

    def open(self):
        self.opu.open()

    def close(self):
        self.opu.close()

    def __enter__(self):
        self.open()
        return self

    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.close()