Skip to content

API Reference

This section documents the Python API for rclib. The C++ core is wrapped efficiently to provide a seamless experience.

Reservoirs

rclib.reservoirs

Reservoir configurations.

RandomSparse

Random Sparse Reservoir configuration.

Source code in python/rclib/reservoirs.py
class RandomSparse:
    """Random Sparse Reservoir configuration."""

    def __init__(
        self,
        n_neurons: int,
        spectral_radius: float,
        sparsity: float = 0.1,
        leak_rate: float = 1.0,
        input_scaling: float = 1.0,
        *,
        include_bias: bool = False,
        seed: int = 42,
    ) -> None:
        """Initialize the Random Sparse Reservoir.

        Args:
            n_neurons: Number of neurons in the reservoir.
            spectral_radius: Spectral radius of the reservoir weight matrix.
            sparsity: Sparsity of the reservoir weight matrix (0.0 to 1.0).
            leak_rate: Leaking rate of the neurons.
            input_scaling: Scaling factor for the input weights.
            include_bias: Whether to include a bias term.
            seed: Random seed for weights initialization.
        """
        self.n_neurons = n_neurons
        self.spectral_radius = spectral_radius
        self.sparsity = sparsity
        self.leak_rate = leak_rate
        self.input_scaling = input_scaling
        self.include_bias = include_bias
        self.seed = seed

__init__(n_neurons, spectral_radius, sparsity=0.1, leak_rate=1.0, input_scaling=1.0, *, include_bias=False, seed=42)

Initialize the Random Sparse Reservoir.

Args: n_neurons: Number of neurons in the reservoir. spectral_radius: Spectral radius of the reservoir weight matrix. sparsity: Sparsity of the reservoir weight matrix (0.0 to 1.0). leak_rate: Leaking rate of the neurons. input_scaling: Scaling factor for the input weights. include_bias: Whether to include a bias term. seed: Random seed for weights initialization.

Source code in python/rclib/reservoirs.py
def __init__(
    self,
    n_neurons: int,
    spectral_radius: float,
    sparsity: float = 0.1,
    leak_rate: float = 1.0,
    input_scaling: float = 1.0,
    *,
    include_bias: bool = False,
    seed: int = 42,
) -> None:
    """Initialize the Random Sparse Reservoir.

    Args:
        n_neurons: Number of neurons in the reservoir.
        spectral_radius: Spectral radius of the reservoir weight matrix.
        sparsity: Sparsity of the reservoir weight matrix (0.0 to 1.0).
        leak_rate: Leaking rate of the neurons.
        input_scaling: Scaling factor for the input weights.
        include_bias: Whether to include a bias term.
        seed: Random seed for weights initialization.
    """
    self.n_neurons = n_neurons
    self.spectral_radius = spectral_radius
    self.sparsity = sparsity
    self.leak_rate = leak_rate
    self.input_scaling = input_scaling
    self.include_bias = include_bias
    self.seed = seed

Nvar

NVAR Reservoir configuration.

Source code in python/rclib/reservoirs.py
class Nvar:
    """NVAR Reservoir configuration."""

    def __init__(self, num_lags: int) -> None:
        """Initialize the NVAR Reservoir.

        Args:
            num_lags: Number of time lags to include.
        """
        self.num_lags = num_lags

__init__(num_lags)

Initialize the NVAR Reservoir.

Args: num_lags: Number of time lags to include.

Source code in python/rclib/reservoirs.py
def __init__(self, num_lags: int) -> None:
    """Initialize the NVAR Reservoir.

    Args:
        num_lags: Number of time lags to include.
    """
    self.num_lags = num_lags

Readouts

rclib.readouts

Readout configurations.

Ridge

Ridge Regression Readout configuration.

Source code in python/rclib/readouts.py
class Ridge:
    """Ridge Regression Readout configuration."""

    def __init__(
        self,
        alpha: float,
        *,
        include_bias: bool,
        solver: str = "auto",
        tolerance: float = 1e-10,
    ) -> None:
        """Initialize the Ridge Readout.

        Args:
            alpha: Regularization parameter.
            include_bias: Whether to include a bias term.
            solver: Solver to use ("auto", "cholesky", "dual_cholesky",
                "conjugate_gradient", "conjugate_gradient_implicit").
            tolerance: Convergence tolerance for iterative solvers (CG).
        """
        self.alpha = alpha
        self.include_bias = include_bias
        self.solver = solver
        self.tolerance = tolerance

