qoro-divi 0.3.4__py3-none-any.whl → 0.4.0__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.

Potentially problematic release.


This version of qoro-divi might be problematic. Click here for more details.

divi/utils.py CHANGED
@@ -10,11 +10,88 @@ import pennylane as qml
10
10
  import scipy.sparse as sps
11
11
 
12
12
 
13
+ def hamiltonian_to_pauli_string(hamiltonian: qml.Hamiltonian, n_qubits: int) -> str:
14
+ """
15
+ Convert a QNode Hamiltonian to a semicolon-separated string of Pauli operators.
16
+
17
+ Each term in the Hamiltonian is represented as a string of Pauli letters ('I', 'X', 'Y', 'Z'),
18
+ one per qubit. Multiple terms are separated by semicolons.
19
+
20
+ Args:
21
+ hamiltonian (qml.Hamiltonian): The Pennylane Hamiltonian object to convert.
22
+ n_qubits (int): Number of qubits to represent in the string.
23
+
24
+ Returns:
25
+ str: The Hamiltonian as a semicolon-separated string of Pauli operators.
26
+
27
+ Raises:
28
+ ValueError: If an unknown Pauli operator is encountered or wire index is out of range.
29
+ """
30
+ pauli_letters = {"PauliX": "X", "PauliY": "Y", "PauliZ": "Z"}
31
+ identity_row = np.full(n_qubits, "I", dtype="<U1")
32
+
33
+ terms = []
34
+ for term in hamiltonian.operands:
35
+ op = term.base if isinstance(term, qml.ops.SProd) else term
36
+ ops = op.operands if isinstance(op, qml.ops.Prod) else [op]
37
+
38
+ paulis = identity_row.copy()
39
+ for p in ops:
40
+ # Better fallback logic with validation
41
+ if p.name in pauli_letters:
42
+ pauli = pauli_letters[p.name]
43
+ elif p.name.startswith("Pauli") and len(p.name) > 5:
44
+ pauli = p.name[5:] # Only if safe to slice
45
+ else:
46
+ raise ValueError(
47
+ f"Unknown Pauli operator: {p.name}. "
48
+ "Expected 'PauliX', 'PauliY', 'PauliZ', or a name starting with 'Pauli'."
49
+ )
50
+
51
+ # Bounds checking for wire indices
52
+ if not p.wires:
53
+ raise ValueError(f"Pauli operator {p.name} has no wires")
54
+
55
+ wire = int(p.wires[0])
56
+ if wire < 0 or wire >= n_qubits:
57
+ raise ValueError(
58
+ f"Wire index {wire} out of range for {n_qubits} qubits. "
59
+ f"Valid range: [0, {n_qubits - 1}]"
60
+ )
61
+
62
+ paulis[wire] = pauli
63
+ terms.append("".join(paulis))
64
+
65
+ return ";".join(terms)
66
+
67
+
68
+ def reverse_dict_endianness(
69
+ probs_dict: dict[str, dict[str, float]],
70
+ ) -> dict[str, dict[str, float]]:
71
+ """Reverse endianness of all bitstrings in a dictionary of probability distributions."""
72
+ return {
73
+ tag: {bitstring[::-1]: prob for bitstring, prob in probs.items()}
74
+ for tag, probs in probs_dict.items()
75
+ }
76
+
77
+
13
78
  def _is_sanitized(
14
79
  qubo_matrix: np.ndarray | sps.spmatrix,
15
80
  ) -> np.ndarray | sps.spmatrix:
16
- # Sanitize the QUBO matrix to ensure it is either symmetric or upper triangular.
81
+ """
82
+ Check if a QUBO matrix is either symmetric or upper triangular.
83
+
84
+ This function validates that the input QUBO matrix is in a proper format
85
+ for conversion to an Ising Hamiltonian. The matrix should be either
86
+ symmetric (equal to its transpose) or upper triangular.
87
+
88
+ Args:
89
+ qubo_matrix (np.ndarray | sps.spmatrix): The QUBO matrix to validate.
90
+ Can be a dense NumPy array or a sparse SciPy matrix.
17
91
 
92
+ Returns:
93
+ bool: True if the matrix is symmetric or upper triangular, False otherwise.
94
+ """
18
95
  is_sparse = sps.issparse(qubo_matrix)
