Пример #1
0
    def test_save_load_meta_parameter(self):
        """Test saving and loading a device with custom parameters."""
        # Create the device and the array.
        rpu_config = SingleRPUConfig(
            forward=IOParameters(inp_noise=0.321),
            backward=IOParameters(inp_noise=0.456),
            update=UpdateParameters(desired_bl=78),
            device=ConstantStepDevice(w_max=0.987)
        )

        model = self.get_layer(rpu_config=rpu_config)

        # Save the model to a file.
        with TemporaryFile() as file:
            save(model, file)
            # Load the model.
            file.seek(0)
            new_model = load(file)

        # Assert over the new model tile parameters.
        new_analog_tile = self.get_analog_tile(new_model)
        analog_tile = self.get_analog_tile(model)

        parameters = new_analog_tile.tile.get_parameters()
        self.assertAlmostEqual(parameters.forward_io.inp_noise, 0.321)
        self.assertAlmostEqual(parameters.backward_io.inp_noise, 0.456)
        self.assertAlmostEqual(parameters.update.desired_bl, 78)
        self.assertTrue(new_analog_tile.is_cuda == analog_tile.is_cuda)
Пример #2
0
    def get_noisefree_tile(self, out_size, in_size):
        """Return a tile of the specified dimensions with noisiness turned off."""
        rpu_config = None

        if 'FloatingPoint' not in self.parameter:
            rpu_config = SingleRPUConfig(
                forward=IOParameters(is_perfect=True),
                backward=IOParameters(is_perfect=True),
                device=IdealDevice())

        python_tile = self.get_tile(out_size, in_size, rpu_config)
        self.set_init_weights(python_tile)

        return python_tile
Пример #3
0
    def test_config_tile_parameters(self):
        """Test modifying the tile parameters."""
        rpu_config = self.get_rpu_config()

        rpu_config.forward = IOParameters(inp_noise=0.321)
        rpu_config.backward = IOParameters(inp_noise=0.456)
        rpu_config.update = UpdateParameters(desired_bl=78)

        tile = self.get_tile(11, 22, rpu_config).tile

        # Assert over the parameters in the binding objects.
        parameters = tile.get_parameters()
        self.assertAlmostEqual(parameters.forward_io.inp_noise, 0.321)
        self.assertAlmostEqual(parameters.backward_io.inp_noise, 0.456)
        self.assertAlmostEqual(parameters.update.desired_bl, 78)
Пример #4
0
def plot_device(device: PulsedDevice,
                w_noise: float = 0.0,
                **kwargs: Any) -> None:
    """Plots the step response figure for a given device (preset).

    Note:
        It will use an amount of read weight noise ``w_noise`` for
        reading the weights.

    Args:
        device: PulsedDevice parameters
        w_noise: Weight noise standard deviation during read
        kwargs: for other parameters, see :func:`plot_response_overview`
    """
    plt.figure(figsize=[7, 7])
    # To simulate some weight read noise.
    io_pars = IOParameters(
        out_noise=0.0,  # no out noise
        w_noise=w_noise,  # quite low
        inp_res=-1.,  # turn off DAC
        out_bound=100.,  # not limiting
        out_res=-1.,  # turn off ADC
        bound_management=BoundManagementType.NONE,
        noise_management=NoiseManagementType.NONE,
        w_noise_type=WeightNoiseType.ADDITIVE_CONSTANT)

    rpu_config = SingleRPUConfig(device=device, forward=io_pars)

    plot_response_overview(rpu_config, **kwargs)