__init__(alpha, *, include_bias, solver='auto', tolerance=1e-10)

Initialize the Ridge Readout.

Args: alpha: Regularization parameter. include_bias: Whether to include a bias term. solver: Solver to use ("auto", "cholesky", "dual_cholesky", "conjugate_gradient", "conjugate_gradient_implicit"). tolerance: Convergence tolerance for iterative solvers (CG).

Source code in python/rclib/readouts.py
def __init__(
    self,
    alpha: float,
    *,
    include_bias: bool,
    solver: str = "auto",
    tolerance: float = 1e-10,
) -> None:
    """Initialize the Ridge Readout.

    Args:
        alpha: Regularization parameter.
        include_bias: Whether to include a bias term.
        solver: Solver to use ("auto", "cholesky", "dual_cholesky",
            "conjugate_gradient", "conjugate_gradient_implicit").
        tolerance: Convergence tolerance for iterative solvers (CG).
    """
    self.alpha = alpha
    self.include_bias = include_bias
    self.solver = solver
    self.tolerance = tolerance

Rls

Recursive Least Squares (RLS) Readout configuration.

Source code in python/rclib/readouts.py
class Rls:
    """Recursive Least Squares (RLS) Readout configuration."""

    def __init__(
        self,
        lambda_: float,
        delta: float,
        *,
        include_bias: bool,
        solver: str = "rank1_update",
    ) -> None:
        """Initialize the RLS Readout.

        Args:
            lambda_: Forgetting factor (0.0 to 1.0).
            delta: Initial value for the covariance matrix diagonal.
            include_bias: Whether to include a bias term.
            solver: Solver type ("rank1_update" or "rank_k_update").
                "rank1_update" is traditional sequential RLS.
                "rank_k_update" is optimized for mini-batches using Woodbury identity.
        """
        self.lambda_ = lambda_
        self.delta = delta
        self.include_bias = include_bias
        self.solver = solver

__init__(lambda_, delta, *, include_bias, solver='rank1_update')

Initialize the RLS Readout.

Args: lambda_: Forgetting factor (0.0 to 1.0). delta: Initial value for the covariance matrix diagonal. include_bias: Whether to include a bias term. solver: Solver type ("rank1_update" or "rank_k_update"). "rank1_update" is traditional sequential RLS. "rank_k_update" is optimized for mini-batches using Woodbury identity.

Source code in python/rclib/readouts.py
def __init__(
    self,
    lambda_: float,
    delta: float,
    *,
    include_bias: bool,
    solver: str = "rank1_update",
) -> None:
    """Initialize the RLS Readout.

    Args:
        lambda_: Forgetting factor (0.0 to 1.0).
        delta: Initial value for the covariance matrix diagonal.
        include_bias: Whether to include a bias term.
        solver: Solver type ("rank1_update" or "rank_k_update").
            "rank1_update" is traditional sequential RLS.
            "rank_k_update" is optimized for mini-batches using Woodbury identity.
    """
    self.lambda_ = lambda_
    self.delta = delta
    self.include_bias = include_bias
    self.solver = solver

Lms

Least Mean Squares (LMS) Readout configuration.

Source code in python/rclib/readouts.py
class Lms:
    """Least Mean Squares (LMS) Readout configuration."""

    def __init__(self, learning_rate: float, *, include_bias: bool) -> None:
        """Initialize the LMS Readout.

        Args:
            learning_rate: Learning rate for the LMS algorithm.
            include_bias: Whether to include a bias term.
        """
        self.learning_rate = learning_rate
        self.include_bias = include_bias

__init__(learning_rate, *, include_bias)

Initialize the LMS Readout.

Args: learning_rate: Learning rate for the LMS algorithm. include_bias: Whether to include a bias term.

Source code in python/rclib/readouts.py
def __init__(self, learning_rate: float, *, include_bias: bool) -> None:
    """Initialize the LMS Readout.

    Args:
        learning_rate: Learning rate for the LMS algorithm.
        include_bias: Whether to include a bias term.
    """
    self.learning_rate = learning_rate
    self.include_bias = include_bias

