quantum-simulation-lab 1.0.4__tar.gz → 2.0.1__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 (82) hide show
  1. quantum_simulation_lab-2.0.1/CHANGELOG.md +125 -0
  2. quantum_simulation_lab-2.0.1/CONVENTIONS.md +440 -0
  3. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/DIARY.md +11 -0
  4. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/PKG-INFO +91 -12
  5. quantum_simulation_lab-2.0.1/README.md +205 -0
  6. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/ROADMAP.md +28 -15
  7. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/pyproject.toml +1 -1
  8. quantum_simulation_lab-2.0.1/tensor_network_library/algorithms/tebd.py +1072 -0
  9. quantum_simulation_lab-2.0.1/tensor_network_library/core/gate_application.py +229 -0
  10. quantum_simulation_lab-2.0.1/tensor_network_library/core/gates.py +202 -0
  11. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/core/utils.py +1 -1
  12. quantum_simulation_lab-2.0.1/tensor_network_library/requirements.txt +11 -0
  13. quantum_simulation_lab-2.0.1/tensor_network_library/states/entangled_states.py +252 -0
  14. quantum_simulation_lab-2.0.1/tests/algorithms/test_dmrg.py +107 -0
  15. quantum_simulation_lab-2.0.1/tests/algorithms/test_ground_state_search.py +246 -0
  16. quantum_simulation_lab-2.0.1/tests/algorithms/test_tebd.py +337 -0
  17. quantum_simulation_lab-2.0.1/tests/core/test_gate_application.py +221 -0
  18. quantum_simulation_lab-2.0.1/tests/core/test_gates.py +113 -0
  19. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tests/examples/test_dmrg_hamiltonians.py +4 -4
  20. quantum_simulation_lab-2.0.1/tests/examples/test_tebd_physical_problem.py +107 -0
  21. quantum_simulation_lab-2.0.1/tests/states/test_entnagled_states.py +156 -0
  22. quantum_simulation_lab-1.0.4/README.md +0 -126
  23. quantum_simulation_lab-1.0.4/tensor_network_library/requirements.txt +0 -3
  24. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/.gitattributes +0 -0
  25. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/.github/workflows/publish.yml +0 -0
  26. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/.github/workflows/tests.yml +0 -0
  27. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/.github/workflows/update-loc.yml +0 -0
  28. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/.gitignore +0 -0
  29. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/.vscode/settings.json +0 -0
  30. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/LICENSE +0 -0
  31. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/__init__.py +0 -0
  32. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/dmrg_hamiltonians.py +0 -0
  33. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_x_field/__init__.py +0 -0
  34. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_x_field/results/H2_convergence.csv +0 -0
  35. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_x_field/results/H2_energy_convergence.png +0 -0
  36. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_x_field/results/H2_itensors_convergence.csv +0 -0
  37. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_x_field/results/H2_itensors_energy.png +0 -0
  38. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_x_field/run_dmrg.py +0 -0
  39. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_x_field/run_dmrg_itensors.jl +0 -0
  40. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_x_field/test_run_dmrg.py +0 -0
  41. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_z_field/__init__.py +0 -0
  42. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_z_field/results/H1_convergence.csv +0 -0
  43. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_z_field/results/H1_energy_convergence.png +0 -0
  44. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_z_field/results/H1_itensors_convergence.csv +0 -0
  45. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_z_field/results/H1_itensors_energy.png +0 -0
  46. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_z_field/run_dmrg.py +0 -0
  47. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_z_field/run_dmrg_itensors.jl +0 -0
  48. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/random_z_field/test_run_dmrg.py +0 -0
  49. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/zz_plus_z/__init__.py +0 -0
  50. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/zz_plus_z/results/H3_convergence.csv +0 -0
  51. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/zz_plus_z/results/H3_energy_convergence.png +0 -0
  52. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/zz_plus_z/results/H3_itensors_convergence.csv +0 -0
  53. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/zz_plus_z/results/H3_itensors_energy.png +0 -0
  54. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/zz_plus_z/run_dmrg.py +0 -0
  55. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/examples/zz_plus_z/run_dmrg_itensors.jl +0 -0
  56. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/__init__.py +0 -0
  57. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/algorithms/__init__.py +0 -0
  58. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/algorithms/dmrg.py +0 -0
  59. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/core/__init__.py +0 -0
  60. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/core/canonical.py +0 -0
  61. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/core/env.py +0 -0
  62. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/core/index.py +0 -0
  63. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/core/mpo.py +0 -0
  64. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/core/mps.py +0 -0
  65. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/core/policy.py +0 -0
  66. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/core/tensor.py +0 -0
  67. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/hamiltonian/__init__.py +0 -0
  68. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/hamiltonian/models.py +0 -0
  69. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/hamiltonian/operators.py +0 -0
  70. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/states/__init__.py +0 -0
  71. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tensor_network_library/states/qubit_states.py +0 -0
  72. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tests/core/test_canonical.py +0 -0
  73. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tests/core/test_env.py +0 -0
  74. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tests/core/test_index.py +0 -0
  75. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tests/core/test_mpo.py +0 -0
  76. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tests/core/test_mps.py +0 -0
  77. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tests/core/test_policy.py +0 -0
  78. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tests/core/test_tensor.py +0 -0
  79. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tests/core/test_utils.py +0 -0
  80. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tests/hamiltonian/test_hamiltonians.py +0 -0
  81. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tests/hamiltonian/test_operators.py +0 -0
  82. {quantum_simulation_lab-1.0.4 → quantum_simulation_lab-2.0.1}/tests/states/test_qubit_states.py +0 -0
