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)
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
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)
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)
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
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)
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)
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