Model

rclib.model

Model module for Reservoir Computing.

ESN

Echo State Network (ESN) model.

Source code in python/rclib/model.py
class ESN:
    """Echo State Network (ESN) model."""

    def __init__(self, connection_type: str = "serial") -> None:
        """Initialize the ESN model.

        Parameters
        ----------
        connection_type : str, optional
            The type of connection between reservoirs ("serial" or "parallel").
            Default is "serial".
        """
        self.connection_type = connection_type
        self._reservoirs_params: list[Any] = []  # Store parameters for Python-side reservoir objects
        self._readout_params: Any = None  # Store parameters for Python-side readout object
        self._cpp_model = _rclib.Model()  # Initialize the C++ Model object

    def add_reservoir(self, reservoir: Any) -> None:  # noqa: ANN401
        """Add a reservoir to the model.

        Parameters
        ----------
        reservoir : Any
            The reservoir object to add.

        Raises
        ------
        TypeError
            If the reservoir type is unsupported.
        """
        # Store the Python reservoir object's parameters
        self._reservoirs_params.append(reservoir)
        # Create and add the C++ reservoir to the C++ model
        if isinstance(reservoir, reservoirs.RandomSparse):
            cpp_res = _rclib.RandomSparseReservoir(
                reservoir.n_neurons,
                reservoir.spectral_radius,
                reservoir.sparsity,
                reservoir.leak_rate,
                reservoir.input_scaling,
                reservoir.include_bias,
                reservoir.seed,
            )
            self._cpp_model.addReservoir(cpp_res, self.connection_type)
        elif isinstance(reservoir, reservoirs.Nvar):
            cpp_res = _rclib.NvarReservoir(reservoir.num_lags)
            self._cpp_model.addReservoir(cpp_res, self.connection_type)
        # Add other reservoir types here as they are implemented
        else:
            msg = "Unsupported reservoir type"
            raise TypeError(msg)

        # Update readout in case it's using "auto" solver
        self._update_readout()

    def set_readout(self, readout: Any) -> None:  # noqa: ANN401
        """Set the readout for the model.

        Parameters
        ----------
        readout : Any
            The readout object to set.

        Raises
        ------
        TypeError
            If the readout type is unsupported.
        """
        # Store the Python readout object's parameters
        self._readout_params = readout
        self._update_readout()

    def _update_readout(self) -> None:
        """Instantiate or update the C++ readout based on current parameters."""
        if self._readout_params is None:
            return

        # Don't re-instantiate if already exists (to preserve online learning state)
        try:
            if self._cpp_model.getReadout() is not None:
                return
        except RuntimeError:
            # getReadout() throws if not set
            pass

        readout = self._readout_params

        # Create and set the C++ readout to the C++ model
        if isinstance(readout, readouts.Ridge):
            solver_map = {
                "auto": _rclib.RidgeReadout.Solver.AUTO,
                "cholesky": _rclib.RidgeReadout.Solver.CHOLESKY,
                "dual_cholesky": _rclib.RidgeReadout.Solver.DUAL_CHOLESKY,
                "conjugate_gradient": _rclib.RidgeReadout.Solver.CONJUGATE_GRADIENT,
                "conjugate_gradient_implicit": _rclib.RidgeReadout.Solver.CONJUGATE_GRADIENT_IMPLICIT,
            }
            if readout.solver not in solver_map:
                msg = f"Unsupported solver: {readout.solver}"
                raise ValueError(msg)

            cpp_readout = _rclib.RidgeReadout(
                readout.alpha, readout.include_bias, solver_map[readout.solver], readout.tolerance
            )
            self._cpp_model.setReadout(cpp_readout)
        elif isinstance(readout, readouts.Rls):
            solver_map = {
                "rank1_update": _rclib.RlsReadout.Solver.RANK1_UPDATE,
                "rank_k_update": _rclib.RlsReadout.Solver.RANK_K_UPDATE,
            }
            if readout.solver not in solver_map:
                msg = f"Unsupported RLS solver: {readout.solver}"
                raise ValueError(msg)

            cpp_readout = _rclib.RlsReadout(
                readout.lambda_, readout.delta, readout.include_bias, solver_map[readout.solver]
            )
            self._cpp_model.setReadout(cpp_readout)
        elif isinstance(readout, readouts.Lms):
            cpp_readout = _rclib.LmsReadout(readout.learning_rate, readout.include_bias)
            self._cpp_model.setReadout(cpp_readout)
        else:
            msg = "Unsupported readout type"
            raise TypeError(msg)

    def fit(self, x: ArrayLike, y: ArrayLike, washout_len: int = 0) -> None:
        """Fit the model to the data.

        Parameters
        ----------
        x : ArrayLike
            Input data.
        y : ArrayLike
            Target data.
        washout_len : int, optional
            Number of initial samples to discard. Default is 0.
        """
        # Ensure readout is correctly initialized (especially for "auto" solver)
        self._update_readout()
        # Call the C++ model's fit method
        self._cpp_model.fit(x, y, washout_len)

    def predict(self, x: ArrayLike, *, reset_state_before_predict: bool = True) -> np.ndarray:
        """Predict using the trained model.

        Parameters
        ----------
        x : ArrayLike
            Input data.
        reset_state_before_predict : bool, optional
            Whether to reset the reservoir state before prediction. Default is True.

        Returns
        -------
        np.ndarray
            The predicted values.
        """
        # Call the C++ model's predict method
        return self._cpp_model.predict(x, reset_state_before_predict)

    def predict_online(self, x: ArrayLike) -> np.ndarray:
        """Predict in online mode (updating state).

        Parameters
        ----------
        x : ArrayLike
            Input data.

        Returns
        -------
        np.ndarray
            The predicted values.
        """
        # Call the C++ model's predictOnline method
        return self._cpp_model.predictOnline(x)

    def predict_generative(self, prime_data: ArrayLike, n_steps: int) -> np.ndarray:
        """Generative prediction.

        Parameters
        ----------
        prime_data : ArrayLike
            Initial data to prime the reservoir.
        n_steps : int
            Number of steps to generate.

        Returns
        -------
        np.ndarray
            The generated data.
        """
        # Call the C++ model's predictGenerative method
        return self._cpp_model.predictGenerative(prime_data, n_steps)

    def get_reservoir(self, index: int) -> Any:  # noqa: ANN401
        """Get the reservoir object at the specified index.

        Parameters
        ----------
        index : int
            The index of the reservoir.

        Returns
        -------
        Any
            The C++ reservoir object.
        """
        # Return the C++ reservoir object
        return self._cpp_model.getReservoir(index)

    def reset_reservoirs(self) -> None:
        """Reset the states of all reservoirs."""
        # Call the C++ model's resetReservoirs method
        self._cpp_model.resetReservoirs()

    def partial_fit(self, x: ArrayLike | None, y: ArrayLike) -> None:
        """Update the model with a single sample (online learning).

        Parameters
        ----------
        x : ArrayLike, optional
            Input data sample. If None, the reservoir state is not advanced
            (useful if predict_online was already called).
        y : ArrayLike
            Target data sample.

        Raises
        ------
        RuntimeError
            If no reservoir or readout is set.
        """
        # Assuming only one reservoir for simplicity in online learning for now.
        # If multiple reservoirs are present, the logic would need to be more complex
        # to handle how their states are combined before feeding to the readout.
        if not self._reservoirs_params:
            msg = "No reservoir added to the model."
            raise RuntimeError(msg)
        if not self._readout_params:
            msg = "No readout set for the model."
            raise RuntimeError(msg)

        # Ensure readout is correctly initialized
        self._update_readout()

        if x is None:
            # If x is None, we use the current state of reservoirs
            # But the C++ partialFit expects an input to advance.
            # So if x is None, we call readout.partialFit directly with current state.
            cpp_readout = self._cpp_model.getReadout()
            # For multiple reservoirs, we'd need to combine states, which Model::partialFit handles.
            # If we want to support x=None for multiple reservoirs, we should add it to Model.cpp.
            # For now, let's keep it simple as it was.
            cpp_res = self._cpp_model.getReservoir(0)
            cpp_readout.partialFit(cpp_res.getState(), y)
        else:
            self._cpp_model.partialFit(x, y)

