qthermo 0.6.0__tar.gz

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 (68) hide show
  1. qthermo-0.6.0/LICENSE +7 -0
  2. qthermo-0.6.0/PKG-INFO +487 -0
  3. qthermo-0.6.0/README.md +455 -0
  4. qthermo-0.6.0/pyproject.toml +42 -0
  5. qthermo-0.6.0/qthermo/__init__.py +179 -0
  6. qthermo-0.6.0/qthermo/_benchmark_suite.py +537 -0
  7. qthermo-0.6.0/qthermo/analysis.py +353 -0
  8. qthermo-0.6.0/qthermo/audit.py +317 -0
  9. qthermo-0.6.0/qthermo/baths.py +494 -0
  10. qthermo-0.6.0/qthermo/batteries.py +314 -0
  11. qthermo-0.6.0/qthermo/benchmarks.py +157 -0
  12. qthermo-0.6.0/qthermo/channels.py +78 -0
  13. qthermo-0.6.0/qthermo/core.py +155 -0
  14. qthermo-0.6.0/qthermo/counting.py +240 -0
  15. qthermo-0.6.0/qthermo/cycle.py +319 -0
  16. qthermo-0.6.0/qthermo/engines.py +273 -0
  17. qthermo-0.6.0/qthermo/explorer.py +312 -0
  18. qthermo-0.6.0/qthermo/export.py +126 -0
  19. qthermo-0.6.0/qthermo/floquet.py +242 -0
  20. qthermo-0.6.0/qthermo/fluctuations.py +305 -0
  21. qthermo-0.6.0/qthermo/geometry.py +172 -0
  22. qthermo-0.6.0/qthermo/information.py +383 -0
  23. qthermo-0.6.0/qthermo/models.py +462 -0
  24. qthermo-0.6.0/qthermo/modes.py +78 -0
  25. qthermo-0.6.0/qthermo/network.py +354 -0
  26. qthermo-0.6.0/qthermo/papers.py +225 -0
  27. qthermo-0.6.0/qthermo/plotting.py +629 -0
  28. qthermo-0.6.0/qthermo/report.py +145 -0
  29. qthermo-0.6.0/qthermo/response.py +154 -0
  30. qthermo-0.6.0/qthermo/solver.py +134 -0
  31. qthermo-0.6.0/qthermo/steady.py +653 -0
  32. qthermo-0.6.0/qthermo/stochastic.py +361 -0
  33. qthermo-0.6.0/qthermo/strong_coupling.py +211 -0
  34. qthermo-0.6.0/qthermo/subsystems.py +385 -0
  35. qthermo-0.6.0/qthermo/transient.py +137 -0
  36. qthermo-0.6.0/qthermo/units.py +88 -0
  37. qthermo-0.6.0/qthermo/validation.py +106 -0
  38. qthermo-0.6.0/qthermo.egg-info/PKG-INFO +487 -0
  39. qthermo-0.6.0/qthermo.egg-info/SOURCES.txt +66 -0
  40. qthermo-0.6.0/qthermo.egg-info/dependency_links.txt +1 -0
  41. qthermo-0.6.0/qthermo.egg-info/requires.txt +12 -0
  42. qthermo-0.6.0/qthermo.egg-info/top_level.txt +1 -0
  43. qthermo-0.6.0/setup.cfg +4 -0
  44. qthermo-0.6.0/tests/test_audit.py +59 -0
  45. qthermo-0.6.0/tests/test_batteries.py +91 -0
  46. qthermo-0.6.0/tests/test_counting.py +94 -0
  47. qthermo-0.6.0/tests/test_docs.py +23 -0
  48. qthermo-0.6.0/tests/test_engines.py +138 -0
  49. qthermo-0.6.0/tests/test_explorer.py +28 -0
  50. qthermo-0.6.0/tests/test_export.py +41 -0
  51. qthermo-0.6.0/tests/test_floquet.py +80 -0
  52. qthermo-0.6.0/tests/test_fluctuations.py +126 -0
  53. qthermo-0.6.0/tests/test_geometry.py +60 -0
  54. qthermo-0.6.0/tests/test_information.py +145 -0
  55. qthermo-0.6.0/tests/test_invariants.py +138 -0
  56. qthermo-0.6.0/tests/test_modes.py +63 -0
  57. qthermo-0.6.0/tests/test_network.py +143 -0
  58. qthermo-0.6.0/tests/test_papers.py +65 -0
  59. qthermo-0.6.0/tests/test_physics.py +284 -0
  60. qthermo-0.6.0/tests/test_qutip.py +45 -0
  61. qthermo-0.6.0/tests/test_readme.py +28 -0
  62. qthermo-0.6.0/tests/test_report.py +21 -0
  63. qthermo-0.6.0/tests/test_response.py +45 -0
  64. qthermo-0.6.0/tests/test_steady.py +269 -0
  65. qthermo-0.6.0/tests/test_strong_coupling.py +81 -0
  66. qthermo-0.6.0/tests/test_subsystems.py +263 -0
  67. qthermo-0.6.0/tests/test_transient.py +46 -0
  68. qthermo-0.6.0/tests/test_units.py +33 -0
