openquantumsim 0.1.0a1__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.
Files changed (35) hide show
  1. openquantumsim/__init__.py +202 -0
  2. openquantumsim/_julia_bridge.py +80 -0
  3. openquantumsim/_version.py +3 -0
  4. openquantumsim/correlations.py +144 -0
  5. openquantumsim/hilbert.py +114 -0
  6. openquantumsim/julia/OpenQuantumSimJL/Manifest.toml +1644 -0
  7. openquantumsim/julia/OpenQuantumSimJL/Project.toml +26 -0
  8. openquantumsim/julia/OpenQuantumSimJL/src/Correlations.jl +205 -0
  9. openquantumsim/julia/OpenQuantumSimJL/src/HilbertSpace.jl +51 -0
  10. openquantumsim/julia/OpenQuantumSimJL/src/Lindblad.jl +155 -0
  11. openquantumsim/julia/OpenQuantumSimJL/src/Observables.jl +78 -0
  12. openquantumsim/julia/OpenQuantumSimJL/src/OpenQuantumSimJL.jl +34 -0
  13. openquantumsim/julia/OpenQuantumSimJL/src/Operators.jl +99 -0
  14. openquantumsim/julia/OpenQuantumSimJL/src/Parallel.jl +1 -0
  15. openquantumsim/julia/OpenQuantumSimJL/src/Propagators.jl +40 -0
  16. openquantumsim/julia/OpenQuantumSimJL/src/SteadyState.jl +61 -0
  17. openquantumsim/julia/OpenQuantumSimJL/src/TimeDep.jl +191 -0
  18. openquantumsim/julia/OpenQuantumSimJL/src/Trajectories.jl +600 -0
  19. openquantumsim/julia/OpenQuantumSimJL/src/Utils.jl +1 -0
  20. openquantumsim/julia/OpenQuantumSimJL/test/runtests.jl +497 -0
  21. openquantumsim/observables.py +661 -0
  22. openquantumsim/operators.py +462 -0
  23. openquantumsim/phase_space.py +153 -0
  24. openquantumsim/plot.py +210 -0
  25. openquantumsim/py.typed +1 -0
  26. openquantumsim/result.py +332 -0
  27. openquantumsim/solvers.py +704 -0
  28. openquantumsim/sweep.py +562 -0
  29. openquantumsim/systems.py +136 -0
  30. openquantumsim/timedep.py +162 -0
  31. openquantumsim-0.1.0a1.dist-info/METADATA +427 -0
  32. openquantumsim-0.1.0a1.dist-info/RECORD +35 -0
  33. openquantumsim-0.1.0a1.dist-info/WHEEL +5 -0
  34. openquantumsim-0.1.0a1.dist-info/licenses/LICENSE +21 -0
  35. openquantumsim-0.1.0a1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,202 @@