__init__(connection_type='serial')

Initialize the ESN model.

Parameters:

Name Type Description Default
connection_type str

The type of connection between reservoirs ("serial" or "parallel"). Default is "serial".

'serial'
Source code in python/rclib/model.py
def __init__(self, connection_type: str = "serial") -> None:
    """Initialize the ESN model.

    Parameters
    ----------
    connection_type : str, optional
        The type of connection between reservoirs ("serial" or "parallel").
        Default is "serial".
    """
    self.connection_type = connection_type
    self._reservoirs_params: list[Any] = []  # Store parameters for Python-side reservoir objects
    self._readout_params: Any = None  # Store parameters for Python-side readout object
    self._cpp_model = _rclib.Model()  # Initialize the C++ Model object

add_reservoir(reservoir)

Add a reservoir to the model.

Parameters:

Name Type Description Default
reservoir Any

The reservoir object to add.

required

Raises:

Type Description
TypeError

If the reservoir type is unsupported.

Source code in python/rclib/model.py
def add_reservoir(self, reservoir: Any) -> None:  # noqa: ANN401
    """Add a reservoir to the model.

    Parameters
    ----------
    reservoir : Any
        The reservoir object to add.

    Raises
    ------
    TypeError
        If the reservoir type is unsupported.
    """
    # Store the Python reservoir object's parameters
    self._reservoirs_params.append(reservoir)
    # Create and add the C++ reservoir to the C++ model
    if isinstance(reservoir, reservoirs.RandomSparse):
        cpp_res = _rclib.RandomSparseReservoir(
            reservoir.n_neurons,
            reservoir.spectral_radius,
            reservoir.sparsity,
            reservoir.leak_rate,
            reservoir.input_scaling,
            reservoir.include_bias,
            reservoir.seed,
        )
        self._cpp_model.addReservoir(cpp_res, self.connection_type)
    elif isinstance(reservoir, reservoirs.Nvar):
        cpp_res = _rclib.NvarReservoir(reservoir.num_lags)
        self._cpp_model.addReservoir(cpp_res, self.connection_type)
    # Add other reservoir types here as they are implemented
    else:
        msg = "Unsupported reservoir type"
        raise TypeError(msg)

    # Update readout in case it's using "auto" solver
    self._update_readout()

