Source code for qilisdk.ml.datasets.santa_fe_laser

# Copyright 2026 Qilimanjaro Quantum Tech
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations

from qilisdk.ml.datasets.dataset import Dataset, DatasetSample, build_prediction_sample
from qilisdk.ml.datasets.lorenz import integrate_lorenz


[docs] class SantaFeLaser(Dataset): r""" Santa Fe laser time series (equation-based version). The original *Santa Fe Time Series Competition* Data Set A is a recording of the chaotic intensity pulsations of a far-infrared :math:`\mathrm{NH_3}` laser. To instead generate this data on the fly rather than just using points points, this class reproduces the same qualitative dynamics from first principles using the single-mode **Lorenz--Haken** laser equations: .. math:: \dot{E} = \sigma (P - E), \quad \dot{P} = E (\rho - N) - P, \quad \dot{N} = E P - \beta N, where :math:`E` is the field amplitude, :math:`P` the polarization and :math:`N` the population inversion. The measured quantity is the laser **intensity** :math:`I \propto E^2`, which reproduces the behaviour of the Santa Fe recording. """ _DEFAULT_DRAW_STYLE = "1d" _DRAW_COMPONENT_LABELS = ("intensity",) def __init__( self, *, sigma: float = 10.0, rho: float = 28.0, beta: float = 8.0 / 3.0, initial_state: tuple[float, float, float] = (1.0, 1.0, 1.0), dt: float = 0.01, sample_every: int = 5, washout: int = 1000, horizon: int = 1, seed: int | None = None, ) -> None: """Configure a Santa Fe laser generator. Args: sigma (float): Field relaxation rate :math:`\\sigma`. Defaults to ``10.0``. rho (float): Pump parameter :math:`\\rho`. Defaults to ``28.0``. beta (float): Inversion relaxation rate :math:`\\beta`. Defaults to ``8/3``. initial_state (tuple[float, float, float]): Initial ``(E, P, N)`` state. Defaults to ``(1.0, 1.0, 1.0)``. dt (float): Internal integration step. Defaults to ``0.01``. sample_every (int): Sub-sampling stride. Defaults to ``5``. washout (int): Integration steps discarded as transient. Defaults to ``1000``. horizon (int): Prediction horizon in sampled steps. Defaults to ``1``. seed (int | None): Unused; the system is deterministic. Defaults to ``None``. Raises: ValueError: If ``dt`` or ``sample_every`` is not positive. """ super().__init__(seed=seed) if dt <= 0: raise ValueError(f"dt must be positive, got {dt}.") if sample_every < 1: raise ValueError(f"sample_every must be a positive integer, got {sample_every}.") self._sigma = sigma self._rho = rho self._beta = beta self._initial_state = initial_state self._dt = dt self._sample_every = sample_every self._washout = washout self._horizon = horizon
[docs] def generate(self, npoints: int) -> DatasetSample: """ Integrate the laser equations and build an intensity prediction sample. This produces a single time series of length ``npoints + horizon``, discarding the first ``washout`` steps, and then sub-sampling every ``sample_every` steps. The resulting series is split into ``inputs`` and ``targets``, where ``targets`` is the same series shifted forward by ``horizon``. Args: npoints (int): Number of time steps to produce. Returns: DatasetSample: The DataSetSample containing the inputs and targets. The output is a ``horizon``-step-ahead prediction task, so both ``inputs`` and ``targets`` are shaped ``(npoints, 1)``. Raises: ValueError: If ``npoints`` is not positive. """ if npoints < 1: raise ValueError(f"npoints must be a positive integer, got {npoints}.") needed = npoints + self._horizon n_steps = self._washout + needed * self._sample_every traj = integrate_lorenz( sigma=self._sigma, rho=self._rho, beta=self._beta, initial_state=self._initial_state, dt=self._dt, n_steps=n_steps, ) field = traj[self._washout :: self._sample_every, 0][:needed] intensity = (field**2).reshape(-1, 1) return build_prediction_sample(intensity, self._horizon)