{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "59715f1c", "metadata": { "tags": [ "remove-input" ] }, "outputs": [], "source": [ "%config InlineBackend.figure_formats = ['svg']\n", "\n", "import numpy as np\n", "from numpy.linalg import norm\n", "import matplotlib.pyplot as plt\n", "plt.rcParams['animation.frame_format'] = \"svg\"" ] }, { "cell_type": "markdown", "id": "2dab8087", "metadata": {}, "source": [ "(encoders)=\n", "\n", "# Input encoders\n", "\n", "flatspin input encoders translate a set of input values to a series of external fields.\n", "Encoders provide a flexible way to define field protocols.\n", "A range of encoders are included, e.g., sinusoidal, sawtooth and rotational fields.\n", "\n", "The figure below illustrates the operation of an input encoder.\n", "A set of three input values are input to the encoder, which translates the values to an external field $\\vec{h}_{ext}^{(i)}$.\n", "\n", "![encoder](images/encoder.svg)" ] }, { "cell_type": "markdown", "id": "2da6769c", "metadata": {}, "source": [ "## A simple example\n", "\n", "In the code sample below, we use the {class}Sine  encoder, which encodes input as the amplitude of a sinusoidal field, applied at a fixed angle (here 30°).\n", "After an encoder has been created, it can be called to encode an input to a series of h_ext values." ] }, { "cell_type": "code", "execution_count": null, "id": "0de349c7", "metadata": {}, "outputs": [], "source": [ "from flatspin.encoder import Sine\n", "\n", "encoder = Sine(phi=30)\n", "input = [0.3, 0.5, 1.0]\n", "h_ext = encoder(input)\n", "print(f\"h_ext.shape = {h_ext.shape}\")\n", "\n", "plt.title(str(encoder))\n", "plt.plot(h_ext[:,0], label=\"h_ext[0]\")\n", "plt.plot(h_ext[:,1], label=\"h_ext[1]\")\n", "plt.xlabel(\"t\")\n", "plt.legend();" ] }, { "cell_type": "markdown", "id": "d9abbd27", "metadata": {}, "source": [ "(encoders-input)=\n", "## Input\n", "\n", "Input values should generally be in the range \$0, 1\$ and take the form of a 1D or 2D array.\n", "A 1D array is simply a series of values, whereas a 2D array is a series of n-dimensional input vectors.\n", "Thus the shape of the input array is (num_inputs, input_dim) where input_dim is the input dimensionality (number of values per input).\n", "Passing an 1D array is equivalent to a 2D array with shape (num_inputs, 1)." ] }, { "cell_type": "markdown", "id": "f76fc98d", "metadata": {}, "source": [ "## Output\n", "\n", "The output of an encoder is an array h_ext of external fields over time.\n", "Encoders can produce two different types of external fields:\n", "\n", "1. A global time-dependent field: h_ext.shape = (time, 2)\n", "2. A spatial time-dependent field: h_ext.shape = (time, height, width, 2)\n", "\n", "The first dimension of h_ext is the timestep, i.e., h_ext[t] is the field at timestep t.\n", "The last dimension of h_ext is always 2 (a 2D vector).\n", "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.\n", "\n", "The type of h_ext produced depends on the particular encoder.\n", "Encoders with Grid in the name will generally produce a spatial field.\n", "\n", "Global time-dependent fields can be passed to a model using {func}set_h_ext(h_ext[t]) , while spatial time-dependent fields can be used with {func}set_h_ext_grid(h_ext[t]) ." ] }, { "cell_type": "markdown", "id": "45e52877", "metadata": {}, "source": [ "(encoders-params)=\n", "## Encoder parameters\n", "\n", "Just like [model objects](), encoders take a set of parameters.\n", "In the simple example above, we set the parameter phi=30 during initialization.\n", "Parameters may also be changed after an encoder has been created, by calling {func}set_param()  or {func}set_params() :" ] }, { "cell_type": "code", "execution_count": null, "id": "47eaf9d1", "metadata": {}, "outputs": [], "source": [ "# Change some encoder parameters\n", "encoder.set_params(H0=0.01, H=0.02, timesteps=16)\n", "\n", "h_ext = encoder(input)\n", "print(f\"h_ext.shape = {h_ext.shape}\")\n", "\n", "plt.title(str(encoder))\n", "plt.plot(h_ext[:,0], label=\"h_ext[0]\")\n", "plt.plot(h_ext[:,1], label=\"h_ext[1]\")\n", "plt.legend();" ] }, { "cell_type": "markdown", "id": "ea1fb3d3", "metadata": {}, "source": [ "In the code above, we changed the parameters H0 and H, which define the minimum and maximum amplitude of the sine wave, respectively.\n", "That is, input values between 0 and 1 are mapped linearly to the range H0 - H.\n", "We also changed the time resolution of each period of the sine wave with the timesteps parameter." ] }, { "cell_type": "markdown", "id": "e321a04f", "metadata": {}, "source": [ "{class}Triangle  is another useuful encoder, e.g., for setting up a linear hystersis loop:" ] }, { "cell_type": "code", "execution_count": null, "id": "f5b166e3", "metadata": {}, "outputs": [], "source": [ "from flatspin.encoder import Triangle\n", "\n", "encoder = Triangle(H=0.1, phi=40)\n", "input = [1]\n", "h_ext = encoder(input)\n", "\n", "plt.title(str(encoder))\n", "plt.plot(h_ext[:,0], label=\"h_ext[0]\")\n", "plt.plot(h_ext[:,1], label=\"h_ext[1]\")\n", "plt.legend();" ] }, { "cell_type": "markdown", "id": "5aecdad6", "metadata": {}, "source": [ "## Visualizing h_ext\n", "\n", "Often, the output of an encoder is more easily visualized by animating the vectors." ] }, { "cell_type": "code", "execution_count": null, "id": "52d2ecc0", "metadata": { "tags": [ "hide-input" ] }, "outputs": [], "source": [ "from matplotlib.animation import FuncAnimation\n", "from matplotlib import ticker\n", "from IPython.display import HTML\n", "from flatspin.plotting import plot_vectors\n", "import textwrap\n", "\n", "def animate_h_ext(h_ext, title=\"\", interval=100, cmap='rainbow'):\n", " fig, ax = plt.subplots()\n", " \n", " # Axes setup\n", " ax.set_title(textwrap.fill(title))\n", " ax.xaxis.set_major_locator(ticker.MultipleLocator(1))\n", " ax.yaxis.set_major_locator(ticker.MultipleLocator(1))\n", " ax.tick_params(bottom=False, left=False, labelbottom=False, labelleft=False)\n", "\n", " # Normalize vectors to unit length\n", " nmax = np.max([norm(h_ext.reshape((-1,2)), axis=-1)])\n", " if nmax != 0:\n", " h_ext /= nmax\n", "\n", " # Positions of vectors\n", " if len(h_ext.shape) == 4:\n", " # Spatial field\n", " xx, yy = np.meshgrid(np.arange(h_ext.shape[1]), np.arange(h_ext.shape[2]))\n", " XY = np.column_stack([xx.ravel(), yy.ravel()])\n", " else:\n", " # Global field (single arrow)\n", " XY = [[0,0]]\n", "\n", " # Colors\n", " C = np.linspace(0, 1, len(XY), endpoint=False)\n", " \n", " def do_animate(i):\n", " plot_vectors(XY, h_ext[i], C, clim=(0, 1), cmap=cmap, ax=ax, replace=True, mask_zero=False)\n", "\n", " anim = FuncAnimation(fig, do_animate, frames=len(h_ext), interval=interval, blit=False)\n", " plt.close() # Only show the animation\n", " return HTML(anim.to_jshtml(fps=1000/interval))" ] }, { "cell_type": "code", "execution_count": null, "id": "2c81e2da", "metadata": {}, "outputs": [], "source": [ "from flatspin.encoder import Rotate\n", "\n", "# Gradually decreasing rotating field\n", "encoder = Rotate(H=0.1, timesteps=16)\n", "input = np.linspace(1, 0, 10, endpoint=False)\n", "h_ext = encoder(input)\n", "\n", "animate_h_ext(h_ext, str(encoder))" ] }, { "cell_type": "markdown", "id": "b9ac18cf", "metadata": {}, "source": [ "## Discontinuous encoders\n", "\n", "There are also encoders that produce discontinuous field sequences, e.g., {class}Constant  encodes input directly as the magnitude of a field at some fixed angle:" ] }, { "cell_type": "code", "execution_count": null, "id": "b255a345", "metadata": {}, "outputs": [], "source": [ "from flatspin.encoder import Constant\n", "\n", "encoder = Constant(H=0.1, phi=60)\n", "input = [0.3, -0.6, 1.0]\n", "h_ext = encoder(input)\n", "print(f\"h_ext.shape = {h_ext.shape}\")\n", "\n", "animate_h_ext(h_ext, str(encoder), interval=500)" ] }, { "cell_type": "markdown", "id": "29e7b471", "metadata": {}, "source": [ "## Encoding input as field angle\n", "\n", "The encoders discussed so far have all encoded input as the field amplitude.\n", "{class}Angle , {class}AngleSine  and {class}AngleTriangle  encode input as the **angle** of the field.\n", "\n", "Here is {class}Angle  in action:" ] }, { "cell_type": "code", "execution_count": null, "id": "7c494ac4", "metadata": {}, "outputs": [], "source": [ "from flatspin.encoder import AngleTriangle\n", "\n", "# Encode input as the angle of triangle wave, between 0 and 90 degrees\n", "encoder = AngleTriangle(H=0.1, phi=90, timesteps=16)\n", "input = [0, 0.5, 1]\n", "h_ext = encoder(input)\n", "\n", "animate_h_ext(h_ext, str(encoder))" ] }, { "cell_type": "markdown", "id": "fa50e0f7", "metadata": {}, "source": [ "## Encoding spatial fields\n", "\n", "Encoders that produce spatial time-dependent fields are also available, and can be identified by having Grid in the name.\n", "Spatial fields are defined in terms of a *grid*, where each cell has an associated weight.\n", "The weights are used by the encoder to alter the field on a per-cell basis, e.g., to scale the field strength.\n", "\n", "The following example demonstrates how to set up a spatial field composed of Sine waves.\n", "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." ] }, { "cell_type": "code", "execution_count": null, "id": "3090a3b9", "metadata": {}, "outputs": [], "source": [ "from flatspin.encoder import SineGrid\n", "\n", "# Center cell strongest, edges half strength, corners zero\n", "# Input is multiplied by the grid weights, before scaled by H\n", "grid = [[0, .5, 0],\n", " [.5, 1, .5],\n", " [0, .5, 0]]\n", "encoder = SineGrid(H=0.1, grid=grid, phi=30, timesteps=16)\n", "input = [0.3, 0.5, 1.0]\n", "h_ext = encoder(input)\n", "\n", "print(f\"h_ext.shape = {h_ext.shape}\")\n", "\n", "animate_h_ext(h_ext, str(encoder))" ] }, { "cell_type": "markdown", "id": "61d61bae", "metadata": {}, "source": [ "## Available encoders\n", "\n", "There are more encoders available than the ones demonstrated here.\n", "For a full list of encoders included in flatspin, please see {mod}flatspin.encoder.\n", "\n", "If none of the included encoders are suitable for your particular application, it is also possible to create your own [custom encoder](extending)." ] } ], "metadata": { "jupytext": { "formats": "md:myst", "text_representation": { "extension": ".md", "format_name": "myst", "format_version": 0.13, "jupytext_version": "1.11.5" } }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" }, "source_map": [ 15, 24, 39, 46, 59, 69, 88, 97, 108, 114, 118, 129, 135, 178, 187, 193, 202, 211, 220, 231, 246 ] }, "nbformat": 4, "nbformat_minor": 5 }