fit(x, y, washout_len=0)

Fit the model to the data.

Parameters:

Name Type Description Default
x ArrayLike

Input data.

required
y ArrayLike

Target data.

required
washout_len int

Number of initial samples to discard. Default is 0.

0
Source code in python/rclib/model.py
def fit(self, x: ArrayLike, y: ArrayLike, washout_len: int = 0) -> None:
    """Fit the model to the data.

    Parameters
    ----------
    x : ArrayLike
        Input data.
    y : ArrayLike
        Target data.
    washout_len : int, optional
        Number of initial samples to discard. Default is 0.
    """
    # Ensure readout is correctly initialized (especially for "auto" solver)
    self._update_readout()
    # Call the C++ model's fit method
    self._cpp_model.fit(x, y, washout_len)

get_reservoir(index)

Get the reservoir object at the specified index.

Parameters:

Name Type Description Default
index int

The index of the reservoir.

required

Returns:

Type Description
Any

The C++ reservoir object.

Source code in python/rclib/model.py
def get_reservoir(self, index: int) -> Any:  # noqa: ANN401
    """Get the reservoir object at the specified index.

    Parameters
    ----------
    index : int
        The index of the reservoir.

    Returns
    -------
    Any
        The C++ reservoir object.
    """
    # Return the C++ reservoir object
    return self._cpp_model.getReservoir(index)

partial_fit(x, y)

Update the model with a single sample (online learning).

Parameters:

Name Type Description Default
x ArrayLike

Input data sample. If None, the reservoir state is not advanced (useful if predict_online was already called).

required
y ArrayLike

Target data sample.

required

Raises:

Type Description
RuntimeError

If no reservoir or readout is set.

