Código fuente para qilisdk.ml.datasets.lorenz

# 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 typing import TYPE_CHECKING, Any, ClassVar, cast

import numpy as np

from qilisdk.ml.datasets.dataset import Dataset, DatasetSample, FloatArray, build_prediction_sample, rk4_step

if TYPE_CHECKING:
    from qilisdk.utils.visualization.dataset_renderers import Transform


[documentos] def integrate_lorenz( *, sigma: float, rho: float, beta: float, initial_state: tuple[float, float, float], dt: float, n_steps: int, ) -> FloatArray: r""" Integrate the Lorenz system with a fixed-step RK4 scheme. The Lorenz equations are .. math:: \dot{x} = \sigma (y - x), \quad \dot{y} = x (\rho - z) - y, \quad \dot{z} = x y - \beta z. Args: sigma (float): Prandtl number :math:`\sigma`. rho (float): Rayleigh number :math:`\rho`. beta (float): Geometric factor :math:`\beta`. initial_state (tuple[float, float, float]): Initial ``(x, y, z)`` state. dt (float): Integration step. n_steps (int): Number of RK4 steps to take. Returns: FloatArray: The trajectory, shaped ``(n_steps + 1, 3)``. """ def deriv(state: FloatArray) -> FloatArray: x, y, z = state return np.array([sigma * (y - x), x * (rho - z) - y, x * y - beta * z], dtype=np.float64) traj = np.empty((n_steps + 1, 3), dtype=np.float64) traj[0] = np.asarray(initial_state, dtype=np.float64) for i in range(n_steps): traj[i + 1] = rk4_step(cast("FloatArray", traj[i]), dt, deriv) return traj
[documentos] class Lorenz(Dataset): r""" Lorenz attractor, a chaotic dynamical system. """ _DEFAULT_DRAW_STYLE = "3d" _DRAW_COMPONENT_LABELS = ("x", "y", "z") _DRAW_STYLE_DEFAULTS: ClassVar[dict[str, dict[str, Any]]] = { "2d": {"title": "Lorenz attractor (x-z projection)"}, "3d": {"title": "Lorenz attractor"}, } _DRAW_TRANSFORMS: ClassVar[dict[str, Transform]] = { "2d": lambda d: [("x", d[:, 0]), ("z", d[:, 2])], } 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 Lorenz generator. Args: sigma (float): Prandtl number :math:`\\sigma`. Defaults to ``10.0``. rho (float): Rayleigh number :math:`\\rho`. Defaults to ``28.0``. beta (float): Geometric factor :math:`\\beta`. Defaults to ``8/3``. initial_state (tuple[float, float, float]): Initial ``(x, y, z)`` 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
[documentos] def generate(self, npoints: int) -> DatasetSample: """ Integrate the Lorenz system and build a 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: A ``horizon``-step-ahead prediction pair, both arrays shaped ``(npoints, 3)``. 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, ) sampled = traj[self._washout :: self._sample_every][:needed] return build_prediction_sample(sampled, self._horizon)