qoro-divi 0.2.2b1__py3-none-any.whl → 0.3.1b0__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/qprog/batch.py CHANGED
@@ -2,6 +2,7 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ import atexit
5
6
  import traceback
6
7
  from abc import ABC, abstractmethod
7
8
  from concurrent.futures import ProcessPoolExecutor, as_completed
@@ -51,6 +52,10 @@ def queue_listener(
51
52
  )
52
53
 
53
54
 
55
+ def _default_task_function(program: QuantumProgram):
56
+ return program.run()
57
+
58
+
54
59
  class ProgramBatch(ABC):
55
60
  """This abstract class provides the basic scaffolding for higher-order
56
61
  computations that require more than one quantum program to achieve its goal.
@@ -74,6 +79,7 @@ class ProgramBatch(ABC):
74
79
 
75
80
  self.backend = backend
76
81
  self._executor = None
82
+ self._task_fn = _default_task_function
77
83
  self.programs = {}
78
84
 
79
85
  self._total_circuit_count = 0
@@ -95,6 +101,12 @@ class ProgramBatch(ABC):
95
101
 
96
102
  @abstractmethod
97
103
  def create_programs(self):
104
+ if len(self.programs) > 0:
105
+ raise RuntimeError(
106
+ "Some programs already exist. "
107
+ "Clear the program dictionary before creating new ones by using batch.reset()."
108
+ )
109
+
98
110
  self._manager = Manager()
99
111
  self._queue = self._manager.Queue()
100
112
 
@@ -118,27 +130,36 @@ class ProgramBatch(ABC):
118
130
  if getattr(self, "_listener_thread", None) is not None:
119
131
  self._listener_thread.join(timeout=1)
120
132
  if self._listener_thread.is_alive():
121
- warn(
122
- "Listener thread did not terminate within timeout and may still be running."
123
- )
124
- else:
125
- self._listener_thread = None
126
- self._queue.close()
127
- self._queue.join_thread()
133
+ warn("Listener thread did not terminate within timeout.")
134
+ self._listener_thread = None
135
+
136
+ # Shut down the manager process, which handles the queue cleanup.
137
+ if hasattr(self, "_manager") and self._manager is not None:
138
+ self._manager.shutdown()
139
+ self._manager = None
128
140
  self._queue = None
129
141
 
130
142
  # Stop the progress bar if it's still active
131
143
  if getattr(self, "_progress_bar", None) is not None:
132
144
  try:
133
145
  self._progress_bar.stop()
134
- self._progress_bar = None
135
146
  except Exception:
136
147
  pass # Already stopped or not running
137
-
148
+ self._progress_bar = None
138
149
  self._pb_task_map.clear()
139
150
 
151
+ def _atexit_cleanup_hook(self):
152
+ # This hook is only registered for non-blocking runs.
153
+ if self._executor is not None:
154
+ warn(
155
+ "A non-blocking ProgramBatch run was not explicitly closed with "
156
+ "'join()'. The batch was cleaned up automatically on exit.",
157
+ UserWarning,
158
+ )
159
+ self.reset()
160
+
140
161
  def add_program_to_executor(self, program):
141
- self.futures.append(self._executor.submit(program.run))
162
+ self.futures.append(self._executor.submit(self._task_fn, program))
142
163
 
143
164
  if self._progress_bar is not None:
144
165
  with self._pb_lock:
@@ -153,7 +174,7 @@ class ProgramBatch(ABC):
153
174
  mode=("simulation" if self._is_local else "network"),
154
175
  )
155
176
 
156
- def run(self):
177
+ def run(self, blocking: bool = False):
157
178
  if self._executor is not None:
158
179
  raise RuntimeError("A batch is already being run.")
159
180
 
@@ -193,10 +214,19 @@ class ProgramBatch(ABC):
193
214
  for program in self.programs.values():
194
215
  self.add_program_to_executor(program)
195
216
 
217
+ if not blocking:
218
+ # Arm safety net
219
+ atexit.register(self._atexit_cleanup_hook)
220
+
221
+ if blocking:
222
+ self.join()
223
+
224
+ return self
225
+
196
226
  def check_all_done(self):
197
227
  return all(future.done() for future in self.futures)
198
228
 
199
- def wait_for_all(self):
229
+ def join(self):
200
230
  if self._executor is None:
201
231
  return
202
232
 
@@ -228,8 +258,25 @@ class ProgramBatch(ABC):
228
258
 
229
259
  self._total_circuit_count += sum(future.result()[0] for future in self.futures)
230
260
  self._total_run_time += sum(future.result()[1] for future in self.futures)
231
- self.futures = []
261
+ self.futures.clear()
262
+
263
+ # After successful cleanup, try to unregister the hook.
264
+ # This will only succeed if it was a non-blocking run.
265
+ try:
266
+ atexit.unregister(self._atexit_cleanup_hook)
267
+ except TypeError:
268
+ # This is expected for blocking runs where the hook was never registered.
269
+ pass
232
270
 
233
271
  @abstractmethod
234
272
  def aggregate_results(self):
235
- raise NotImplementedError
273
+ if len(self.programs) == 0:
274
+ raise RuntimeError("No programs to aggregate. Run create_programs() first.")
275
+
276
+ if self._executor is not None:
277
+ self.join()
278
+
279
+ if any(len(program.losses) == 0 for program in self.programs.values()):
280
+ raise RuntimeError(
281
+ "Some/All programs have empty losses. Did you call run()?"
282
+ )
@@ -7,10 +7,9 @@ import pickle
7
7
  from abc import ABC, abstractmethod
8
8
  from functools import partial
9
9
  from queue import Queue
10
- from typing import Optional
11
10
 
12
11
  import numpy as np
13
- from qiskit.result import marginal_counts, sampled_expectation_value
12
+ from pennylane.measurements import ExpectationMP
14
13
  from scipy.optimize import OptimizeResult, minimize
15
14
 
16
15
  from divi import QoroService
@@ -28,8 +27,8 @@ class QuantumProgram(ABC):
28
27
  def __init__(
29
28
  self,
30
29
  backend: CircuitRunner,
31
- seed: Optional[int] = None,
32
- progress_queue: Optional[Queue] = None,
30
+ seed: int | None = None,
31
+ progress_queue: Queue | None = None,
33
32
  **kwargs,
34
33
  ):
35
34
  """
