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/9a37568ce379ceb3f2b948227ced652c0fa6c07dbe753e67a2bfa053f2b85fb8.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/8554467d1ea81c55ddd0155ca668906e105b3cb21ae35a89ebdbb6953df11195.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/1d022efb560fc6a3f8a0a37a5daf2c1de6f92b7b8ee8af406d8c53964b5048a8.svg

Visualizing h_ext#

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

Hide code cell source
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))

Discontinuous encoders#

There are also encoders that produce discontinuous field sequences, e.g., Constant encodes input directly as the magnitude of a field at some fixed angle:

from flatspin.encoder import Constant

encoder = Constant(H=0.1, phi=60)
input = [0.3, -0.6, 1.0]
h_ext = encoder(input)
print(f"h_ext.shape = {h_ext.shape}")

animate_h_ext(h_ext, str(encoder), interval=500)
h_ext.shape = (3, 2)

Encoding input as field angle#

The encoders discussed so far have all encoded input as the field amplitude. Angle, AngleSine and AngleTriangle encode input as the angle of the field.

Here is Angle in action:

from flatspin.encoder import AngleTriangle

# Encode input as the angle of triangle wave, between 0 and 90 degrees
encoder = AngleTriangle(H=0.1, phi=90, timesteps=16)
input = [0, 0.5, 1]
h_ext = encoder(input)

animate_h_ext(h_ext, str(encoder))

Encoding spatial fields#

Encoders that produce spatial time-dependent fields are also available, and can be identified by having Grid in the name. Spatial fields are defined in terms of a grid, where each cell has an associated weight. The weights are used by the encoder to alter the field on a per-cell basis, e.g., to scale the field strength.

The following example demonstrates how to set up a spatial field composed of Sine waves. A 3x3 grid is defined to set up a field which is strongest in the center, half as strong along the edges, and zero at the corners.

from flatspin.encoder import SineGrid

# Center cell strongest, edges half strength, corners zero
# Input is multiplied by the grid weights, before scaled by H
grid = [[0, .5, 0],
        [.5, 1, .5],
        [0, .5, 0]]
encoder = SineGrid(H=0.1, grid=grid, phi=30, timesteps=16)
input = [0.3, 0.5, 1.0]
h_ext = encoder(input)

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

animate_h_ext(h_ext, str(encoder))
h_ext.shape = (48, 3, 3, 2)

Available encoders#

There are more encoders available than the ones demonstrated here. For a full list of encoders included in flatspin, please see flatspin.encoder.

If none of the included encoders are suitable for your particular application, it is also possible to create your own custom encoder.