# 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)}$$.

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


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


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


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