Ejemplo n.º 1
0
    def _raw_linear_transform(self, X, traits=None, user_input=None):
        """
        Do linear_transform of X, and return both raw OPU output and decoded output in a tuple
        """

        if traits is None:
            assert self._runner, "Call fit1d or fit2d before linear_transform"
            traits = self._runner.traits
        if user_input is None:
            user_input = OpuUserInput.from_traits(X, traits)

        if self._s.simulated:
            prepared_X = X
        else:
            assert self.device.acq_state.value != AcqState.online.value, \
                "Can't do linear transform when acquisition is" \
                " in online mode, only single vectors"
            assert self._runner.t.input_roi_strategy == InputRoiStrategy.full, \
                "ROI strategy must be full for linear_transform to be correct.\n" \
                "Set input_roi_strategy attribute to InputRoiStrategy.full."

            # X2 is now numpy 2D, whatever the initial shape and the type (torch or numpy)
            X2 = user_input.reshape_input(raveled_features=True,
                                          leave_single_dim=True)

            try:
                import lightonopu.linear_reconstruction as reconstruction
            except ImportError:
                raise RuntimeError(
                    "Need a lightonopu version with linear_reconstruction module"
                )

            start = time.time()
            prepared_X = reconstruction.encode_batch(X2)
            self._trace(f"Encoding time {time.time() - start} s")
            # Restore the dimension after batch encoding to something suitable for formatting
            prepared_X = user_input.unravel_features(prepared_X)
        # Run the OPU transform
        prepared_input = OpuUserInput.from_traits(prepared_X, traits)
        start = time.time()
        with self.device.acquiring(n_images=self._s.n_samples_by_pass):
            rp_opu = self._runner.transform(prepared_input, linear=True)
        self._trace(f"Transform time {time.time() - start} s")

        if self._s.simulated:
            result_ctx = rp_opu
        else:
            # Decoding forgets about the context, re-add it to result afterwards
            start = time.time()
            result = reconstruction.decode_batch(rp_opu)
            self._trace(f"Decoding time {time.time() - start} s")

            result_ctx = ContextArray(result, rp_opu.context)
        return rp_opu, result_ctx
Ejemplo n.º 2
0
    def __fit(self, X, n_features: IntOrTuple, packed: bool, online: bool,
              is_2d_features: bool, **override):
        """Internal working of the fitXd calls

        Instantiates a TransformRunner, and start online acq if needs be.
        """
        if X is not None:
            # Input is provided, do the fit with user input
            user_input = OpuUserInput.from_input(X, packed, is_2d_features,
                                                 n_features)
            tr_settings = self._tr_settings(no_input=False, **override)
            self._runner = FitTransformRunner(self._s,
                                              tr_settings,
                                              user_input,
                                              device=self.device,
                                              disable_pbar=self.disable_pbar)
        else:
            # Only dimensions are provided, no fitting happens on input
            assert n_features, "either input vector or n_features must be specified"
            # tr_settings has no input_roi, since it uses X to compute it
            tr_settings = self._tr_settings(no_input=True, **override)
            traits = InputTraits(n_features, packed)
            self._runner = TransformRunner(self._s,
                                           tr_settings,
                                           traits,
                                           device=self.device,
                                           disable_pbar=self.disable_pbar)

        self._acq_stack.close()
        if online:
            if self._s.no_single_transform:
                raise RuntimeError(
                    "Online transform isn't available with this OPU")
            # Start acquisition only if online. Batch transform start their own.
            self._acq_stack.enter_context(self.device.acquiring(online=True))
