Basic Usage
This section seeks to provide a simple example of how to interact with the reservoir as well as some background on other basic functionality provided for interacting with the device.
Sine Wave
To illustrate a basic usage pattern, sequential observations from the sine wave function will be modelled by the reservoir. The first step will be to create the sine wave data for our example.
- import numpy as np
- import matplotlib.pyplot as plt
- # Define the x-values for the domain of the sine function input
- x = np.linspace(0, 6 * np.pi, 500)
- # Calculate the y-values using the sine function and probability function
- y = np.sin(x)
- # Plot the oscillator with probability-based amplitudes
- plt.plot(x, y)
- plt.xlabel('x')
- plt.ylabel('y')
- plt.title('Sine Wave')
- plt.grid(True)
- plt.show()
In the below code block make sure to replace both IP_ADDR and PORT with your specific network location for your EmuCore device
- from emucore_direct.client import EmuCoreClient
- from time import time
- IP_ADDR = "YOUR DEVICE IP ADDRESS"
- PORT = "YOUR DEVICE PORT"
- # Instantiate an EmuCore instance
- ec_client = EmuCoreClient(ip_addr=IP_ADDR, port = PORT)
Reservoir systems require state while processing so in order to prevent collisions a locking mechanism on the device is available. To acquire the execution lock a user must run:
- lock_id, _, _ = ec_client.wait_for_lock()
This function waits for the execution lock indefinitely until it becomes available.
The basic recipe for modeling using EmuCore is to sequentially feed data to the reservoir. This process creates an expanded representation based on the current configuration of the reservoir model. Subsequently, a model is applied to the augmented data from the reservoir. This expanded representation should be easier to model and may require less complicated models to achieve good prediction results for a given series. Before running another dataset, it is important to reset the reservoir; otherwise, it will retain information from previous data that was modeled. Next, the reservoir model must be configured. There are a variety of parameters that can be tuned to obtain better results from predictions. Here is an example of a possible configuration:
- vbias=0.3
- gain=0.5
- num_nodes=800
- num_taps=400
- input_scaling=0.33
- density=0.5
- ec_client.reservoir_reset(lock_id=lock_id)
- print("Reservoir config")
- # Configure
- ec_client.rc_config(
- lock_id=lock_id,
- vbias=vbias,
- gain=gain,
- num_nodes=num_nodes,
- num_taps=num_taps,
- )
- Reservoir config
- {'status': 0, 'message': 'Success'}
Now we begin modeling the data with the reservoir:
- y_resp, max_scale_val, weights = ec_client.process_all_data(
- input_data=y.reshape((-1,1)),
- num_nodes=num_nodes,
- density=density,
- feature_scaling=input_scaling,
- lock_id=lock_id,
- max_scale_val=None)
After processing it is important to release the device execution lock to free up the device for other usage:
- # releasing lock after finished with device execution
- ec_client.release_lock(lock_id=lock_id)
- {'status': 0, 'message': 'Success'}
Below, the output of the reservoir is visualized, with the coloring representing the value for each combination of a given node and a given value in our sequence from the sine function. Since the sine function generates a repeating sequence, observe how the values across nodes repeat for this particular series due to the cyclic nature of the underlying function. However, with more complex inputs, this representation can become much more varied.
- plt.imshow(y_resp, cmap="nipy_spectral")
- plt.colorbar()
- plt.xlabel("Node")
- plt.ylabel("Observation number (x)")
- plt.show()
Having acquired the data any number of models can be fit to predict future outcomes using the dynamics of the reservoir model.
System Information
In order to obtain system information the package provides the method emucore_direct.client.EmuCoreClient.system_info()
.
An example of how to call this function is provided below:
- from emucore_direct.client import EmuCoreClient
- # replace with your network location
- IP_ADDR = "YOUR DEVICE IP ADDRESS"
- PORT = "YOUR DEVICE PORT"
- # Instantiate an EmuCore instance
- ec_client = EmuCoreClient(ip_addr=IP_ADDR, port = PORT)
- ec_client.system_info()
- # Example output:
- # {'system_name': 'Bumblebee', 'system_version': 'v0.1-10-ga4a5c1f'}
Device Lock Basics
Device locking ensures that only one process is running on the device at a time. In order to ensure this functionality, there are a few useful functions:
emucore_direct.client.EmuCoreClient.acquire_lock()
—makes a single attempt to acquire the device lockemucore_direct.client.EmuCoreClient.release_lock()
—releases lock if lock currently held by the user.emucore_direct.client.EmuCoreClient.check_lock()
—checks device lock status without attempting to acquire lockemucore_direct.client.EmuCoreClient.wait_for_lock()
—waits for device lock indefinitely
In order to ensure access to users there is a timer on the server that if a user is inactive for more than one minute and another user wishes to use the device they will be able to acquire the device
lock and the previous idle user will lose it’s lock on the device. In order to help ease the process of acquiring the lock it’s recommended to use
emucore_direct.client.EmuCoreClient.wait_for_lock()
. In order to ensure maximum device availability it is a good practice to release the lock if taking time between runs on the device. Here is a basic example of how to use emucore_direct.client.EmuCoreClient.wait_for_lock()
and emucore_direct.client.EmuCoreClient.release_lock()
:
- from emucore_direct.client import EmuCoreClient
- # replace with your network location
- IP_ADDR = "YOUR DEVICE IP ADDRESS"
- PORT = "YOUR DEVICE PORT"
- # Instantiate an EmuCore instance
- ec_client = EmuCoreClient(ip_addr=IP_ADDR, port = PORT)
- lock_id, _, _ = ec_client.wait_for_lock()
- ec_client.release_lock(lock_id=lock_id)