Codi font per a qilisdk.ml.datasets.henon
# 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
import numpy as np
from qilisdk.ml.datasets.dataset import Dataset, DatasetSample, build_prediction_sample
[documents]
class HenonMap(Dataset):
r"""Hénon map, a two-dimensional discrete chaotic system.
The Hénon map uses:
.. math::
x_{n+1} = 1 - a\, x_n^2 + y_n, \qquad
y_{n+1} = b\, x_n,
which is chaotic for the parameters :math:`a = 1.4`,
:math:`b = 0.3`. :meth:`generate` returns a ``horizon``-step-ahead
prediction task over the two-dimensional state, so ``inputs`` and
``targets`` are both shaped ``(npoints, 2)``.
"""
_DEFAULT_DRAW_STYLE = "2d"
_DRAW_COMPONENT_LABELS = ("x", "y")
def __init__(
self,
*,
a: float = 1.4,
b: float = 0.3,
initial_state: tuple[float, float] = (0.0, 0.0),
washout: int = 100,
horizon: int = 1,
seed: int | None = None,
) -> None:
"""Configure a Hénon map generator.
Args:
a (float): Nonlinearity parameter :math:`a`. Defaults to ``1.4``.
b (float): Contraction parameter :math:`b`. Defaults to ``0.3``.
initial_state (tuple[float, float]): Initial ``(x, y)`` state. Defaults to ``(0.0, 0.0)``.
washout (int): Number of initial iterations discarded as transient. Defaults to ``100``.
horizon (int): Prediction horizon in steps. Defaults to ``1``.
seed (int | None): Unused; the system is deterministic. Defaults to ``None``.
"""
super().__init__(seed=seed)
self._a = a
self._b = b
self._initial_state = initial_state
self._washout = washout
self._horizon = horizon
[documents]
def generate(self, npoints: int) -> DatasetSample:
"""
Iterate the Hénon map and build a prediction sample.
This produces a single time series of length ``npoints + horizon``, discarding
the first ``washout`` 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, 2)``.
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
total = self._washout + needed
traj = np.empty((total, 2), dtype=np.float64)
traj[0] = np.asarray(self._initial_state, dtype=np.float64)
for i in range(total - 1):
x, y = traj[i]
traj[i + 1] = (1.0 - self._a * x * x + y, self._b * x)
sampled = traj[self._washout :]
return build_prediction_sample(sampled, self._horizon)