tnfr 3.0.3__py3-none-any.whl → 8.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tnfr might be problematic. Click here for more details.
- tnfr/__init__.py +375 -56
- tnfr/__init__.pyi +33 -0
- tnfr/_compat.py +10 -0
- tnfr/_generated_version.py +34 -0
- tnfr/_version.py +49 -0
- tnfr/_version.pyi +7 -0
- tnfr/alias.py +723 -0
- tnfr/alias.pyi +108 -0
- tnfr/backends/__init__.py +354 -0
- tnfr/backends/jax_backend.py +173 -0
- tnfr/backends/numpy_backend.py +238 -0
- tnfr/backends/optimized_numpy.py +420 -0
- tnfr/backends/torch_backend.py +408 -0
- tnfr/cache.py +171 -0
- tnfr/cache.pyi +13 -0
- tnfr/cli/__init__.py +110 -0
- tnfr/cli/__init__.pyi +26 -0
- tnfr/cli/arguments.py +489 -0
- tnfr/cli/arguments.pyi +29 -0
- tnfr/cli/execution.py +914 -0
- tnfr/cli/execution.pyi +70 -0
- tnfr/cli/interactive_validator.py +614 -0
- tnfr/cli/utils.py +51 -0
- tnfr/cli/utils.pyi +7 -0
- tnfr/cli/validate.py +236 -0
- tnfr/compat/__init__.py +85 -0
- tnfr/compat/dataclass.py +136 -0
- tnfr/compat/jsonschema_stub.py +61 -0
- tnfr/compat/matplotlib_stub.py +73 -0
- tnfr/compat/numpy_stub.py +155 -0
- tnfr/config/__init__.py +224 -0
- tnfr/config/__init__.pyi +10 -0
- tnfr/config/constants.py +104 -0
- tnfr/config/constants.pyi +12 -0
- tnfr/config/defaults.py +54 -0
- tnfr/config/defaults_core.py +212 -0
- tnfr/config/defaults_init.py +33 -0
- tnfr/config/defaults_metric.py +104 -0
- tnfr/config/feature_flags.py +81 -0
- tnfr/config/feature_flags.pyi +16 -0
- tnfr/config/glyph_constants.py +31 -0
- tnfr/config/init.py +77 -0
- tnfr/config/init.pyi +8 -0
- tnfr/config/operator_names.py +254 -0
- tnfr/config/operator_names.pyi +36 -0
- tnfr/config/physics_derivation.py +354 -0
- tnfr/config/presets.py +83 -0
- tnfr/config/presets.pyi +7 -0
- tnfr/config/security.py +927 -0
- tnfr/config/thresholds.py +114 -0
- tnfr/config/tnfr_config.py +498 -0
- tnfr/constants/__init__.py +92 -0
- tnfr/constants/__init__.pyi +92 -0
- tnfr/constants/aliases.py +33 -0
- tnfr/constants/aliases.pyi +27 -0
- tnfr/constants/init.py +33 -0
- tnfr/constants/init.pyi +12 -0
- tnfr/constants/metric.py +104 -0
- tnfr/constants/metric.pyi +19 -0
- tnfr/core/__init__.py +33 -0
- tnfr/core/container.py +226 -0
- tnfr/core/default_implementations.py +329 -0
- tnfr/core/interfaces.py +279 -0
- tnfr/dynamics/__init__.py +238 -0
- tnfr/dynamics/__init__.pyi +83 -0
- tnfr/dynamics/adaptation.py +267 -0
- tnfr/dynamics/adaptation.pyi +7 -0
- tnfr/dynamics/adaptive_sequences.py +189 -0
- tnfr/dynamics/adaptive_sequences.pyi +14 -0
- tnfr/dynamics/aliases.py +23 -0
- tnfr/dynamics/aliases.pyi +19 -0
- tnfr/dynamics/bifurcation.py +232 -0
- tnfr/dynamics/canonical.py +229 -0
- tnfr/dynamics/canonical.pyi +48 -0
- tnfr/dynamics/coordination.py +385 -0
- tnfr/dynamics/coordination.pyi +25 -0
- tnfr/dynamics/dnfr.py +3034 -0
- tnfr/dynamics/dnfr.pyi +26 -0
- tnfr/dynamics/dynamic_limits.py +225 -0
- tnfr/dynamics/feedback.py +252 -0
- tnfr/dynamics/feedback.pyi +24 -0
- tnfr/dynamics/fused_dnfr.py +454 -0
- tnfr/dynamics/homeostasis.py +157 -0
- tnfr/dynamics/homeostasis.pyi +14 -0
- tnfr/dynamics/integrators.py +661 -0
- tnfr/dynamics/integrators.pyi +36 -0
- tnfr/dynamics/learning.py +310 -0
- tnfr/dynamics/learning.pyi +33 -0
- tnfr/dynamics/metabolism.py +254 -0
- tnfr/dynamics/nbody.py +796 -0
- tnfr/dynamics/nbody_tnfr.py +783 -0
- tnfr/dynamics/propagation.py +326 -0
- tnfr/dynamics/runtime.py +908 -0
- tnfr/dynamics/runtime.pyi +77 -0
- tnfr/dynamics/sampling.py +36 -0
- tnfr/dynamics/sampling.pyi +7 -0
- tnfr/dynamics/selectors.py +711 -0
- tnfr/dynamics/selectors.pyi +85 -0
- tnfr/dynamics/structural_clip.py +207 -0
- tnfr/errors/__init__.py +37 -0
- tnfr/errors/contextual.py +492 -0
- tnfr/execution.py +223 -0
- tnfr/execution.pyi +45 -0
- tnfr/extensions/__init__.py +205 -0
- tnfr/extensions/__init__.pyi +18 -0
- tnfr/extensions/base.py +173 -0
- tnfr/extensions/base.pyi +35 -0
- tnfr/extensions/business/__init__.py +71 -0
- tnfr/extensions/business/__init__.pyi +11 -0
- tnfr/extensions/business/cookbook.py +88 -0
- tnfr/extensions/business/cookbook.pyi +8 -0
- tnfr/extensions/business/health_analyzers.py +202 -0
- tnfr/extensions/business/health_analyzers.pyi +9 -0
- tnfr/extensions/business/patterns.py +183 -0
- tnfr/extensions/business/patterns.pyi +8 -0
- tnfr/extensions/medical/__init__.py +73 -0
- tnfr/extensions/medical/__init__.pyi +11 -0
- tnfr/extensions/medical/cookbook.py +88 -0
- tnfr/extensions/medical/cookbook.pyi +8 -0
- tnfr/extensions/medical/health_analyzers.py +181 -0
- tnfr/extensions/medical/health_analyzers.pyi +9 -0
- tnfr/extensions/medical/patterns.py +163 -0
- tnfr/extensions/medical/patterns.pyi +8 -0
- tnfr/flatten.py +262 -0
- tnfr/flatten.pyi +21 -0
- tnfr/gamma.py +354 -0
- tnfr/gamma.pyi +36 -0
- tnfr/glyph_history.py +377 -0
- tnfr/glyph_history.pyi +35 -0
- tnfr/glyph_runtime.py +19 -0
- tnfr/glyph_runtime.pyi +8 -0
- tnfr/immutable.py +218 -0
- tnfr/immutable.pyi +36 -0
- tnfr/initialization.py +203 -0
- tnfr/initialization.pyi +65 -0
- tnfr/io.py +10 -0
- tnfr/io.pyi +13 -0
- tnfr/locking.py +37 -0
- tnfr/locking.pyi +7 -0
- tnfr/mathematics/__init__.py +79 -0
- tnfr/mathematics/backend.py +453 -0
- tnfr/mathematics/backend.pyi +99 -0
- tnfr/mathematics/dynamics.py +408 -0
- tnfr/mathematics/dynamics.pyi +90 -0
- tnfr/mathematics/epi.py +391 -0
- tnfr/mathematics/epi.pyi +65 -0
- tnfr/mathematics/generators.py +242 -0
- tnfr/mathematics/generators.pyi +29 -0
- tnfr/mathematics/metrics.py +119 -0
- tnfr/mathematics/metrics.pyi +16 -0
- tnfr/mathematics/operators.py +239 -0
- tnfr/mathematics/operators.pyi +59 -0
- tnfr/mathematics/operators_factory.py +124 -0
- tnfr/mathematics/operators_factory.pyi +11 -0
- tnfr/mathematics/projection.py +87 -0
- tnfr/mathematics/projection.pyi +33 -0
- tnfr/mathematics/runtime.py +182 -0
- tnfr/mathematics/runtime.pyi +64 -0
- tnfr/mathematics/spaces.py +256 -0
- tnfr/mathematics/spaces.pyi +83 -0
- tnfr/mathematics/transforms.py +305 -0
- tnfr/mathematics/transforms.pyi +62 -0
- tnfr/metrics/__init__.py +79 -0
- tnfr/metrics/__init__.pyi +20 -0
- tnfr/metrics/buffer_cache.py +163 -0
- tnfr/metrics/buffer_cache.pyi +24 -0
- tnfr/metrics/cache_utils.py +214 -0
- tnfr/metrics/coherence.py +2009 -0
- tnfr/metrics/coherence.pyi +129 -0
- tnfr/metrics/common.py +158 -0
- tnfr/metrics/common.pyi +35 -0
- tnfr/metrics/core.py +316 -0
- tnfr/metrics/core.pyi +13 -0
- tnfr/metrics/diagnosis.py +833 -0
- tnfr/metrics/diagnosis.pyi +86 -0
- tnfr/metrics/emergence.py +245 -0
- tnfr/metrics/export.py +179 -0
- tnfr/metrics/export.pyi +7 -0
- tnfr/metrics/glyph_timing.py +379 -0
- tnfr/metrics/glyph_timing.pyi +81 -0
- tnfr/metrics/learning_metrics.py +280 -0
- tnfr/metrics/learning_metrics.pyi +21 -0
- tnfr/metrics/phase_coherence.py +351 -0
- tnfr/metrics/phase_compatibility.py +349 -0
- tnfr/metrics/reporting.py +183 -0
- tnfr/metrics/reporting.pyi +25 -0
- tnfr/metrics/sense_index.py +1203 -0
- tnfr/metrics/sense_index.pyi +9 -0
- tnfr/metrics/trig.py +373 -0
- tnfr/metrics/trig.pyi +13 -0
- tnfr/metrics/trig_cache.py +233 -0
- tnfr/metrics/trig_cache.pyi +10 -0
- tnfr/multiscale/__init__.py +32 -0
- tnfr/multiscale/hierarchical.py +517 -0
- tnfr/node.py +763 -0
- tnfr/node.pyi +139 -0
- tnfr/observers.py +255 -130
- tnfr/observers.pyi +31 -0
- tnfr/ontosim.py +144 -137
- tnfr/ontosim.pyi +28 -0
- tnfr/operators/__init__.py +1672 -0
- tnfr/operators/__init__.pyi +31 -0
- tnfr/operators/algebra.py +277 -0
- tnfr/operators/canonical_patterns.py +420 -0
- tnfr/operators/cascade.py +267 -0
- tnfr/operators/cycle_detection.py +358 -0
- tnfr/operators/definitions.py +4108 -0
- tnfr/operators/definitions.pyi +78 -0
- tnfr/operators/grammar.py +1164 -0
- tnfr/operators/grammar.pyi +140 -0
- tnfr/operators/hamiltonian.py +710 -0
- tnfr/operators/health_analyzer.py +809 -0
- tnfr/operators/jitter.py +272 -0
- tnfr/operators/jitter.pyi +11 -0
- tnfr/operators/lifecycle.py +314 -0
- tnfr/operators/metabolism.py +618 -0
- tnfr/operators/metrics.py +2138 -0
- tnfr/operators/network_analysis/__init__.py +27 -0
- tnfr/operators/network_analysis/source_detection.py +186 -0
- tnfr/operators/nodal_equation.py +395 -0
- tnfr/operators/pattern_detection.py +660 -0
- tnfr/operators/patterns.py +669 -0
- tnfr/operators/postconditions/__init__.py +38 -0
- tnfr/operators/postconditions/mutation.py +236 -0
- tnfr/operators/preconditions/__init__.py +1226 -0
- tnfr/operators/preconditions/coherence.py +305 -0
- tnfr/operators/preconditions/dissonance.py +236 -0
- tnfr/operators/preconditions/emission.py +128 -0
- tnfr/operators/preconditions/mutation.py +580 -0
- tnfr/operators/preconditions/reception.py +125 -0
- tnfr/operators/preconditions/resonance.py +364 -0
- tnfr/operators/registry.py +74 -0
- tnfr/operators/registry.pyi +9 -0
- tnfr/operators/remesh.py +1809 -0
- tnfr/operators/remesh.pyi +26 -0
- tnfr/operators/structural_units.py +268 -0
- tnfr/operators/unified_grammar.py +105 -0
- tnfr/parallel/__init__.py +54 -0
- tnfr/parallel/auto_scaler.py +234 -0
- tnfr/parallel/distributed.py +384 -0
- tnfr/parallel/engine.py +238 -0
- tnfr/parallel/gpu_engine.py +420 -0
- tnfr/parallel/monitoring.py +248 -0
- tnfr/parallel/partitioner.py +459 -0
- tnfr/py.typed +0 -0
- tnfr/recipes/__init__.py +22 -0
- tnfr/recipes/cookbook.py +743 -0
- tnfr/rng.py +178 -0
- tnfr/rng.pyi +26 -0
- tnfr/schemas/__init__.py +8 -0
- tnfr/schemas/grammar.json +94 -0
- tnfr/sdk/__init__.py +107 -0
- tnfr/sdk/__init__.pyi +19 -0
- tnfr/sdk/adaptive_system.py +173 -0
- tnfr/sdk/adaptive_system.pyi +21 -0
- tnfr/sdk/builders.py +370 -0
- tnfr/sdk/builders.pyi +51 -0
- tnfr/sdk/fluent.py +1121 -0
- tnfr/sdk/fluent.pyi +74 -0
- tnfr/sdk/templates.py +342 -0
- tnfr/sdk/templates.pyi +41 -0
- tnfr/sdk/utils.py +341 -0
- tnfr/secure_config.py +46 -0
- tnfr/security/__init__.py +70 -0
- tnfr/security/database.py +514 -0
- tnfr/security/subprocess.py +503 -0
- tnfr/security/validation.py +290 -0
- tnfr/selector.py +247 -0
- tnfr/selector.pyi +19 -0
- tnfr/sense.py +378 -0
- tnfr/sense.pyi +23 -0
- tnfr/services/__init__.py +17 -0
- tnfr/services/orchestrator.py +325 -0
- tnfr/sparse/__init__.py +39 -0
- tnfr/sparse/representations.py +492 -0
- tnfr/structural.py +705 -0
- tnfr/structural.pyi +83 -0
- tnfr/telemetry/__init__.py +35 -0
- tnfr/telemetry/cache_metrics.py +226 -0
- tnfr/telemetry/cache_metrics.pyi +64 -0
- tnfr/telemetry/nu_f.py +422 -0
- tnfr/telemetry/nu_f.pyi +108 -0
- tnfr/telemetry/verbosity.py +36 -0
- tnfr/telemetry/verbosity.pyi +15 -0
- tnfr/tokens.py +58 -0
- tnfr/tokens.pyi +36 -0
- tnfr/tools/__init__.py +20 -0
- tnfr/tools/domain_templates.py +478 -0
- tnfr/tools/sequence_generator.py +846 -0
- tnfr/topology/__init__.py +13 -0
- tnfr/topology/asymmetry.py +151 -0
- tnfr/trace.py +543 -0
- tnfr/trace.pyi +42 -0
- tnfr/tutorials/__init__.py +38 -0
- tnfr/tutorials/autonomous_evolution.py +285 -0
- tnfr/tutorials/interactive.py +1576 -0
- tnfr/tutorials/structural_metabolism.py +238 -0
- tnfr/types.py +775 -0
- tnfr/types.pyi +357 -0
- tnfr/units.py +68 -0
- tnfr/units.pyi +13 -0
- tnfr/utils/__init__.py +282 -0
- tnfr/utils/__init__.pyi +215 -0
- tnfr/utils/cache.py +4223 -0
- tnfr/utils/cache.pyi +470 -0
- tnfr/utils/callbacks.py +375 -0
- tnfr/utils/callbacks.pyi +49 -0
- tnfr/utils/chunks.py +108 -0
- tnfr/utils/chunks.pyi +22 -0
- tnfr/utils/data.py +428 -0
- tnfr/utils/data.pyi +74 -0
- tnfr/utils/graph.py +85 -0
- tnfr/utils/graph.pyi +10 -0
- tnfr/utils/init.py +821 -0
- tnfr/utils/init.pyi +80 -0
- tnfr/utils/io.py +559 -0
- tnfr/utils/io.pyi +66 -0
- tnfr/utils/numeric.py +114 -0
- tnfr/utils/numeric.pyi +21 -0
- tnfr/validation/__init__.py +257 -0
- tnfr/validation/__init__.pyi +85 -0
- tnfr/validation/compatibility.py +460 -0
- tnfr/validation/compatibility.pyi +6 -0
- tnfr/validation/config.py +73 -0
- tnfr/validation/graph.py +139 -0
- tnfr/validation/graph.pyi +18 -0
- tnfr/validation/input_validation.py +755 -0
- tnfr/validation/invariants.py +712 -0
- tnfr/validation/rules.py +253 -0
- tnfr/validation/rules.pyi +44 -0
- tnfr/validation/runtime.py +279 -0
- tnfr/validation/runtime.pyi +28 -0
- tnfr/validation/sequence_validator.py +162 -0
- tnfr/validation/soft_filters.py +170 -0
- tnfr/validation/soft_filters.pyi +32 -0
- tnfr/validation/spectral.py +164 -0
- tnfr/validation/spectral.pyi +42 -0
- tnfr/validation/validator.py +1266 -0
- tnfr/validation/window.py +39 -0
- tnfr/validation/window.pyi +1 -0
- tnfr/visualization/__init__.py +98 -0
- tnfr/visualization/cascade_viz.py +256 -0
- tnfr/visualization/hierarchy.py +284 -0
- tnfr/visualization/sequence_plotter.py +784 -0
- tnfr/viz/__init__.py +60 -0
- tnfr/viz/matplotlib.py +278 -0
- tnfr/viz/matplotlib.pyi +35 -0
- tnfr-8.5.0.dist-info/METADATA +573 -0
- tnfr-8.5.0.dist-info/RECORD +353 -0
- tnfr-8.5.0.dist-info/entry_points.txt +3 -0
- tnfr-3.0.3.dist-info/licenses/LICENSE.txt → tnfr-8.5.0.dist-info/licenses/LICENSE.md +1 -1
- tnfr/constants.py +0 -183
- tnfr/dynamics.py +0 -543
- tnfr/helpers.py +0 -198
- tnfr/main.py +0 -37
- tnfr/operators.py +0 -296
- tnfr-3.0.3.dist-info/METADATA +0 -35
- tnfr-3.0.3.dist-info/RECORD +0 -13
- {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/WHEEL +0 -0
- {tnfr-3.0.3.dist-info → tnfr-8.5.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"""Spectral dynamics helpers driven by ΔNFR generators."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import field
|
|
6
|
+
from typing import Any, NamedTuple, Sequence
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from ..compat.dataclass import dataclass
|
|
11
|
+
from .backend import MathematicsBackend, ensure_array, ensure_numpy, get_backend
|
|
12
|
+
from .spaces import HilbertSpace
|
|
13
|
+
|
|
14
|
+
try: # pragma: no cover - optional SciPy dependency
|
|
15
|
+
from scipy.linalg import expm as _scipy_expm # type: ignore
|
|
16
|
+
except Exception: # pragma: no cover - SciPy not installed
|
|
17
|
+
_scipy_expm = None
|
|
18
|
+
|
|
19
|
+
__all__ = ["MathematicalDynamicsEngine", "ContractiveDynamicsEngine"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _has_backend_matrix_exp(backend: MathematicsBackend) -> bool:
|
|
23
|
+
"""Return ``True`` when ``backend`` exposes a usable ``matrix_exp``."""
|
|
24
|
+
|
|
25
|
+
matrix_exp = getattr(backend, "matrix_exp", None)
|
|
26
|
+
if not callable(matrix_exp):
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
probe = ensure_array([[0.0]], dtype=np.complex128, backend=backend)
|
|
31
|
+
matrix_exp(probe)
|
|
32
|
+
except (AttributeError, NotImplementedError):
|
|
33
|
+
return False
|
|
34
|
+
except Exception:
|
|
35
|
+
# Older backends may surface missing implementations as runtime errors;
|
|
36
|
+
# treat them as signals to fall back to SciPy when available.
|
|
37
|
+
return False
|
|
38
|
+
return True
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _as_matrix(
|
|
42
|
+
matrix: Sequence[Sequence[complex]] | np.ndarray | Any,
|
|
43
|
+
*,
|
|
44
|
+
backend: MathematicsBackend,
|
|
45
|
+
) -> Any:
|
|
46
|
+
arr = ensure_array(matrix, dtype=np.complex128, backend=backend)
|
|
47
|
+
shape = getattr(arr, "shape", None)
|
|
48
|
+
if shape is None or len(shape) != 2 or shape[0] != shape[1]:
|
|
49
|
+
raise ValueError("Generator matrix must be square.")
|
|
50
|
+
return arr
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _is_hermitian(
|
|
54
|
+
matrix: Any, *, atol: float = 1e-9, backend: MathematicsBackend
|
|
55
|
+
) -> bool:
|
|
56
|
+
matrix_np = ensure_numpy(matrix, backend=backend)
|
|
57
|
+
return bool(np.allclose(matrix_np, matrix_np.conj().T, atol=atol))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _vectorize_density(matrix: Any, *, backend: MathematicsBackend) -> Any:
|
|
61
|
+
arr = ensure_array(matrix, dtype=np.complex128, backend=backend)
|
|
62
|
+
return arr.transpose(1, 0).reshape((-1,))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _devectorize_density(vector: Any, dim: int, *, backend: MathematicsBackend) -> Any:
|
|
66
|
+
arr = ensure_array(vector, dtype=np.complex128, backend=backend)
|
|
67
|
+
return arr.reshape((dim, dim)).transpose(1, 0)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class TraceValue(NamedTuple):
|
|
71
|
+
"""Container for trace evaluations in both backend and NumPy space."""
|
|
72
|
+
|
|
73
|
+
backend: Any
|
|
74
|
+
numpy: complex
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _trace(matrix: Any, *, backend: MathematicsBackend) -> TraceValue:
|
|
78
|
+
traced_backend = backend.einsum("ii->", matrix)
|
|
79
|
+
traced_numpy = complex(np.asarray(ensure_numpy(traced_backend, backend=backend)))
|
|
80
|
+
return TraceValue(traced_backend, traced_numpy)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass(slots=True)
|
|
84
|
+
class MathematicalDynamicsEngine:
|
|
85
|
+
"""Unitary evolution generated by Hermitian ΔNFR operators.
|
|
86
|
+
|
|
87
|
+
The engine accepts inputs expressed as backend-native tensors (NumPy,
|
|
88
|
+
:mod:`jax`, :mod:`torch`). When the configured backend supports automatic
|
|
89
|
+
differentiation the evolution map ``exp(-i·Δ·dt)`` remains differentiable
|
|
90
|
+
because native propagators are now preferred. Passing ``use_scipy=True``
|
|
91
|
+
explicitly opts into SciPy's exponential; we only fall back automatically
|
|
92
|
+
when the backend lacks a ``matrix_exp`` implementation.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
generator: np.ndarray
|
|
96
|
+
hilbert_space: HilbertSpace
|
|
97
|
+
atol: float = 1e-9
|
|
98
|
+
_use_scipy: bool = False
|
|
99
|
+
backend: MathematicsBackend = field(init=False, repr=False)
|
|
100
|
+
_generator_backend: Any = field(init=False, repr=False)
|
|
101
|
+
_numpy_generator: np.ndarray = field(init=False, repr=False)
|
|
102
|
+
|
|
103
|
+
def __init__(
|
|
104
|
+
self,
|
|
105
|
+
generator: Sequence[Sequence[complex]] | np.ndarray | Any,
|
|
106
|
+
hilbert_space: HilbertSpace,
|
|
107
|
+
*,
|
|
108
|
+
atol: float = 1e-9,
|
|
109
|
+
use_scipy: bool | None = None,
|
|
110
|
+
backend: MathematicsBackend | None = None,
|
|
111
|
+
) -> None:
|
|
112
|
+
resolved_backend = backend or get_backend()
|
|
113
|
+
matrix = _as_matrix(generator, backend=resolved_backend)
|
|
114
|
+
matrix_np = ensure_numpy(matrix, backend=resolved_backend)
|
|
115
|
+
if matrix_np.shape != (hilbert_space.dimension, hilbert_space.dimension):
|
|
116
|
+
raise ValueError("Generator dimension must match the Hilbert space.")
|
|
117
|
+
if not _is_hermitian(matrix, atol=atol, backend=resolved_backend):
|
|
118
|
+
raise ValueError("Dynamics generator must be Hermitian.")
|
|
119
|
+
self.backend = resolved_backend
|
|
120
|
+
self._generator_backend = matrix
|
|
121
|
+
self._numpy_generator = matrix_np
|
|
122
|
+
self.generator = matrix_np
|
|
123
|
+
self.hilbert_space = hilbert_space
|
|
124
|
+
self.atol = float(atol)
|
|
125
|
+
if use_scipy is None:
|
|
126
|
+
has_matrix_exp = _has_backend_matrix_exp(self.backend)
|
|
127
|
+
if has_matrix_exp:
|
|
128
|
+
self._use_scipy = False
|
|
129
|
+
elif _scipy_expm is not None:
|
|
130
|
+
self._use_scipy = True
|
|
131
|
+
else:
|
|
132
|
+
raise RuntimeError(
|
|
133
|
+
"Backend lacks matrix_exp and SciPy is unavailable for fallback."
|
|
134
|
+
)
|
|
135
|
+
else:
|
|
136
|
+
if use_scipy and _scipy_expm is None:
|
|
137
|
+
raise RuntimeError("SciPy expm requested but SciPy is not available.")
|
|
138
|
+
self._use_scipy = bool(use_scipy and _scipy_expm is not None)
|
|
139
|
+
|
|
140
|
+
def _unitary_backend(self, dt: float) -> Any:
|
|
141
|
+
if self._use_scipy and _scipy_expm is not None:
|
|
142
|
+
return ensure_array(
|
|
143
|
+
_scipy_expm(-1j * dt * self._numpy_generator),
|
|
144
|
+
backend=self.backend,
|
|
145
|
+
)
|
|
146
|
+
return self.backend.matrix_exp(-1j * dt * self._generator_backend)
|
|
147
|
+
|
|
148
|
+
def step(
|
|
149
|
+
self,
|
|
150
|
+
state: Sequence[complex] | np.ndarray | Any,
|
|
151
|
+
*,
|
|
152
|
+
dt: float = 1.0,
|
|
153
|
+
normalize: bool = True,
|
|
154
|
+
) -> Any:
|
|
155
|
+
"""Evolve ``state`` by ``dt`` using the unitary ``exp(-i·Δ·dt)``."""
|
|
156
|
+
|
|
157
|
+
vector = ensure_array(state, dtype=np.complex128, backend=self.backend)
|
|
158
|
+
if vector.shape != (self.hilbert_space.dimension,):
|
|
159
|
+
raise ValueError(
|
|
160
|
+
"State dimension mismatch: "
|
|
161
|
+
f"expected ({self.hilbert_space.dimension},), received {vector.shape!r}."
|
|
162
|
+
)
|
|
163
|
+
unitary = self._unitary_backend(dt)
|
|
164
|
+
evolved = self.backend.matmul(unitary, vector)
|
|
165
|
+
if normalize:
|
|
166
|
+
norm_backend = self.backend.norm(evolved)
|
|
167
|
+
norm_numpy = float(
|
|
168
|
+
np.asarray(ensure_numpy(norm_backend, backend=self.backend))
|
|
169
|
+
)
|
|
170
|
+
if np.isclose(norm_numpy, 0.0, atol=self.atol):
|
|
171
|
+
raise ValueError("Cannot normalise a null state vector.")
|
|
172
|
+
evolved = evolved / norm_backend
|
|
173
|
+
return evolved
|
|
174
|
+
|
|
175
|
+
def evolve(
|
|
176
|
+
self,
|
|
177
|
+
state: Sequence[complex] | np.ndarray | Any,
|
|
178
|
+
*,
|
|
179
|
+
steps: int,
|
|
180
|
+
dt: float = 1.0,
|
|
181
|
+
normalize: bool = True,
|
|
182
|
+
) -> Any:
|
|
183
|
+
"""Return trajectory of length ``steps + 1`` starting from ``state``."""
|
|
184
|
+
|
|
185
|
+
if steps < 0:
|
|
186
|
+
raise ValueError("steps must be non-negative.")
|
|
187
|
+
current = ensure_array(state, dtype=np.complex128, backend=self.backend)
|
|
188
|
+
if current.shape != (self.hilbert_space.dimension,):
|
|
189
|
+
raise ValueError(
|
|
190
|
+
"State dimension mismatch: "
|
|
191
|
+
f"expected ({self.hilbert_space.dimension},), received {current.shape!r}."
|
|
192
|
+
)
|
|
193
|
+
trajectory: list[Any] = [current]
|
|
194
|
+
for _ in range(steps):
|
|
195
|
+
current = self.step(current, dt=dt, normalize=normalize)
|
|
196
|
+
trajectory.append(current)
|
|
197
|
+
return self.backend.stack(trajectory, axis=0)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@dataclass(slots=True)
|
|
201
|
+
class ContractiveDynamicsEngine:
|
|
202
|
+
"""Contractive semigroup evolution driven by Lindblad ΔNFR generators.
|
|
203
|
+
|
|
204
|
+
Backend-native tensors are accepted for all density operators. When the
|
|
205
|
+
chosen backend supports automatic differentiation we keep gradients intact
|
|
206
|
+
by default because native semigroup propagators are preferred. Requesting
|
|
207
|
+
``use_scipy=True`` still falls back to SciPy's :func:`scipy.linalg.expm`,
|
|
208
|
+
primarily for generators missing backend support.
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
generator: np.ndarray
|
|
212
|
+
hilbert_space: HilbertSpace
|
|
213
|
+
atol: float = 1e-9
|
|
214
|
+
_use_scipy: bool = False
|
|
215
|
+
backend: MathematicsBackend = field(init=False, repr=False)
|
|
216
|
+
_generator_backend: Any = field(init=False, repr=False)
|
|
217
|
+
_numpy_generator: np.ndarray = field(init=False, repr=False)
|
|
218
|
+
_identity_backend: Any = field(init=False, repr=False)
|
|
219
|
+
_last_contractivity_gap: float = field(init=False, repr=False)
|
|
220
|
+
|
|
221
|
+
def __init__(
|
|
222
|
+
self,
|
|
223
|
+
generator: Sequence[Sequence[complex]] | np.ndarray | Any,
|
|
224
|
+
hilbert_space: HilbertSpace,
|
|
225
|
+
*,
|
|
226
|
+
atol: float = 1e-9,
|
|
227
|
+
ensure_contractive: bool = True,
|
|
228
|
+
use_scipy: bool | None = None,
|
|
229
|
+
backend: MathematicsBackend | None = None,
|
|
230
|
+
) -> None:
|
|
231
|
+
resolved_backend = backend or get_backend()
|
|
232
|
+
matrix = _as_matrix(generator, backend=resolved_backend)
|
|
233
|
+
matrix_np = ensure_numpy(matrix, backend=resolved_backend)
|
|
234
|
+
expected = hilbert_space.dimension * hilbert_space.dimension
|
|
235
|
+
if matrix_np.shape != (expected, expected):
|
|
236
|
+
raise ValueError(
|
|
237
|
+
"Generator must act on vectorised density operators with dimension "
|
|
238
|
+
f"{expected} × {expected}."
|
|
239
|
+
)
|
|
240
|
+
self.backend = resolved_backend
|
|
241
|
+
self._generator_backend = matrix
|
|
242
|
+
self._numpy_generator = matrix_np.astype(np.complex128, copy=False)
|
|
243
|
+
self.generator = self._numpy_generator
|
|
244
|
+
self.hilbert_space = hilbert_space
|
|
245
|
+
self.atol = float(atol)
|
|
246
|
+
if use_scipy is None:
|
|
247
|
+
has_matrix_exp = _has_backend_matrix_exp(self.backend)
|
|
248
|
+
if has_matrix_exp:
|
|
249
|
+
self._use_scipy = False
|
|
250
|
+
elif _scipy_expm is not None:
|
|
251
|
+
self._use_scipy = True
|
|
252
|
+
else:
|
|
253
|
+
raise RuntimeError(
|
|
254
|
+
"Backend lacks matrix_exp and SciPy is unavailable for fallback."
|
|
255
|
+
)
|
|
256
|
+
else:
|
|
257
|
+
if use_scipy and _scipy_expm is None:
|
|
258
|
+
raise RuntimeError("SciPy expm requested but SciPy is not available.")
|
|
259
|
+
self._use_scipy = bool(use_scipy and _scipy_expm is not None)
|
|
260
|
+
|
|
261
|
+
self._identity_backend = ensure_array(
|
|
262
|
+
np.eye(hilbert_space.dimension, dtype=np.complex128),
|
|
263
|
+
backend=self.backend,
|
|
264
|
+
)
|
|
265
|
+
self._last_contractivity_gap = float("nan")
|
|
266
|
+
if ensure_contractive:
|
|
267
|
+
eigenvalues_backend, _ = self.backend.eig(self._generator_backend)
|
|
268
|
+
eigenvalues = ensure_numpy(eigenvalues_backend, backend=self.backend)
|
|
269
|
+
if np.max(eigenvalues.real) > self.atol:
|
|
270
|
+
raise ValueError(
|
|
271
|
+
"ΔNFR generator is not contractive: positive real eigenvalues detected."
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
def _propagator_backend(self, dt: float) -> Any:
|
|
275
|
+
if self._use_scipy and _scipy_expm is not None:
|
|
276
|
+
return ensure_array(
|
|
277
|
+
_scipy_expm(dt * self._numpy_generator),
|
|
278
|
+
backend=self.backend,
|
|
279
|
+
)
|
|
280
|
+
return self.backend.matrix_exp(dt * self._generator_backend)
|
|
281
|
+
|
|
282
|
+
def frobenius_norm(
|
|
283
|
+
self,
|
|
284
|
+
density: Sequence[Sequence[complex]] | np.ndarray | Any,
|
|
285
|
+
*,
|
|
286
|
+
center: bool = False,
|
|
287
|
+
) -> float:
|
|
288
|
+
"""Return the Frobenius norm associated with the Hilbert space."""
|
|
289
|
+
|
|
290
|
+
matrix = ensure_array(density, dtype=np.complex128, backend=self.backend)
|
|
291
|
+
if matrix.shape != (self.hilbert_space.dimension, self.hilbert_space.dimension):
|
|
292
|
+
raise ValueError(
|
|
293
|
+
"Density operator dimension mismatch: "
|
|
294
|
+
f"expected {(self.hilbert_space.dimension, self.hilbert_space.dimension)}, "
|
|
295
|
+
f"received {matrix.shape!r}."
|
|
296
|
+
)
|
|
297
|
+
if center:
|
|
298
|
+
trace_value = _trace(matrix, backend=self.backend)
|
|
299
|
+
trace_backend = trace_value.backend / self.hilbert_space.dimension
|
|
300
|
+
matrix = matrix - trace_backend * self._identity_backend
|
|
301
|
+
norm_backend = self.backend.norm(matrix, ord="fro")
|
|
302
|
+
return float(np.asarray(ensure_numpy(norm_backend, backend=self.backend)))
|
|
303
|
+
|
|
304
|
+
@property
|
|
305
|
+
def last_contractivity_gap(self) -> float:
|
|
306
|
+
"""Return the latest monitored contractivity gap (NaN if unavailable)."""
|
|
307
|
+
|
|
308
|
+
return float(self._last_contractivity_gap)
|
|
309
|
+
|
|
310
|
+
def step(
|
|
311
|
+
self,
|
|
312
|
+
density: Sequence[Sequence[complex]] | np.ndarray | Any,
|
|
313
|
+
*,
|
|
314
|
+
dt: float = 1.0,
|
|
315
|
+
normalize_trace: bool = True,
|
|
316
|
+
enforce_contractivity: bool = True,
|
|
317
|
+
raise_on_violation: bool = False,
|
|
318
|
+
symmetrize: bool = True,
|
|
319
|
+
) -> Any:
|
|
320
|
+
"""Advance ``density`` by ``dt`` enforcing trace and contractivity control."""
|
|
321
|
+
|
|
322
|
+
matrix = ensure_array(density, dtype=np.complex128, backend=self.backend)
|
|
323
|
+
dim = self.hilbert_space.dimension
|
|
324
|
+
if matrix.shape != (dim, dim):
|
|
325
|
+
raise ValueError(
|
|
326
|
+
"Density operator dimension mismatch: "
|
|
327
|
+
f"expected {(dim, dim)}, received {matrix.shape!r}."
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
initial_norm = None
|
|
331
|
+
if enforce_contractivity:
|
|
332
|
+
trace_value = _trace(matrix, backend=self.backend)
|
|
333
|
+
trace_backend = trace_value.backend / dim
|
|
334
|
+
centered = matrix - trace_backend * self._identity_backend
|
|
335
|
+
initial_norm_backend = self.backend.norm(centered, ord="fro")
|
|
336
|
+
initial_norm = float(
|
|
337
|
+
np.asarray(ensure_numpy(initial_norm_backend, backend=self.backend))
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
vector = _vectorize_density(matrix, backend=self.backend)
|
|
341
|
+
propagator = self._propagator_backend(dt)
|
|
342
|
+
evolved_vec = self.backend.matmul(propagator, vector)
|
|
343
|
+
evolved = _devectorize_density(evolved_vec, dim, backend=self.backend)
|
|
344
|
+
|
|
345
|
+
if symmetrize:
|
|
346
|
+
evolved = 0.5 * (evolved + self.backend.conjugate_transpose(evolved))
|
|
347
|
+
|
|
348
|
+
if normalize_trace:
|
|
349
|
+
trace_value = _trace(evolved, backend=self.backend)
|
|
350
|
+
if np.isclose(trace_value.numpy, 0.0, atol=self.atol):
|
|
351
|
+
raise ValueError("Trace collapsed below tolerance during evolution.")
|
|
352
|
+
if not np.isclose(trace_value.numpy, 1.0, atol=10 * self.atol):
|
|
353
|
+
evolved = evolved / trace_value.backend
|
|
354
|
+
|
|
355
|
+
if enforce_contractivity and initial_norm is not None:
|
|
356
|
+
trace_value = _trace(evolved, backend=self.backend)
|
|
357
|
+
trace_backend = trace_value.backend / dim
|
|
358
|
+
centered = evolved - trace_backend * self._identity_backend
|
|
359
|
+
evolved_norm_backend = self.backend.norm(centered, ord="fro")
|
|
360
|
+
evolved_norm = float(
|
|
361
|
+
np.asarray(ensure_numpy(evolved_norm_backend, backend=self.backend))
|
|
362
|
+
)
|
|
363
|
+
self._last_contractivity_gap = initial_norm - evolved_norm
|
|
364
|
+
if raise_on_violation and self._last_contractivity_gap < -5 * self.atol:
|
|
365
|
+
raise ValueError(
|
|
366
|
+
"Contractivity violated: Frobenius norm increased beyond tolerance."
|
|
367
|
+
)
|
|
368
|
+
else:
|
|
369
|
+
self._last_contractivity_gap = float("nan")
|
|
370
|
+
|
|
371
|
+
return evolved
|
|
372
|
+
|
|
373
|
+
def evolve(
|
|
374
|
+
self,
|
|
375
|
+
density: Sequence[Sequence[complex]] | np.ndarray | Any,
|
|
376
|
+
*,
|
|
377
|
+
steps: int,
|
|
378
|
+
dt: float = 1.0,
|
|
379
|
+
normalize_trace: bool = True,
|
|
380
|
+
enforce_contractivity: bool = True,
|
|
381
|
+
raise_on_violation: bool = False,
|
|
382
|
+
symmetrize: bool = True,
|
|
383
|
+
) -> Any:
|
|
384
|
+
"""Return trajectory of density operators for the contractive semigroup."""
|
|
385
|
+
|
|
386
|
+
if steps < 0:
|
|
387
|
+
raise ValueError("steps must be non-negative.")
|
|
388
|
+
|
|
389
|
+
current = ensure_array(density, dtype=np.complex128, backend=self.backend)
|
|
390
|
+
dim = self.hilbert_space.dimension
|
|
391
|
+
if current.shape != (dim, dim):
|
|
392
|
+
raise ValueError(
|
|
393
|
+
"Density operator dimension mismatch: "
|
|
394
|
+
f"expected {(dim, dim)}, received {current.shape!r}."
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
trajectory: list[Any] = [current]
|
|
398
|
+
for _ in range(steps):
|
|
399
|
+
current = self.step(
|
|
400
|
+
current,
|
|
401
|
+
dt=dt,
|
|
402
|
+
normalize_trace=normalize_trace,
|
|
403
|
+
enforce_contractivity=enforce_contractivity,
|
|
404
|
+
raise_on_violation=raise_on_violation,
|
|
405
|
+
symmetrize=symmetrize,
|
|
406
|
+
)
|
|
407
|
+
trajectory.append(current)
|
|
408
|
+
return self.backend.stack(trajectory, axis=0)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from .backend import MathematicsBackend
|
|
5
|
+
from .spaces import HilbertSpace
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import Any, NamedTuple, Sequence
|
|
8
|
+
|
|
9
|
+
__all__ = ["MathematicalDynamicsEngine", "ContractiveDynamicsEngine"]
|
|
10
|
+
|
|
11
|
+
class TraceValue(NamedTuple):
|
|
12
|
+
backend: Any
|
|
13
|
+
numpy: complex
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class MathematicalDynamicsEngine:
|
|
17
|
+
generator: np.ndarray
|
|
18
|
+
hilbert_space: HilbertSpace
|
|
19
|
+
atol: float = ...
|
|
20
|
+
backend: MathematicsBackend = field(init=False, repr=False)
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
generator: Sequence[Sequence[complex]] | np.ndarray | Any,
|
|
24
|
+
hilbert_space: HilbertSpace,
|
|
25
|
+
*,
|
|
26
|
+
atol: float = 1e-09,
|
|
27
|
+
use_scipy: bool | None = None,
|
|
28
|
+
backend: MathematicsBackend | None = None,
|
|
29
|
+
) -> None: ...
|
|
30
|
+
def step(
|
|
31
|
+
self,
|
|
32
|
+
state: Sequence[complex] | np.ndarray | Any,
|
|
33
|
+
*,
|
|
34
|
+
dt: float = 1.0,
|
|
35
|
+
normalize: bool = True,
|
|
36
|
+
) -> Any: ...
|
|
37
|
+
def evolve(
|
|
38
|
+
self,
|
|
39
|
+
state: Sequence[complex] | np.ndarray | Any,
|
|
40
|
+
*,
|
|
41
|
+
steps: int,
|
|
42
|
+
dt: float = 1.0,
|
|
43
|
+
normalize: bool = True,
|
|
44
|
+
) -> Any: ...
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class ContractiveDynamicsEngine:
|
|
48
|
+
generator: np.ndarray
|
|
49
|
+
hilbert_space: HilbertSpace
|
|
50
|
+
atol: float = ...
|
|
51
|
+
backend: MathematicsBackend = field(init=False, repr=False)
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
generator: Sequence[Sequence[complex]] | np.ndarray | Any,
|
|
55
|
+
hilbert_space: HilbertSpace,
|
|
56
|
+
*,
|
|
57
|
+
atol: float = 1e-09,
|
|
58
|
+
ensure_contractive: bool = True,
|
|
59
|
+
use_scipy: bool | None = None,
|
|
60
|
+
backend: MathematicsBackend | None = None,
|
|
61
|
+
) -> None: ...
|
|
62
|
+
def frobenius_norm(
|
|
63
|
+
self,
|
|
64
|
+
density: Sequence[Sequence[complex]] | np.ndarray | Any,
|
|
65
|
+
*,
|
|
66
|
+
center: bool = False,
|
|
67
|
+
) -> float: ...
|
|
68
|
+
@property
|
|
69
|
+
def last_contractivity_gap(self) -> float: ...
|
|
70
|
+
def step(
|
|
71
|
+
self,
|
|
72
|
+
density: Sequence[Sequence[complex]] | np.ndarray | Any,
|
|
73
|
+
*,
|
|
74
|
+
dt: float = 1.0,
|
|
75
|
+
normalize_trace: bool = True,
|
|
76
|
+
enforce_contractivity: bool = True,
|
|
77
|
+
raise_on_violation: bool = False,
|
|
78
|
+
symmetrize: bool = True,
|
|
79
|
+
) -> Any: ...
|
|
80
|
+
def evolve(
|
|
81
|
+
self,
|
|
82
|
+
density: Sequence[Sequence[complex]] | np.ndarray | Any,
|
|
83
|
+
*,
|
|
84
|
+
steps: int,
|
|
85
|
+
dt: float = 1.0,
|
|
86
|
+
normalize_trace: bool = True,
|
|
87
|
+
enforce_contractivity: bool = True,
|
|
88
|
+
raise_on_violation: bool = False,
|
|
89
|
+
symmetrize: bool = True,
|
|
90
|
+
) -> Any: ...
|