Source code in python/rclib/model.py
def partial_fit(self, x: ArrayLike | None, y: ArrayLike) -> None:
    """Update the model with a single sample (online learning).

    Parameters
    ----------
    x : ArrayLike, optional
        Input data sample. If None, the reservoir state is not advanced
        (useful if predict_online was already called).
    y : ArrayLike
        Target data sample.

    Raises
    ------
    RuntimeError
        If no reservoir or readout is set.
    """
    # Assuming only one reservoir for simplicity in online learning for now.
    # If multiple reservoirs are present, the logic would need to be more complex
    # to handle how their states are combined before feeding to the readout.
    if not self._reservoirs_params:
        msg = "No reservoir added to the model."
        raise RuntimeError(msg)
    if not self._readout_params:
        msg = "No readout set for the model."
        raise RuntimeError(msg)

    # Ensure readout is correctly initialized
    self._update_readout()

    if x is None:
        # If x is None, we use the current state of reservoirs
        # But the C++ partialFit expects an input to advance.
        # So if x is None, we call readout.partialFit directly with current state.
        cpp_readout = self._cpp_model.getReadout()
        # For multiple reservoirs, we'd need to combine states, which Model::partialFit handles.
        # If we want to support x=None for multiple reservoirs, we should add it to Model.cpp.
        # For now, let's keep it simple as it was.
        cpp_res = self._cpp_model.getReservoir(0)
        cpp_readout.partialFit(cpp_res.getState(), y)
    else:
        self._cpp_model.partialFit(x, y)

predict(x, *, reset_state_before_predict=True)

Predict using the trained model.

Parameters:

Name Type Description Default
x ArrayLike

Input data.

required
reset_state_before_predict bool

Whether to reset the reservoir state before prediction. Default is True.

True

Returns:

Type Description
ndarray

The predicted values.

Source code in python/rclib/model.py
def predict(self, x: ArrayLike, *, reset_state_before_predict: bool = True) -> np.ndarray:
    """Predict using the trained model.

    Parameters
    ----------
    x : ArrayLike
        Input data.
    reset_state_before_predict : bool, optional
        Whether to reset the reservoir state before prediction. Default is True.

    Returns
    -------
    np.ndarray
        The predicted values.
    """
    # Call the C++ model's predict method
    return self._cpp_model.predict(x, reset_state_before_predict)

predict_generative(prime_data, n_steps)

Generative prediction.

Parameters:

Name Type Description Default
prime_data ArrayLike

Initial data to prime the reservoir.

required
n_steps int

Number of steps to generate.

required

Returns:

Type Description
ndarray

The generated data.

Source code in python/rclib/model.py
def predict_generative(self, prime_data: ArrayLike, n_steps: int) -> np.ndarray:
    """Generative prediction.

    Parameters
    ----------
    prime_data : ArrayLike
        Initial data to prime the reservoir.
    n_steps : int
        Number of steps to generate.

    Returns
    -------
    np.ndarray
        The generated data.
    """
    # Call the C++ model's predictGenerative method
    return self._cpp_model.predictGenerative(prime_data, n_steps)

predict_online(x)

Predict in online mode (updating state).

Parameters:

Name Type Description Default
x ArrayLike

Input data.

required

Returns:

Type Description
ndarray

The predicted values.

Source code in python/rclib/model.py
def predict_online(self, x: ArrayLike) -> np.ndarray:
    """Predict in online mode (updating state).

    Parameters
    ----------
    x : ArrayLike
        Input data.

    Returns
    -------
    np.ndarray
        The predicted values.
    """
    # Call the C++ model's predictOnline method
    return self._cpp_model.predictOnline(x)

reset_reservoirs()

Reset the states of all reservoirs.

Source code in python/rclib/model.py
def reset_reservoirs(self) -> None:
    """Reset the states of all reservoirs."""
    # Call the C++ model's resetReservoirs method
    self._cpp_model.resetReservoirs()

set_readout(readout)

Set the readout for the model.

Parameters:

Name Type Description Default
readout Any

The readout object to set.

required

Raises:

Type Description
TypeError

If the readout type is unsupported.

Source code in python/rclib/model.py
def set_readout(self, readout: Any) -> None:  # noqa: ANN401
    """Set the readout for the model.

    Parameters
    ----------
    readout : Any
        The readout object to set.

    Raises
    ------
    TypeError
        If the readout type is unsupported.
    """
    # Store the Python readout object's parameters
    self._readout_params = readout
    self._update_readout()