qthermo-0.6.0/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vivek Khanna
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction.
qthermo-0.6.0/PKG-INFO ADDED
@@ -0,0 +1,487 @@
1
+ Metadata-Version: 2.4
2
+ Name: qthermo
3
+ Version: 0.6.0
4
+ Summary: Heat, work and entropy for open quantum systems, with the modelling traps checked and published results reproduced
5
+ Author: Vivek Khanna
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/VivekKhanna0/Qthermo
8
+ Project-URL: Documentation, https://github.com/VivekKhanna0/Qthermo/blob/main/docs/api.md
9
+ Project-URL: Changelog, https://github.com/VivekKhanna0/Qthermo/blob/main/CHANGELOG.md
10
+ Project-URL: Issues, https://github.com/VivekKhanna0/Qthermo/issues
11
+ Keywords: quantum thermodynamics,open quantum systems,master equation,Lindblad,heat engines,quantum refrigerators,physics
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering :: Physics
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: numpy>=1.23
23
+ Requires-Dist: scipy>=1.9
24
+ Provides-Extra: qutip
25
+ Requires-Dist: qutip>=4.7; extra == "qutip"
26
+ Provides-Extra: plot
27
+ Requires-Dist: matplotlib>=3.5; extra == "plot"
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=7; extra == "dev"
30
+ Requires-Dist: matplotlib>=3.5; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ # qthermo
34
+
35
+ Thermodynamic bookkeeping for open quantum systems — heat, work, entropy
36
+ production, currents and their fluctuations — with the modelling traps checked
37
+ for you.
38
+
39
+ You write down a Hamiltonian and the baths. `qthermo` gives you per-bath heat
40
+ currents, entropy production, efficiencies against their Carnot bounds,
41
+ site-resolved energy flows, exact current fluctuations and uncertainty-relation
42
+ ratios, for stroke machines and continuous machines, from one qubit to a few
43
+ hundred levels. It also tells you when the model you wrote down cannot mean
44
+ what you think it means.
45
+
46
+ ```bash
47
+ pip install "qthermo[plot]" # numpy, scipy, matplotlib
48
+ python -m qthermo.benchmarks # 44 checks against published results, ~40 s
49
+ ```
50
+
51
+ For the latest version, `pip install "qthermo[plot] @ git+https://github.com/VivekKhanna0/Qthermo"`.
52
+ To run the examples and tests, `git clone` the repository and
53
+ `pip install -e ".[dev]"`.
54
+
55
+ **Highlights**, each reproduced by the package and checked in CI:
56
+
57
+ - Local master equations can make heat flow from cold to hot. `qthermo`
58
+ detects it and runs the same machine under global baths in one call.
59
+ - The three-qubit absorption fridge hits COP = ω_c/ω_h to 1e-15, and its
60
+ target qubit transiently cools *below* its steady state.
61
+ - The coherently driven maser beats the classical thermodynamic uncertainty
62
+ relation (exact current noise, no sampling).
63
+ - Optimal finite-time protocols for *any* drive: simulations land on the
64
+ predicted minimum L²/τ to 0.2%.
65
+ - A quantum Szilard engine attains the Sagawa–Ueda bound exactly, and a
66
+ zz-coupled transistor amplifies heat currents 4.8×.
67
+ - Which master equation is right? For coupled oscillators, where the exact
68
+ answer is known, the global equation is off by 25× at weak coupling and the
69
+ local one by 10% when detuned. The Redfield bath matches exact to 0.1% in
70
+ both regimes.
71
+ - Agrees with QuTiP to 1e-16 (steady states) and 1e-10 (dynamics).
72
+
73
+ **New here?** [`examples/tutorial.ipynb`](https://github.com/VivekKhanna0/Qthermo/blob/main/examples/tutorial.ipynb) takes one
74
+ research question (a two-qubit thermal diode) from a Hamiltonian to publishable
75
+ numbers, including the checks a referee would ask about. It renders on GitHub
76
+ with all outputs.
77
+
78
+ **Docs:** [physics definitions](https://github.com/VivekKhanna0/Qthermo/blob/main/docs/physics.md) · [API reference](https://github.com/VivekKhanna0/Qthermo/blob/main/docs/api.md) ·
79
+ [figure gallery](https://github.com/VivekKhanna0/Qthermo/blob/main/docs/gallery.md) · [examples](https://github.com/VivekKhanna0/Qthermo/blob/main/examples/README.md) ·
80
+ [changelog](https://github.com/VivekKhanna0/Qthermo/blob/main/CHANGELOG.md)
81
+
82
+ ## Reproduce a paper in 10 lines
83
+
84
+ A question papers in this field argue about: the *local* and *global* master
85
+ equations give different heat currents, so which one is right? For two
86
+ coupled oscillators the exact answer is known, so each can be graded:
87
+
88
+ ```python
89
+ import qthermo as qt
90
+
91
+ r = qt.papers.local_vs_global(detuning=0.1) # Hofer et al., NJP 19, 123037 (2017)
92
+ print(r.report())
93
+ r.plot()
94
+ ```
95
+ ```
96
+ local: worst error 10.3%
97
+ global: worst error 4.0%
98
+ redfield: worst error 0.0%
99
+ ```
100
+
101
+ <img src="https://raw.githubusercontent.com/VivekKhanna0/Qthermo/main/examples/figures/local_vs_global_exact.png" width="720" alt="local, global and Redfield heat currents against the exact result">
102
+
103
+ Each standard approximation fails somewhere (top: global, for resonant
104
+ oscillators at weak coupling; bottom: local, for detuned ones). The Redfield
105
+ bath (`qt.redfield_bath`) is right in both cases. The same three baths work on
106
+ any Hamiltonian you write down, so you can run this check on your own model:
107
+ see [Your own model](#your-own-model). The script that draws this figure is
108
+ [`examples/reproduce_local_vs_global.py`](https://github.com/VivekKhanna0/Qthermo/blob/main/examples/reproduce_local_vs_global.py).
109
+
110
+ ## Thirty seconds
111
+
112
+ ```python
113
+ import qthermo as qt
114
+
115
+ fridge = qt.models.absorption_refrigerator(T_c=1.0, T_h=6.0, T_r=1.5)
116
+ steady = fridge.analyze()
117
+ print(steady.report())
118
+ ```
119
+ ```
120
+ bath T J (into system) -J/T
121
+ -------------------------------------------------------
122
+ cold 1 6.963227e-04 -6.9632e-04
123
+ hot 6 2.088968e-03 -3.4816e-04
124
+ room 1.5 -2.785291e-03 1.8569e-03
125
+ -------------------------------------------------------
126
+ sum 1.301e-18 8.1238e-04
127
+ entropy production rate: 8.1238e-04
128
+ note: local baths -- valid only for inter-site coupling << bath rates; compare with global baths
129
+ ```
130
+ ```python
131
+ steady.cop("cold", "hot") # 0.3333 = w_c / w_h exactly
132
+ steady.absorption_carnot_cop("cold", "hot", "room") # 1.5
133
+ print(fridge.compare().report()) # would "you used a local master equation" change anything?
134
+ ```
135
+ ```
136
+ bath J local J global rel. diff
137
+ ------------------------------------------------------
138
+ cold 6.96323e-04 7.53796e-04 7.62%
139
+ hot 2.08897e-03 2.27304e-03 8.10%
140
+ room -2.78529e-03 -3.02684e-03 7.98%
141
+ ------------------------------------------------------
142
+ entropy production rate: local 8.1238e-04, global 8.8526e-04
143
+ steady-state trace distance: 1.040e-02
144
+ ```
145
+ ```python
146
+ qt.plot_machine(steady) # the figure below
147
+ ```
148
+
149
+ <img src="https://raw.githubusercontent.com/VivekKhanna0/Qthermo/main/examples/figures/absorption_fridge.png" width="620" alt="heat-flow network of the absorption refrigerator">
150
+
151
+ Baths (squares) and qubits (circles) share one temperature colour scale; each
152
+ qubit is coloured by its **virtual temperature**. The cold qubit is colder
153
+ than every bath, which nothing passive can do. Arrow widths are heat
154
+ currents, and the internal balances close to ~1e-17.
155
+
156
+ ## Your own model
157
+
158
+ Nothing above is special-cased. Describe the sites, the couplings and which
159
+ bath touches which site:
160
+
161
+ ```python
162
+ machine = qt.build_model(
163
+ local_H=[qt.qubit_hamiltonian(1.0), qt.qubit_hamiltonian(0.6)],
164
+ interactions={(0, 1): (0.3 * qt.sigma_x, qt.sigma_x)}, # 0.3 sx (x) sx
165
+ baths=[dict(name="hot", site=0, coupling=qt.sigma_x, T=2.0, gamma=0.1),
166
+ dict(name="cold", site=1, coupling=qt.sigma_x, T=1.0, gamma=0.1)],
167
+ master_equation="global")
168
+
169
+ steady = machine.analyze() # exact steady state, per-bath currents
170
+ print(qt.audit(machine)) # every consistency check
171
+ machine.compare() # the same machine under local baths
172
+ qt.current_statistics(machine, "hot") # exact noise, TUR / KUR ratios
173
+ qt.plot_machine(steady) # heat-flow network, virtual temperatures
174
+ qt.report(machine, "machine.html") # all of it in one shareable page
175
+ ```
176
+
177
+ Sites can have any dimension, and interactions can be given as full matrices.
178
+ For full control, build the baths yourself:
179
+ `qt.davies_bath(H, coupling, T)` gives the *global* (secular,
180
+ detailed-balance) bath for any Hamiltonian, coupled, degenerate or many-body,
181
+ and `qt.local_bath` gives the local one. `qt.analyze(H, baths)` takes any list
182
+ of them. QuTiP `Qobj`s are accepted anywhere an array is.
183
+
184
+ ## I want to...
185
+
186
+ | ...do this | call |
187
+ |---|---|
188
+ | get currents, entropy production, COP/efficiency of a continuous machine | `qt.analyze(H, baths)` or `model.analyze()` |
189
+ | check my model for known modelling mistakes | `qt.audit(model)` or `qt.audit(H, baths)` |
190
+ | describe my own machine once and get every tool | `qt.build_model(local_H, interactions, baths)` |
191
+ | check which master equation is right for my model | `model.rebuild("redfield").analyze()` against `model.compare()`; see `qt.papers.local_vs_global()` |
192
+ | build baths for a coupled / many-body system | `qt.davies_bath` (global), `qt.local_bath` (local) |
193
+ | know whether local vs global master equations matter | `model.compare()` |
194
+ | see where heat flows inside a multi-qubit machine | `qt.heat_flow_map(steady)`, `qt.plot_machine(steady)` |
195
+ | follow each qubit (energy, T*, entanglement) through a cycle | `qt.site_dynamics(result, dims, local_H)`, `qt.plot_site_dynamics` |
196
+ | get the noise of a current and the TUR/KUR ratios | `qt.current_statistics(model, "bath")` |
197
+ | simulate a stroke cycle and its limit cycle | `qt.Cycle([qt.Stroke(...), ...]).limit_cycle(rho0)` |
198
+ | know the quasi-static limit of an interacting Otto engine | `qt.ideal_otto(H_c, H_h, T_c, T_h)` |
199
+ | get the exact heat distribution of one cycle | `qt.cycle_counting(cycle, {"stroke": "quanta"})` |
200
+ | map engine / fridge / heater / accelerator regions | `qt.mode_map(build, xs, ys)`, `qt.plot_mode_map` |
201
+ | scan any machine over parameters | `qt.sweep`, `qt.scan_2d`, `qt.pareto_front` |
202
+ | go beyond weak coupling | `qt.reaction_coordinate_model`, `qt.rc_convergence` |
203
+ | cost of erasure and the optimal protocol | `qt.landauer_erasure`, `qt.geodesic_schedule` |
204
+ | work from information (measurement + feedback) | `qt.szilard_engine(T, error=..., tau=...)` |
205
+ | the least-dissipative schedule for *my* drive | `qt.optimal_schedule(H_of, baths_of, T, path)` |
206
+ | a machine powered by a periodic drive | `qt.floquet_analyze(H_of_t, period, [qt.DrivenBath(...)])` |
207
+ | what happens after switching a machine on | `qt.transient(model, rho0, duration)` |
208
+ | conductance matrix, Onsager check, transistor gain | `qt.response(build, {bath: T})` |
209
+ | analyse a quantum battery | `qt.batteries.ergotropy_split`, `locked_ergotropy`, `dicke_battery` |
210
+ | use lab numbers (GHz, mK, µs, W) | `lab = qt.LabUnits(5.0)`; `lab.temperature(20)`, `lab.rate(1/T1)`, `lab.to_watts(J)` |
211
+ | save results with provenance | `qt.save(result, "file.json")` |
212
+ | send a colleague the result *and* the checks | `qt.report(model, "machine.html")`, a single self-contained page |
213
+ | an interactive page with a parameter slider | `qt.explorer(build, values, path="x.html")` |
214
+ | confirm the package is right | `python -m qthermo.benchmarks` |
215
+
216
+ ## What it catches
217
+
218
+ Most of the value is here. Each of these is a mistake that produces a plausible
219
+ number with no error message, and each is detected and explained. `qt.audit`
220
+ runs every check in one call:
221
+
222
+ ```python
223
+ print(qt.audit(qt.models.two_qubit_heat_valve(master_equation="local")))
224
+ ```
225
+ ```
226
+ [ERROR] local vs global: the two master equations disagree on the direction of heat flow for hot, cold
227
+ Inter-site coupling is too strong for the local approximation. Use the
228
+ global model.
229
+ [info] relaxation time: 2.72 (populations)
230
+ [ok] steady state: unique
231
+ [ok] second law: entropy production rate 1.937e-02 >= 0
232
+ [ok] internal currents: site energy balances close to 3.5e-17
233
+ [ok] uncertainty relation: 'hot': TUR ratio 9.626 >= 2
234
+ [ok] uncertainty relation: 'cold': TUR ratio 5.117 >= 2
235
+ ------------------------------------------------------------
236
+ 1 error(s), 0 warning(s), 6 other checks
237
+ ```
238
+
239
+ | Trap | What happens | What `qthermo` does |
240
+ |---|---|---|
241
+ | Local master equation on coupled sites | Heat can flow cold → hot; σ < 0 (Levy & Kosloff 2014) | Warns on σ < 0; `model.compare()` runs local vs global side by side; bare-Hamiltonian accounting with explicit boundary work (De Chiara et al. 2018) |
242
+ | Non-unique steady state | Identical qubits on one bath, dark states, conserved quantities: "the" steady state doesn't exist | Detected from the conditioning of the linear solve; raises unless you give an initial state, then returns the state it actually relaxes to |
243
+ | Baths that respect a symmetry of H | e.g. σx couplings on an Ising medium conserve parity; isochores never thermalise and the "efficiency" describes a different machine | `otto_cycle` refuses and says which symmetry-breaking coupling to use |
244
+ | Level crossings in an interacting Otto engine | Energy-rank pairing of levels gives the wrong quasi-static cycle | `ideal_otto` follows adiabatic continuation along the actual drive |
245
+ | Degenerate levels + flat spectral density | The zero-frequency channel has an undefined rate; thermalisation silently takes 1000× longer | Warns; `relaxation_time()` exposes the time scale; `otto_cycle` checks τ_iso against it |
246
+ | Global ME and local currents | The secular steady state is diagonal in H, so every bond current i⟨[V,h_i]⟩ is identically zero | `heat_flow_map` says so instead of printing zeros |
247
+ | Reaction-coordinate truncation | Strong coupling displaces the RC; too few levels gives unconverged currents | `rc_convergence()` reports the change between truncations |
248
+ | Temperature inconsistent with the channel | σ < 0 from a mislabelled bath | Warning naming the likely cause |
249
+ | Bath temperature inconsistent with its rates | A bath labelled T=2 whose jump rates encode T=3 gives a meaningless σ | `audit` infers the detailed-balance temperature from the jump operators and compares |
250
+ | Invalid input | Non-Hermitian H, unnormalised ρ, wrong dimensions | `QThermoError` naming the violated requirement, never a LinAlg traceback |
251
+
252
+ ## What it does
253
+
254
+ | Area | Entry points | Validated against |
255
+ |---|---|---|
256
+ | **Stroke machines** (Otto, arbitrary cycles) | `Stroke`, `Cycle`, `limit_cycle` | Otto COP/efficiency limits; first law to 1e-16 by construction |
257
+ | **Interacting working media** | `ideal_otto`, `otto_cycle`, `resolve_stroke` | Exact quasi-static limit (simulation agrees to 1e-8); coupling-enhanced efficiency (Thomas & Johal 2011) |
258
+ | **Continuous machines** | `analyze`, `steady_state`, `models.*` | LPS fridge COP = ω_c/ω_h exactly; SSDB maser η = 1 − ω_c/ω_h; Carnot bounds |
259
+ | **Bath models** | `davies_bath`, `local_bath`, `instantaneous_bath` | Gibbs fixed point for random H to 1e-16; Levy–Kosloff violation reproduced and resolved |
260
+ | **Where the heat goes** | `heat_flow_map`, `plot_machine`, virtual temperatures, concurrence, negativity | Site balances close to 1e-17; Werner-state entanglement threshold |
261
+ | **Fluctuations** | `current_statistics`, `scaled_cgf` | Exact vs tilted-generator vs independent classical FCS (1e-10); Gallavotti–Cohen symmetry to 1e-15; TUR holds for all classical machines, violated by the maser as published |
262
+ | **Strong coupling** | `reaction_coordinate_model`, `mean_force_state` | O(λ²) weak-coupling limit; Cresser–Anders ultrastrong limit; heat-current turnover |
263
+ | **Information** | `landauer_erasure`, `geodesic_schedule`, `thermodynamic_length`, `szilard_engine` | Erasure → T ln 2; excess ∝ 1/τ matching slow-driving theory to <1%; geodesic attains L²/τ. Szilard engine attains the Sagawa–Ueda bound T·I exactly, including with measurement errors |
264
+ | **Periodically driven machines** | `floquet_analyze`, `DrivenBath`, `window_spectrum` | Undriven limit = static Davies (1e-14); Bessel sideband weights; tight-coupling efficiency and COP of the modulated-qubit machine to 1e-7; σ ≥ 0 for random drives |
265
+ | **Linear response** | `response` → conductance, Onsager matrix, coupling `q`, `amplification` | Onsager reciprocity to 1e-9; tight coupling \|q\| = 1 for the local fridge; thermal-transistor gain 4.8 |
266
+ | **Transients** | `transient`, `product_thermal_state` | Long-time limit = steady state; cumulative heat balances ΔE; single-shot cooling below steady state |
267
+ | **Optimal protocols** (any H(λ), any thermalising bath) | `friction`, `optimal_schedule`, `excess_work` | Friction metric reproduces the erasure closed form to 1e-11; non-commuting drive: simulated excess within 0.2% of L²/τ, 34% below a linear ramp |
268
+ | **Quantum batteries** | `batteries.ergotropy_split`, `locked_ergotropy`, `asymptotic_ergotropy`, `dicke_battery`, `collective_advantage` | Dicke √N power advantage (exponent 0.499); locked ergotropy of a Bell pair; activation of passive states |
269
+ | **Per-cycle statistics** | `cycle_counting` (exact), `unravel` (sampled) | Exact P(n) vs independent classical telegraph model (1e-12); vs trajectories; Jarzynski to 1e-16 |
270
+ | **Operation modes** | `classify`, `mode_map`, `plot_mode_map`, `.mode()` on every result | Quasi-static qubit Otto boundary ω_c/ω_h = T_c/T_h reproduced exactly |
271
+ | **Optimisation** | `sweep`, `scan_2d`, `pareto_front` (cycles *and* continuous models) | Interior optimum located; sequential vs joint tuning |
272
+
273
+ ## Figures
274
+
275
+ <table>
276
+ <tr>
277
+ <td width="50%"><img src="https://raw.githubusercontent.com/VivekKhanna0/Qthermo/main/examples/figures/local_vs_global.png" alt="local vs global master equation"></td>
278
+ <td width="50%"><img src="https://raw.githubusercontent.com/VivekKhanna0/Qthermo/main/examples/figures/tur.png" alt="TUR violation in the maser"></td>
279
+ </tr>
280
+ <tr>
281
+ <td><b>When the local master equation breaks.</b> Past g ≈ 0.55 it moves heat
282
+ from cold to hot. The global model never does.</td>
283
+ <td><b>Beyond any classical machine.</b> The maser's power output is more
284
+ precise than the thermodynamic uncertainty relation allows a classical
285
+ process. Computed exactly, with no sampling.</td>
286
+ </tr>
287
+ <tr>
288
+ <td width="50%"><img src="https://raw.githubusercontent.com/VivekKhanna0/Qthermo/main/examples/figures/thermal_transistor.png" alt="quantum thermal transistor"></td>
289
+ <td width="50%"><img src="https://raw.githubusercontent.com/VivekKhanna0/Qthermo/main/examples/figures/szilard_engine.png" alt="quantum Szilard engine"></td>
290
+ </tr>
291
+ <tr>
292
+ <td><b>A quantum thermal transistor.</b> The base heat current steers up to
293
+ 4.8× as much collector current. The conductance matrix is checked for Onsager
294
+ symmetry.</td>
295
+ <td><b>Information into work.</b> Optimal feedback attains the Sagawa–Ueda
296
+ bound T·I exactly, even with measurement errors. In finite time it falls
297
+ short by ~1/τ.</td>
298
+ </tr>
299
+ </table>
300
+
301
+ **[The full gallery: 16 figures, with what each shows →](https://github.com/VivekKhanna0/Qthermo/blob/main/docs/gallery.md)**, covering
302
+ strong coupling, Landauer erasure, optimal protocols, interacting Otto
303
+ engines, entanglement inside a two-qubit engine cycle, operation-mode maps,
304
+ periodically driven machines, transient cooling, transport scaling and
305
+ quantum batteries.
306
+
307
+ ## Verification
308
+
309
+ ```bash
310
+ python -m qthermo.benchmarks # computed value next to the published / analytic one
311
+ python -m pytest # ~270 tests, ~95 s
312
+ ```
313
+
314
+ Every benchmark is a number fixed independently of this code: a closed-form
315
+ limit, an exact identity, or a published bound or violation. A failure means
316
+ the physics is wrong, not that an interface changed. The test suite adds
317
+ randomised property tests. They draw random Hamiltonians, couplings, baths and
318
+ temperatures (qubits, qutrits, mixed dimensions, degenerate spectra) and
319
+ assert what must always hold:
320
+
321
+ - the steady state is a valid, stationary density matrix;
322
+ - energy is conserved and σ ≥ 0;
323
+ - a single bath gives the Gibbs state;
324
+ - counting statistics reproduce the currents;
325
+ - site flows close;
326
+ - ergotropy is non-negative and zero for passive states.
327
+
328
+ It is also cross-checked against QuTiP. CI runs all of this, and every
329
+ example, on every push.
330
+
331
+ ```
332
+ [continuous machines]
333
+ ok 3-qubit absorption fridge COP (local ME, tight coupling) 0.333333 0.333333 ==
334
+ w_c/w_h; Linden, Popescu & Skrzypczyk, PRL 105, 130401 (2010)
335
+ ok three-level maser efficiency 0.666667 0.666667 ==
336
+ 1 - w_c/w_h; Scovil & Schulz-DuBois, PRL 2, 262 (1959)
337
+ ok local ME, detuned XX qubits: heat current from hot bath -0.00044776 0 <=
338
+ < 0, i.e. cold -> hot; Levy & Kosloff, EPL 107, 20004 (2014)
339
+ [fluctuations]
340
+ ok TUR ratio, three-level maser power (coherent drive) 1.96697 2 <=
341
+ < 2 possible; Kalaee, Wacker & Potts, PRE 104, L012103 (2021)
342
+ ok Gallavotti-Cohen symmetry of heat FCS, 3-qubit chain 1.84228e-15 0 ==
343
+ [information]
344
+ ok geodesic erasure protocol: excess heat x tau 0.890737 0.886534 ==
345
+ = L^2 (thermodynamic length); Scandi & Perarnau-Llobet, Quantum 3, 197 (2019)
346
+ ...
347
+ 44/44 passed in 39.3 s
348
+ ```
349
+
350
+ The exact definitions of every computed quantity (sign conventions, which
351
+ energy operator defines heat under local baths, rate conventions of the
352
+ spectral densities, the FCS formulas) are in [docs/physics.md](https://github.com/VivekKhanna0/Qthermo/blob/main/docs/physics.md).
353
+
354
+ ## Stroke machines and trajectories
355
+
356
+ The original core is still here and unchanged: a cycle is a list of strokes,
357
+ and whether it is a fridge or an engine follows from the parameters.
358
+
359
+ ```python
360
+ H_cold, H_hot = qt.qubit_hamiltonian(1.0), qt.qubit_hamiltonian(1.5)
361
+ ramp = lambda a, b, tau: (lambda t: qt.qubit_hamiltonian(a + (t/tau)*(b-a)))
362
+
363
+ cycle = qt.Cycle([
364
+ qt.Stroke("cold_iso", H_cold, 12.0, qt.thermal_bath(1.0, 1.0, 1.0), temperature=1.0),
365
+ qt.Stroke("compress", ramp(1.0, 1.5, 0.02), 0.02),
366
+ qt.Stroke("hot_iso", H_hot, 12.0, qt.thermal_bath(1.0, 1.5, 1.3), temperature=1.3),
367
+ qt.Stroke("expand", ramp(1.5, 1.0, 0.02), 0.02),
368
+ ])
369
+ result, passes, converged = cycle.limit_cycle(qt.thermal_state(H_cold, 1.0))
370
+ result.cop(("cold_iso",))
371
+
372
+ counts = qt.cycle_counting(cycle, {"cold_iso": "quanta"}) # exact, no sampling
373
+ counts.probability(lambda n: n <= 0) # 0.7955: 80% of cycles extract nothing
374
+ print(counts.report())
375
+
376
+ ensemble = qt.unravel(cycle, result.strokes[-1].rho_final, trajectories=6000)
377
+ ensemble.probability_of("cold_iso", lambda q: q <= 0) # ~0.79, sampled
378
+ ```
379
+ ```
380
+ net quanta exchanged per cycle, counting cold_iso (quanta)
381
+ n P(n), one cycle
382
+ -1 0.175299 #######
383
+ 0 0.620249 #########################
384
+ 1 0.204453 ########
385
+ one cycle: mean +0.029154, P(n <= 0) = 0.795547
386
+ long run: mean +0.029154 per cycle, variance 0.378901, Fano 12.9965
387
+ ```
388
+
389
+ On average this refrigerator cools. In 80% of individual cycles it draws
390
+ nothing from the cold bath, a number the master equation cannot give.
391
+ `cycle_counting` computes the single-cycle distribution *exactly*, by
392
+ propagating the counting-field master equation through the strokes, and the
393
+ long-run noise from its dominant eigenvalue. `unravel` samples the same thing
394
+ with quantum-jump trajectories. (Earlier versions of this README said "about
395
+ half": that was floating-point round-off in the sampled heats misclassifying
396
+ zero-heat trajectories, found by comparing against the exact result, and now
397
+ fixed.)
398
+
399
+ Heat and work use the midpoint Alicki split, which satisfies `dU = Q + W` to
400
+ machine precision at every step. That makes the first-law residual a test of
401
+ the solver rather than of the discretisation. Strokes also accept `Bath`
402
+ objects, including several at once and baths that follow a driven Hamiltonian
403
+ (`instantaneous_bath`), and then report heat per bath. See
404
+ `examples/full_demo.py` for sweeps, joint scans, Pareto fronts and trajectory
405
+ distributions.
406
+
407
+ ## Scope and limitations
408
+
409
+ - **Markovian** master equations (Lindblad form). Non-Markovian and
410
+ strong-coupling effects enter through the reaction-coordinate mapping, which
411
+ is exact for a single-peaked (Brownian) spectral density and approximate
412
+ otherwise. No HEOM. `redfield_bath` (not of Lindblad form, so positivity is
413
+ not guaranteed) works for steady states only, not for trajectories,
414
+ fluctuations or cycles.
415
+ - **Lamb shifts are neglected** in the Davies construction.
416
+ - **Size:** dense or sparse matrices, no tensor networks.
417
+ - Global-bath steady states run to a few hundred levels; the 200-level RC
418
+ model takes about 20 s.
419
+ - Local-bath chains take seconds up to 7 qubits and about 25 s at 8 qubits
420
+ (0.6 GB), using ILU-preconditioned GMRES above 128 levels. 9 qubits takes
421
+ minutes.
422
+ - Current fluctuations use dense superoperators and suit a few dozen levels.
423
+ - **Periodically driven machines** use the full-secular Floquet–Markov
424
+ equation. It is valid when quasienergy differences are resolved on the scale
425
+ of the bath rates, and warns when they are not.
426
+ - Simulation only. Nothing here has been checked against hardware data.
427
+
428
+ ## Feedback wanted
429
+
430
+ This is being shared with researchers in quantum thermodynamics to find out
431
+ what is actually useful. The most valuable replies are specific: *"I would
432
+ use this if it did X"*, *"this number disagrees with Ref. Y"*, *"the local/global
433
+ check would have saved me a week"*. Please open an issue.
434
+
435
+ Some concrete open questions where input from people who work on these
436
+ problems would help:
437
+
438
+ - **The regime between local and global.** With J ≈ γ, neither master
439
+ equation is controlled; the tutorial's thermal diode even changes direction
440
+ between them. A naive "unified" (partially secular) construction was
441
+ prototyped and gave erratic currents, so it was not shipped. `redfield_bath`
442
+ now matches the exact oscillator current in both limits. Is Redfield what
443
+ you would trust for coupled *qubits* too, or is there a benchmark where it
444
+ fails that should be added?
445
+ - **Collective baths.** N qubits coupled to one bath through their total
446
+ spin carry more heat per qubit than N independent ones. At the temperatures
447
+ tried, the gain saturates near 2.6× instead of growing with N. Is there a
448
+ published benchmark for superradiant heat transport to validate against
449
+ before this becomes a feature?
450
+ - **Strong coupling beyond one reaction coordinate.** Is the RC mapping
451
+ enough for the systems you study, or is a HEOM or TEMPO backend the
452
+ missing piece?
453
+ - **Hardware.** Which experimental platform (superconducting circuits,
454
+ trapped ions, NV centres, quantum dots) should the models and `LabUnits`
455
+ presets target first?
456
+
457
+ ## Units and conventions
458
+
459
+ `ħ = k_B = 1`, with an energy unit of your choice; `qt.LabUnits(f0_GHz)`
460
+ converts to and from GHz, mK, seconds and watts for the unit `E0 = h f0`.
461
+ `Q > 0` and `J > 0` mean energy flowing **into** the system;
462
+ `W > 0` means work done **on** it. `qubit_hamiltonian(ω) = −(ω/2)σ_z`, so
463
+ |1⟩ is the excited state.
464
+
465
+ ## References
466
+
467
+ Implemented quantities are standard; the package does not introduce new
468
+ physics. Each benchmark cites its source (run `python -m qthermo.benchmarks`).
469
+ Main ones: Alicki (1979) heat/work split; Spohn (1978) entropy production;
470
+ Davies (1974) weak-coupling generator; Levy & Kosloff, EPL 107, 20004 (2014);
471
+ De Chiara et al., NJP 20, 113024 (2018); Linden, Popescu & Skrzypczyk, PRL 105,
472
+ 130401 (2010); Brunner et al., PRE 85, 051117 (2012); Scovil & Schulz-DuBois,
473
+ PRL 2, 262 (1959); Barato & Seifert, PRL 114, 158101 (2015); Kalaee, Wacker &
474
+ Potts, PRE 104, L012103 (2021); Landi et al., PRX Quantum 5, 020201 (2024);
475
+ Strasberg et al., NJP 18, 073007 (2016); Cresser & Anders, PRL 127, 250601
476
+ (2021); Scandi & Perarnau-Llobet, Quantum 3, 197 (2019); Thomas & Johal, PRE
477
+ 83, 031135 (2011); Jarzynski, PRL 78, 2690 (1997).
478
+
479
+ ## Citing
480
+
481
+ See [`CITATION.cff`](https://github.com/VivekKhanna0/Qthermo/blob/main/CITATION.cff) (GitHub shows a "Cite this repository"
482
+ button). Please also cite the original papers for the physics you use; each
483
+ benchmark names its source.
484
+
485
+ ## License
486
+
487
+ MIT