staging > main: refactored numerical experiment as module
Browse files
src/experiments/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
|
| 4 |
+
"""
|
| 5 |
+
Module containing experiments
|
| 6 |
+
"""
|
src/experiments/choi_cholesky/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
|
| 4 |
+
"""
|
| 5 |
+
Module containing experiments for Choi--Cholesky decomposition
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
# ----------------------------------------------------------------
|
| 9 |
+
# IMPORTS
|
| 10 |
+
# ----------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
from .verify import *
|
| 13 |
+
|
| 14 |
+
# ----------------------------------------------------------------
|
| 15 |
+
# EXPORTS
|
| 16 |
+
# ----------------------------------------------------------------
|
| 17 |
+
|
| 18 |
+
__all__ = [
|
| 19 |
+
"verify_choi_cholesky",
|
| 20 |
+
]
|
src/experiments/choi_cholesky/verify.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
|
| 4 |
+
"""
|
| 5 |
+
- author: Raj Dahya
|
| 6 |
+
- institute: Universität Leipzig
|
| 7 |
+
- department: Fakultät für Mathematik und Informatik
|
| 8 |
+
- created: 2025-12-27
|
| 9 |
+
- updated: 2026-03-29
|
| 10 |
+
- description:
|
| 11 |
+
Verifies the Choi-Cholesky decomposition of a CP/CPTP map numerically,
|
| 12 |
+
as presented in the paper _The Choi-Cholesky algorithm for completely positive maps_
|
| 13 |
+
(see <https://arxiv.org/abs/2603.19444>).
|
| 14 |
+
|
| 15 |
+
Verification occurs by running the experiment multiple times without failure.
|
| 16 |
+
|
| 17 |
+
Failure criteria:
|
| 18 |
+
|
| 19 |
+
- [critical] algorithm fails to produce positive entries for the diagonal operator (within tolerance)
|
| 20 |
+
- in final result L·L^* is not close to Choi matrix
|
| 21 |
+
|
| 22 |
+
NOTE: Due to the discontinuous nature of pseudo-inverses (cf Remark 3.3 in §3),
|
| 23 |
+
the algorithm can get close to a critical error under low numerical precision (e.g. Float32).
|
| 24 |
+
For this reason we use double precision (Float64) numbers.
|
| 25 |
+
|
| 26 |
+
NOTE: The option `map_kind = "CP"` establishes a proper confirmation.
|
| 27 |
+
For completeness we have included the option `"CPTP"`,
|
| 28 |
+
but this provides a circular test, as the generation of CPTP-maps here assumes results of paper in advance.
|
| 29 |
+
|
| 30 |
+
"""
|
| 31 |
+
|
| 32 |
+
# ----------------------------------------------------------------
|
| 33 |
+
# IMPORTS
|
| 34 |
+
# ----------------------------------------------------------------
|
| 35 |
+
|
| 36 |
+
import logging
|
| 37 |
+
|
| 38 |
+
import torch
|
| 39 |
+
from tabulate import tabulate
|
| 40 |
+
|
| 41 |
+
from ..._core.utils.linalg import *
|
| 42 |
+
from ...algorithms.choi_cholesky import *
|
| 43 |
+
from ...algorithms.constants import *
|
| 44 |
+
from ...algorithms.generation import *
|
| 45 |
+
|
| 46 |
+
# ----------------------------------------------------------------
|
| 47 |
+
# SETTINGS, CONSTANTS
|
| 48 |
+
# ----------------------------------------------------------------
|
| 49 |
+
|
| 50 |
+
TOL = 1e-6
|
| 51 |
+
# PRECISION = torch.float32 # works but to lower precision
|
| 52 |
+
PRECISION = torch.float64
|
| 53 |
+
# PRECISION = torch.double # same as f64
|
| 54 |
+
|
| 55 |
+
# ----------------------------------------------------------------
|
| 56 |
+
# EXECUTION
|
| 57 |
+
# ----------------------------------------------------------------
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def verify_choi_cholesky(
|
| 61 |
+
*,
|
| 62 |
+
d1: int,
|
| 63 |
+
d2: int,
|
| 64 |
+
num_experiments: int,
|
| 65 |
+
map_kind: MAP_CATEGORY,
|
| 66 |
+
algorithm_choice: ALGORITHM_CHOICE,
|
| 67 |
+
):
|
| 68 |
+
"""
|
| 69 |
+
Experimental verification of Choi--Cholesky decomposition of CP-/CPTP-maps
|
| 70 |
+
"""
|
| 71 |
+
# execute main method
|
| 72 |
+
tol = TOL
|
| 73 |
+
diffs: list[float] = []
|
| 74 |
+
for index in range(1, 1 + num_experiments):
|
| 75 |
+
logging.info(f"run experiment {index}")
|
| 76 |
+
|
| 77 |
+
"""
|
| 78 |
+
Create a random CP/CPTP-map
|
| 79 |
+
"""
|
| 80 |
+
|
| 81 |
+
match map_kind:
|
| 82 |
+
case "CPTP":
|
| 83 |
+
logging.info("generate Choi matrix C of a random CPTP map Φ")
|
| 84 |
+
C = random_cptp(d1, d2, precision=PRECISION)
|
| 85 |
+
diff = torch.norm(trace_2(C) - torch.eye(d1)).item()
|
| 86 |
+
assert diff < TOL, \
|
| 87 |
+
f"generated map failed to be trace preserving within tolerance {tol:.4g}" # fmt: skip
|
| 88 |
+
logging.info(f"generated map is trace preserving within tolerance: ‖tr₂(C) – I‖ = {diff:.4g} < {tol:.4g}") # fmt: skip
|
| 89 |
+
|
| 90 |
+
case _:
|
| 91 |
+
logging.info("generate Choi matrix C of a random CP map Φ")
|
| 92 |
+
C = random_cp(d1, d2, precision=PRECISION)
|
| 93 |
+
|
| 94 |
+
"""
|
| 95 |
+
Perform Choi-Cholesky decomposition
|
| 96 |
+
"""
|
| 97 |
+
|
| 98 |
+
logging.info("compute Choi-Cholesky decomposition for Φ")
|
| 99 |
+
match algorithm_choice:
|
| 100 |
+
case "CHOI-CHOLESKY-MODIFIED":
|
| 101 |
+
L = algorithm_choi_cholesky_modified(C, tol=TOL)
|
| 102 |
+
|
| 103 |
+
# case "CHOI-CHOLESKY":
|
| 104 |
+
case _:
|
| 105 |
+
L = algorithm_choi_cholesky(C, tol=TOL)
|
| 106 |
+
|
| 107 |
+
"""
|
| 108 |
+
Verify validity of Choi-Cholesky decomposition
|
| 109 |
+
"""
|
| 110 |
+
|
| 111 |
+
C_recovered = tensor_multiply(L, tensor_conj(L))
|
| 112 |
+
diff = tensor_diff(C, C_recovered, rel=True)
|
| 113 |
+
diffs.append(diff)
|
| 114 |
+
|
| 115 |
+
"""
|
| 116 |
+
Perform statistical evaluation of success rate
|
| 117 |
+
"""
|
| 118 |
+
|
| 119 |
+
diffs = torch.asarray(diffs)
|
| 120 |
+
levels = [0.25, 0.5, 0.75, 0.975]
|
| 121 |
+
quantiles = torch.quantile(diffs, q=torch.asarray(levels)).tolist()
|
| 122 |
+
table = tabulate(
|
| 123 |
+
zip(levels, quantiles),
|
| 124 |
+
headers=("quantile", "Diff"),
|
| 125 |
+
tablefmt="rst",
|
| 126 |
+
colalign=("right", "right"),
|
| 127 |
+
floatfmt=(".2%", ".3e"),
|
| 128 |
+
)
|
| 129 |
+
logging.info(f"quantiles for rel. differences ‖L·L^* – C(Φ)‖/‖C(Φ)‖:\n{table}")
|
| 130 |
+
diff = diffs[-1] # get 97.5% quantile of rel differences
|
| 131 |
+
assert diff < 0.5*TOL, \
|
| 132 |
+
f"Algorithm failed to produce decomposition that recovers original Choi matrix within tolerance {TOL:.4g}." # fmt: skip
|
| 133 |
+
logging.info(f"Algorithm succeeded in producing decomposition that recovers original Choi matrix within tolerance {TOL:.4g}.") # fmt: skip
|
| 134 |
+
return
|