@@ -52,14 +51,14 @@ class QuantumProgram(ABC):
52
51
  progress_queue (Queue): a queue for progress bar updates.
53
52
 
54
53
  **kwargs: Additional keyword arguments that influence behaviour.
55
- - grouping_strategy (Optional[Any]): A strategy for grouping operations, used in Pennylane's transforms.
54
+ - grouping_strategy (Literal["default", "wires", "qwc"]): A strategy for grouping operations, used in Pennylane's transforms.
56
55
  Defaults to None.
57
- - qem_protocol (Optional[QEMProtocol]): the quantum error mitigation protocol to apply.
56
+ - qem_protocol (QEMProtocol, optional): the quantum error mitigation protocol to apply.
58
57
  Must be of type QEMProtocol. Defaults to None.
59
58
 
60
59
  The following key values are reserved for internal use and should not be set by the user:
61
- - losses (Optional[list]): A list to initialize the `losses` attribute. Defaults to an empty list.
62
- - final_params (Optional[list]): A list to initialize the `final_params` attribute. Defaults to an empty list.
60
+ - losses (list, optional): A list to initialize the `losses` attribute. Defaults to an empty list.
61
+ - final_params (list, optional): A list to initialize the `final_params` attribute. Defaults to an empty list.
63
62
 
64
63
  """
65
64
 
@@ -264,9 +263,9 @@ class QuantumProgram(ABC):
264
263
  curr_marginal_results = []
265
264
  for observable in curr_measurement_group:
266
265
  intermediate_exp_values = [
267
- sampled_expectation_value(
268
- marginal_counts(shots_dict, observable.wires.tolist()),
269
- "Z" * len(observable.wires),
266
+ ExpectationMP(observable).process_counts(
267
+ shots_dict,
268
+ tuple(reversed(range(len(next(iter(shots_dict.keys())))))),
270
269
  )
271
270
  for shots_dict in group_results.values()
272
271
  ]
divi/qpu_system.py ADDED
@@ -0,0 +1,20 @@
1
+ # SPDX-FileCopyrightText: 2025 Qoro Quantum Ltd <divi@qoroquantum.de>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass(frozen=True, repr=True)
9
+ class QPU:
10
+ nickname: str
11
+ q_bits: int
12
+ status: str
13
+ system_kind: str
14
+
15
+
16
+ @dataclass(frozen=True, repr=True)
17
+ class QPUSystem:
18
+ name: str
19
+ qpus: list[QPU]
20
+ access_level: str
@@ -0,0 +1,3 @@
1
+ SPDX-FileCopyrightText: 2025 Qoro Quantum Ltd <divi@qoroquantum.de>
2
+
3
+ SPDX-License-Identifier: Apache-2.0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qoro-divi
3
- Version: 0.2.2b1
4
- Summary:
3
+ Version: 0.3.1b0
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
7
7
  Requires-Python: >=3.11,<3.13
@@ -10,6 +10,7 @@ Classifier: Programming Language :: Python :: 3.11
10
10
  Classifier: Programming Language :: Python :: 3.12
11
11
  Requires-Dist: cirq-core (<1.5.0)
12
12
  Requires-Dist: dill (>=0.4.0,<0.5.0)
13
+ Requires-Dist: dwave-hybrid (>=0.6.14,<0.7.0)
13
14
  Requires-Dist: matplotlib (>=3.10.3,<4.0.0)
14
15
  Requires-Dist: mitiq (>=0.46,<0.47)
15
16
  Requires-Dist: networkx (>=3.5,<4.0)
@@ -19,7 +20,6 @@ Requires-Dist: pymetis (>=2025.1.1,<2026.0.0)
19
20
  Requires-Dist: python-dotenv (>=1.1.1,<2.0.0)
20
21
  Requires-Dist: qiskit (<2.0)
21
22
  Requires-Dist: qiskit-aer (>=0.17.1,<0.18.0)
22
- Requires-Dist: qiskit-algorithms (>=0.3.1,<0.4.0)
23
23
  Requires-Dist: qiskit-ibm-runtime (>=0.37,<0.38)
24
24
  Requires-Dist: qiskit-optimization (>=0.6.1,<0.7.0)
25
25
  Requires-Dist: requests (>=2.32.4,<3.0.0)
@@ -37,6 +37,9 @@ Divi is designed to allow researchers, students, and enterprises to deploy quant
37
37
 
38
38
  ---
39
39
 
40
+ > [!IMPORTANT]
41
+ > Divi is under active development and in early stages. Users should expect frequent changes that are likely to be incompatible with previously published versions.
42
+
40
43
  ## 🚀 Features
41
44
 
42
45
  - 🧠 **Smart Job Parallelization**: Automatically parallelizes your quantum programs based on task structure.
@@ -56,5 +59,6 @@ pip install qoro-divi
56
59
 
57
60
  ## 📚 Documentation
58
61
 
59
- Full documentation is available at: <https://docs.qoroquantum.net/divi>
62
+ - Full documentation is available at: <https://docs.qoroquantum.net/divi>
63
+ - Tutorials can be found in the `tutorials` folder.
60
64
 
@@ -1,11 +1,12 @@
1
1
  divi/__init__.py,sha256=WNAlzdjio0M5la_NTGk4-0AFqItI6HEBExRs6ck9j0Y,203
2
- divi/_pbar.py,sha256=YDTXMPa8INGzY2zJRloUj5YVINg6TYDqKDY4Yt1qtek,2051
3
- divi/circuits.py,sha256=tNNCZ61w06aaysOkr_1-USaeIDu63MbF1k1Ry7Y_T6g,4492
4
- divi/exp/cirq/__init__.py,sha256=TLt2WskG9j-y17Lv0Zgsg-gQgmZGFbp_lwHQYpj2GsM,312
2
+ divi/_pbar.py,sha256=M6FQByH63bWwzsKtedW49Hm7hFsnb1X5AlFfCZFSvuU,2019
3
+ divi/circuits.py,sha256=OKcwTMXIvCIjvpLvdeQBWGYElcVUNNyA3z8yXnxUXeo,4476
4
+ divi/exp/cirq/__init__.py,sha256=4wd5rEiNgsPVSK8uLYdJMf54B1ITdtKfMJtdnRpfwTE,350
5
5
  divi/exp/cirq/_lexer.py,sha256=x5UArrnN_JEyiq7E02ikq0wdmqZ2vEQ3_FADL1LIhQI,3187
6
6
  divi/exp/cirq/_parser.py,sha256=z_bSn4m03-sfRlN8eL99Mo4LnjY-zmR-Xt6UrjzstZc,29279
7
7
  divi/exp/cirq/_qasm_export.py,sha256=8C5xLYvIIkQTWWAAYo7ZjwtQjvYXNSflbf5UyUx6YUE,1024
8
8
  divi/exp/cirq/_qasm_import.py,sha256=HbehrgfLl3iDdRyWr4o26Bek3ZpN-_dvNVSexl5-aVE,969
9
+ divi/exp/cirq/_validator.py,sha256=nYbJdT_0hCVUgZA0YiKZ8G1oDgwmcqEiy-LZH41EgUw,20481
9
10
  divi/exp/cirq/exception.py,sha256=w1w2vSubOGMRmyKBFqXejxfeIAzkPZ6V7gSrDX_ap4A,765
10
11
  divi/exp/scipy/_cobyla.py,sha256=cnCf5AsOM8JWIMiujuUbWMNOgmUr3ZET9y04hUyumHs,10937
11
12
  divi/exp/scipy/pyprima/LICENCE.txt,sha256=mXN5ssG_U6OR0v0yldznW_PJTtKNZIgu3jDRtRjLDdY,1533
@@ -36,23 +37,25 @@ divi/exp/scipy/pyprima/common/ratio.py,sha256=taadehpW0c3ULUggH5frIMpvTom53dsEhv
36
37
  divi/exp/scipy/pyprima/common/redrho.py,sha256=J6rJILcOoCeo942LUAXxpUvKLarUGNCGdC-zcmIlVHE,1264
37
38
  divi/exp/scipy/pyprima/common/selectx.py,sha256=mXVS2L6AuTmbOhpp1KlXwOBR54ttnbjwagYfnXhezJY,14713
38
39
  divi/interfaces.py,sha256=P-8lekQIGwbhHv4ewfMFtKYIjmQHu7nmscwAmxsx72U,594
39
- divi/parallel_simulator.py,sha256=SjL23zcS-AKyxHG8TeAowDFytpXAshhy64GEkEUnogs,8988
40
- divi/qasm.py,sha256=agHSRqkIYdfVx1w0zcaIXYe_KDpNz38tni-cu20SMN4,7418
40
+ divi/parallel_simulator.py,sha256=2JCwmootXLtDplplEqtykKc8HiYe6G0zKWemIb-IIdE,8947
41
+ divi/qasm.py,sha256=lfWky4E9qetotVo8BII58svutVVZEV14BntoC14bo40,7384
41
42
  divi/qem.py,sha256=o6rMPUcxLuCBitBb8-QcxveUiKZVsP3HMamxyVFLi6M,6805
42
43
  divi/qlogger.py,sha256=VaCnw-u8VkR7JukjOp39r0V89wO-SSpDj5l50ejnHUg,3801
43
- divi/qoro_service.py,sha256=Ry7Pt7EZCpnwwLJ_5Fx2WhXHiFmI6z23zUdAaygkx3Y,11386
44
- divi/qprog/__init__.py,sha256=z8OTd_PdcnEvrcRGvoGsPjj8WYU8HuMHhNyuVMBsNAE,457
45
- divi/qprog/_graph_partitioning.py,sha256=61bkhz5VTS_pYHSEKCcnvaawD5p0AHk8Fh1EtEjdLkE,22006
46
- divi/qprog/_mlae.py,sha256=eLEJqYESZG3oI-XsRjgA5aoNs7Er_CspnJ0wJBX0qdU,5769
47
- divi/qprog/_qaoa.py,sha256=pDMf5RJQ2YHVYiHBpSPu0WkwD4JjF1ARubRD6xbtceA,15445
48
- divi/qprog/_vqe.py,sha256=JAow9uDTsAOa-n-N5t4CrA04uVuAbGi20s9qB-FSqYU,9427
49
- divi/qprog/_vqe_sweep.py,sha256=eKvF6sUeqkyRDKuRxd_k-WoQa9ARkzoOLKolLqkpd5s,4873
50
- divi/qprog/batch.py,sha256=lXID75p4xU88UbukhGIIv6SI1ZrPHsTBzS9u6fPVyhE,7960
44
+ divi/qoro_service.py,sha256=sv94S_xdY4k4YYSK3PgX4sgvgldFMkjJ3wtQO1a_2Yc,13557
45
+ divi/qprog/__init__.py,sha256=quz1zEdrY71TuNVXUHq9u92sORgPmmNq8y6ozMiYnTI,507
46
+ divi/qprog/_graph_partitioning.py,sha256=ZVDKc5--YB9EmuA7Qsyc0Rc5P-eojWpglO-XqoMmbZk,23620
47
+ divi/qprog/_qaoa.py,sha256=FCO3s0mA6rlrymqUjvpfY640DoLLIFP8tqi8z7UAamM,16129
48
+ divi/qprog/_qubo_partitioning.py,sha256=7MxeUfZ5PDkfzPprcXh_1omdvUuCK_I4qh1915XAVhk,7388
49
+ divi/qprog/_vqe.py,sha256=0MNSZA9wb6CKhLIT5SpM_J2ncW-p1frKBp8k0ipOYwQ,9985
50
+ divi/qprog/_vqe_sweep.py,sha256=lTWPG7vwtVS91Piqc5-_tlhglO8RFkMhlatCTz-8PZg,17214
51
+ divi/qprog/batch.py,sha256=UWiDsABFWNggvR136jBnTg6r5OUuHHh3nOCGL1keAy8,9654
51
52
  divi/qprog/optimizers.py,sha256=l19zIyt1l0IhUbLofEXlwE9XnsILk7ANlwACi3KKN4w,2034
52
- divi/qprog/quantum_program.py,sha256=ZMGPFLYZeLiOiU5S7NSlCPAY5vl0R_bv5SRYdEArjSM,16952
53
+ divi/qprog/quantum_program.py,sha256=hJ5uraX9ZNicfoJu3X0aAe8KTJUzndgX5GyoHZ1GOB8,16921
54
+ divi/qpu_system.py,sha256=teVeG18ukyzMFgbPSr4BLx4MJUHVK382RqZMOy2voFk,374
53
55
  divi/utils.py,sha256=EEyGakoz33AvU0Rq7iijL8AULMg4FGcGBtmOlpSw-tY,3603
54
- qoro_divi-0.2.2b1.dist-info/LICENSE,sha256=NS4JlQrgNwg1bvB3kE5shE-P4cJgnntgl-kClbOpG_Q,10760
55
- qoro_divi-0.2.2b1.dist-info/LICENSES/Apache-2.0.txt,sha256=yoILHpvVuguUBpk8UwMnzJbcHUUyst9iGNNuEwUtWVc,10270
56
- qoro_divi-0.2.2b1.dist-info/METADATA,sha256=sDVZbOU64XYPZBF_woJU64eoYKihJgcEfEBnhVdLufM,2102
57
- qoro_divi-0.2.2b1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
58
- qoro_divi-0.2.2b1.dist-info/RECORD,,
56
+ qoro_divi-0.3.1b0.dist-info/LICENSE,sha256=NS4JlQrgNwg1bvB3kE5shE-P4cJgnntgl-kClbOpG_Q,10760
57
+ qoro_divi-0.3.1b0.dist-info/LICENSES/.license-header,sha256=2jN_xtJscqP8LG-NaveY2KHUkfRCC543Y_XjOyKEfWY,105
58
+ qoro_divi-0.3.1b0.dist-info/LICENSES/Apache-2.0.txt,sha256=yoILHpvVuguUBpk8UwMnzJbcHUUyst9iGNNuEwUtWVc,10270
59
+ qoro_divi-0.3.1b0.dist-info/METADATA,sha256=NMSrA8Dko7m626atJX0Cpkp1QaauZBozWFLyNeGQBNQ,2419
60
+ qoro_divi-0.3.1b0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
61
+ qoro_divi-0.3.1b0.dist-info/RECORD,,
divi/qprog/_mlae.py DELETED
@@ -1,182 +0,0 @@
1
- # SPDX-FileCopyrightText: 2025 Qoro Quantum Ltd <divi@qoroquantum.de>
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- from functools import reduce
6
-
7
- import numpy as np
8
- import scipy.optimize as optimize
9
- from qiskit import QuantumCircuit
10
- from qiskit_algorithms import EstimationProblem, MaximumLikelihoodAmplitudeEstimation
11
-
12
- from divi.circuits import Circuit
13
- from divi.qprog.quantum_program import QuantumProgram
14
-
15
-
16
- class BernoulliA(QuantumCircuit):
17
- """A circuit representing the Bernoulli A operator."""
18
-
19
- def __init__(self, probability):
20
- super().__init__(1)
21
-
22
- theta_p = 2 * np.arcsin(np.sqrt(probability))
23
- self.ry(theta_p, 0)
24
-
25
-
26
- class BernoulliQ(QuantumCircuit):
27
- """A circuit representing the Bernoulli Q operator."""
28
-
29
- def __init__(self, probability):
30
- super().__init__(1)
31
-
32
- self._theta_p = 2 * np.arcsin(np.sqrt(probability))
33
- self.ry(2 * self._theta_p, 0)
34
-
35
- def power(self, k):
36
- # implement the efficient power of Q
37
- q_k = QuantumCircuit(1)
38
- q_k.ry(2 * k * self._theta_p, 0)
39
- return q_k
40
-
41
-
42
- class MLAE(QuantumProgram):
43
- """
44
- An implementation of the Maximum Likelihood Amplitude Estimateion described in
45
- https://arxiv.org/pdf/1904.10246
46
- """
47
-
48
- def __init__(
49
- self,
50
- grovers: list[int],
51
- qubits_to_measure: list[int],
52
- probability: float,
53
- **kwargs,
54
- ):
55
- """
56
- Initializes the MLAE problem.
57
- args:
58
- grovers (list): A list of non-negative integers corresponding to the powers of the Grover
59
- operator for each iteration
60
- qubits: An integer or list of integers containing the index of the qubits to measure
61
- probability: The probability of being in the good state to estimate
62
- shots: The number of shots to run for each circuit. Default set at 5000.
63
- """
64
-
65
- super().__init__(**kwargs)
66
-
67
- self.grovers = grovers
68
- self.qubits_to_measure = qubits_to_measure
69
- self.probability = probability
70
- self.likelihood_functions = []
71
-
72
- def _create_meta_circuits_dict(self):
73
- return super()._create_meta_circuits_dict()
74
-
75
- def _generate_circuits(self, params=None, **kwargs):
76
- """
77
- Generates the circuits that perform step one of the MLAE algorithm,
78
- the quantum amplitude amplification.
79
-
80
- Inputs a selection of m values corresponding to the powers of the
81
- Grover operatorfor each iteration.
82
-
83
- Returns:
84
- A list of QASM circuits to run on various devices
85
- """
86
- self.circuits.clear()
87
-
88
- A = BernoulliA(self.probability)
89
- Q = BernoulliQ(self.probability)
90
-
91
- problem = EstimationProblem(
92
- state_preparation=A,
93
- grover_operator=Q,
94
- objective_qubits=self.qubits_to_measure,
95
- )
96
-
97
- qiskit_circuits = MaximumLikelihoodAmplitudeEstimation(
98
- self.grovers
99
- ).construct_circuits(problem)
100
-
101
- for circuit, grover in zip(qiskit_circuits, self.grovers):
102
- circuit.measure_all()
103
- self.circuits.append(Circuit(circuit, tags=[f"{grover}"]))
104
-
105
- def run(self, store_data=False, data_file=None):
106
- self._generate_circuits()
107
- self._dispatch_circuits_and_process_results(
108
- store_data=store_data, data_file=data_file
109
- )
110
-
111
- def _post_process_results(self, results):
112
- """
113
- Generates the likelihood function for each circuit of the quantum
114
- amplitude amplification. These likelihood functions will then
115
- be combined to create a maximum likelihood function to analyze.
116
-
117
- Returns:
118
- A callable maximum likelihood function
119
- """
120
-
121
- # Define the necessary variables Nk, Mk, Lk
122
- for label, shots_dict in results.items():
123
- mk = int(label)
124
- Nk = 0
125
- hk = 0
126
- for key, shots in shots_dict.items():
127
- Nk += shots
128
- hk += shots if key.count("1") == len(key) else 0
129
-
130
- def likelihood_function(theta, mk=mk, hk=hk, Nk=Nk):
131
- as_theta = np.arcsin(np.sqrt(theta))
132
- return ((np.sin((2 * mk + 1) * as_theta)) ** (2 * hk)) * (
133
- (np.cos((2 * mk + 1) * as_theta)) ** (2 * (Nk - hk))
134
- )
135
-
136
- self.likelihood_functions.append(likelihood_function)
137
-
138
- def generate_maximum_likelihood_function(self, factor=1.0):
139
- """
140
- Post-processing takes in likelihood functions.
141
-
142
- A large factor (e.g. 1e200) should be used for visualization purposes.
143
- Returns:
144
- The maximum likelihood function.
145
- """
146
-
147
- def combined_likelihood_function(theta):
148
- return (
149
- reduce(
150
- lambda result, f: result * f(theta), self.likelihood_functions, 1.0
151
- )
152
- * factor
153
- )
154
-
155
- self.maximum_likelihood_fn = combined_likelihood_function
156
-
157
- return combined_likelihood_function
158
-
159
- def estimate_amplitude(self, factor):
160
- """
161
- Uses the maximum likelihood function to ascertain
162
- a value for the amplitude.
163
-
164
- Returns
165
- Estimation of the amplitude
166
- """
167
-
168
- def minimum_likelihood_function(theta):
169
- # The factor to set to -10e30 in the older branch
170
- return (
171
- reduce(
172
- lambda result, f: result * f(theta), self.likelihood_functions, 1.0
173
- )
174
- * factor
175
- )
176
-
177
- # create the range of possible amplitudes
178
- amplitudes = np.linspace(0, 1, 100)
179
-
180
- bounds = [(min(amplitudes), max(amplitudes))]
181
-
182
- return optimize.differential_evolution(minimum_likelihood_function, bounds).x[0]