1
+ """OpenQuantumSim public Python API."""
2
+
3
+ from ._version import __version__
4
+ from .correlations import correlation_2op_1t, correlation_2op_2t
5
+ from .hilbert import CompositeSpace, DickeSpace, FockSpace, HilbertSpace, SpinSpace
6
+ from .observables import (
7
+ StateObservable,
8
+ bipartite_mutual_information,
9
+ bloch_observables,
10
+ bloch_vector,
11
+ bures_angle,
12
+ bures_distance,
13
+ entropy_observable,
14
+ evaluate_state_observables,
15
+ expect,
16
+ fidelity,
17
+ fidelity_observable,
18
+ hilbert_schmidt_distance,
19
+ infidelity,
20
+ is_density_matrix,
21
+ is_hermitian,
22
+ l1_coherence,
23
+ l1_coherence_observable,
24
+ linear_entropy,
25
+ linear_entropy_observable,
26
+ mutual_information,
27
+ normalize_state,
28
+ partial_trace,
29
+ partial_traces,
30
+ participation_ratio,
31
+ participation_ratio_observable,
32
+ population_observable,
33
+ population_observables,
34
+ populations,
35
+ purity,
36
+ purity_observable,
37
+ renyi_entropy,
38
+ state_metrics,
39
+ trace_distance,
40
+ trace_distance_observable,
41
+ trace_norm,
42
+ von_neumann_entropy,
43
+ )
44
+ from .operators import (
45
+ Operator,
46
+ basis,
47
+ coherent,
48
+ collective_excitation,
49
+ collective_lowering,
50
+ collective_raising,
51
+ collective_x,
52
+ collective_z,
53
+ create,
54
+ destroy,
55
+ dicke_excitation,
56
+ dicke_jm,
57
+ dicke_jp,
58
+ dicke_jx,
59
+ dicke_jz,
60
+ dicke_state,
61
+ eye,
62
+ fock,
63
+ ket2dm,
64
+ num,
65
+ sigmam,
66
+ sigmap,
67
+ sigmax,
68
+ sigmay,
69
+ sigmaz,
70
+ spin_jm,
71
+ spin_jp,
72
+ spin_jx,
73
+ spin_jz,
74
+ tensor,
75
+ thermal_dm,
76
+ )
77
+ from .phase_space import phase_space_grid, q_function, wigner
78
+ from .plot import (
79
+ expect_plot,
80
+ plot_density_matrix,
81
+ plot_expectations,
82
+ plot_phase_space,
83
+ plot_q_function,
84
+ plot_state_observable,
85
+ plot_wigner,
86
+ )
87
+ from .result import Options, QuantumSystem, Result, load_result
88
+ from .solvers import mcsolve, mesolve, single_trajectory, steadystate
89
+ from .sweep import ParameterSweep, SweepPoint, SweepRunResult
90
+ from .systems import JaynesCummingsSystem, MCWFSystem, jaynes_cummings_system
91
+ from .timedep import (
92
+ HamiltonianTerm,
93
+ InterpolatedCoefficient,
94
+ TimeDependentHamiltonian,
95
+ time_dependent_hamiltonian,
96
+ )
97
+
98
+ __all__ = [
99
+ "__version__",
100
+ "CompositeSpace",
101
+ "DickeSpace",
102
+ "FockSpace",
103
+ "HamiltonianTerm",
104
+ "HilbertSpace",
105
+ "InterpolatedCoefficient",
106
+ "JaynesCummingsSystem",
107
+ "MCWFSystem",
108
+ "Operator",
109
+ "Options",
110
+ "ParameterSweep",
111
+ "QuantumSystem",
112
+ "Result",
113
+ "StateObservable",
114
+ "SpinSpace",
115
+ "SweepPoint",
116
+ "SweepRunResult",
117
+ "TimeDependentHamiltonian",
118
+ "basis",
119
+ "bipartite_mutual_information",
120
+ "bloch_vector",
121
+ "bloch_observables",
122
+ "bures_angle",
123
+ "bures_distance",
124
+ "coherent",
125
+ "collective_excitation",
126
+ "collective_lowering",
127
+ "collective_raising",
128
+ "collective_x",
129
+ "collective_z",
130
+ "correlation_2op_1t",
131
+ "correlation_2op_2t",
132
+ "create",
133
+ "destroy",
134
+ "dicke_excitation",
135
+ "dicke_jm",
136
+ "dicke_jp",
137
+ "dicke_jx",
138
+ "dicke_jz",
139
+ "dicke_state",
140
+ "entropy_observable",
141
+ "evaluate_state_observables",
142
+ "expect",
143
+ "expect_plot",
144
+ "fidelity",
145
+ "fidelity_observable",
146
+ "eye",
147
+ "fock",
148
+ "hilbert_schmidt_distance",
149
+ "infidelity",
150
+ "is_density_matrix",
151
+ "is_hermitian",
152
+ "ket2dm",
153
+ "jaynes_cummings_system",
154
+ "l1_coherence",
155
+ "l1_coherence_observable",
156
+ "linear_entropy",
157
+ "linear_entropy_observable",
158
+ "load_result",
159
+ "mcsolve",
160
+ "mesolve",
161
+ "mutual_information",
162
+ "normalize_state",
163
+ "num",
164
+ "partial_trace",
165
+ "partial_traces",
166
+ "participation_ratio",
167
+ "participation_ratio_observable",
168
+ "phase_space_grid",
169
+ "plot_density_matrix",
170
+ "plot_expectations",
171
+ "plot_phase_space",
172
+ "plot_q_function",
173
+ "plot_state_observable",
174
+ "plot_wigner",
175
+ "population_observable",
176
+ "population_observables",
177
+ "populations",
178
+ "purity",
179
+ "purity_observable",
180
+ "q_function",
181
+ "renyi_entropy",
182
+ "sigmam",
183
+ "sigmap",
184
+ "single_trajectory",
185
+ "spin_jm",
186
+ "spin_jp",
187
+ "spin_jx",
188
+ "spin_jz",
189
+ "sigmax",
190
+ "sigmay",
191
+ "sigmaz",
192
+ "steadystate",
193
+ "state_metrics",
194
+ "tensor",
195
+ "thermal_dm",
196
+ "time_dependent_hamiltonian",
197
+ "trace_distance",
198
+ "trace_distance_observable",
199
+ "trace_norm",
200
+ "von_neumann_entropy",
201
+ "wigner",
202
+ ]
@@ -0,0 +1,80 @@
1
+ """Lazy Julia backend bridge.
2
+
3
+ The bridge intentionally imports `juliacall` lazily so ordinary Python-side
4
+ operator work and tests do not pay Julia startup cost.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+
13
+ class JuliaBridgeUnavailable(RuntimeError):
14
+ """Raised when the Julia backend cannot be loaded."""
15
+
16
+
17
+ _JL: Any | None = None
18
+ _BACKEND: Any | None = None
19
+
20
+
21
+ def backend_path() -> Path:
22
+ """Return the Julia backend package path."""
23
+ package_path = Path(__file__).resolve().parent / "julia" / "OpenQuantumSimJL"
24
+ dev_path = Path(__file__).resolve().parents[1] / "src" / "OpenQuantumSimJL"
25
+ if dev_path.exists():
26
+ return dev_path
27
+ return package_path
28
+
29
+
30
+ def get_julia() -> Any:
31
+ """Return the `juliacall.Main` object, importing it on first use."""
32
+ global _JL
33
+ if _JL is not None:
34
+ return _JL
35
+ try:
36
+ from juliacall import Main as jl # type: ignore[import-untyped]
37
+ except Exception as exc: # pragma: no cover - depends on local Julia setup
38
+ msg = "juliacall is required to use the Julia backend."
39
+ raise JuliaBridgeUnavailable(msg) from exc
40
+ _JL = jl
41
+ return jl
42
+
43
+
44
+ def load_backend() -> Any:
45
+ """Activate and load the `OpenQuantumSimJL` backend module."""
46
+ global _BACKEND
47
+ if _BACKEND is not None:
48
+ return _BACKEND
49
+
50
+ jl = get_julia()
51
+ path = str(backend_path())
52
+ try:
53
+ jl.seval("using Pkg")
54
+ jl.Pkg.activate(path)
55
+ _instantiate_and_load_backend(jl)
56
+ _BACKEND = jl.OpenQuantumSimJL
57
+ except Exception as exc: # pragma: no cover - depends on local Julia setup
58
+ msg = f"Unable to load Julia backend from {path}."
59
+ raise JuliaBridgeUnavailable(msg) from exc
60
+ return _BACKEND
61
+
62
+
63
+ def _instantiate_and_load_backend(jl: Any) -> None:
64
+ """Instantiate/load the backend, resolving stale manifests on retry."""
65
+ try:
66
+ jl.Pkg.instantiate()
67
+ jl.seval("using OpenQuantumSimJL")
68
+ except Exception:
69
+ jl.Pkg.resolve()
70
+ jl.Pkg.instantiate()
71
+ jl.seval("using OpenQuantumSimJL")
72
+
73
+
74
+ def backend_available() -> bool:
75
+ """Return whether the Julia backend can be loaded."""
76
+ try:
77
+ load_backend()
78
+ except JuliaBridgeUnavailable:
79
+ return False
80
+ return True
@@ -0,0 +1,3 @@
1
+ """Package version."""
2
+
3
+ __version__ = "0.1.0a1"
@@ -0,0 +1,144 @@
1
+ """Two-time correlation functions via the quantum regression theorem."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+ from typing import cast
7
+
8
+ import numpy as np
9
+ from numpy.typing import NDArray
10
+
11
+ from ._julia_bridge import JuliaBridgeUnavailable, load_backend
12
+ from .operators import Operator
13
+ from .result import Options
14
+
15
+ Array = NDArray[np.complex128]
16
+ FloatArray = NDArray[np.float64]
17
+
18
+
19
+ def correlation_2op_1t(
20
+ H: Operator,
21
+ rho0: Array,
22
+ taulist: Sequence[float],
23
+ a_op: Operator,
24
+ b_op: Operator,
25
+ *,
26
+ c_ops: Sequence[Operator] | None = None,
27
+ options: Options | None = None,
28
+ ) -> Array:
29
+ """Return ``<A(tau) B(0)>`` using quantum regression.
30
+
31
+ This first implementation supports time-independent Lindblad systems.
32
+ """
33
+ opts = options or Options()
34
+ rho0_array = np.asarray(rho0, dtype=np.complex128)
35
+ taus = np.asarray(taulist, dtype=np.float64)
36
+ c_arrays = [op.to_numpy() for op in c_ops or []]
37
+ _validate_correlation_inputs(H, rho0_array, taus, a_op, b_op, c_arrays, "taulist")
38
+
39
+ try:
40
+ backend = load_backend()
41
+ except JuliaBridgeUnavailable as exc:
42
+ msg = "correlation_2op_1t requires the Julia backend; run setup_julia.py first."
43
+ raise NotImplementedError(msg) from exc
44
+
45
+ raw = backend.correlation_2op_1t(
46
+ H.to_numpy(),
47
+ rho0_array,
48
+ taus,
49
+ a_op.to_numpy(),
50
+ b_op.to_numpy(),
51
+ c_arrays,
52
+ rtol=float(opts.rtol),
53
+ atol=float(opts.atol),
54
+ method=str(opts.method),
55
+ krylov_dim=int(opts.krylov_dim),
56
+ )
57
+ return np.asarray(_field(raw, "correlations"), dtype=np.complex128)
58
+
59
+
60
+ def correlation_2op_2t(
61
+ H: Operator,
62
+ rho0: Array,
63
+ tlist: Sequence[float],
64
+ taulist: Sequence[float],
65
+ a_op: Operator,
66
+ b_op: Operator,
67
+ *,
68
+ c_ops: Sequence[Operator] | None = None,
69
+ options: Options | None = None,
70
+ ) -> Array:
71
+ """Return ``<A(t + tau) B(t)>`` for each ``t`` and ``tau``."""
72
+ opts = options or Options()
73
+ rho0_array = np.asarray(rho0, dtype=np.complex128)
74
+ times = np.asarray(tlist, dtype=np.float64)
75
+ taus = np.asarray(taulist, dtype=np.float64)
76
+ c_arrays = [op.to_numpy() for op in c_ops or []]
77
+ _validate_correlation_inputs(H, rho0_array, times, a_op, b_op, c_arrays, "tlist")
78
+ _validate_nonnegative_sorted_times(taus, "taulist")
79
+
80
+ try:
81
+ backend = load_backend()
82
+ except JuliaBridgeUnavailable as exc:
83
+ msg = "correlation_2op_2t requires the Julia backend; run setup_julia.py first."
84
+ raise NotImplementedError(msg) from exc
85
+
86
+ raw = backend.correlation_2op_2t(
87
+ H.to_numpy(),
88
+ rho0_array,
89
+ times,
90
+ taus,
91
+ a_op.to_numpy(),
92
+ b_op.to_numpy(),
93
+ c_arrays,
94
+ rtol=float(opts.rtol),
95
+ atol=float(opts.atol),
96
+ method=str(opts.method),
97
+ krylov_dim=int(opts.krylov_dim),
98
+ )
99
+ return np.asarray(_field(raw, "correlations"), dtype=np.complex128)
100
+
101
+
102
+ def _validate_correlation_inputs(
103
+ H: Operator,
104
+ rho0: Array,
105
+ times: FloatArray,
106
+ a_op: Operator,
107
+ b_op: Operator,
108
+ c_ops: Sequence[Array],
109
+ time_name: str,
110
+ ) -> None:
111
+ if H.shape[0] != H.shape[1]:
112
+ msg = "H must be square."
113
+ raise ValueError(msg)
114
+ if rho0.shape != H.shape:
115
+ msg = "rho0 must have the same shape as H."
116
+ raise ValueError(msg)
117
+ _validate_nonnegative_sorted_times(times, time_name)
118
+ for op_name, operator in (("a_op", a_op), ("b_op", b_op)):
119
+ if operator.shape != H.shape:
120
+ msg = f"{op_name} must have the same shape as H."
121
+ raise ValueError(msg)
122
+ for collapse in c_ops:
123
+ if collapse.shape != H.shape:
124
+ msg = "collapse operators must have the same shape as H."
125
+ raise ValueError(msg)
126
+
127
+
128
+ def _validate_nonnegative_sorted_times(times: FloatArray, name: str) -> None:
129
+ if times.ndim != 1 or len(times) == 0:
130
+ msg = f"{name} must be a non-empty one-dimensional sequence."
131
+ raise ValueError(msg)
132
+ if np.any(times < 0):
133
+ msg = f"{name} must be non-negative."
134
+ raise ValueError(msg)
135
+ if np.any(np.diff(times) < 0):
136
+ msg = f"{name} must be sorted in ascending order."
137
+ raise ValueError(msg)
138
+
139
+
140
+ def _field(raw: object, name: str) -> object:
141
+ try:
142
+ return getattr(raw, name)
143
+ except AttributeError:
144
+ return cast(object, raw[name]) # type: ignore[index]
@@ -0,0 +1,114 @@
1
+ """Hilbert-space descriptors for OpenQuantumSim."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator
6
+ from dataclasses import dataclass
7
+ from functools import reduce
8
+ from math import isclose
9
+ from operator import mul
10
+
11
+
12
+ class HilbertSpace:
13
+ """Base Hilbert-space descriptor."""
14
+
15
+ @property
16
+ def dim(self) -> int:
17
+ """Return the finite Hilbert-space dimension."""
18
+ raise NotImplementedError
19
+
20
+ def __mul__(self, other: HilbertSpace) -> CompositeSpace:
21
+ """Tensor-product composition using `space_a * space_b`."""
22
+ if isinstance(self, CompositeSpace):
23
+ left = self.spaces
24
+ else:
25
+ left = (self,)
26
+ if isinstance(other, CompositeSpace):
27
+ right = other.spaces
28
+ else:
29
+ right = (other,)
30
+ return CompositeSpace(left + right)
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class FockSpace(HilbertSpace):
35
+ """Truncated bosonic Fock space with states `|0>` through `|N-1>`."""
36
+
37
+ N: int = 2
38
+ label: str | None = None
39
+
40
+ def __post_init__(self) -> None:
41
+ if self.N <= 0:
42
+ msg = "FockSpace dimension N must be positive."
43
+ raise ValueError(msg)
44
+
45
+ @property
46
+ def dim(self) -> int:
47
+ """Return the truncation dimension."""
48
+ return self.N
49
+
50
+
51
+ @dataclass(frozen=True)
52
+ class SpinSpace(HilbertSpace):
53
+ """Spin-S irreducible representation with dimension `2S + 1`."""
54
+
55
+ S: float = 0.5
56
+ label: str | None = None
57
+
58
+ def __post_init__(self) -> None:
59
+ dim = 2 * self.S + 1
60
+ if self.S < 0 or not isclose(dim, round(dim), rel_tol=0.0, abs_tol=1e-12):
61
+ msg = "SpinSpace requires S >= 0 and 2S + 1 to be an integer."
62
+ raise ValueError(msg)
63
+
64
+ @property
65
+ def dim(self) -> int:
66
+ """Return `2S + 1`."""
67
+ return int(round(2 * self.S + 1))
68
+
69
+
70
+ @dataclass(frozen=True)
71
+ class DickeSpace(HilbertSpace):
72
+ """Permutation-symmetric Dicke manifold for ``n_spins`` two-level systems."""
73
+
74
+ n_spins: int
75
+ label: str | None = None
76
+
77
+ def __post_init__(self) -> None:
78
+ if self.n_spins < 0:
79
+ msg = "DickeSpace requires n_spins >= 0."
80
+ raise ValueError(msg)
81
+
82
+ @property
83
+ def dim(self) -> int:
84
+ """Return the symmetric-manifold dimension ``n_spins + 1``."""
85
+ return self.n_spins + 1
86
+
87
+ @property
88
+ def total_spin(self) -> float:
89
+ """Return the collective spin ``S = n_spins / 2``."""
90
+ return self.n_spins / 2
91
+
92
+
93
+ @dataclass(frozen=True)
94
+ class CompositeSpace(HilbertSpace):
95
+ """Tensor product of multiple Hilbert spaces."""
96
+
97
+ spaces: tuple[HilbertSpace, ...]
98
+ label: str | None = None
99
+
100
+ def __post_init__(self) -> None:
101
+ if len(self.spaces) == 0:
102
+ msg = "CompositeSpace requires at least one subsystem."
103
+ raise ValueError(msg)
104
+
105
+ @property
106
+ def dim(self) -> int:
107
+ """Return the product dimension."""
108
+ return reduce(mul, (space.dim for space in self.spaces), 1)
109
+
110
+ def __iter__(self) -> Iterator[HilbertSpace]:
111
+ return iter(self.spaces)
112
+
113
+ def __len__(self) -> int:
114
+ return len(self.spaces)