19
96
 
20
97
  return (
@@ -33,18 +110,37 @@ def _is_sanitized(
33
110
  def convert_qubo_matrix_to_pennylane_ising(
34
111
  qubo_matrix: np.ndarray | sps.spmatrix,
35
112
  ) -> tuple[qml.operation.Operator, float]:
36
- """Convert QUBO matrix to Ising Hamiltonian in Pennylane.
113
+ """
114
+ Convert a QUBO matrix to an Ising Hamiltonian in PennyLane format.
37
115
 
38
116
  The conversion follows the mapping from QUBO variables x_i ∈ {0,1} to
39
117
  Ising variables σ_i ∈ {-1,1} via the transformation x_i = (1 - σ_i)/2. This
40
118
  transforms a QUBO minimization problem into an equivalent Ising minimization
41
119
  problem.
42
120
 
121
+ The function handles both dense NumPy arrays and sparse SciPy matrices efficiently.
122
+ If the input matrix is neither symmetric nor upper triangular, it will be
123
+ symmetrized automatically with a warning.
124
+
43
125
  Args:
44
- qubo_matrix: The QUBO matrix Q where the objective is to minimize x^T Q x
126
+ qubo_matrix (np.ndarray | sps.spmatrix): The QUBO matrix Q where the
127
+ objective is to minimize x^T Q x. Can be a dense NumPy array or a
128
+ sparse SciPy matrix (any format). Should be square and either
129
+ symmetric or upper triangular.
45
130
 
46
131
  Returns:
47
- A tuple of (Ising Hamiltonian as a PennyLane operator, constant term)
132
+ tuple[qml.operation.Operator, float]: A tuple containing:
133
+ - Ising Hamiltonian as a PennyLane operator (sum of Pauli Z terms)
134
+ - Constant offset term to be added to energy calculations
135
+
136
+ Raises:
137
+ UserWarning: If the QUBO matrix is neither symmetric nor upper triangular.
138
+
139
+ Example:
140
+ >>> import numpy as np
141
+ >>> qubo = np.array([[1, 2], [0, 3]])
142
+ >>> hamiltonian, offset = convert_qubo_matrix_to_pennylane_ising(qubo)
143
+ >>> print(f"Offset: {offset}")
48
144
  """
49
145
 
50
146
  if not _is_sanitized(qubo_matrix):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qoro-divi
3
- Version: 0.3.4
3
+ Version: 0.4.0
4
4
  Summary: A Python library to automate generating, parallelizing, and executing quantum programs.
5
5
  Author: Ahmed Darwish
6
6
  Author-email: ahmed@qoroquantum.de
@@ -17,6 +17,7 @@ Requires-Dist: networkx (>=3.5,<4.0)
17
17
  Requires-Dist: pennylane (>=0.42.3,<0.43.0)
18
18
  Requires-Dist: ply (>=3.11,<4.0)
19
19
  Requires-Dist: pymetis (>=2025.1.1,<2026.0.0)
20
+ Requires-Dist: pymoo (>=0.6.1.5,<0.7.0.0)
20
21
  Requires-Dist: python-dotenv (>=1.1.1,<2.0.0)
21
22
  Requires-Dist: qiskit (<2.0)
22
23
  Requires-Dist: qiskit-aer (>=0.17.1,<0.18.0)
@@ -62,3 +63,17 @@ pip install qoro-divi
62
63
  - Full documentation is available at: <https://docs.qoroquantum.net/divi>
63
64
  - Tutorials can be found in the `tutorials` folder.
64
65
 
66
+ ## 🧪 Testing
67
+
68
+ To run the test suite:
69
+
70
+ ```bash
71
+ # Run all tests
72
+ pytest
73
+
74
+ # Run only API tests (requires API token)
75
+ pytest --run-api-tests
76
+ ```
77
+
78
+ **Note:** Some tests require a Qoro API token to test the cloud REST API. Set the `QORO_API_KEY` environment variable or use the `--api-token` option. For local development, you can create a `.env`.
79
+
@@ -1,19 +1,19 @@
1
1
  divi/__init__.py,sha256=SyBWflbDS6qGEtHg-AfzD1TRNgfXoW2H5qTYGJ-W3XQ,167
2
- divi/backends/__init__.py,sha256=1S3EJH5XXou2s7EnZowhfI4b3u3zLUG05x7etj4W-IM,264
3
- divi/backends/_circuit_runner.py,sha256=P-8lekQIGwbhHv4ewfMFtKYIjmQHu7nmscwAmxsx72U,594
4
- divi/backends/_parallel_simulator.py,sha256=jHt6C2-H65cYIMA0zc-gkSt69tNgaM98eXSRZqmI4UI,8979
5
- divi/backends/_qoro_service.py,sha256=_x5CSkmcP_LFiqRdDax5vBcEJ7XRjtY6ZOfyPuwPn6U,14057
6
- divi/backends/_qpu_system.py,sha256=teVeG18ukyzMFgbPSr4BLx4MJUHVK382RqZMOy2voFk,374
2
+ divi/backends/__init__.py,sha256=xi1thgxkqNVIbKO1ZOkn1qewlO8iV8dYijUPIn1WxzU,275
3
+ divi/backends/_circuit_runner.py,sha256=IDObfDWvFMSXi0JODul1x6Fg9MwTEOGGLYdGiE6KCDg,1988
4
+ divi/backends/_parallel_simulator.py,sha256=J2EfRUl8VlmUrxPYbE3HdCMN-xlS5vZnWePldEapMH0,13517
5
+ divi/backends/_qoro_service.py,sha256=R31waE67ZmyTNuVPa2XDyisouIPqLtjjEfD9QVlZmTA,24313
6
+ divi/backends/_qpu_system.py,sha256=Qrn7lp4NsN5rP7AEpo9MCGv7ndDAuQHvbHB1KD-Upgo,2863
7
7
  divi/circuits/__init__.py,sha256=Wl4BF0_TwG1Ol4oaftCD5lpkgS9us9EW7F4hu6r_eXM,151
8
- divi/circuits/_core.py,sha256=TsCa7n1Gwd8thxn5JYF2rcKVnHFDXOD_8ZEwG749az4,4022
9
- divi/circuits/qasm.py,sha256=5z_o4mY_eK24AXToFSugf74gTT_3iQuJddBZ7m5mvFU,7396
8
+ divi/circuits/_core.py,sha256=DzD-0F9YmT7Qdj6lzwm6X41nnmZU_TDfr06Y1Od7KZI,9286
9
+ divi/circuits/qasm.py,sha256=6FDcNNdXGfYVGK64YTt22r2Jqz6c_TzNBC0NRjFs34g,8116
10
10
  divi/circuits/qem.py,sha256=o6rMPUcxLuCBitBb8-QcxveUiKZVsP3HMamxyVFLi6M,6805
11
11
  divi/extern/cirq/__init__.py,sha256=6NjP3TlQn_oNkg8VrKvEIoYQxB5Bx0mLLFOT3SXReTc,371
12
12
  divi/extern/cirq/_lexer.py,sha256=x5UArrnN_JEyiq7E02ikq0wdmqZ2vEQ3_FADL1LIhQI,3187
13
13
  divi/extern/cirq/_parser.py,sha256=z_bSn4m03-sfRlN8eL99Mo4LnjY-zmR-Xt6UrjzstZc,29279
14
14
  divi/extern/cirq/_qasm_export.py,sha256=8C5xLYvIIkQTWWAAYo7ZjwtQjvYXNSflbf5UyUx6YUE,1024
15
15
  divi/extern/cirq/_qasm_import.py,sha256=HbehrgfLl3iDdRyWr4o26Bek3ZpN-_dvNVSexl5-aVE,969
16
- divi/extern/cirq/_validator.py,sha256=GVCeoi3IGUCPcqmg6E7lPtD52h-63_0AH4-MToajL4o,20509
16
+ divi/extern/cirq/_validator.py,sha256=od1vilsqn0T8wYAU1u1Rr5pZfjyaqHlIMwD_NWzUU4Y,20875
17
17
  divi/extern/cirq/exception.py,sha256=w1w2vSubOGMRmyKBFqXejxfeIAzkPZ6V7gSrDX_ap4A,765
18
18
  divi/extern/scipy/_cobyla.py,sha256=cnCf5AsOM8JWIMiujuUbWMNOgmUr3ZET9y04hUyumHs,10937
19
19
  divi/extern/scipy/pyprima/LICENCE.txt,sha256=mXN5ssG_U6OR0v0yldznW_PJTtKNZIgu3jDRtRjLDdY,1533
@@ -43,26 +43,28 @@ divi/extern/scipy/pyprima/common/present.py,sha256=caedmqSB5ggGXNfky0A6v6FAdyaEG
43
43
  divi/extern/scipy/pyprima/common/ratio.py,sha256=taadehpW0c3ULUggH5frIMpvTom53dsEhvFaXrIKvOI,1833
44
44
  divi/extern/scipy/pyprima/common/redrho.py,sha256=J6rJILcOoCeo942LUAXxpUvKLarUGNCGdC-zcmIlVHE,1264
45
45
  divi/extern/scipy/pyprima/common/selectx.py,sha256=mXVS2L6AuTmbOhpp1KlXwOBR54ttnbjwagYfnXhezJY,14713
46
- divi/qprog/__init__.py,sha256=3X7MK7PjXI-rCZQ9OYq314w14npmasCNSBECq23DW7U,622
46
+ divi/qprog/__init__.py,sha256=AkyI0JDBK4REYS_Z0op2sM81nFW6HEmqWJtfg6ARMJw,693
47
47
  divi/qprog/algorithms/__init__.py,sha256=KLGD3zRk3z4nJQDqBjejTGgJ5m0n02jyBVHLJihMMws,355
48
- divi/qprog/algorithms/_ansatze.py,sha256=ATep35g7CD_t7wrmW62g5a8_XbXkbwwjU8Y4Poux4qA,7933
49
- divi/qprog/algorithms/_qaoa.py,sha256=cGYE0xkO7sBfqVyO5HxJNWi9-Vu84eTjJEGcIfbwweg,15951
50
- divi/qprog/algorithms/_vqe.py,sha256=FE0lnX_WKgKJnARdMvzehlLgmXgfBwGapf-fGCiIqMI,6499
51
- divi/qprog/batch.py,sha256=F_s83FDqnbm7UAYeTizKQqNysE6bACTorwtv8hmcJm4,10036
52
- divi/qprog/optimizers.py,sha256=SYWknntCl_QI4icoVdFvunxY5rZ2S0GGBVvtIWypk30,6433
53
- divi/qprog/quantum_program.py,sha256=2q4SDZ9TwTu_HdqNr3EWRNXBLlFEGSSWdHoUyAljT_c,14978
48
+ divi/qprog/algorithms/_ansatze.py,sha256=bc-8AtJhaM45iwA5LoTup2mxlecHlIgjia6gT5c5W6E,11422
49
+ divi/qprog/algorithms/_qaoa.py,sha256=uNqNxTyMvIyKRJ6uABiKq9H6g_FPN7Wxtn-LeoaxehU,20080
50
+ divi/qprog/algorithms/_vqe.py,sha256=_xbuhCI8BPwiULhH6SKSfz1noI1Z63u9WZC7ivNcCAI,12197
51
+ divi/qprog/batch.py,sha256=1EDkM4NFr_wjxjpdFNJpBufpylG6Bcg-9v_TaPR2vgQ,18605
52
+ divi/qprog/exceptions.py,sha256=2VvUf8qgNBw60Q4wyt_2nbE4JHHMmZiT2JaGmWChp2o,231
53
+ divi/qprog/optimizers.py,sha256=xw3KIIwWEGYSC_RnXCBARWvkHuzmKDmUt1wuRhrrIqg,17123
54
+ divi/qprog/quantum_program.py,sha256=Bvq_CWWc8gea9i4aQ36jw8rSDKR3jNgHnUqErSMfeDs,8767
55
+ divi/qprog/variational_quantum_algorithm.py,sha256=kohRUBKyrto04vJedR9uZC766Vcl6g9lSvtkI541a6Y,29433
54
56
  divi/qprog/workflows/__init__.py,sha256=_GAFsZsgj9p61E1xUXasa1aspwcOWp4s8i6hA6mQ9eg,320
55
- divi/qprog/workflows/_graph_partitioning.py,sha256=Y_LpQy9djbeXplVmyfr2E2aWjmwkM0GY7xU2N8hJhYY,23675
56
- divi/qprog/workflows/_qubo_partitioning.py,sha256=3IFD_N2vjwaFuK155fg26rbrGyYqEjzEnkxlw2BqRF0,7473
57
- divi/qprog/workflows/_vqe_sweep.py,sha256=eoontmK1z0KTdkEW2TbfCs9UKI4ThlFYXAivpO9GWK4,17974
57
+ divi/qprog/workflows/_graph_partitioning.py,sha256=QvyxN50I1R3SkCMzZpjpdoXfDSC8eskY8tancRmTUF8,24217
58
+ divi/qprog/workflows/_qubo_partitioning.py,sha256=6_z095R6kFtuL8bHVQMCM7S8h4tTOQOqpthsqFzkaoQ,8248
59
+ divi/qprog/workflows/_vqe_sweep.py,sha256=l0dndM7jl9WUAZxF6sAd6Gnx37-vkBCNKpgZI07LR3s,19291
58
60
  divi/reporting/__init__.py,sha256=gaBUZrhNxR53VHojZxfjvQRDl-eDHo901vLE8I95kIw,290
59
- divi/reporting/_pbar.py,sha256=I8AWI93mpcJXIXTIvsa3JghazO65Pmrq-bW2bJTf2Ik,2131
60
- divi/reporting/_qlogger.py,sha256=hZ6hELP5Z41P1KpJV9xZ0TuHeI0ztswpcZrJ4yHKdAg,3860
61
- divi/reporting/_reporter.py,sha256=AgB2Mno1sIXF-VhOi8i-LGuWG5FMG1HelMw5cMc84HM,2977
62
- divi/utils.py,sha256=txdkb2y_z99-ysxdmC3Rl3tw0hkJsvRltLybCXb89bA,3816
63
- qoro_divi-0.3.4.dist-info/LICENSE,sha256=NS4JlQrgNwg1bvB3kE5shE-P4cJgnntgl-kClbOpG_Q,10760
64
- qoro_divi-0.3.4.dist-info/LICENSES/.license-header,sha256=2jN_xtJscqP8LG-NaveY2KHUkfRCC543Y_XjOyKEfWY,105
65
- qoro_divi-0.3.4.dist-info/LICENSES/Apache-2.0.txt,sha256=yoILHpvVuguUBpk8UwMnzJbcHUUyst9iGNNuEwUtWVc,10270
66
- qoro_divi-0.3.4.dist-info/METADATA,sha256=Or34jlJGk7jDChjEGjpE__8zaX58jlxCF9-MjBHpK-w,2428
67
- qoro_divi-0.3.4.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
68
- qoro_divi-0.3.4.dist-info/RECORD,,
61
+ divi/reporting/_pbar.py,sha256=I5UcriPLMKdTf_wusFCbxVH4MlA1Etui2GcYxRPC9Is,3686
62
+ divi/reporting/_qlogger.py,sha256=moFF9k_KECdhEmbsUTZHFXwVh30CuRCcChil_LBQUXM,5000
63
+ divi/reporting/_reporter.py,sha256=hqKWkpezolm80bJ16Tk4byjWxtZFEzgFAH9_yaXkQgM,2703
64
+ divi/utils.py,sha256=eOLcA7kY0Pib4pUsrfhN6l_GgNcKM95KVUSQLlh6W0s,7569
65
+ qoro_divi-0.4.0.dist-info/LICENSE,sha256=NS4JlQrgNwg1bvB3kE5shE-P4cJgnntgl-kClbOpG_Q,10760
66
+ qoro_divi-0.4.0.dist-info/LICENSES/.license-header,sha256=2jN_xtJscqP8LG-NaveY2KHUkfRCC543Y_XjOyKEfWY,105
67
+ qoro_divi-0.4.0.dist-info/LICENSES/Apache-2.0.txt,sha256=yoILHpvVuguUBpk8UwMnzJbcHUUyst9iGNNuEwUtWVc,10270
68
+ qoro_divi-0.4.0.dist-info/METADATA,sha256=TH0_pY9aljjQGcGnqabmavas03vLnhGXAam1vFq2SGU,2813
69
+ qoro_divi-0.4.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
70
+ qoro_divi-0.4.0.dist-info/RECORD,,