Пример #5
0
class InferenceRPUConfig:
    """Configuration for an analog tile that is used only for inference.

    Training is done in *hardware-aware* manner, thus using only the
    non-idealities of the forward-pass, but backward and update passes
    are ideal.

    During inference, statistical models of programming, drift
    and read noise can be used.
    """
    # pylint: disable=too-many-instance-attributes

    bindings_class: ClassVar[Type] = devices.AnalogTileParameter

    forward: IOParameters = field(default_factory=IOParameters)
    """Input-output parameter setting for the forward direction."""

    noise_model: BaseNoiseModel = field(default_factory=PCMLikeNoiseModel)
    """Statistical noise model to be used during (realistic) inference."""

    drift_compensation: BaseDriftCompensation = field(
        default_factory=GlobalDriftCompensation)
    """For compensating the drift during inference only."""

    clip: WeightClipParameter = field(default_factory=WeightClipParameter)
    """Parameters for weight clip."""

    modifier: WeightModifierParameter = field(
        default_factory=WeightModifierParameter)
    """Parameters for weight modifier."""

    # The following fields are not included in `__init__`, and should be
    # treated as read-only.

    device: IdealDevice = field(default_factory=IdealDevice, init=False)
    """Parameters that modify the behavior of the pulsed device: ideal device."""

    backward: IOParameters = field(
        default_factory=lambda: IOParameters(is_perfect=True), init=False)
    """Input-output parameter setting for the backward direction: perfect."""

    update: UpdateParameters = field(
        default_factory=lambda: UpdateParameters(pulse_type=PulseType.NONE),
        init=False)
    """Parameter for the update behavior: ``NONE`` pulse type."""
    def as_bindings(self) -> devices.AnalogTileParameter:
        """Return a representation of this instance as a simulator bindings object."""
        return tile_parameters_to_bindings(self)

    def requires_diffusion(self) -> bool:
        """Return whether device has diffusion enabled."""
        return self.device.diffusion > 0.0

    def requires_decay(self) -> bool:
        """Return whether device has decay enabled."""
        return self.device.lifetime > 0.0
Пример #6
0
    def set_noise_free(dev: Any) -> Any:
        if hasattr(dev, 'dw_min_std'):
            dev.dw_min_std = 0.0  # Noise free.

        if hasattr(dev, 'refresh_forward'):
            setattr(dev, 'refresh_forward', IOParameters(is_perfect=True))

        if hasattr(dev, 'refresh_update'):
            setattr(dev, 'refresh_update',
                    UpdateParameters(pulse_type=PulseType.NONE))

        if hasattr(dev, 'transfer_forward'):
            setattr(dev, 'refresh_forward', IOParameters(is_perfect=True))

        if hasattr(dev, 'transfer_update'):
            setattr(dev, 'transfer_update',
                    UpdateParameters(pulse_type=PulseType.NONE))

        if (hasattr(dev, 'write_noise_std')
                and getattr(dev, 'write_noise_std') > 0.0):
            # Just make very small to avoid hidden parameter mismatch.
            setattr(dev, 'write_noise_std', 1e-6)
Пример #7
0
def plot_device_symmetry(
    device: PulsedDevice,
    w_noise: float = 0.0,
    n_pulses: int = 10000,
    n_traces: int = 3,
    use_cuda: bool = False,
    w_init: float = 1.0,
) -> None:
    """Plot the response figure for a given device (preset).

    It will show the response to alternating up down pulses.

    Note:
        It will use an amount of read weight noise ``w_noise`` for
        reading the weights.

    Args:
        device: PulsedDevice parameters
        n_pulses: total number of pulses
        w_noise: Weight noise standard deviation during read
        n_traces: Number of device traces
        use_cuda: Whether to use CUDA,
        w_init: Initial value of the weights
    """
    plt.figure(figsize=[10, 5])

    io_pars = IOParameters(
        out_noise=0.0,  # no out noise
        w_noise=w_noise,  # quite low
        inp_res=-1.,  # turn off DAC
        out_bound=100.,  # not limiting
        out_res=-1.,  # turn off ADC
        bound_management=BoundManagementType.NONE,
        noise_management=NoiseManagementType.NONE,
        w_noise_type=WeightNoiseType.ADDITIVE_CONSTANT)

    rpu_config = SingleRPUConfig(device=device, forward=io_pars)

    direction = np.sign(np.cos(np.pi * np.arange(n_pulses)))
    plt.clf()

    analog_tile = get_tile_for_plotting(rpu_config,
                                        n_traces,
                                        use_cuda,
                                        noise_free=False)
    weights = w_init * ones((n_traces, 1))
    analog_tile.set_weights(weights)

    plot_pulse_response(analog_tile, direction, use_forward=False)
    plt.ylim([-1, 1])
    plt.grid(True)