Ejemplo n.º 3
0
    def transform(self, input_vectors: OpuUserInput, linear=False):
        """Do the OPU transform of input
        If batch transform, device acquisition must be started
        """
        assert self.device.active and input_vectors.traits == self._traits
        if not self.s.simulated:
            input_vectors.binary_check()
        else:
            # In simulated mode, SimulatedDevice must whether the transform is linear or not
            self.device._linear = linear
        context = self._get_context()
        self._pre_print(self.device, input_vectors.n_samples)
        X = input_vectors.reshape_input()

        if input_vectors.is_batch:
            t0 = time.time()
            nb_retries = 0
            # Batch transform, allocate the result, progress bar, and start acquisition
            # allocation of empty vector for iteration
            n_samples = input_vectors.n_samples_s
            Y = np.empty((n_samples, self._out_size),
                         dtype=self.device.output_dtype)
            self._trace("Y allocated")
            indices = self.indices(input_vectors.n_samples_s)
            with Progress(n_samples, "OPU: transform", self.disable_pbar) as p:
                # iterate over consecutive pairs of indices
                # (https://stackoverflow.com/a/21303286)
                for i, (start, end) in enumerate(zip(indices, indices[1:])):
                    c = self.__batch_transform(X[start:end], Y[start:end], i)
                    p.update(end - start)
                    nb_retries += c
            t1 = time.time()
            # Fill context from opu settings at the end of the transform
            context.from_opu(self.device, dt.datetime.fromtimestamp(t0),
                             dt.datetime.fromtimestamp(t1))
        else:
            Y, nb_retries = self.__single_transform(X)
            context.from_opu(self.device, dt.datetime.now())

        if nb_retries:
            self._print("OPU number of retries: ", nb_retries)
        return ContextArray(Y, context)
Ejemplo n.º 4
0
    def linear_transform(self,
                         X,
                         encoder_cls=NoEncoding,
                         decoder_cls=NoDecoding) -> TransformOutput:
        """
        Do a linear transform of X, for Nitro (non-linear) photonic cores.

        Parameters
        ----------
        X:  np.ndarray or torch.Tensor
            input vector, or batch of input vectors.
            Each vector must have the same dimensions as the one given in `fit1d` or `fit2d`.
        encoder_cls: encoding.base.BaseTransformer, optional
            class or instance of class that transform the input into binary vectors to be processed by the opu.
        decoder_cls: encoding.base.BaseTransformer, optional
            class or instance of class that transforms the output of the opu back into the appropriate format.

        Returns
        -------
        Y: np.ndarray or torch.Tensor
             complete array of nonlinear random projections of X,
             of size self.n_components
             If input is an ndarray, type is actually ContextArray,
             with a context attribute to add metadata
        """
        assert self._runner, "Call fit1d or fit2d before linear_transform"
        traits = self._runner.traits

        if traits.packed:
            # TODO implement for packed
            raise RuntimeError(
                "Linear transform isn't yet implemented for packed input :/")

        if inspect.isclass(encoder_cls):
            encoder = encoder_cls()
        else:
            encoder = encoder_cls

        X_enc = encoder.transform(X)

        user_input = OpuUserInput.from_traits(X_enc, traits)
        _, result_ctx = self._raw_linear_transform(X_enc, traits, user_input)
        # Decoding, add context, and optional convert back to torch if needed
        output = self._post_transform(result_ctx, user_input, encoder,
                                      decoder_cls)
        # Rescale the output, intentionally after the decoding step
        if self.rescale is OutputRescaling.variance:
            n_features = user_input.n_features_s
            output = output / (self._s.stdev * sqrt(n_features))
        elif self.rescale is OutputRescaling.norm:
            output = output / (self._s.stdev * sqrt(self.n_components))
        return output
