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/3376f14e0ff006a910bcbbc867c981e9806aec28337f21e77b2ff9153cff68ba.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/d9d75532ac219605e81d6e6d60ed9c46a8386c13d42d29fa043abc13acf7decc.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/5d061ba096ff8d5b5a4ffbe6712ccbb679eb8b9c1f4cb9249682ccb0988fade0.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))