iqm-benchmarks 2.44__py3-none-any.whl → 2.46__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.
@@ -96,7 +96,7 @@ def fidelity_ghz_randomized_measurements(
96
96
  p_sum = []
97
97
  for sb in c_id_keys:
98
98
  for sa in c_keys:
99
- exponent = hamming(list(sa), list(sb)) * num_qubits
99
+ exponent = hamming(np.array(list(sa)), np.array(list(sb))) * num_qubits
100
100
  p_sum.append(np.power(-2, -exponent) * probabilities_sample[sa] * ideal_probabilities[u][sb])
101
101
  fid_rm.append((2**num_qubits) * sum(p_sum))
102
102
  values = {"fidelity": np.mean(fid_rm)}
@@ -117,7 +117,7 @@ def fidelity_ghz_randomized_measurements(
117
117
  p_sum = []
118
118
  for sb in c_id_keys:
119
119
  for sa in c_keys:
120
- exponent = hamming(list(sa), list(sb)) * num_qubits
120
+ exponent = hamming(np.array(list(sa)), np.array(list(sb))) * num_qubits
121
121
  p_sum.append(np.power(-2, -exponent) * probabilities_sample[sa] * ideal_probabilities[u][sb])
122
122
  fid_rm_rem.append((2**num_qubits) * sum(p_sum))
123
123
  values = values | {"fidelity_rem": np.mean(fid_rm_rem)}
@@ -83,9 +83,9 @@ def calculate_optimal_angles_for_QAOA_p1(graph: Graph) -> List[float]:
83
83
  x_init = [0.15, -0.28]
84
84
 
85
85
  minimizer_kwargs = {"method": "L-BFGS-B", "bounds": bounds}
86
- res = basinhopping(get_expected_zz_edgedensity, x_init, minimizer_kwargs=minimizer_kwargs, niter=10, T=2)
86
+ res = basinhopping(get_expected_zz_edgedensity, x_init, minimizer_kwargs=minimizer_kwargs, niter=10, T=2) # type: ignore
87
87
 
88
- return res.x
88
+ return list(res.x)
89
89
 
90
90
 
91
91
  def cut_cost_function(x: str, graph: Graph) -> int:
@@ -436,12 +436,11 @@ def qscore_analysis(run: BenchmarkRunResult) -> BenchmarkAnalysisResult:
436
436
  instances_with_edges = set(range(num_instances)) - set(no_edge_instances)
437
437
  num_instances_with_edges = len(instances_with_edges)
438
438
  execution_results = xrvariable_to_counts(dataset, num_nodes, num_instances_with_edges)
439
-
440
439
  for inst_idx, instance in enumerate(list(instances_with_edges)):
441
440
  cut_sizes = run_QAOA(
442
441
  execution_results[inst_idx],
443
442
  graph_list[instance],
444
- qubit_to_node_list[instance],
443
+ qubit_to_node_list[inst_idx],
445
444
  use_classically_optimized_angles,
446
445
  num_qaoa_layers,
447
446
  virtual_node_list[instance],
@@ -790,6 +789,7 @@ class QScoreBenchmark(Benchmark):
790
789
  qubit_to_node_list = []
791
790
  no_edge_instances = []
792
791
  qc_all = [] # all circuits, including those with no edges
792
+ start_seed = seed
793
793
  for instance in range(self.num_instances):
794
794
  qcvv_logger.debug(f"Executing graph {instance} with {num_nodes} nodes.")
795
795
  graph = nx.generators.erdos_renyi_graph(num_nodes, 0.5, seed=seed)
@@ -848,6 +848,7 @@ class QScoreBenchmark(Benchmark):
848
848
  qubit_to_node_list.append(qubit_to_node_copy)
849
849
  else:
850
850
  qc_all.append([])
851
+ no_edge_instances.append(instance)
851
852
 
852
853
  seed += 1
853
854
  qcvv_logger.debug(f"Solved the MaxCut on graph {instance+1}/{self.num_instances}.")
@@ -902,7 +903,7 @@ class QScoreBenchmark(Benchmark):
902
903
  {
903
904
  num_nodes: {
904
905
  "qubit_set": qubit_set_list,
905
- "seed_start": seed,
906
+ "seed_start": start_seed,
906
907
  "graph": graph_list,
907
908
  "virtual_nodes": virtual_node_list,
908
909
  "qubit_to_node": qubit_to_node_list,
@@ -914,7 +915,7 @@ class QScoreBenchmark(Benchmark):
914
915
 
915
916
  qcvv_logger.debug(f"Adding counts for the random graph for {num_nodes} nodes to the dataset")
916
917
  dataset, _ = add_counts_to_dataset(execution_results, str(num_nodes), dataset)
917
- self.untranspiled_circuits.circuit_groups.append(CircuitGroup(name=str(num_nodes), circuits=qc_all))
918
+ self.untranspiled_circuits.circuit_groups.append(CircuitGroup(name=str(num_nodes), circuits=qc_list))
918
919
  self.transpiled_circuits.circuit_groups.append(
919
920
  CircuitGroup(name=str(num_nodes), circuits=qc_transpiled_list)
920
921
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iqm-benchmarks
3
- Version: 2.44
3
+ Version: 2.46
4
4
  Summary: A package for implementation of Quantum Characterization, Verification and Validation (QCVV) techniques on IQM's hardware at gate level abstraction
5
5
  Author-email: IQM Finland Oy <developers@meetiqm.com>, Adrian Auer <adrian.auer@meetiqm.com>, Raphael Brieger <raphael.brieger@meetiqm.com>, Alessio Calzona <alessio.calzona@meetiqm.com>, Pedro Figueroa Romero <pedro.romero@meetiqm.com>, Amin Hosseinkhani <amin.hosseinkhani@meetiqm.com>, Miikka Koistinen <miikka@meetiqm.com>, Nadia Milazzo <nadia.milazzo@meetiqm.com>, Vicente Pina Canelles <vicente.pina@meetiqm.com>, Aniket Rath <aniket.rath@meetiqm.com>, Jami Rönkkö <jami@meetiqm.com>, Stefan Seegerer <stefan.seegerer@meetiqm.com>
6
6
  Project-URL: Homepage, https://github.com/iqm-finland/iqm-benchmarks
@@ -63,7 +63,7 @@ Below is a list of the benchmarks currently available in the suite:
63
63
  * Gates / Layers:
64
64
  - Standard Clifford Randomized Benchmarking [[Phys. Rev. A 85, 042311](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.85.042311) (2012)]
65
65
  - Interleaved Randomized Benchmarking [[Phys. Rev. Lett. 109, 080505](https://doi.org/10.1103/PhysRevLett.109.080505) (2012)]
66
- - Compressive Gate Set Tomography [[PRX Quantum 4, 010325](https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.4.010325) (2023)]
66
+ - Compressive Gate Set Tomography [[PRX Quantum 4, 010325](https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.4.010325) (2023)] (Optional dependencies required)
67
67
  - Mirror Randomized Benchmarking [[Phys. Rev. Lett. 129, 150502](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.129.150502) (2022)]
68
68
  - Error Per Layered Gate [[arXiv:2311.05933 [quant-ph]](https://arxiv.org/abs/2311.05933) (2023)]
69
69
  * Holistic:
@@ -1,6 +1,6 @@
1
1
  iqm/benchmarks/__init__.py,sha256=-drZ_whue067Tu4I9RxBCqrrzu38Tm5Kqf9jHTftUPk,3070
2
2
  iqm/benchmarks/benchmark.py,sha256=3E7g7RQjCIGIpSI1gOSrI3V9SAVs-XEOMrPgToK_7vw,4972
3
- iqm/benchmarks/benchmark_definition.py,sha256=e4xe0wlWKZqj48_6-zTglMaMeoiA9aGkHrrSgoCfPkM,11271
3
+ iqm/benchmarks/benchmark_definition.py,sha256=7sq9_Y0iu1ZJOjG0gAVpm_BK3hbJBa6bdcRKhcVFOJw,11321
4
4
  iqm/benchmarks/circuit_containers.py,sha256=anEtZEsodYqOX-34oZRmuKGeEpp_VfgG5045Mz4-4hI,7562
5
5
  iqm/benchmarks/logging_config.py,sha256=U7olP5Kr75AcLJqNODf9VBhJLVqIvA4AYR6J39D5rww,1052
6
6
  iqm/benchmarks/readout_mitigation.py,sha256=Q2SOGWTNgmklOYkNxepAaSaXlxSj0QQyymYY1bOkT8A,11756
@@ -10,13 +10,13 @@ iqm/benchmarks/utils_shadows.py,sha256=e77PV_uaAO5m_woox9lAzompKAvFeDJ-0AKJrNJ7N
10
10
  iqm/benchmarks/coherence/__init__.py,sha256=yeyhk-_Lp8IbJ-f5lQj0HP5Q1HSKK_FzuXHazotUrVY,704
11
11
  iqm/benchmarks/coherence/coherence.py,sha256=zX_6A8vCS2zeWesMDXPFZBfrJ8wUG90JI9_tFsonwXk,21191
12
12
  iqm/benchmarks/compressive_gst/__init__.py,sha256=LneifgYXtcwo2jcXo7GdUEHL6_peipukShhkrdaTRCA,929
13
- iqm/benchmarks/compressive_gst/compressive_gst.py,sha256=2kiRttog4jR-vtMHu847GTFe5qL_i_uYr_4WMGqt9Ww,25653
14
- iqm/benchmarks/compressive_gst/gst_analysis.py,sha256=g0kEovWbetoDRvX7JFrS9oOoNrqBxaFmprujJi7qQbU,36297
13
+ iqm/benchmarks/compressive_gst/compressive_gst.py,sha256=c33PUisGlmPUkt4Nx2Xqmduji5qFCrWT9jMcoThcs7U,27992
14
+ iqm/benchmarks/compressive_gst/gst_analysis.py,sha256=H6EQGbpI_sig69Jy6hflg6alMTtjB0t9tHftygzA2YA,41240
15
15
  iqm/benchmarks/entanglement/__init__.py,sha256=sHVVToRWRCz0LSntk1rQaoSNNeyZLPoiTjUKWZWrk1E,778
16
- iqm/benchmarks/entanglement/ghz.py,sha256=nJ0gsLRfl50mcGrBVWjx22gXY9fwn9-sO3St2SbM4MA,41254
16
+ iqm/benchmarks/entanglement/ghz.py,sha256=7QI7r9x5VEYQu5kqmdsLV9nq3eedYV_WRVqCQTbjapk,41294
17
17
  iqm/benchmarks/entanglement/graph_states.py,sha256=saoAw2QF8j7W4nZMOElnjuNylqDAbY9cBwBypWZKUz8,62521
18
18
  iqm/benchmarks/optimization/__init__.py,sha256=_ajW_OibYLCtzU5AUv5c2zuuVYn8ZNeZUcUUSIGt51M,747
19
- iqm/benchmarks/optimization/qscore.py,sha256=D2BVVNAqO32uGu5_kLVl2XJUOBlRl1C-c6zYenaCBMg,37259
19
+ iqm/benchmarks/optimization/qscore.py,sha256=55NdsDsY9hwQLIZZdmf46BhU4C0hxjV5CCNYZnyoMAI,37372
20
20
  iqm/benchmarks/quantum_volume/__init__.py,sha256=i-Q4SpDWELBw7frXnxm1j4wJRcxbIyrS5uEK_v06YHo,951
21
21
  iqm/benchmarks/quantum_volume/clops.py,sha256=QS9iK-gtop0zix6IBeUumQeG01-0dXsv0jsYSDhgEu0,31071
22
22
  iqm/benchmarks/quantum_volume/quantum_volume.py,sha256=pro7Lz-A5pPpT9UZ8wtXKTyhdWmTjQjRHt4BylDR-3U,36553
@@ -35,18 +35,18 @@ iqm/benchmarks/randomized_benchmarking/interleaved_rb/__init__.py,sha256=sq6MgN_
35
35
  iqm/benchmarks/randomized_benchmarking/interleaved_rb/interleaved_rb.py,sha256=TaR1YFWBhOgm1hmEQzuwLYpp0yl0Xpuo3jAT6YhiXpc,28471
36
36
  iqm/benchmarks/randomized_benchmarking/mirror_rb/__init__.py,sha256=jRKbivWCZ3xdO1k0sx-ygC3s5DUkGSModd975PoAtcg,692
37
37
  iqm/benchmarks/randomized_benchmarking/mirror_rb/mirror_rb.py,sha256=n_5gt9636ZDMsM9hC3Zm5qP2bQr2sy41zxGhOh0XMjI,32932
38
- iqm_benchmarks-2.44.dist-info/licenses/LICENSE,sha256=2Ncb40-hqkTil78RPv3-YiJfKaJ8te9USJgliKqIdSY,11558
38
+ iqm_benchmarks-2.46.dist-info/licenses/LICENSE,sha256=2Ncb40-hqkTil78RPv3-YiJfKaJ8te9USJgliKqIdSY,11558
39
39
  mGST/LICENSE,sha256=TtHNq55cUcbglb7uhVudeBLUh_qPdUoAEvU0BBwFz-k,1098
40
40
  mGST/README.md,sha256=v_5kw253csHF4-RfE-44KqFmBXIsSMRmOtN0AUPrRxE,5050
41
- mGST/additional_fns.py,sha256=_SEJ10FRNM7_CroysT8hCLZTfpm6ZhEIDCY5zPTnhjo,31390
42
- mGST/algorithm.py,sha256=mXcDG9_0j3hVjxLyc585dsj_yvydfrWNlCrcnUOQHPw,26625
41
+ mGST/additional_fns.py,sha256=MV0Pm5ap59IjhT_E3QhsZyM7lXOF1RZ9SD11zoaf43A,31781
42
+ mGST/algorithm.py,sha256=2xeIiPP1ae8IgBgrc7boTk-NyirUMJimcZTvKMbZqKY,27504
43
43
  mGST/compatibility.py,sha256=00DsPnNfOtrQcDTvxBDs-0aMhmuXmOIIxl_Ohy-Emkg,8920
44
- mGST/low_level_jit.py,sha256=uE1D3v01FbPpsbP92C4220OQalzOfxgL1Ku89BNkxLY,27377
45
- mGST/optimization.py,sha256=YHwkzIkYvsZOPjclR-BCQWh24jeqjuXp0BB0WX5Lwow,10559
46
- mGST/qiskit_interface.py,sha256=ajx6Zn5FnrX_T7tMP8xnBLyG4c2ddFRm0Fu2_3r1t30,10118
47
- mGST/reporting/figure_gen.py,sha256=6Xd8vwfy09hLY1YbJY6TRevuMsQSU4MsWqemly3ZO0I,12970
48
- mGST/reporting/reporting.py,sha256=B8NWfpZrrSmyH7lwZxd0EbZMYLsAGK1YsHRB4D5qXH4,26002
49
- iqm_benchmarks-2.44.dist-info/METADATA,sha256=g7tbfF4edqqPEnS50lTJ_0pdp9KzCKMs4RNUQeCxUcU,10872
50
- iqm_benchmarks-2.44.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
51
- iqm_benchmarks-2.44.dist-info/top_level.txt,sha256=3G23Z-1LGf-IOzTCUl6QwWqiQ3USz25Zt90Ihq192to,9
52
- iqm_benchmarks-2.44.dist-info/RECORD,,
44
+ mGST/low_level_jit.py,sha256=Ih1MxcwU0GnSRu0TI9GaYxpB94CMuJ8Is1eTeOwsfA8,30772
45
+ mGST/optimization.py,sha256=x9tJ9wMQ5aONWpNpBMVtK0rwE6DRcOU33htNgrt0tx4,11015
46
+ mGST/qiskit_interface.py,sha256=uCdn-Q9CXI2f4FQSxGUy8GmmzQhr9NhCOFb2VPj0gTs,10061
47
+ mGST/reporting/figure_gen.py,sha256=xFPAHx1Trdqz7swn0kRqwc_jbRaNxhG9Nvx0jeitooo,25847
48
+ mGST/reporting/reporting.py,sha256=Wss1-zFsMEhzrrXKfP-RICau80ezjDIzcN555KhSehc,34160
49
+ iqm_benchmarks-2.46.dist-info/METADATA,sha256=si3yJgRHE9BhCYE9di9FKDsw_dIXUUm39MmvoggV_1c,10905
50
+ iqm_benchmarks-2.46.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
51
+ iqm_benchmarks-2.46.dist-info/top_level.txt,sha256=3G23Z-1LGf-IOzTCUl6QwWqiQ3USz25Zt90Ihq192to,9
52
+ iqm_benchmarks-2.46.dist-info/RECORD,,
mGST/additional_fns.py CHANGED
@@ -2,8 +2,10 @@
2
2
  Utility functions used by mGST modules
3
3
  """
4
4
 
5
+ import contextlib
5
6
  import os
6
7
  import random
8
+ import sys
7
9
  import warnings
8
10
 
9
11
  import numpy as np
@@ -13,6 +15,18 @@ from scipy.linalg import expm, qr
13
15
  from mGST.low_level_jit import Mp_norm_lower, MVE_lower, contract, local_basis
14
16
 
15
17
 
18
+ @contextlib.contextmanager
19
+ def suppress_stdout():
20
+ """Context manager to temporarily suppress stdout output."""
21
+ with open(os.devnull, "w", encoding="utf-8") as devnull:
22
+ old_stdout = sys.stdout
23
+ sys.stdout = devnull
24
+ try:
25
+ yield
26
+ finally:
27
+ sys.stdout = old_stdout
28
+
29
+
16
30
  def transp(dim1, dim2):
17
31
  """Superoperator of a map that performs the transpose operation
18
32
 
@@ -261,6 +275,10 @@ def random_gs(d, r, rK, n_povm):
261
275
  The second axis enumerates Kraus operators for a gate specified by the first axis.
262
276
  X: 3D numpy array
263
277
  Array where random CPT superoperators are stacked along the first axis.
278
+ E: 2D numpy array
279
+ Randomly generated POVM elements stacked along the first axis.
280
+ rho: 1D numpy array
281
+ Random initial state vector
264
282
 
265
283
  Notes:
266
284
  The Kraus operators are generated from random unitaries, see function randKrausSet
@@ -327,7 +345,7 @@ def perturbed_target_init(X_target, rK):
327
345
  """
328
346
  d, r, _ = X_target.shape
329
347
  pdim = int(np.sqrt(r))
330
- K_perturb = randKrausSet(d, r, rK, a=0.1)
348
+ K_perturb = randKrausSet(d, r, rK, a=0.05)
331
349
  X_perturb = np.einsum("ijkl,ijnm -> iknlm", K_perturb, K_perturb.conj()).reshape((d, r, r))
332
350
  X_init = np.einsum("ikl,ilm ->ikm", X_perturb, X_target)
333
351
  K_init = Kraus_rep(X_init, d, pdim, rK)
@@ -627,8 +645,7 @@ def sampled_measurements(y, n):
627
645
  y_new = np.maximum(np.minimum(y, 1), 0)
628
646
  if np.sum(np.abs(y_new - y)) > 1e-6:
629
647
  warnings.warn(
630
- f"Warning: Probabilities capped to interval [0,1]",
631
- f"l1-difference to input:%f" % np.sum(np.abs(y_new - y)),
648
+ f"Warning: Probabilities capped to interval [0,1], l1-difference to input: {np.sum(np.abs(y_new - y))}"
632
649
  )
633
650
  y = y_new
634
651
  rng = np.random.default_rng()
@@ -723,24 +740,22 @@ def is_positive(X, E, rho):
723
740
  eigvals = np.array([la.eigvals(X_choi[i]) for i in range(d)])
724
741
  partial_traces = np.einsum("aiikl -> akl", X.reshape(d, pdim, pdim, pdim, pdim))
725
742
  povm_eigvals = np.array([la.eigvals(E[i].reshape(pdim, pdim)) for i in range(n_povm)])
726
- if np.any(np.imag(eigvals.reshape(-1) > 1e-10)):
727
- print("Gates are not all hermitian.")
728
- else:
729
- for i in range(d):
730
- print(f"Gate %i positive:" % i, np.all(eigvals[i, :] > -1e-10))
731
- print(f"Gate %i trace preserving:" % i, la.norm(partial_traces[i] - np.eye(pdim)) < 1e-10)
732
- print(f"Initial state positive:", np.all(la.eigvals(rho.reshape(pdim, pdim)) > -1e-10))
733
- print(f"Initial state normalization:", np.trace(rho.reshape(pdim, pdim)))
734
- print(
735
- "fPOVM valid:",
736
- np.all(
737
- [
738
- la.norm(np.sum(E, axis=0).reshape(pdim, pdim) - np.eye(pdim)) < 1e-10,
739
- np.all(povm_eigvals.reshape(-1) > -1e-10),
740
- ]
741
- ),
742
- )
743
743
 
744
+ # Check if gates are hermitian
745
+ assert np.all(np.imag(eigvals.reshape(-1)) < 1e-10)
746
+
747
+ # Check if gates are positive and trace preserving
748
+ for i in range(d):
749
+ assert np.all(eigvals[i, :] > -1e-10)
750
+ assert la.norm(partial_traces[i] - np.eye(pdim)) < 1e-10
751
+
752
+ # Initial state positivity and normalization
753
+ assert np.all(la.eigvals(rho.reshape(pdim, pdim)) > -1e-10)
754
+ assert np.abs(np.trace(rho.reshape(pdim, pdim))) - 1 < 1e-10
755
+
756
+ # POVM positivity and normalization
757
+ assert la.norm(np.sum(E, axis=0).reshape(pdim, pdim) - np.eye(pdim)) < 1e-6
758
+ assert np.all(povm_eigvals.reshape(-1) > -1e-10)
744
759
 
745
760
  def tvd(X, E, rho, J, y_data):
746
761
  """Return the total variation distance between model probabilities for the circuits in J
mGST/algorithm.py CHANGED
@@ -28,7 +28,7 @@ from mGST.optimization import (
28
28
  from mGST.reporting.figure_gen import plot_objf
29
29
 
30
30
 
31
- def A_SFN_riem_Hess(K, A, B, y, J, d, r, n_povm, lam=1e-3):
31
+ def A_SFN_riem_Hess(K, A, B, y, J, d, r, n_povm, lam=1e-3, mle=False):
32
32
  """Riemannian saddle free Newton step on the POVM parametrization
33
33
 
34
34
  Parameters
@@ -73,7 +73,7 @@ def A_SFN_riem_Hess(K, A, B, y, J, d, r, n_povm, lam=1e-3):
73
73
  Fyy = np.zeros((n_povm, r, n_povm, r)).astype(np.complex128)
74
74
 
75
75
  X = np.einsum("ijkl,ijnm -> iknlm", K, K.conj()).reshape((d, r, r))
76
- dA_, dMdM, dMconjdM, dconjdA = ddA_derivs(X, A, B, J, y, r, pdim, n_povm)
76
+ dA_, dMdM, dMconjdM, dconjdA = ddA_derivs(X, A, B, J, y, r, pdim, n_povm, mle=mle)
77
77
 
78
78
  # Second derivatives
79
79
  for i in range(n_povm):
@@ -133,12 +133,12 @@ def A_SFN_riem_Hess(K, A, B, y, J, d, r, n_povm, lam=1e-3):
133
133
 
134
134
  Delta = tangent_proj(A, Delta_A, 1, n_povm)[0]
135
135
 
136
- a = minimize(lineobjf_A_geodesic, 1e-9, args=(Delta, X, A, rho, J, y), method="COBYLA").x
136
+ a = minimize(lineobjf_A_geodesic, 1e-9, args=(Delta, X, A, rho, J, y, mle), method="COBYLA").x
137
137
  A_new = update_A_geodesic(A, Delta, a)
138
138
  return A_new
139
139
 
140
140
 
141
- def B_SFN_riem_Hess(K, A, B, y, J, d, r, n_povm, lam=1e-3):
141
+ def B_SFN_riem_Hess(K, A, B, y, J, d, r, n_povm, lam=1e-3, mle=False):
142
142
  """Riemannian saddle free Newton step on the initial state parametrization
143
143
 
144
144
  Parameters
@@ -180,11 +180,9 @@ def B_SFN_riem_Hess(K, A, B, y, J, d, r, n_povm, lam=1e-3):
180
180
  E = np.array([(A[i].T.conj() @ A[i]).reshape(-1) for i in range(n_povm)])
181
181
  H = np.zeros((2, nt, 2, nt)).astype(np.complex128)
182
182
  P_T = np.zeros((2, nt, 2, nt)).astype(np.complex128)
183
- Fyconjy = np.zeros((r, r)).astype(np.complex128)
184
- Fyy = np.zeros((r, r)).astype(np.complex128)
185
183
 
186
184
  X = np.einsum("ijkl,ijnm -> iknlm", K, K.conj()).reshape((d, r, r))
187
- dB_, dMdM, dMconjdM, dconjdB = ddB_derivs(X, A, B, J, y, r, pdim)
185
+ dB_, dMdM, dMconjdM, dconjdB = ddB_derivs(X, A, B, J, y, r, pdim, mle=mle)
188
186
 
189
187
  # Second derivatives
190
188
  Fyconjy = dMconjdM + dconjdB
@@ -241,14 +239,15 @@ def B_SFN_riem_Hess(K, A, B, y, J, d, r, n_povm, lam=1e-3):
241
239
  Delta = (H_abs_inv @ G)[:nt]
242
240
  # Projection onto tangent space
243
241
  Delta = Delta - Y * (Y.T.conj() @ Delta + Delta.T.conj() @ Y) / 2
244
- res = minimize(lineobjf_B_geodesic, 1e-9, args=(Delta, X, E, B, J, y), method="COBYLA", options={"maxiter": 20})
242
+ res = minimize(
243
+ lineobjf_B_geodesic, 1e-9, args=(Delta, X, E, B, J, y, mle), method="COBYLA", options={"maxiter": 20}
244
+ )
245
245
  a = res.x
246
-
247
246
  B_new = update_B_geodesic(B, Delta, a)
248
247
  return B_new
249
248
 
250
249
 
251
- def gd(K, E, rho, y, J, d, r, rK, fixed_gates, ls="COBYLA"):
250
+ def gd(K, E, rho, y, J, d, r, rK, fixed_gates, ls="COBYLA", mle=False):
252
251
  """Do Riemannian gradient descent optimization step on gates
253
252
 
254
253
  Parameters
@@ -291,7 +290,7 @@ def gd(K, E, rho, y, J, d, r, rK, fixed_gates, ls="COBYLA"):
291
290
  Delta = np.zeros((d, n, pdim)).astype(np.complex128)
292
291
  X = np.einsum("ijkl,ijnm -> iknlm", K, K.conj()).reshape((d, r, r))
293
292
 
294
- dK_ = dK(X, K, E, rho, J, y, d, r, rK)
293
+ dK_ = dK(X, K, E, rho, J, y, d, r, rK, mle=mle)
295
294
  for k in np.where(~fixed_gates)[0]:
296
295
  # derivative
297
296
  Fy = dK_[k].reshape(n, pdim)
@@ -299,14 +298,20 @@ def gd(K, E, rho, y, J, d, r, rK, fixed_gates, ls="COBYLA"):
299
298
  # Riem. gradient taken from conjugate derivative
300
299
  rGrad = 2 * (Fy.conj() - Y @ Fy.T @ Y)
301
300
  Delta[k] = rGrad
302
- res = minimize(lineobjf_isom_geodesic, 1e-8, args=(Delta, K, E, rho, J, y), method=ls, options={"maxiter": 200})
301
+
302
+ # Additional projection onto tangent space to avoid numerical instability
303
+ Delta = tangent_proj(K, Delta, d, rK)
304
+
305
+ res = minimize(
306
+ lineobjf_isom_geodesic, 1e-8, args=(Delta, K, E, rho, J, y, mle), method=ls, options={"maxiter": 200}
307
+ )
303
308
  a = res.x
304
309
  K_new = update_K_geodesic(K, Delta, a)
305
310
 
306
311
  return K_new
307
312
 
308
313
 
309
- def SFN_riem_Hess(K, E, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA", fixed_gates=None):
314
+ def SFN_riem_Hess(K, E, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA", fixed_gates=None, mle=False):
310
315
  """Riemannian saddle free Newton step on each gate individually
311
316
 
312
317
  Parameters
@@ -355,8 +360,8 @@ def SFN_riem_Hess(K, E, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA", fixed_gates=
355
360
  fixed_gates = []
356
361
 
357
362
  # compute derivatives
358
- dK_, dM10, dM11 = dK_dMdM(X, K, E, rho, J, y, d, r, rK)
359
- dd, dconjd = ddM(X, K, E, rho, J, y, d, r, rK)
363
+ dK_, dM10, dM11 = dK_dMdM(X, K, E, rho, J, y, d, r, rK, mle=mle)
364
+ dd, dconjd = ddM(X, K, E, rho, J, y, d, r, rK, mle=mle)
360
365
 
361
366
  # Second derivatives
362
367
  Fyconjy = dM11.reshape(d, nt, d, nt) + np.einsum("ijklmnop->ikmojlnp", dconjd).reshape((d, nt, d, nt))
@@ -408,14 +413,16 @@ def SFN_riem_Hess(K, E, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA", fixed_gates=
408
413
 
409
414
  Delta = tangent_proj(K, Delta_K, d, rK)
410
415
 
411
- res = minimize(lineobjf_isom_geodesic, 1e-8, args=(Delta, K, E, rho, J, y), method=ls, options={"maxiter": 200})
416
+ res = minimize(
417
+ lineobjf_isom_geodesic, 1e-8, args=(Delta, K, E, rho, J, y, mle), method=ls, options={"maxiter": 200}
418
+ )
412
419
  a = res.x
413
420
  K_new = update_K_geodesic(K, Delta, a)
414
421
 
415
422
  return K_new
416
423
 
417
424
 
418
- def SFN_riem_Hess_full(K, E, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA"):
425
+ def SFN_riem_Hess_full(K, E, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA", mle=False):
419
426
  """Riemannian saddle free Newton step on product manifold of all gates
420
427
 
421
428
  Parameters
@@ -459,8 +466,8 @@ def SFN_riem_Hess_full(K, E, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA"):
459
466
  X = np.einsum("ijkl,ijnm -> iknlm", K, K.conj()).reshape((d, r, r))
460
467
 
461
468
  # compute derivatives
462
- dK_, dM10, dM11 = dK_dMdM(X, K, E, rho, J, y, d, r, rK)
463
- dd, dconjd = ddM(X, K, E, rho, J, y, d, r, rK)
469
+ dK_, dM10, dM11 = dK_dMdM(X, K, E, rho, J, y, d, r, rK, mle=mle)
470
+ dd, dconjd = ddM(X, K, E, rho, J, y, d, r, rK, mle=mle)
464
471
 
465
472
  # Second derivatives
466
473
  Fyconjy = dM11.reshape(d, nt, d, nt) + np.einsum("ijklmnop->ikmojlnp", dconjd).reshape((d, nt, d, nt))
@@ -522,13 +529,13 @@ def SFN_riem_Hess_full(K, E, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA"):
522
529
 
523
530
  # Delta_K is already in tangent space but not to sufficient numerical accuracy
524
531
  Delta = tangent_proj(K, Delta_K, d, rK)
525
- res = minimize(lineobjf_isom_geodesic, 1e-8, args=(Delta, K, E, rho, J, y), method=ls, options={"maxiter": 20})
532
+ res = minimize(lineobjf_isom_geodesic, 1e-8, args=(Delta, K, E, rho, J, y, mle), method=ls, options={"maxiter": 20})
526
533
  a = res.x
527
534
  K_new = update_K_geodesic(K, Delta, a)
528
535
  return K_new
529
536
 
530
537
 
531
- def optimize(y, J, d, r, rK, n_povm, method, K, rho, A, B, fixed_elements):
538
+ def optimize(y, J, d, r, rK, n_povm, method, K, rho, A, B, fixed_elements, mle=False):
532
539
  """Full gate set optimization update alternating on E, K and rho
533
540
 
534
541
  Parameters
@@ -578,27 +585,27 @@ def optimize(y, J, d, r, rK, n_povm, method, K, rho, A, B, fixed_elements):
578
585
  A_new = A
579
586
  E_new = np.array([(A_new[i].T.conj() @ A_new[i]).reshape(-1) for i in range(n_povm)])
580
587
  else:
581
- A_new = A_SFN_riem_Hess(K, A, B, y, J, d, r, n_povm)
588
+ A_new = A_SFN_riem_Hess(K, A, B, y, J, d, r, n_povm, mle=mle)
582
589
  E_new = np.array([(A_new[i].T.conj() @ A_new[i]).reshape(-1) for i in range(n_povm)])
583
-
584
590
  if any(((f"G%i" % i in fixed_elements) for i in range(d))):
585
591
  fixed_gates = np.array([(f"G%i" % i in fixed_elements) for i in range(d)])
586
592
  if method == "SFN":
587
- K_new = SFN_riem_Hess(K, E_new, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA", fixed_gates=fixed_gates)
593
+ K_new = SFN_riem_Hess(
594
+ K, E_new, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA", fixed_gates=fixed_gates, mle=mle
595
+ )
588
596
  else:
589
- K_new = gd(K, E_new, rho, y, J, d, r, rK, ls="COBYLA", fixed_gates=fixed_gates)
597
+ K_new = gd(K, E_new, rho, y, J, d, r, rK, ls="COBYLA", fixed_gates=fixed_gates, mle=mle)
590
598
  else:
591
599
  if method == "SFN":
592
- K_new = SFN_riem_Hess_full(K, E_new, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA")
600
+ K_new = SFN_riem_Hess_full(K, E_new, rho, y, J, d, r, rK, lam=1e-3, ls="COBYLA", mle=mle)
593
601
  else:
594
602
  fixed_gates = np.array([(f"G%i" % i in fixed_elements) for i in range(d)])
595
- K_new = gd(K, E_new, rho, y, J, d, r, rK, fixed_gates=fixed_gates, ls="COBYLA")
596
-
603
+ K_new = gd(K, E_new, rho, y, J, d, r, rK, fixed_gates=fixed_gates, ls="COBYLA", mle=mle)
597
604
  if "rho" in fixed_elements:
598
605
  rho_new = rho
599
606
  B_new = B
600
607
  else:
601
- B_new = B_SFN_riem_Hess(K_new, A_new, B, y, J, d, r, n_povm, lam=1e-3)
608
+ B_new = B_SFN_riem_Hess(K_new, A_new, B, y, J, d, r, n_povm, lam=1e-3, mle=mle)
602
609
  rho_new = (B_new @ B_new.T.conj()).reshape(-1)
603
610
  X_new = np.einsum("ijkl,ijnm -> iknlm", K_new, K_new.conj()).reshape((d, r, r))
604
611
  return K_new, X_new, E_new, rho_new, A_new, B_new
@@ -609,13 +616,13 @@ def run_mGST(
609
616
  method="SFN",
610
617
  max_inits=10,
611
618
  max_iter=200,
612
- final_iter=70,
613
- target_rel_prec=1e-4,
614
- threshold_multiplier=3,
619
+ final_iter=120,
620
+ target_rel_prec=1e-5,
621
+ threshold_multiplier=5,
615
622
  fixed_elements=None,
616
623
  init=None,
617
- testing=False,
618
- ): # pylint: disable=too-many-branches
624
+ verbose_level=0,
625
+ ): # pylint: disable=too-many-branches, too-many-statements
619
626
  """Main mGST routine
620
627
 
621
628
  Parameters
@@ -653,11 +660,11 @@ def run_mGST(
653
660
 
654
661
  Returns
655
662
  -------
656
- K : numpy array
663
+ K: numpy array
657
664
  Updated estimates of Kraus operators
658
- X : numpy array
665
+ X: numpy array
659
666
  Updated estimates of superoperatos corresponding to K_new
660
- E : numpy array
667
+ E: numpy array
661
668
  Updated POVM estimate
662
669
  rho : numpy array
663
670
  Updated initial state estimate
@@ -682,7 +689,8 @@ def run_mGST(
682
689
  )
683
690
 
684
691
  success = False
685
- qcvv_logger.info(f"Starting mGST optimization...")
692
+ if verbose_level > 0:
693
+ qcvv_logger.info(f"Starting mGST optimization...")
686
694
 
687
695
  if init:
688
696
  K, E = (init[0], init[1])
@@ -698,8 +706,8 @@ def run_mGST(
698
706
  A = np.array([la.cholesky(E[k].reshape(pdim, pdim) + 1e-14 * np.eye(pdim)).T.conj() for k in range(n_povm)])
699
707
  B = la.cholesky(rho.reshape(pdim, pdim))
700
708
  res_list = [objf(X, E, rho, J, y)]
701
- with logging_redirect_tqdm(loggers=[qcvv_logger]):
702
- for _ in trange(max_iter):
709
+ with logging_redirect_tqdm(loggers=[qcvv_logger] if verbose_level > 0 else None):
710
+ for _ in trange(max_iter, disable=verbose_level == 0):
703
711
  yb, Jb = batch(y, J, bsize)
704
712
  K, X, E, rho, A, B = optimize(yb, Jb, d, r, rK, n_povm, method, K, rho, A, B, fixed_elements)
705
713
  res_list.append(objf(X, E, rho, J, y))
@@ -707,29 +715,37 @@ def run_mGST(
707
715
  qcvv_logger.info(f"Batch optimization successful, improving estimate over full data....")
708
716
  success = True
709
717
  break
710
- if testing:
711
- plot_objf(res_list, delta, f"Objective function for batch optimization")
718
+ if verbose_level == 2:
719
+ plot_objf(res_list, f"Objective function for batch optimization", delta=delta)
712
720
  if success:
713
721
  break
714
- qcvv_logger.info(f"Run {i+1}/{max_inits} failed, trying new initialization...")
722
+ if verbose_level > 0:
723
+ qcvv_logger.info(f"Run {i+1}/{max_inits} failed, trying new initialization...")
715
724
 
716
- if not success and max_inits > 0:
725
+ if not success and init is None and verbose_level > 0:
717
726
  qcvv_logger.info(f"Success threshold not reached, attempting optimization over full data set...")
718
- with logging_redirect_tqdm(loggers=[qcvv_logger]):
719
- for _ in trange(final_iter):
720
- K, X, E, rho, A, B = optimize(y, J, d, r, rK, n_povm, method, K, rho, A, B, fixed_elements)
727
+ with logging_redirect_tqdm(loggers=[qcvv_logger] if verbose_level > 0 else None):
728
+ res_list_mle = []
729
+ for _ in trange(final_iter, disable=verbose_level == 0):
730
+ K, X, E, rho, A, B = optimize(y, J, d, r, rK, n_povm, method, K, rho, A, B, fixed_elements, mle=True)
721
731
  res_list.append(objf(X, E, rho, J, y))
722
- if len(res_list) >= 2 and np.abs(res_list[-2] - res_list[-1]) < delta * target_rel_prec:
732
+ res_list_mle.append(objf(X, E, rho, J, y, mle=True))
733
+ if (
734
+ len(res_list_mle) >= 2
735
+ and np.abs(res_list_mle[-2] - res_list_mle[-1]) < res_list_mle[-1] * target_rel_prec
736
+ ):
723
737
  break
724
- if testing:
725
- plot_objf(res_list, delta, f"Objective function over batches and full data")
726
- if success or (res_list[-1] < delta):
727
- qcvv_logger.info(f"Convergence criterion satisfied")
728
- else:
738
+ if verbose_level == 2:
739
+ plot_objf(res_list, f"Least squares error over batches and full data", delta=delta)
740
+ plot_objf(res_list_mle, f"Negative log-likelihood over full data")
741
+ if verbose_level > 0:
742
+ if success or (res_list[-1] < delta):
743
+ qcvv_logger.info(f"Convergence criterion satisfied")
744
+ else:
745
+ qcvv_logger.warning(
746
+ f"Convergence criterion not satisfied. Potential causes include too low max_iterations, bad initialization or model mismatch."
747
+ )
729
748
  qcvv_logger.info(
730
- f"Convergence criterion not satisfied, inspect results and consider increasing max_iter or using new initializations.",
749
+ f"Final objective {Decimal(res_list[-1]):.2e} in time {(time.time() - t0):.2f}s",
731
750
  )
732
- qcvv_logger.info(
733
- f"Final objective {Decimal(res_list[-1]):.2e} in time {(time.time() - t0):.2f}s",
734
- )
735
751
  return K, X, E, rho, res_list