Ejemplo n.º 5
0
    def transform(self,
                  X,
                  encoder_cls=NoEncoding,
                  decoder_cls=NoDecoding) -> TransformOutput:
        """
        Performs the nonlinear random projections of one or several input vectors.

        The `fit1d` or `fit2d` method must be called before, for setting vector dimensions
        or online option.
        If you need to transform one vector after each other, add `online=True` in the fit function.

        Parameters
        ----------
        X:  np.ndarray or torch.Tensor
            input vector, or batch of input vectors.
            Each vector must have the same dimensions as the one given in `fit1d` or `fit2d`.
        encoder_cls: encoder.base.BaseTransformer, optional
            class or instance of class that transform the input into binary vectors to be processed by the opu.
        decoder_cls: encoder.base.BaseTransformer, optional
            class or instance of class that transforms the output of the opu back into the appropriate format.

        Returns
        -------
        Y: np.ndarray or torch.Tensor
             complete array of nonlinear random projections of X,
             of size self.n_components
             If input is an ndarray, type is actually ContextArray,
             with a context attribute to add metadata
        """
        assert self._runner, "Call fit1d or fit2d before transform"
        assert self.device.active, "OPU device isn't active, use opu.open() or \"with opu:\""

        if inspect.isclass(encoder_cls):
            encoder = encoder_cls()
        else:
            encoder = encoder_cls

        X_enc = encoder.transform(X)

        user_input = OpuUserInput.from_traits(X_enc, self._runner.traits)
        self._debug(str(user_input))

        if user_input.is_batch and not self._s.simulated:
            # With batch input start acquisition first
            assert self.device.acq_state.value != AcqState.online.value, \
                "Can't transform a batch of vectors when acquisition is" \
                " in online mode, only single vectors"
            with self.device.acquiring(n_images=self._s.n_samples_by_pass):
                out = self._runner.transform(user_input)
        else:
            out = self._runner.transform(user_input)
        return self._post_transform(out, user_input, encoder, decoder_cls)
Ejemplo n.º 6
0
 def roi_compute(self, user_input: OpuUserInput):
     # with automatic input roi, compute number of ones before
     # This case is met only when input is provided to runner creation
     # (i.e. opu.fit with an input)
     #
     # Count number of ones, and then compute ROI with "auto" strategy
     roi = InputRoi(self.s.input_shape, self.s.ones_range)
     n_ones = self._count_ones(user_input.reshape_input(), user_input.traits.packed)
     n_features_s = self._traits.n_features_s
     self._debug("Counted an average of"
                 " {:.2f} ones in input of size {} ({:.2f}%)."
                 .format(n_ones, n_features_s, 100 * n_ones / n_features_s))
     return roi.compute_roi(self.t.input_roi_strategy,
                            self._traits.n_features, n_ones, self.ones_info)
Ejemplo n.º 7
0
    def transform(self, X) -> TransformOutput:
        """
        Performs the nonlinear random projections of one or several input vectors.

        The `fit1d` or `fit2d` method must be called before, for setting vector dimensions
        or online option.
        If you need to transform one vector after each other,

        Parameters
        ----------
        X:  np.ndarray or torch.Tensor
            input vector, or batch of input vectors.
            Each vector must have the same dimensions as the one given in `fit1d` or `fit2d`.

        Returns
        -------
        Y: np.ndarray or torch.Tensor
             complete array of nonlinear random projections of X,
             of size self.n_components
             If input is an ndarray, type is actually ContextArray,
             with a context attribute to add metadata
        """
        assert self._runner, "Call fit1d or fit2d before transform"
        assert self.device.active, "OPU device isn't active, use opu.open() or \"with opu:\""

        user_input = OpuUserInput.from_traits(X, self._runner.traits)
        self._debug(str(user_input))

        if user_input.is_batch:
            # With batch input start acquisition first
            assert self.device.acq_state != AcqState.online, \
                "Can't transform a batch of vectors when acquisition is" \
                " in online mode, only single vectors"
            with self.device.acquiring(n_images=self._s.n_samples_by_pass):
                out = self._runner.transform(user_input)
        else:
            out = self._runner.transform(user_input)
        Y = user_input.reshape_output(out)
        # if the input is a tensor, return a tensor in CPU memory
        if user_input.is_tensor:
            # noinspection PyPackageRequirements
            import torch
            return torch.from_numpy(Y)
        else:
            return Y