Input encoders#

flatspin input encoders translate a set of input values to a series of external fields. Encoders provide a flexible way to define field protocols. A range of encoders are included, e.g., sinusoidal, sawtooth and rotational fields.

The figure below illustrates the operation of an input encoder. A set of three input values are input to the encoder, which translates the values to an external field \(\vec{h}_{ext}^{(i)}\).

encoder

A simple example#

In the code sample below, we use the Sine encoder, which encodes input as the amplitude of a sinusoidal field, applied at a fixed angle (here 30°). After an encoder has been created, it can be called to encode an input to a series of h_ext values.

from flatspin.encoder import Sine

encoder = Sine(phi=30)
input = [0.3, 0.5, 1.0]
h_ext = encoder(input)
print(f"h_ext.shape = {h_ext.shape}")

plt.title(str(encoder))
plt.plot(h_ext[:,0], label="h_ext[0]")
plt.plot(h_ext[:,1], label="h_ext[1]")
plt.xlabel("t")
plt.legend();
h_ext.shape = (300, 2)
_images/encoders_3_1.svg

Input#

Input values should generally be in the range [0, 1] and take the form of a 1D or 2D array. A 1D array is simply a series of values, whereas a 2D array is a series of n-dimensional input vectors. Thus the shape of the input array is (num_inputs, input_dim) where input_dim is the input dimensionality (number of values per input). Passing an 1D array is equivalent to a 2D array with shape (num_inputs, 1).

Output#

The output of an encoder is an array h_ext of external fields over time. Encoders can produce two different types of external fields:

  1. A global time-dependent field: h_ext.shape = (time, 2)

  2. A spatial time-dependent field: h_ext.shape = (time, height, width, 2)

The first dimension of h_ext is the timestep, i.e., h_ext[t] is the field at timestep t. The last dimension of h_ext is always 2 (a 2D vector). Spatial fields are defined on a grid, and the two extra dimensions height and width define the number of grid cells in the vertical and horizontal direction, respectively.

The type of h_ext produced depends on the particular encoder. Encoders with Grid in the name will generally produce a spatial field.

Global time-dependent fields can be passed to a model using set_h_ext(h_ext[t]), while spatial time-dependent fields can be used with set_h_ext_grid(h_ext[t]).

Encoder parameters#

Just like model objects, encoders take a set of parameters. In the simple example above, we set the parameter phi=30 during initialization. Parameters may also be changed after an encoder has been created, by calling set_param() or set_params():

# Change some encoder parameters
encoder.set_params(H0=0.01, H=0.02, timesteps=16)

h_ext = encoder(input)
print(f"h_ext.shape = {h_ext.shape}")

plt.title(str(encoder))
plt.plot(h_ext[:,0], label="h_ext[0]")
plt.plot(h_ext[:,1], label="h_ext[1]")
plt.legend();
h_ext.shape = (48, 2)
_images/encoders_7_1.svg

In the code above, we changed the parameters H0 and H, which define the minimum and maximum amplitude of the sine wave, respectively. That is, input values between 0 and 1 are mapped linearly to the range H0 - H. We also changed the time resolution of each period of the sine wave with the timesteps parameter.

Triangle is another useuful encoder, e.g., for setting up a linear hystersis loop:

from flatspin.encoder import Triangle

encoder = Triangle(H=0.1, phi=40)
input = [1]
h_ext = encoder(input)

plt.title(str(encoder))
plt.plot(h_ext[:,0], label="h_ext[0]")
plt.plot(h_ext[:,1], label="h_ext[1]")
plt.legend();
_images/encoders_10_0.svg

Visualizing h_ext#

Often, the output of an encoder is more easily visualized by animating the vectors.

from matplotlib.animation import FuncAnimation
from matplotlib import ticker
from IPython.display import HTML
from flatspin.plotting import plot_vectors
import textwrap

def animate_h_ext(h_ext, title="", interval=100, cmap='rainbow'):
    fig, ax = plt.subplots()
    
    # Axes setup
    ax.set_title(textwrap.fill(title))
    ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
    ax.yaxis.set_major_locator(ticker.MultipleLocator(1))
    ax.tick_params(bottom=False, left=False, labelbottom=False, labelleft=False)

    # Normalize vectors to unit length
    nmax = np.max([norm(h_ext.reshape((-1,2)), axis=-1)])
    if nmax != 0:
        h_ext /= nmax

    # Positions of vectors
    if len(h_ext.shape) == 4:
        # Spatial field
        xx, yy = np.meshgrid(np.arange(h_ext.shape[1]), np.arange(h_ext.shape[2]))
        XY = np.column_stack([xx.ravel(), yy.ravel()])
    else:
        # Global field (single arrow)
        XY = [[0,0]]

    # Colors
    C = np.linspace(0, 1, len(XY), endpoint=False)
    
    def do_animate(i):
        plot_vectors(XY, h_ext[i], C, clim=(0, 1), cmap=cmap, ax=ax, replace=True, mask_zero=False)

    anim = FuncAnimation(fig, do_animate, frames=len(h_ext), interval=interval, blit=False)
    plt.close() # Only show the animation
    return HTML(anim.to_jshtml(fps=1000/interval))
from flatspin.encoder import Rotate

# Gradually decreasing rotating field
encoder = Rotate(H=0.1, timesteps=16)
input = np.linspace(1, 0, 10, endpoint=False)
h_ext = encoder(input)

animate_h_ext(h_ext, str(encoder))