@@ -0,0 +1,125 @@
1
+ # Changelog
2
+
3
+ All notable changes to `quantum-simulation-lab` are documented here.
4
+ This project adheres to [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and [Semantic Versioning](https://semver.org/).
5
+
6
+ ---
7
+
8
+ ## [Unreleased]
9
+
10
+ _Changes that are merged to `main` but not yet tagged._
11
+
12
+ ---
13
+
14
+ ## [2.0.0] — 2026-06-06
15
+
16
+ ### Added
17
+ - **`ground_state_search`** (`tebd.py`): high-level imaginary-time TEBD ground-state search.
18
+ Runs a first-order Trotter loop of `exp(-Δτ H)` steps, renormalises after every step, and
19
+ stops when `|E_n − E_{n−1}| < energy_tol`. Returns a `GroundStateResult` with the
20
+ converged MPS, full energy history, norm history, convergence flag, and step count.
21
+ - **`GroundStateResult`** dataclass: structured return type for `ground_state_search`.
22
+ - **`_left_canonicalize_inplace`** (internal helper): left-to-right QR sweep bringing any
23
+ MPS into left-canonical form in O(L χ² d). Used before every energy measurement in
24
+ `ground_state_search` to ensure Im(⟨H⟩) ≈ 0 regardless of the TEBD gauge.
25
+ - **`CONVENTIONS.md`** — tensor and index ordering documentation.
26
+ - **Second-order (Strang) Trotter splitting** (`finite_tebd_strang`).
27
+
28
+ ### Fixed
29
+ - **Spurious imaginary energy warning in `ground_state_search`**: after a TEBD sweep the MPS
30
+ is in a mixed gauge (S absorbed into the right tensor of the last updated bond). Calling
31
+ `measure_bond_energies` on this state produces non-identity transfer matrices, causing
32
+ Im(⟨H⟩) up to ~0.35 early in the evolution and firing the `> 1e-10` guard ~40 times per
33
+ test run. Fix: `ground_state_search` now QR-sweeps a *copy* of the MPS to left-canonical
34
+ form before every energy measurement; Im(⟨H⟩) drops to machine precision (~1e-14).
35
+
36
+ ### Changed
37
+ - `finite_tebd_imaginary` now accepts an optional `measure_fn: (MPS) -> float` callback for
38
+ in-loop energy tracking, populating `TEBDResult.energy_history`.
39
+
40
+ ---
41
+
42
+ ## [1.2.2] — 2026-04-23
43
+
44
+ ### Fixed
45
+ - CI `publish.yml` heredoc scoping bug: `NEW_VERSION` shell variable was out of scope across `run:` steps; replaced with Actions expression syntax `${{ steps.version.outputs.version }}` substituted by the runner before shell execution.
46
+
47
+ ---
48
+
49
+ ## [1.2.1] — 2026-04-23
50
+
51
+ ### Fixed
52
+ - `ComplexWarning` in `utils.py` and `test_dmrg_hamiltonians.py` caused by implicit cast from complex to float in `np.vdot` calls; cast is now explicit.
53
+ - Flaky `test_strang_more_accurate_than_first_order` test: tightened dt and tolerance so second-order convergence is reliably distinguishable from first-order.
54
+
55
+ ---
56
+
57
+ ## [1.2.0] — 2026-04-23
58
+
59
+ ### Added
60
+ - **Entangled-state helpers** (`tensor_network_library/states/entangled_states.py`):
61
+ - All four Bell states as statevectors and as MPS
62
+ - GHZ states for arbitrary `L` as statevectors and MPS
63
+ - W states for arbitrary `L` as statevectors and MPS
64
+ - Public re-exports via `tensor_network_library/__init__.py`
65
+ - **Two-site gate application** (`apply_two_site_gate`): in-place SVD-based gate on adjacent MPS sites with optional `TruncationPolicy`.
66
+ - **Gate builders**:
67
+ - `two_site_gate_from_hamiltonian(H, dt)` — real-time gate via exact diagonalisation of a 4×4 local Hamiltonian.
68
+ - `two_site_gate_imaginary(H, dt)` — imaginary-time gate (non-unitary) for ground-state preparation.
69
+ - **Finite TEBD** (`finite_tebd`): first-order Trotter time-stepper sweeping even/odd bond layers.
70
+ - **Imaginary-time TEBD** (`finite_tebd_imaginary`): Euclidean time evolution converging to the ground state; validated against DMRG energies for TFIM and Heisenberg.
71
+ - **`measure_local`**: single-site expectation values via efficient transfer-matrix sweep without forming the full statevector.
72
+ - **`TEBDConfig`**: dataclass for step count, truncation policy, and normalisation flag.
73
+ - **Transverse Heisenberg MPO builder** (`transverse_heisenberg_mpo`).
74
+
75
+ ### Changed
76
+ - `MPS` constructors unified: `from_statevector`, `from_qubit_labels`, and product-state paths now all pass through a single canonicalisation routine.
77
+ - `TruncationPolicy` gains a `strict` flag; truncation errors are now returned from `apply_two_site_gate` for downstream inspection.
78
+
79
+ ### Tests
80
+ - 47 new tests covering entangled states, gate builders, TEBD convergence, and imaginary-time evolution.
81
+ - Total test count: **362**.
82
+
83
+ ---
84
+
85
+ ## [1.0.4] — 2026-03-08
86
+
87
+ ### Fixed
88
+ - Edge-case in right-to-left DMRG sweep: gauge was not restored after last site update, causing incorrect energies on subsequent sweeps for open boundary conditions with `L=4`.
89
+ - `MPO.to_dense()` memory layout was transposing physical indices for `L > 8`; now consistent with statevector qubit ordering.
90
+
91
+ ---
92
+
93
+ ## [1.0.3] — 2026-03-08
94
+
95
+ ### Added
96
+ - `MPO.apply(mps)` — applies an MPO to an MPS and returns a new MPS, used as the basis for the DMRG effective Hamiltonian contraction.
97
+ - ZZ+Z random field Hamiltonian builder.
98
+ - Random X-field Hamiltonian builder.
99
+
100
+ ### Fixed
101
+ - `Environment.update_left` / `update_right` were not normalising the boundary tensors, leading to numerical drift over many sweeps.
102
+
103
+ ---
104
+
105
+ ## [1.0.2] — 2026-03-08
106
+
107
+ ### Added
108
+ - `heisenberg_mpo` and `xx_mpo` Hamiltonian builders for XXZ and isotropic XX models.
109
+ - Finite 2-site DMRG converges on Heisenberg and random-field models; energies match iTensor to `1e-8`.
110
+
111
+ ---
112
+
113
+ ## [1.0.0] — 2026-03-04
114
+
115
+ ### Added
116
+ - Initial release.
117
+ - `Tensor`, `Index` — numpy-backed tensors with named indices.
118
+ - `MPS` — product-state, statevector, and qubit-label constructors; left/right canonicalisation; SVD truncation via `TruncationPolicy`.
119
+ - `MPO` — identity construction and dense conversion.
120
+ - `Environment` — qubit chain and spin-1/2 bosonic chain; incremental left/right environment updates.
121
+ - `FiniteChain` geometry, `QubitSite` site type.
122
+ - `tfim_mpo` — transverse-field Ising model MPO.
123
+ - Finite 2-site DMRG (`finite_dmrg`) with `DMRGConfig`; converges on TFIM; energies match iTensor.
124
+ - GitHub Actions CI: pytest, LOC auto-badge, PyPI trusted publishing.
125
+ - 315 tests at initial release.
@@ -0,0 +1,440 @@
1
+ # Conventions
2
+
3
+ > **Scope:** This document records every tensor-ordering, index-naming, sign, and algorithmic convention used in `quantum-simulation-lab`. It is the single source of truth. When code and this document disagree, fix the code.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ 1. [Site and chain labelling](#1-site-and-chain-labelling)
10
+ 2. [Tensor axis ordering](#2-tensor-axis-ordering)
11
+ 3. [Index naming](#3-index-naming)
12
+ 4. [Bond dimensions](#4-bond-dimensions)
13
+ 5. [Canonicalisation gauge](#5-canonicalisation-gauge)
14
+ 6. [SVD and singular-value absorption](#6-svd-and-singular-value-absorption)
15
+ 7. [Gate ordering](#7-gate-ordering)
16
+ 8. [MPO structure](#8-mpo-structure)
17
+ 9. [Hamiltonian sign conventions](#9-hamiltonian-sign-conventions)
18
+ 10. [Statevector qubit ordering](#10-statevector-qubit-ordering)
19
+ 11. [DMRG conventions](#11-dmrg-conventions)
20
+ 12. [TEBD conventions](#12-tebd-conventions)
21
+ 13. [Imaginary-time evolution](#13-imaginary-time-evolution)
22
+ 14. [Measurement conventions](#14-measurement-conventions)
23
+ 15. [Dtype and precision](#15-dtype-and-precision)
24
+
25
+ ---
26
+
27
+ ## 1. Site and chain labelling
28
+
29
+ - Sites are labelled $0, 1, \ldots, L-1$ (zero-indexed, left to right).
30
+ - **Open boundary conditions (OBC)** are the default for all finite algorithms. Periodic boundaries are not yet implemented.
31
+ - Bond $b$ connects site $b-1$ to site $b$. Bond $0$ is the left virtual vacuum (dimension 1); bond $L$ is the right virtual vacuum (dimension 1).
32
+ - A chain of length $L$ has $L$ site tensors and $L+1$ bonds (including both boundary bonds of dimension 1).
33
+
34
+ ```
35
+ bond_0 site_0 bond_1 site_1 bond_2 ... bond_{L-1} site_{L-1} bond_L
36
+ [1] ── [A_0] ── [χ_1] ── [A_1] ── [χ_2] ── ... ── [χ_{L-1}] ── [A_{L-1}] ── [1]
37
+ ```
38
+
39
+ ---
40
+
41
+ ## 2. Tensor axis ordering
42
+
43
+ ### MPS site tensor
44
+
45
+ Every site tensor $A_i$ is a rank-3 array with axes in this fixed order:
46
+
47
+ ```
48
+ A_i.data.shape == (χ_left, d_i, χ_right)
49
+ axis 0 axis 1 axis 2
50
+ ```
51
+
52
+ | Axis | Role | Symbol |
53
+ |------|------|--------|
54
+ | 0 | Left virtual bond | $\chi_\text{left}$ |
55
+ | 1 | Physical (local Hilbert space) | $d_i$ |
56
+ | 2 | Right virtual bond | $\chi_\text{right}$ |
57
+
58
+ This matches the `_create_empty_tensors` ordering in `mps.py`:
59
+ ```python
60
+ inds = [self.bonds[i], self.indices[i], self.bonds[i + 1]]
61
+ ```
62
+
63
+ ### MPO site tensor
64
+
65
+ Every MPO site tensor $W_i$ is rank-4:
66
+
67
+ ```
68
+ W_i.shape == (χ_left, d_i, d_i, χ_right)
69
+ axis 0 axis 1 axis 2 axis 3
70
+ ```
71
+
72
+ | Axis | Role |
73
+ |------|------|
74
+ | 0 | Left MPO virtual bond |
75
+ | 1 | Physical ket (output) index |
76
+ | 2 | Physical bra (input) index |
77
+ | 3 | Right MPO virtual bond |
78
+
79
+ The ket index (axis 1) always comes before the bra index (axis 2).
80
+
81
+ ### Two-site (theta) tensor
82
+
83
+ When two adjacent MPS tensors are contracted together during a gate application or DMRG update:
84
+
85
+ ```
86
+ theta[a, i, j, b] == sum_c A_i[a, i, c] * A_j[c, j, b]
87
+ ```
88
+
89
+ Shape: `(χ_left, d_i, d_j, χ_right)` — left bond, left physical, right physical, right bond.
90
+
91
+ This is the ordering used in `gate_application.py`:
92
+ ```python
93
+ theta = np.einsum("aic,cjb->aijb", A_i, A_j)
94
+ ```
95
+
96
+ ### Gate tensor (rank-4)
97
+
98
+ A two-site gate acting on sites $(i, i+1)$ is stored as:
99
+
100
+ ```
101
+ U[i', j', i, j]
102
+ ```
103
+
104
+ where primed indices are **output (ket)** and unprimed are **input (bra)**. As a matrix it is flattened as `U.reshape(d*d, d*d)` with row index $(i', j')$ and column index $(i, j)$ — standard operator convention.
105
+
106
+ ```python
107
+ # gate_application.py applies it as:
108
+ theta_prime = np.einsum("mnij,aijb->amnb", U, theta)
109
+ # U[m=i', n=j', i, j] * theta[a, i, j, b]
110
+ ```
111
+
112
+ ---
113
+
114
+ ## 3. Index naming
115
+
116
+ `Index` objects carry a human-readable `name` and a frozen set of `tags`. The naming scheme:
117
+
118
+ | Index type | Name pattern | Tags |
119
+ |-----------|--------------|------|
120
+ | Physical index at site $i$ | `{mps_name}_phys_{i}` | `{"phys", "i={i}"}` |
121
+ | Bond between sites $i$ and $i+1$ | `{mps_name}_bond_{i+1}` | `{"bond", "b={i+1}"}` |
122
+ | Left boundary bond | `{mps_name}_bond_0` | `{"bond", "b=0"}` |
123
+ | Right boundary bond | `{mps_name}_bond_{L}` | `{"bond", "b={L}"}` |
124
+
125
+ Bond index $b$ sits between site $b-1$ and site $b$, so `bond_1` is shared between `tensors[0]` (as its `axis 2`) and `tensors[1]` (as its `axis 0`).
126
+
127
+ After a gate application, the shared bond between site $i$ and $i+1$ is recreated with the same name and tags as the original `bonds[i+1]` but with the updated dimension `chi_new`.
128
+
129
+ ---
130
+
131
+ ## 4. Bond dimensions
132
+
133
+ - `mps.bonds` is a list of `Index` objects of length $L+1$. `bonds[b].dim` gives $\chi_b$.
134
+ - `mps._bond_dims` mirrors this as a plain `List[int]` for fast arithmetic.
135
+ - Boundary bonds: `bonds[0].dim == bonds[L].dim == 1` always.
136
+ - **Default bond policy** (`bond_policy="default"`): $\chi_b = \min\!\left(\prod_{k<b} d_k,\; \prod_{k \geq b} d_k\right)$, optionally capped by `TruncationPolicy.max_bond_dim`.
137
+ - **Uniform bond policy** (`bond_policy="uniform"`): all interior bonds set to `truncation.max_bond_dim`; boundary bonds remain 1.
138
+
139
+ ### TruncationPolicy
140
+
141
+ `TruncationPolicy` (in `core/policy.py`) controls SVD truncation in both `from_statevector` and `apply_two_site_gate`:
142
+
143
+ - `max_bond_dim`: hard cap on $\chi$ after SVD.
144
+ - `svd_cutoff`: singular values $\sigma < \text{cutoff}$ are discarded (default `1e-12`).
145
+ - `strict`: if `True`, raises on truncation error above threshold (default `False`).
146
+
147
+ At least one singular value is always kept, even if all fall below the cutoff.
148
+
149
+ ---
150
+
151
+ ## 5. Canonicalisation gauge
152
+
153
+ ### Left-canonical tensor
154
+
155
+ $A_i$ is **left-canonical** if:
156
+
157
+ $$\sum_{a,\sigma} (A_i)^*_{a\sigma c}\,(A_i)_{a\sigma c'} = \delta_{cc'}$$
158
+
159
+ i.e. `A_i.reshape(χ_left * d, χ_right)` has orthonormal columns.
160
+
161
+ ### Right-canonical tensor
162
+
163
+ $B_i$ is **right-canonical** if:
164
+
165
+ $$\sum_{\sigma,c} (B_i)^*_{a\sigma c}\,(B_i)_{a'\sigma c} = \delta_{aa'}$$
166
+
167
+ i.e. `B_i.reshape(χ_left, d * χ_right)` has orthonormal rows.
168
+
169
+ ### Mixed-canonical (site-canonical) form
170
+
171
+ An MPS is in **mixed-canonical form centred at site $k$** when:
172
+ - Sites $0, \ldots, k-1$ are left-canonical.
173
+ - Sites $k+1, \ldots, L-1$ are right-canonical.
174
+ - Site $k$ is unconstrained (carries all norm).
175
+
176
+ This is the standard gauge used by the DMRG local update: the effective Hamiltonian at site $k$ is orthogonally projected by the left/right environments.
177
+
178
+ ### Canonicalisation in `canonical.py`
179
+
180
+ `left_canonicalize(mps, site)` and `right_canonicalize(mps, site)` sweep QR decompositions across the chain. Singular values from QR are absorbed **rightward** during left-sweep, **leftward** during right-sweep. The norm is concentrated at the orthogonality centre.
181
+
182
+ ---
183
+
184
+ ## 6. SVD and singular-value absorption
185
+
186
+ When splitting a two-site tensor $\Theta$ via SVD:
187
+
188
+ $$\Theta = U\, S\, V^\dagger$$
189
+
190
+ the singular values $S$ must be absorbed into one of the two new tensors. The `absorb` parameter controls this:
191
+
192
+ | `absorb` value | Left tensor $A'$ | Right tensor $B'$ | Use case |
193
+ |---------------|-----------------|------------------|----------|
194
+ | `"right"` (default) | $U$ | $S V^\dagger$ | Left-sweep; keeps $A'$ left-canonical |
195
+ | `"left"` | $U S$ | $V^\dagger$ | Right-sweep; keeps $B'$ right-canonical |
196
+ | `"both"` | $U \sqrt{S}$ | $\sqrt{S} V^\dagger$ | Symmetric split; neither tensor is canonical |
197
+
198
+ The default in `apply_two_site_gate` is `absorb="right"` — appropriate for a left-to-right TEBD sweep where $A'$ should be left-isometric before the next bond update.
199
+
200
+ In `from_statevector`, the default is `absorb="right"` as well, producing a right-canonical MPS after the full left-to-right SVD sweep (with the last site holding the norm).
201
+
202
+ ---
203
+
204
+ ## 7. Gate ordering
205
+
206
+ ### Two-site gate matrix convention
207
+
208
+ A two-site gate $U$ on sites $(i, i+1)$ with local dimension $d$ is stored as a $(d^2 \times d^2)$ matrix or equivalently a $(d, d, d, d)$ rank-4 tensor. The index ordering is:
209
+
210
+ ```
211
+ U[i', j', i, j] — output-left, output-right, input-left, input-right
212
+ ```
213
+
214
+ As a matrix: rows index the **output** basis $(i' d + j')$, columns index the **input** basis $(i\,d + j)$.
215
+
216
+ **Example — CNOT with $d=2$, control = left site:**
217
+
218
+ ```
219
+ U_CNOT[i', j', i, j]:
220
+ i'=i (control unchanged)
221
+ j'=j XOR i (target flipped if control=1)
222
+ ```
223
+
224
+ ### Real-time gate from Hamiltonian
225
+
226
+ `two_site_gate_from_hamiltonian(H, dt)` computes:
227
+
228
+ $$U = e^{-i\,dt\,H}$$
229
+
230
+ where $H$ is a $(d^2 \times d^2)$ Hermitian matrix for a local two-site interaction. The result is reshaped to `(d, d, d, d)` with the `(i', j', i, j)` ordering above.
231
+
232
+ ### Imaginary-time gate
233
+
234
+ `two_site_gate_imaginary(H, dt)` computes:
235
+
236
+ $$G = e^{-\tau\,H}, \qquad \tau = dt > 0$$
237
+
238
+ This is **not unitary**. After applying $G$ the MPS must be renormalised (handled automatically by `finite_tebd_imaginary` via `TEBDConfig(normalize=True)`).
239
+
240
+ ---
241
+
242
+ ## 8. MPO structure
243
+
244
+ ### Virtual bond ordering (MPO row/column)
245
+
246
+ The MPO virtual bond for a standard two-body Hamiltonian is arranged as:
247
+
248
+ ```
249
+ W_i = [ I 0 0 ]
250
+ [ O_L 0 0 ]
251
+ [ h O_R I ]
252
+ ```
253
+
254
+ where the first row/column corresponds to the **left boundary** (passes the identity through from the left) and the last row/column corresponds to the **right boundary** (accumulates the completed term). This is the standard "zipper" or "finite-state machine" MPO construction.
255
+
256
+ For the TFIM and Heisenberg MPOs in `hamiltonian/`, the MPO bond dimension is:
257
+ - TFIM: $\chi_W = 3$
258
+ - Heisenberg / XXZ: $\chi_W = 5$
259
+
260
+ ### `MPO.apply(mps)`
261
+
262
+ Contracts the MPO with the MPS to produce a new MPS. The result has physical indices in the same ordering as the input MPS. Bond dimensions of the output MPS grow as $\chi_\text{out} = \chi_\text{MPS} \times \chi_W$ before any compression.
263
+
264
+ ### `MPO.to_dense()`
265
+
266
+ Converts the MPO to a dense $(d^L \times d^L)$ matrix. The row index is the **ket** (output) multi-index and the column index is the **bra** (input) multi-index, both in **little-endian qubit order** (site 0 is the least significant bit). This matches `MPS.to_dense()` so that `mpo.to_dense() @ mps.to_dense()` gives the correct action.
267
+
268
+ ---
269
+
270
+ ## 9. Hamiltonian sign conventions
271
+
272
+ All Hamiltonians are written so that the **ground state energy is negative** for ferromagnetic/antiferromagnetic ordering in their standard parameter regime.
273
+
274
+ ### TFIM
275
+
276
+ $$H_\text{TFIM} = -J \sum_i Z_i Z_{i+1} - h \sum_i X_i$$
277
+
278
+ - $J > 0$: ferromagnetic coupling.
279
+ - $h > 0$: transverse field.
280
+ - Critical point at $h/J = 1$ for the infinite chain.
281
+
282
+ ### Heisenberg (XXX)
283
+
284
+ $$H_\text{Heis} = J \sum_i \left(X_i X_{i+1} + Y_i Y_{i+1} + Z_i Z_{i+1}\right)$$
285
+
286
+ - $J > 0$: antiferromagnetic. Ground state energy per bond $\approx -0.4431$ for the infinite chain.
287
+ - $J < 0$: ferromagnetic.
288
+
289
+ ### XX model
290
+
291
+ $$H_\text{XX} = J \sum_i \left(X_i X_{i+1} + Y_i Y_{i+1}\right)$$
292
+
293
+ ### Pauli matrix convention
294
+
295
+ We use the standard Pauli matrices (not $\hbar/2$ normalised):
296
+
297
+ $$X = \begin{pmatrix}0&1\\1&0\end{pmatrix}, \quad Y = \begin{pmatrix}0&-i\\i&0\end{pmatrix}, \quad Z = \begin{pmatrix}1&0\\0&-1\end{pmatrix}$$
298
+
299
+ The computational basis is $|0\rangle = (1,0)^T$ (spin-up, $+Z$ eigenstate) and $|1\rangle = (0,1)^T$ (spin-down, $-Z$ eigenstate).
300
+
301
+ ---
302
+
303
+ ## 10. Statevector qubit ordering
304
+
305
+ `MPS.to_dense()` returns a $d^L$-dimensional statevector. The multi-index is **big-endian**: site 0 is the **most significant** position.
306
+
307
+ For $L=3$, $d=2$:
308
+
309
+ ```
310
+ index k = σ_0 * 4 + σ_1 * 2 + σ_2 * 1
311
+ psi[k] = <σ_0 σ_1 σ_2 | ψ>
312
+ ```
313
+
314
+ So `psi[0]` = $\langle 000|\psi\rangle$, `psi[1]` = $\langle 001|\psi\rangle$, `psi[4]` = $\langle 100|\psi\rangle$, etc.
315
+
316
+ `MPO.to_dense()` follows the same convention so that matrix-vector products are consistent.
317
+
318
+ > **Watch out:** some quantum computing libraries (e.g. Qiskit) use little-endian (site 0 = least significant). When comparing with those libraries, reverse the qubit order.
319
+
320
+ ---
321
+
322
+ ## 11. DMRG conventions
323
+
324
+ ### Two-site update
325
+
326
+ The finite DMRG implemented here uses the **two-site update** at each step:
327
+ 1. Form the effective Hamiltonian $H_\text{eff}$ for sites $(i, i+1)$ from left/right environments and the MPO.
328
+ 2. Diagonalise $H_\text{eff}$ (via `scipy.sparse.linalg.eigsh` with `which="SA"`) to get the ground-state two-site tensor $\Theta$.
329
+ 3. SVD-split $\Theta$ into $A_i$ and $A_{i+1}$ with optional truncation.
330
+ 4. Absorb singular values to the **right** on the left-to-right sweep, and to the **left** on the right-to-left sweep, maintaining mixed-canonical form.
331
+
332
+ ### Sweep direction
333
+
334
+ A full DMRG sweep is: left-to-right (sites $0 \to L-2$) then right-to-left (sites $L-2 \to 0$). One full sweep = one DMRG iteration. Convergence is checked by comparing the energy after each full sweep.
335
+
336
+ ### Environment update
337
+
338
+ Left environment $L_i$ and right environment $R_i$ are updated incrementally:
339
+
340
+ $$L_{i+1}[a', a] = \sum_{\alpha', \alpha, \sigma} A^*_i[\alpha', \sigma, a']\; W_i[\alpha', \sigma', \sigma, \alpha]\; A_i[\alpha, \sigma, a]\; L_i[\alpha', \alpha]$$
341
+
342
+ (and analogously for right environments). This is the standard MPS-MPO-MPS contraction. Environments are stored as rank-3 tensors with shape `(χ_mps, χ_mpo, χ_mps)`.
343
+
344
+ ### DMRGConfig
345
+
346
+ ```python
347
+ @dataclass
348
+ class DMRGConfig:
349
+ n_sweeps: int # number of full sweeps
350
+ max_bond_dim: int # maximum bond dimension χ
351
+ svd_cutoff: float # singular-value cutoff (default 1e-10)
352
+ tol: float # energy convergence tolerance (default 1e-8)
353
+ verbose: bool # print energy per sweep (default False)
354
+ ```
355
+
356
+ ---
357
+
358
+ ## 12. TEBD conventions
359
+
360
+ ### Trotter decomposition
361
+
362
+ **First-order (Lie–Trotter):**
363
+
364
+ $$e^{-i\,dt\,H} \approx \prod_{\langle i,i+1\rangle \in \text{even}} e^{-i\,dt\,h_{i,i+1}} \cdot \prod_{\langle i,i+1\rangle \in \text{odd}} e^{-i\,dt\,h_{i,i+1}} + O(dt^2)$$
365
+
366
+ Even bonds: $(0,1), (2,3), \ldots$ — applied first.
367
+ Odd bonds: $(1,2), (3,4), \ldots$ — applied second.
368
+
369
+ **Second-order (Strang / Leapfrog):**
370
+
371
+ $$e^{-i\,dt\,H} \approx \prod_\text{even} e^{-i\,\frac{dt}{2}\,h} \cdot \prod_\text{odd} e^{-i\,dt\,h} \cdot \prod_\text{even} e^{-i\,\frac{dt}{2}\,h} + O(dt^3)$$
372
+
373
+ `finite_tebd_strang` implements this: half-step even gates → full odd gates → half-step even gates per time step.
374
+
375
+ ### Gate layer ordering
376
+
377
+ Within each layer, gates are applied **left to right** (bond 0 first, then bond 2, then bond 4, … for even; bond 1, then bond 3, … for odd). This is purely a sweep convention and has no physical significance for commuting gates within a layer.
378
+
379
+ ### TEBDConfig
380
+
381
+ ```python
382
+ @dataclass
383
+ class TEBDConfig:
384
+ n_steps: int # number of Trotter steps
385
+ truncation: TruncationPolicy | None # SVD truncation per gate application
386
+ normalize: bool # renormalize MPS after each full step
387
+ verbose: bool # print norm/energy per step
388
+ ```
389
+
390
+ ---
391
+
392
+ ## 13. Imaginary-time evolution
393
+
394
+ Imaginary-time TEBD (`finite_tebd_imaginary`) evolves under:
395
+
396
+ $$|\psi(\tau)\rangle = e^{-\tau H}|\psi(0)\rangle \Big/ \|e^{-\tau H}|\psi(0)\rangle\|$$
397
+
398
+ with $\tau = n\_steps \times dt$. The gates are **non-unitary** ($G = e^{-dt\,h_{ij}}$, real positive spectrum for $h_{ij}$ positive semi-definite). After each full Trotter step the MPS is renormalised so the norm does not underflow.
399
+
400
+ **Ground-state preparation recipe:**
401
+
402
+ 1. Start from a random or product MPS that is not orthogonal to the ground state.
403
+ 2. Choose $dt$ small enough that the first-order Trotter error is acceptable (typically $dt \in [0.01, 0.1]$).
404
+ 3. Run for enough steps that $\langle E \rangle$ converges to within tolerance.
405
+ 4. Cross-check final energy against DMRG.
406
+
407
+ The imaginary-time gates are built by `two_site_gate_imaginary(H_local, dt)` which computes $e^{-dt H_\text{local}}$ via `scipy.linalg.expm` (exact for the small $d^2 \times d^2$ matrix).
408
+
409
+ ---
410
+
411
+ ## 14. Measurement conventions
412
+
413
+ ### `measure_local(mps, operators)`
414
+
415
+ Computes single-site expectation values $\langle \psi | O_i | \psi \rangle$ for each site $i$ via a left–right transfer-matrix sweep:
416
+
417
+ 1. Build left environments $\{L_i\}$ by contracting MPS tensors left to right.
418
+ 2. For each site $i$, insert $O_i$ and contract with the matching right environment $R_i$.
419
+ 3. Normalise by $\langle\psi|\psi\rangle$ (computed from the same sweep).
420
+ 4. Return `float(Re(...))` — imaginary parts are discarded after a warning if $|\text{Im}| > 10^{-10}$.
421
+
422
+ `operators` is a `dict[int, np.ndarray]` mapping site index to a $(d \times d)$ operator matrix. Sites not in the dict are skipped.
423
+
424
+ ### Return type
425
+
426
+ `measure_local` returns `dict[int, float]` — one real number per queried site.
427
+
428
+ ### Operator convention
429
+
430
+ Operator matrices follow the same basis as the Pauli matrices in §9: row = ket (output), column = bra (input), with $|0\rangle = (1,0)^T$.
431
+
432
+ ---
433
+
434
+ ## 15. Dtype and precision
435
+
436
+ - Default dtype throughout the library: `np.complex128`.
437
+ - All internal contractions are performed in `complex128` even when the state is known to be real, to avoid silent cast errors.
438
+ - When computing norms or energies (real scalars), the result is explicitly cast via `float(np.real(...))` — never `float(complex_value)` directly, which raises `ComplexWarning`.
439
+ - Singular values are always real and non-negative; they are stored as `float64` arrays.
440
+ - `TruncationPolicy.svd_cutoff` defaults to `1e-12` — one order of magnitude below `complex128` machine epsilon ($\approx 2.2 \times 10^{-16}$) relative to the largest singular value.
@@ -2,6 +2,17 @@
2
2
 
3
3
  A chronological log of design decisions, bugs found, and milestones reached.
4
4
 
5
+ ## v1.0.7 • 2026-04-24
6
+
7
+ ## v1.0.6 • 2026-04-23
8
+
9
+ ## v1.0.5 • 2026-04-21
10
+
11
+ - Added dense statevector helpers for standard entangled state generation (GHZ, Bell, w)
12
+ - Added MPS helpers to generate and initialize entangled states from dense state vectors (Bell, GHZ, w)
13
+ - Added appropriate tests for these above new functions.
14
+
15
+ ---
5
16
 
6
17
  ## v1.0.3 · 2026-03-31
7
18