classiq 0.77.0__py3-none-any.whl → 0.78.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.
- classiq/applications/iqae/__init__.py +0 -0
- classiq/applications/iqae/iqae.py +207 -0
- classiq/execution/__init__.py +1 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/applications/iqae/__init__.py +0 -0
- classiq/interface/applications/iqae/generic_iqae.py +222 -0
- classiq/interface/applications/iqae/iqae_result.py +45 -0
- classiq/interface/debug_info/debug_info.py +3 -0
- classiq/interface/executor/execution_result.py +1 -1
- classiq/interface/executor/user_budget.py +1 -1
- classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +9 -3
- classiq/interface/generator/generated_circuit_data.py +18 -7
- classiq/interface/ide/visual_model.py +2 -0
- classiq/model_expansions/closure.py +1 -58
- classiq/model_expansions/evaluators/argument_types.py +1 -2
- classiq/model_expansions/function_builder.py +0 -45
- classiq/model_expansions/interpreters/base_interpreter.py +12 -14
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +0 -17
- classiq/model_expansions/quantum_operations/call_emitter.py +71 -32
- classiq/model_expansions/quantum_operations/emitter.py +6 -1
- classiq/model_expansions/quantum_operations/function_calls_cache.py +84 -0
- classiq/model_expansions/scope.py +20 -10
- classiq/model_expansions/transformers/type_qualifier_inference.py +183 -0
- classiq/model_expansions/utils/text_utils.py +4 -2
- classiq/model_expansions/visitors/symbolic_param_inference.py +1 -12
- classiq/qmod/builtins/classical_execution_primitives.py +1 -1
- classiq/qmod/qmod_variable.py +7 -4
- classiq/qmod/utilities.py +4 -0
- {classiq-0.77.0.dist-info → classiq-0.78.0.dist-info}/METADATA +1 -1
- {classiq-0.77.0.dist-info → classiq-0.78.0.dist-info}/RECORD +31 -25
- classiq/interface/executor/iqae_result.py +0 -17
- {classiq-0.77.0.dist-info → classiq-0.78.0.dist-info}/WHEEL +0 -0
File without changes
|
@@ -0,0 +1,207 @@
|
|
1
|
+
from typing import Literal, Optional, cast
|
2
|
+
|
3
|
+
from classiq.interface.applications.iqae.generic_iqae import GenericIQAE
|
4
|
+
from classiq.interface.applications.iqae.iqae_result import (
|
5
|
+
IQAEIterationData,
|
6
|
+
IQAEResult,
|
7
|
+
)
|
8
|
+
from classiq.interface.executor.execution_preferences import ExecutionPreferences
|
9
|
+
from classiq.interface.generator.model import Constraints, Preferences
|
10
|
+
from classiq.interface.generator.quantum_program import QuantumProgram
|
11
|
+
from classiq.interface.model.model import SerializedModel
|
12
|
+
|
13
|
+
from classiq.execution import ExecutionSession
|
14
|
+
from classiq.open_library import amplitude_amplification
|
15
|
+
from classiq.qmod import (
|
16
|
+
CInt,
|
17
|
+
Output,
|
18
|
+
QArray,
|
19
|
+
QBit,
|
20
|
+
QCallable,
|
21
|
+
)
|
22
|
+
from classiq.qmod.builtins import Z, allocate, bind, within_apply
|
23
|
+
from classiq.qmod.create_model_function import create_model
|
24
|
+
from classiq.qmod.qfunc import qfunc
|
25
|
+
from classiq.synthesis import synthesize
|
26
|
+
|
27
|
+
|
28
|
+
class IQAE:
|
29
|
+
"""
|
30
|
+
Implementation of Iterative Quantum Amplitude Estimation [1].
|
31
|
+
Given $A$ s.t. $A|0>_n|0> = \\sqrt{1-a}|\\psi_0>_n|0> + \\sqrt{a}|\\psi_1>_n|1>$, the algorithm estimates
|
32
|
+
$a$ by iteratively sampling $Q^kA$, where $Q=AS_0A^{\\dagger}S_{\\psi_0}$, and $k$ is an integer variable.
|
33
|
+
|
34
|
+
For estimating $a$, The algorithm estimates $\\theta_a$ which is defined by $a = sin^2(\\theta_a)$, so it starts with a
|
35
|
+
confidence interval $(0, \\pi/2)$ and narrows down this interval on each iteration according to the sample results.
|
36
|
+
|
37
|
+
References:
|
38
|
+
[1]: Grinko, D., Gacon, J., Zoufal, C., & Woerner, S. (2019).
|
39
|
+
Iterative Quantum Amplitude Estimation.
|
40
|
+
`arXiv:1912.05559 <https://arxiv.org/abs/1912.05559>`.
|
41
|
+
"""
|
42
|
+
|
43
|
+
_NUM_SHOUTS = 2048
|
44
|
+
|
45
|
+
def __init__(
|
46
|
+
self,
|
47
|
+
state_prep_op: QCallable[QArray[QBit, Literal["problem_vars_size"]], QBit],
|
48
|
+
problem_vars_size: int,
|
49
|
+
constraints: Optional[Constraints] = None,
|
50
|
+
preferences: Optional[Preferences] = None,
|
51
|
+
) -> None:
|
52
|
+
self._state_prep_op = state_prep_op
|
53
|
+
self._problem_vars_size: int = problem_vars_size
|
54
|
+
self._constraints: Optional[Constraints] = constraints
|
55
|
+
self._preferences: Optional[Preferences] = preferences
|
56
|
+
self._model: Optional[SerializedModel] = None
|
57
|
+
self._qprog: Optional[QuantumProgram] = None
|
58
|
+
|
59
|
+
"""
|
60
|
+
Args:
|
61
|
+
state_prep_op (Qfunc): implementation of the operator $A$ in Qmod.
|
62
|
+
problem_vars_size (int): The size of the problem in terms of the number of qubits, e.g., $n$ of the first register A works on.
|
63
|
+
constraints (Constraints): Constraints for the synthesis of the model. See Constraints (Optional).
|
64
|
+
preferences (Preferences): Preferences for the synthesis of the model. See Preferences (Optional).
|
65
|
+
"""
|
66
|
+
|
67
|
+
def get_model(self) -> SerializedModel:
|
68
|
+
"""
|
69
|
+
Implement the quantum part of IQAE in terms of the Qmod Model
|
70
|
+
|
71
|
+
Args:
|
72
|
+
|
73
|
+
|
74
|
+
Returns:
|
75
|
+
SerializedModel (str): A serialized model.
|
76
|
+
|
77
|
+
"""
|
78
|
+
|
79
|
+
state_prep_op = self._state_prep_op
|
80
|
+
problem_vars_size = self._problem_vars_size
|
81
|
+
|
82
|
+
@qfunc
|
83
|
+
def space_transform(est_reg: QArray) -> None:
|
84
|
+
state_prep_op(est_reg[0 : est_reg.len - 1], est_reg[est_reg.len - 1])
|
85
|
+
|
86
|
+
@qfunc
|
87
|
+
def oracle(est_reg: QArray) -> None:
|
88
|
+
Z(est_reg[est_reg.len - 1])
|
89
|
+
|
90
|
+
@qfunc
|
91
|
+
def main(
|
92
|
+
k: CInt,
|
93
|
+
indicator: Output[QBit],
|
94
|
+
) -> None:
|
95
|
+
est_reg: QArray = QArray("est_reg")
|
96
|
+
problem_vars: QArray = QArray("problem_vars", length=problem_vars_size)
|
97
|
+
allocate(problem_vars)
|
98
|
+
allocate(indicator)
|
99
|
+
within_apply(
|
100
|
+
lambda: bind([problem_vars, indicator], est_reg),
|
101
|
+
lambda: amplitude_amplification(
|
102
|
+
k,
|
103
|
+
oracle,
|
104
|
+
space_transform,
|
105
|
+
est_reg,
|
106
|
+
),
|
107
|
+
)
|
108
|
+
|
109
|
+
if self._model is None:
|
110
|
+
self._model = create_model(
|
111
|
+
main,
|
112
|
+
constraints=self._constraints,
|
113
|
+
preferences=self._preferences,
|
114
|
+
)
|
115
|
+
return self._model
|
116
|
+
|
117
|
+
def get_qprog(self) -> QuantumProgram:
|
118
|
+
"""
|
119
|
+
Create an executable quantum Program for IQAE.
|
120
|
+
|
121
|
+
Args:
|
122
|
+
|
123
|
+
Returns:
|
124
|
+
QuantumProgram (QuantumProgram): Quantum program. See QuantumProgram.
|
125
|
+
"""
|
126
|
+
|
127
|
+
if self._qprog is None:
|
128
|
+
model = self.get_model()
|
129
|
+
self._qprog = synthesize(model)
|
130
|
+
return self._qprog
|
131
|
+
|
132
|
+
def run(
|
133
|
+
self,
|
134
|
+
epsilon: float,
|
135
|
+
alpha: float,
|
136
|
+
execution_preferences: Optional[ExecutionPreferences] = None,
|
137
|
+
) -> IQAEResult:
|
138
|
+
"""
|
139
|
+
Executes IQAE's quantum program with the provided epsilon, alpha, and execution
|
140
|
+
preferences.
|
141
|
+
If execution_preferences has been proved, or if it does not contain num_shot, then num_shot is set to 2048.
|
142
|
+
|
143
|
+
Args:
|
144
|
+
epsilon (float): Target accuracy in therm of $\\theta_a$ e.g $a = sin^2(\\theta_a \\pm \\epsilon)$ .
|
145
|
+
alpha (float): Specifies the confidence level (1 - alpha)
|
146
|
+
execution_preferences (Preferences): Preferences for the execution of the model. See ExecutionPreferences (Optional).
|
147
|
+
Returns:
|
148
|
+
IQAEResult (IQAEResult): A result of the IQAE algorithm. See IQAEResult.
|
149
|
+
"""
|
150
|
+
|
151
|
+
if self._qprog is None:
|
152
|
+
self._qprog = self.get_qprog()
|
153
|
+
|
154
|
+
if execution_preferences is None:
|
155
|
+
execution_preferences = ExecutionPreferences(
|
156
|
+
num_shots=self._NUM_SHOUTS,
|
157
|
+
)
|
158
|
+
elif execution_preferences.num_shots is None:
|
159
|
+
execution_preferences.num_shots = self._NUM_SHOUTS
|
160
|
+
|
161
|
+
return self._run(
|
162
|
+
epsilon=epsilon,
|
163
|
+
alpha=alpha,
|
164
|
+
execution_preferences=execution_preferences,
|
165
|
+
quantum_program=self._qprog,
|
166
|
+
)
|
167
|
+
|
168
|
+
def _run(
|
169
|
+
self,
|
170
|
+
epsilon: float,
|
171
|
+
alpha: float,
|
172
|
+
execution_preferences: ExecutionPreferences,
|
173
|
+
quantum_program: QuantumProgram,
|
174
|
+
) -> IQAEResult:
|
175
|
+
|
176
|
+
iterations_data: list[IQAEIterationData] = []
|
177
|
+
warnings: list[str] = []
|
178
|
+
with ExecutionSession(quantum_program, execution_preferences) as executor:
|
179
|
+
|
180
|
+
def _iqae_sample(k: int, _: int) -> int:
|
181
|
+
sample_results = executor.sample({"k": k})
|
182
|
+
iterations_data.append(
|
183
|
+
IQAEIterationData(
|
184
|
+
grover_iterations=k,
|
185
|
+
sample_results=sample_results,
|
186
|
+
)
|
187
|
+
)
|
188
|
+
return sample_results.counts_of_output("indicator").get("1", 0)
|
189
|
+
|
190
|
+
iqae = GenericIQAE(
|
191
|
+
epsilon=epsilon,
|
192
|
+
alpha=alpha,
|
193
|
+
num_shots=cast(int, execution_preferences.num_shots),
|
194
|
+
sample_callable=_iqae_sample,
|
195
|
+
)
|
196
|
+
|
197
|
+
try:
|
198
|
+
iqae.run()
|
199
|
+
except RuntimeError as ex:
|
200
|
+
warnings.append(f"Algorithm error: {ex.args[0]}")
|
201
|
+
|
202
|
+
return IQAEResult(
|
203
|
+
estimation=iqae.current_estimation(),
|
204
|
+
confidence_interval=iqae.current_estimation_confidence_interval().tolist(),
|
205
|
+
iterations_data=iterations_data,
|
206
|
+
warnings=warnings,
|
207
|
+
)
|
classiq/execution/__init__.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
from ..executor import * # noqa: F403
|
2
2
|
from ..executor import __all__ as _exec_all
|
3
|
+
from ..interface.applications.iqae.iqae_result import IQAEResult
|
3
4
|
from ..interface.backend.backend_preferences import * # noqa: F403
|
4
5
|
from ..interface.backend.backend_preferences import __all__ as _be_all
|
5
6
|
from ..interface.executor.execution_preferences import * # noqa: F403
|
6
7
|
from ..interface.executor.execution_preferences import __all__ as _ep_all
|
7
|
-
from ..interface.executor.iqae_result import IQAEResult
|
8
8
|
from ..interface.executor.result import ExecutionDetails
|
9
9
|
from ..interface.executor.vqe_result import VQESolverResult
|
10
10
|
from .execution_session import ExecutionSession
|
classiq/interface/_version.py
CHANGED
File without changes
|
@@ -0,0 +1,222 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from collections.abc import Callable
|
4
|
+
from dataclasses import dataclass
|
5
|
+
|
6
|
+
import numpy as np
|
7
|
+
|
8
|
+
MAX_ITERATIONS_NUMBER = 1000
|
9
|
+
|
10
|
+
|
11
|
+
class GenericIQAE:
|
12
|
+
"""
|
13
|
+
The implementation is based on Algorithm 1 & Algorithm 2 in [1], with the intent of demistifying variables names
|
14
|
+
and simplifying the code flow.
|
15
|
+
Moreover, we separated the algorithm flow from quantum execution to allow migrating this code to any execution
|
16
|
+
interface and to improve its testability.
|
17
|
+
|
18
|
+
References:
|
19
|
+
[1]: Grinko, D., Gacon, J., Zoufal, C., & Woerner, S. (2019).
|
20
|
+
Iterative Quantum Amplitude Estimation.
|
21
|
+
`arXiv:1912.05559 <https://arxiv.org/abs/1912.05559>`.
|
22
|
+
"""
|
23
|
+
|
24
|
+
def __init__(
|
25
|
+
self,
|
26
|
+
epsilon: float,
|
27
|
+
alpha: float,
|
28
|
+
num_shots: int,
|
29
|
+
sample_callable: Callable[[int, int], int],
|
30
|
+
) -> None:
|
31
|
+
"""
|
32
|
+
Parameters:
|
33
|
+
epsilon: Target accuracy.
|
34
|
+
alpha: Specifies the confidence level (1 - alpha).
|
35
|
+
num_shots: The maximum number of shots in each iteration.
|
36
|
+
sample_callable: A callable which gets k and num_shots, and returns the count of good states for $Q^kA$
|
37
|
+
(states with 1 in the last qubit).
|
38
|
+
This callable is responsible for the quantum execution and the results parsing.
|
39
|
+
"""
|
40
|
+
self._epsilon = epsilon
|
41
|
+
self._alpha = alpha
|
42
|
+
self._num_shots = num_shots
|
43
|
+
self._sample_callable = sample_callable
|
44
|
+
|
45
|
+
self.iterations: list[IterationInfo] = []
|
46
|
+
|
47
|
+
# Prepare initial values (lines 2-6, Algorithm 1, [1])
|
48
|
+
self._new_iteration()
|
49
|
+
self._current().k = 0
|
50
|
+
self._current().is_upper_plane = True
|
51
|
+
self._current().confidence_interval = np.array([0, np.pi / 2])
|
52
|
+
|
53
|
+
self._max_rounds = np.ceil(np.log2(np.pi / (8 * self._epsilon)))
|
54
|
+
|
55
|
+
def _new_iteration(self) -> None:
|
56
|
+
if len(self.iterations) > MAX_ITERATIONS_NUMBER:
|
57
|
+
raise RuntimeError(
|
58
|
+
f"Maximum number of iterations ({MAX_ITERATIONS_NUMBER}) achieved."
|
59
|
+
)
|
60
|
+
self.iterations.append(IterationInfo())
|
61
|
+
|
62
|
+
def _current(self) -> IterationInfo:
|
63
|
+
return self.iterations[-1]
|
64
|
+
|
65
|
+
def _prev(self) -> IterationInfo:
|
66
|
+
return self.iterations[-2]
|
67
|
+
|
68
|
+
def run(self) -> float:
|
69
|
+
"""
|
70
|
+
Execute the estimation algorithm.
|
71
|
+
See Algorithm 1, [1].
|
72
|
+
"""
|
73
|
+
while interval_len(self._current().confidence_interval) > 2 * self._epsilon:
|
74
|
+
self._new_iteration()
|
75
|
+
self._find_next_K()
|
76
|
+
self._sample()
|
77
|
+
self._calculate_confidence_interval()
|
78
|
+
|
79
|
+
return self.current_estimation()
|
80
|
+
|
81
|
+
def current_estimation_confidence_interval(self) -> np.ndarray:
|
82
|
+
return np.sin(self._current().confidence_interval) ** 2
|
83
|
+
|
84
|
+
def current_estimation(self) -> float:
|
85
|
+
return self.current_estimation_confidence_interval().mean()
|
86
|
+
|
87
|
+
def _find_next_K(self, r: int = 2) -> None: # noqa: N802
|
88
|
+
self._current().K, self._current().is_upper_plane = self.find_next_K(
|
89
|
+
K=self._prev().K,
|
90
|
+
is_upper_plane=self._prev().is_upper_plane,
|
91
|
+
confidence_interval=self._prev().confidence_interval,
|
92
|
+
r=r,
|
93
|
+
)
|
94
|
+
|
95
|
+
@staticmethod
|
96
|
+
def find_next_K( # noqa: N802
|
97
|
+
K: int, # noqa: N803
|
98
|
+
is_upper_plane: bool,
|
99
|
+
confidence_interval: np.ndarray,
|
100
|
+
r: int = 2,
|
101
|
+
) -> tuple[int, bool]:
|
102
|
+
"""
|
103
|
+
We want to find the largest K (with some lower and upper bounds) such that the K-scaled confidence interval
|
104
|
+
lies completely in the upper or lower half planes.
|
105
|
+
See Algorithm 2, [1].
|
106
|
+
"""
|
107
|
+
K_max = int(np.pi // interval_len(confidence_interval)) # noqa: N806
|
108
|
+
K_max = K_max - (K_max - 2) % 4 # noqa: N806
|
109
|
+
K_min = r * K # noqa: N806
|
110
|
+
|
111
|
+
for K_cand in range(K_max, K_min - 1, -4): # noqa: N806
|
112
|
+
scaled_confidence_interval = (K_cand * confidence_interval) % (2 * np.pi)
|
113
|
+
|
114
|
+
if all(scaled_confidence_interval <= np.pi):
|
115
|
+
return K_cand, True
|
116
|
+
if all(scaled_confidence_interval >= np.pi):
|
117
|
+
return K_cand, False
|
118
|
+
|
119
|
+
return K, is_upper_plane
|
120
|
+
|
121
|
+
def _sample(self) -> None:
|
122
|
+
"""
|
123
|
+
Use the external sample callable to get the count of good states for $Q^kA$ (states with 1 in the last qubit).
|
124
|
+
Effectively implements line 16, Algorithm 1, [1].
|
125
|
+
"""
|
126
|
+
# To optimize results, the paper's algorithm applies the "no-overshooting condition" which limits
|
127
|
+
# the number of shots in each iteration (lines 12-15, Algorithm 1, [1]). As the calculation is not very
|
128
|
+
# simple, we currently don't support this and use constant number of shots.
|
129
|
+
self._current().num_shots = self._num_shots
|
130
|
+
|
131
|
+
self._current().good_counts = self._sample_callable(
|
132
|
+
self._current().k, self._current().num_shots
|
133
|
+
)
|
134
|
+
|
135
|
+
def _calculate_confidence_interval(self) -> None:
|
136
|
+
"""
|
137
|
+
Calculate the next confidence interval based on the last sample's results.
|
138
|
+
Effectively implements lines 17-28, Algorithm 1, [1].
|
139
|
+
"""
|
140
|
+
prob = self._current().good_counts / self._current().num_shots
|
141
|
+
|
142
|
+
# The paper specifies two possibles confidence interval methods: Clopper-Perason or Chernoff-Hoeffding.
|
143
|
+
# We currently support only the latter.
|
144
|
+
prob_min, prob_max = self._chernoff_hoeffding(prob)
|
145
|
+
|
146
|
+
if self._current().is_upper_plane:
|
147
|
+
theta = np.arccos(1 - 2 * np.array([prob_min, prob_max]))
|
148
|
+
else:
|
149
|
+
theta = 2 * np.pi - np.arccos(1 - 2 * np.array([prob_max, prob_min]))
|
150
|
+
|
151
|
+
scaled_confidence_interval = (
|
152
|
+
self._current().K * self._prev().confidence_interval
|
153
|
+
)
|
154
|
+
number_of_wraps = scaled_confidence_interval // (2 * np.pi)
|
155
|
+
|
156
|
+
# Sometimes we have edge cases where the lower or upper bound of the scaled interval fall exactly
|
157
|
+
# on 2pi*T for some integer T, and the number of wraps might be rounded up or down wrongfuly.
|
158
|
+
# To fix it, use the number of wraps of the middle point in the scaled interval.
|
159
|
+
if number_of_wraps[0] + 1 == number_of_wraps[1]:
|
160
|
+
number_of_wraps_of_middle = np.mean(scaled_confidence_interval) // (
|
161
|
+
2 * np.pi
|
162
|
+
)
|
163
|
+
number_of_wraps = np.array(
|
164
|
+
[number_of_wraps_of_middle, number_of_wraps_of_middle]
|
165
|
+
)
|
166
|
+
|
167
|
+
if number_of_wraps[0] != number_of_wraps[1]:
|
168
|
+
raise RuntimeError(
|
169
|
+
f"Number of wraps of the lower and upper bounds should be equal, got {number_of_wraps}"
|
170
|
+
)
|
171
|
+
|
172
|
+
self._current().confidence_interval = (
|
173
|
+
2 * np.pi * number_of_wraps + theta
|
174
|
+
) / self._current().K
|
175
|
+
|
176
|
+
def _chernoff_hoeffding(self, prob: float) -> tuple[float, float]:
|
177
|
+
"""
|
178
|
+
The Chernoff-Hoeffding confidence interval method.
|
179
|
+
Effectively implements lines 20-22, Algorithm 1, [1].
|
180
|
+
"""
|
181
|
+
epsilon = np.sqrt(
|
182
|
+
np.log(2 * self._max_rounds / self._alpha)
|
183
|
+
/ (2 * self._accumulated_num_shots())
|
184
|
+
)
|
185
|
+
return max(0, prob - epsilon), min(1, prob + epsilon)
|
186
|
+
|
187
|
+
def _accumulated_num_shots(self) -> int:
|
188
|
+
num_shots = 0
|
189
|
+
for iteration in reversed(self.iterations):
|
190
|
+
if iteration.K == self._current().K:
|
191
|
+
num_shots += iteration.num_shots
|
192
|
+
else:
|
193
|
+
break
|
194
|
+
return num_shots
|
195
|
+
|
196
|
+
|
197
|
+
@dataclass(init=False, repr=False, eq=False)
|
198
|
+
class IterationInfo:
|
199
|
+
"""
|
200
|
+
The information stored on each iteration of IQAE.
|
201
|
+
"""
|
202
|
+
|
203
|
+
K: int # K = 4k + 2 where k is the power of Q on each iteration
|
204
|
+
is_upper_plane: bool # Wheter the scaled confidence interval is in the upper or lower half plane (in the paper: "up")
|
205
|
+
confidence_interval: (
|
206
|
+
np.ndarray
|
207
|
+
) # The current confidence interval (in the paper: "(theta_l, theta_u)")
|
208
|
+
|
209
|
+
good_counts: int
|
210
|
+
num_shots: int = 0
|
211
|
+
|
212
|
+
@property
|
213
|
+
def k(self) -> int:
|
214
|
+
return (self.K - 2) // 4
|
215
|
+
|
216
|
+
@k.setter
|
217
|
+
def k(self, k: int) -> None:
|
218
|
+
self.K = 4 * k + 2
|
219
|
+
|
220
|
+
|
221
|
+
def interval_len(interval: np.ndarray) -> float:
|
222
|
+
return interval[1] - interval[0]
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from pydantic import BaseModel, Field
|
2
|
+
|
3
|
+
from classiq.interface.executor.result import ExecutionDetails
|
4
|
+
from classiq.interface.generator.functions.classical_type import QmodPyObject
|
5
|
+
from classiq.interface.helpers.versioned_model import VersionedModel
|
6
|
+
|
7
|
+
|
8
|
+
class IQAEIterationData(BaseModel):
|
9
|
+
"""
|
10
|
+
Handles the data storage for a single iteration of the Iterative Quantum Amplitude
|
11
|
+
Estimation algorithm.
|
12
|
+
|
13
|
+
This class is intended to represent the results and state of a single Grover iteration
|
14
|
+
of the IQAE process.
|
15
|
+
|
16
|
+
Attributes:
|
17
|
+
grover_iterations (int): The iteration number of Grover's algorithm.
|
18
|
+
sample_results (ExecutionDetails): The `ExecutionDetails` of Grover iteration. See ExecutionDetails.
|
19
|
+
"""
|
20
|
+
|
21
|
+
grover_iterations: int
|
22
|
+
sample_results: ExecutionDetails
|
23
|
+
|
24
|
+
|
25
|
+
class IQAEResult(VersionedModel, QmodPyObject):
|
26
|
+
"""
|
27
|
+
Represents the result of an Iterative Quantum Amplitude Estimation (IQAE)
|
28
|
+
process.
|
29
|
+
|
30
|
+
This class encapsulates the output of the IQAE algorithm, including the
|
31
|
+
estimated value, confidence interval, intermediate iteration data, and
|
32
|
+
any warnings generated during the computation.
|
33
|
+
|
34
|
+
Attributes:
|
35
|
+
estimation (float): Estimation of the amplitude.
|
36
|
+
confidence_interval (list[float]): The interval in which the amplitude is within, with a probability equal to epsilon.
|
37
|
+
iterations_data (list[IQAEIterationData]): List of `IQAEIterationData` of each Grover iteration.
|
38
|
+
See IQAEIterationData.
|
39
|
+
warnings (list[str]): List of warnings generated during the IQAE process of each Grover iteration.
|
40
|
+
"""
|
41
|
+
|
42
|
+
estimation: float
|
43
|
+
confidence_interval: list[float] = Field(min_length=2, max_length=2)
|
44
|
+
iterations_data: list[IQAEIterationData]
|
45
|
+
warnings: list[str]
|
@@ -24,6 +24,7 @@ class FunctionDebugInfo(BaseModel):
|
|
24
24
|
statement_type: Union[StatementType, None] = None
|
25
25
|
is_inverse: bool = Field(default=False)
|
26
26
|
release_by_inverse: bool = Field(default=False)
|
27
|
+
control_variable: Optional[str] = Field(default=None)
|
27
28
|
port_to_passed_variable_map: dict[str, str] = Field(default_factory=dict)
|
28
29
|
node: Optional[ConcreteQuantumStatement] = None
|
29
30
|
|
@@ -85,6 +86,8 @@ def get_back_refs(
|
|
85
86
|
# For backwards compatibility, we make sure that the back_ref is not a block
|
86
87
|
# Remove this check when we start saving blocks in the debug info collection.
|
87
88
|
assert not isinstance(node, Block)
|
89
|
+
if len(back_refs) > 0 and node.back_ref == back_refs[0].back_ref:
|
90
|
+
break
|
88
91
|
back_refs.insert(0, node)
|
89
92
|
if node.back_ref is None:
|
90
93
|
break
|
@@ -3,8 +3,8 @@ from typing import Annotated, Any, Literal, Union
|
|
3
3
|
from pydantic import BaseModel, ConfigDict, Field
|
4
4
|
from typing_extensions import TypeAlias
|
5
5
|
|
6
|
+
from classiq.interface.applications.iqae.iqae_result import IQAEResult
|
6
7
|
from classiq.interface.enum_utils import StrEnum
|
7
|
-
from classiq.interface.executor.iqae_result import IQAEResult
|
8
8
|
from classiq.interface.executor.result import (
|
9
9
|
EstimationResult,
|
10
10
|
EstimationResults,
|
@@ -29,7 +29,7 @@ class UserBudgets(VersionedModel):
|
|
29
29
|
def format_row(
|
30
30
|
provider: str, available: float, used: float, currency: str
|
31
31
|
) -> str:
|
32
|
-
return f"| {provider:<20} | {available:<18.
|
32
|
+
return f"| {provider:<20} | {available:<18.3f} | {used:<18.3f} | {currency:<8} |"
|
33
33
|
|
34
34
|
table_data: dict = defaultdict(
|
35
35
|
lambda: {"used": 0.0, "available": 0.0, "currency": "USD"}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import inspect
|
2
|
-
from typing import Any
|
2
|
+
from typing import Any, NoReturn
|
3
3
|
|
4
4
|
import sympy
|
5
5
|
|
@@ -37,5 +37,11 @@ class AnyClassicalValue(sympy.Symbol):
|
|
37
37
|
return super().__getattribute__(attr)
|
38
38
|
return AnyClassicalValue(f"get_field({self}, '{attr}')")
|
39
39
|
|
40
|
-
def __len__(self) ->
|
41
|
-
|
40
|
+
def __len__(self) -> NoReturn:
|
41
|
+
raise TypeError("object of type 'AnyClassicalValue' has no len()")
|
42
|
+
|
43
|
+
def __iter__(self) -> NoReturn:
|
44
|
+
raise TypeError("'AnyClassicalValue' object is not iterable")
|
45
|
+
|
46
|
+
def __bool__(self) -> bool:
|
47
|
+
return True
|
@@ -145,6 +145,8 @@ class StatementType(StrEnum):
|
|
145
145
|
POWER = "power"
|
146
146
|
INVERT = "invert"
|
147
147
|
WITHIN_APPLY = "within apply"
|
148
|
+
WITHIN = "within"
|
149
|
+
APPLY = "apply"
|
148
150
|
ASSIGN = "assign"
|
149
151
|
ASSIGN_AMPLITUDE = "assign amplitude"
|
150
152
|
PHASE = "phase"
|
@@ -161,6 +163,9 @@ STATEMENTS_NAME: dict[str, StatementType] = {
|
|
161
163
|
"Power": StatementType.POWER,
|
162
164
|
"Invert": StatementType.INVERT,
|
163
165
|
"WithinApply": StatementType.WITHIN_APPLY,
|
166
|
+
"Compute": StatementType.WITHIN,
|
167
|
+
"Action": StatementType.APPLY,
|
168
|
+
"Uncompute": StatementType.WITHIN,
|
164
169
|
ArithmeticOperationKind.Assignment.value: StatementType.ASSIGN,
|
165
170
|
"InplaceBinaryOperation": StatementType.ASSIGN,
|
166
171
|
"AmplitudeLoadingOperation": StatementType.ASSIGN_AMPLITUDE,
|
@@ -176,6 +181,7 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
176
181
|
children: list["FunctionDebugInfoInterface"]
|
177
182
|
relative_qubits: tuple[int, ...]
|
178
183
|
absolute_qubits: Optional[tuple[int, ...]] = Field(default=None)
|
184
|
+
control_variable: Optional[str] = Field(default=None)
|
179
185
|
is_basis_gate: Optional[bool] = Field(default=None)
|
180
186
|
is_inverse: bool = Field(default=False)
|
181
187
|
is_unitary: bool = Field(default=True, exclude=True)
|
@@ -205,14 +211,21 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
205
211
|
|
206
212
|
if isinstance(back_ref, QuantumFunctionCall):
|
207
213
|
name = generate_original_function_name(back_ref.func_name)
|
208
|
-
|
209
|
-
name += f" [{part_match.group(1)}]"
|
210
|
-
return name.removeprefix(ARITH_ENGINE_PREFIX)
|
214
|
+
return self.add_suffix_from_generated_name(generated_name, name)
|
211
215
|
|
212
216
|
statement_kind: str = back_ref.kind
|
213
217
|
if isinstance(back_ref, ArithmeticOperation):
|
214
218
|
statement_kind = back_ref.operation_kind.value
|
215
|
-
return
|
219
|
+
return self.add_suffix_from_generated_name(
|
220
|
+
generated_name, STATEMENTS_NAME[statement_kind]
|
221
|
+
)
|
222
|
+
|
223
|
+
def add_suffix_from_generated_name(self, generated_name: str, name: str) -> str:
|
224
|
+
if part_match := PART_SUFFIX_REGEX.match(generated_name):
|
225
|
+
suffix = f" [{part_match.group(1)}]"
|
226
|
+
else:
|
227
|
+
suffix = ""
|
228
|
+
return f"{name}{suffix}"
|
216
229
|
|
217
230
|
@property
|
218
231
|
def first_back_ref(self) -> Optional[ConcreteQuantumStatement]:
|
@@ -347,9 +360,7 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
347
360
|
if role is RegisterRole.INPUT:
|
348
361
|
return RegisterRole.OUTPUT
|
349
362
|
if role is RegisterRole.EXPLICIT_ZERO_INPUT or role is RegisterRole.ZERO_INPUT:
|
350
|
-
|
351
|
-
return RegisterRole.ZERO_OUTPUT
|
352
|
-
return RegisterRole.OUTPUT
|
363
|
+
return RegisterRole.ZERO_OUTPUT
|
353
364
|
if role is RegisterRole.AUXILIARY:
|
354
365
|
return RegisterRole.AUXILIARY
|
355
366
|
if role is RegisterRole.OUTPUT or role is RegisterRole.GARBAGE_OUTPUT:
|
@@ -15,6 +15,7 @@ from classiq.interface.helpers.versioned_model import VersionedModel
|
|
15
15
|
|
16
16
|
class OperationType(StrEnum):
|
17
17
|
REGULAR = "REGULAR"
|
18
|
+
INVISIBLE = "INVISIBLE"
|
18
19
|
ALLOCATE = "ALLOCATE"
|
19
20
|
FREE = "FREE"
|
20
21
|
BIND = "BIND"
|
@@ -124,6 +125,7 @@ class Operation(pydantic.BaseModel):
|
|
124
125
|
)
|
125
126
|
is_daggered: bool = pydantic.Field(default=False)
|
126
127
|
expanded: bool = pydantic.Field(default=False)
|
128
|
+
show_expanded_label: bool = pydantic.Field(default=False)
|
127
129
|
|
128
130
|
|
129
131
|
class ProgramVisualModel(VersionedModel):
|