Пример #8
0
def plot_device_compact(device: PulsedDevice,
                        w_noise: float = 0.0,
                        n_steps: int = None,
                        n_traces: int = 3) -> Figure:
    """Plots a compact step response figure for a given device (preset).

    Note:
        It will use an amount of read weight noise ``w_noise`` for
        reading the weights.

    Args:
        device: PulsedDevice parameters
        w_noise: Weight noise standard deviation during read
        n_steps: Number of steps for up/down cycle
        n_traces: Number of traces to plot (for device-to-device variation)

    Returns:
        the compact step response figure.
    """
    # pylint: disable=too-many-locals,too-many-statements
    figure = plt.figure(figsize=[12, 4])

    # To simulate some weight read noise.
    io_pars = IOParameters(
        out_noise=0.0,  # no out noise
        w_noise=w_noise,  # quite low
        inp_res=-1.,  # turn off DAC
        out_bound=100.,  # not limiting
        out_res=-1.,  # turn off ADC
        bound_management=BoundManagementType.NONE,
        noise_management=NoiseManagementType.NONE,
        w_noise_type=WeightNoiseType.ADDITIVE_CONSTANT)

    rpu_config = SingleRPUConfig(device=device, forward=io_pars)

    if n_steps is None:
        n_steps = estimate_n_steps(rpu_config)

    use_cuda = False

    # Noisy tile response curves.
    n_loops = 2
    total_iters = n_loops * 2 * n_steps
    direction = np.sign(np.sin(np.pi * (np.arange(total_iters) + 1) / n_steps))

    analog_tile = get_tile_for_plotting(rpu_config,
                                        n_traces,
                                        use_cuda,
                                        noise_free=False)
    w_trace = compute_pulse_response(analog_tile, direction, use_forward=True)\
        .reshape(-1, n_traces)

    axis = figure.add_subplot(1, 1, 1)
    axis.plot(w_trace, linewidth=1)
    axis.set_title(analog_tile.rpu_config.device.__class__.__name__)
    axis.set_xlabel('Pulse number #')
    limit = np.abs(w_trace).max() * 1.2
    axis.set_ylim(-limit, limit)
    axis.set_xlim(0, total_iters - 1)
    axis.xaxis.set_major_formatter(ticker.ScalarFormatter(useMathText=True))

    # Noise-free tile for statistics.
    n_loops = 1
    total_iters = min(max(n_loops * 2 * n_steps, 1000),
                      max(50000, 2 * n_steps))
    direction = np.sign(np.sin(np.pi * (np.arange(total_iters) + 1) / n_steps))

    analog_tile_noise_free = get_tile_for_plotting(rpu_config,
                                                   n_traces,
                                                   use_cuda,
                                                   noise_free=True)
    analog_tile_noise_free.set_hidden_parameters(
        analog_tile.get_hidden_parameters())

    w_trace = compute_pulse_response(analog_tile_noise_free, direction, False)

    # Compute statistics.
    num_nodes = min(n_steps, 100)
    w_nodes = np.linspace(w_trace.min(), w_trace.max(), num_nodes)

    dw_mean_up = compute_pulse_statistics(w_nodes, w_trace, direction, True)[0]\
        .reshape(-1, n_traces)
    dw_mean_down = compute_pulse_statistics(w_nodes, w_trace, direction, False)[0]\
        .reshape(-1, n_traces)

    # Plot mean up statistics.
    pos = axis.get_position().bounds
    space = 0.1
    gap = 0.01
    axis.set_position(
        [pos[0] + gap + space, pos[1], pos[2] - 2 * gap - 2 * space, pos[3]])
    axis.set_yticks([])

    axis_left = figure.add_axes([pos[0], pos[1], space, pos[3]])
    dw_mean_up = dw_mean_up.reshape(-1, n_traces)
    for i in range(n_traces):
        axis_left.plot(dw_mean_up[:, i], w_nodes)

    axis_left.set_position([pos[0], pos[1], space, pos[3]])
    axis_left.set_xlabel('Up pulse size')
    axis_left.set_ylabel('Weight \n [conductance]')
    axis_left.set_ylim(-limit, limit)

    # Plot mean down statistics.
    axis_right = figure.add_axes(
        [pos[0] + pos[2] - space, pos[1], space, pos[3]])
    dw_mean_down = dw_mean_down.reshape(-1, n_traces)
    for i in range(n_traces):
        axis_right.plot(np.abs(dw_mean_down[:, i]), w_nodes)

    axis_right.set_yticks([])
    axis_right.set_xlabel('Down pulse size')
    axis_right.set_ylim(-limit, limit)

    # Set xlim's.
    limit = np.maximum(np.nanmax(np.abs(dw_mean_down)),
                       np.nanmax(np.abs(dw_mean_up))) * 1.2
    axis_left.set_xlim(0.0, limit)
    axis_right.set_xlim(0.0, limit)

    return figure