classiq 0.77.0__py3-none-any.whl → 0.79.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.
Files changed (69) hide show
  1. classiq/applications/iqae/__init__.py +0 -0
  2. classiq/applications/iqae/iqae.py +207 -0
  3. classiq/execution/__init__.py +1 -1
  4. classiq/interface/_version.py +1 -1
  5. classiq/interface/applications/iqae/__init__.py +0 -0
  6. classiq/interface/applications/iqae/generic_iqae.py +222 -0
  7. classiq/interface/applications/iqae/iqae_result.py +45 -0
  8. classiq/interface/debug_info/debug_info.py +3 -0
  9. classiq/interface/executor/execution_result.py +1 -1
  10. classiq/interface/executor/user_budget.py +1 -1
  11. classiq/interface/generator/arith/arithmetic.py +10 -6
  12. classiq/interface/generator/arith/binary_ops.py +8 -11
  13. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +9 -3
  14. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +90 -23
  15. classiq/interface/generator/expressions/proxies/classical/utils.py +4 -0
  16. classiq/interface/generator/functions/classical_type.py +74 -0
  17. classiq/interface/generator/functions/concrete_types.py +3 -0
  18. classiq/interface/generator/functions/type_name.py +32 -3
  19. classiq/interface/generator/generated_circuit_data.py +21 -8
  20. classiq/interface/helpers/model_normalizer.py +47 -0
  21. classiq/interface/ide/visual_model.py +2 -0
  22. classiq/interface/interface_version.py +1 -1
  23. classiq/interface/model/bounds.py +12 -0
  24. classiq/interface/model/model.py +9 -5
  25. classiq/interface/model/quantum_type.py +25 -3
  26. classiq/interface/model/statement_block.py +2 -0
  27. classiq/model_expansions/atomic_expression_functions_defs.py +20 -6
  28. classiq/model_expansions/closure.py +1 -58
  29. classiq/model_expansions/evaluators/argument_types.py +21 -2
  30. classiq/model_expansions/evaluators/classical_type_inference.py +42 -13
  31. classiq/model_expansions/evaluators/quantum_type_utils.py +31 -3
  32. classiq/model_expansions/function_builder.py +0 -45
  33. classiq/model_expansions/generative_functions.py +1 -1
  34. classiq/model_expansions/interpreters/base_interpreter.py +12 -14
  35. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +0 -17
  36. classiq/model_expansions/interpreters/generative_interpreter.py +9 -0
  37. classiq/model_expansions/quantum_operations/__init__.py +3 -0
  38. classiq/model_expansions/quantum_operations/allocate.py +3 -1
  39. classiq/model_expansions/quantum_operations/assignment_result_processor.py +7 -3
  40. classiq/model_expansions/quantum_operations/bind.py +8 -1
  41. classiq/model_expansions/quantum_operations/bounds.py +30 -0
  42. classiq/model_expansions/quantum_operations/call_emitter.py +116 -37
  43. classiq/model_expansions/quantum_operations/emitter.py +6 -1
  44. classiq/model_expansions/quantum_operations/function_calls_cache.py +91 -0
  45. classiq/model_expansions/quantum_operations/quantum_function_call.py +5 -6
  46. classiq/model_expansions/scope.py +33 -13
  47. classiq/model_expansions/scope_initialization.py +1 -14
  48. classiq/model_expansions/transformers/type_qualifier_inference.py +183 -0
  49. classiq/model_expansions/utils/text_utils.py +4 -2
  50. classiq/model_expansions/visitors/symbolic_param_inference.py +33 -28
  51. classiq/model_expansions/visitors/variable_references.py +39 -43
  52. classiq/open_library/functions/linear_pauli_rotation.py +6 -6
  53. classiq/open_library/functions/state_preparation.py +1 -1
  54. classiq/open_library/functions/utility_functions.py +2 -2
  55. classiq/qmod/builtins/classical_execution_primitives.py +1 -1
  56. classiq/qmod/declaration_inferrer.py +5 -3
  57. classiq/qmod/model_state_container.py +21 -1
  58. classiq/qmod/native/pretty_printer.py +8 -0
  59. classiq/qmod/pretty_print/expression_to_python.py +8 -1
  60. classiq/qmod/pretty_print/pretty_printer.py +7 -0
  61. classiq/qmod/python_classical_type.py +1 -1
  62. classiq/qmod/qmod_parameter.py +43 -7
  63. classiq/qmod/qmod_variable.py +7 -4
  64. classiq/qmod/semantics/annotation/qstruct_annotator.py +15 -4
  65. classiq/qmod/utilities.py +5 -1
  66. {classiq-0.77.0.dist-info → classiq-0.79.0.dist-info}/METADATA +1 -1
  67. {classiq-0.77.0.dist-info → classiq-0.79.0.dist-info}/RECORD +68 -59
  68. classiq/interface/executor/iqae_result.py +0 -17
  69. {classiq-0.77.0.dist-info → classiq-0.79.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
+ )
@@ -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
@@ -3,5 +3,5 @@ from packaging.version import Version
3
3
  # This file was generated automatically
4
4
  # Please don't track in version control (DONTTRACK)
5
5
 
6
- SEMVER_VERSION = '0.77.0'
6
+ SEMVER_VERSION = '0.79.0'
7
7
  VERSION = str(Version(SEMVER_VERSION))
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.0f} | {used:<18.0f} | {currency:<8} |"
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"}
@@ -122,12 +122,16 @@ def get_arithmetic_params(
122
122
  def compute_arithmetic_result_type(
123
123
  expr_str: str, var_types: dict[str, QuantumType], machine_precision: int
124
124
  ) -> QuantumNumeric:
125
- if is_zero(expr_str) or is_bool(expr_str):
126
- return QuantumNumeric(
127
- size=Expression(expr="1"),
128
- is_signed=Expression(expr="False"),
129
- fraction_digits=Expression(expr="0"),
130
- )
125
+ one_qbit_qnum = QuantumNumeric(
126
+ size=Expression(expr="1"),
127
+ is_signed=Expression(expr="False"),
128
+ fraction_digits=Expression(expr="0"),
129
+ )
130
+ if is_zero(expr_str):
131
+ one_qbit_qnum.set_bounds((0, 0))
132
+ return one_qbit_qnum
133
+ if is_bool(expr_str):
134
+ return one_qbit_qnum
131
135
  arith_param = get_arithmetic_params(expr_str, var_types, machine_precision)
132
136
  return register_info_to_quantum_type(
133
137
  arith_param.outputs[ARITHMETIC_EXPRESSION_RESULT_NAME]
@@ -535,14 +535,11 @@ class Multiplier(BinaryOpWithFloatInputs):
535
535
  elif isinstance(right_arg, float) and right_arg == 1.0:
536
536
  assert isinstance(left_arg, RegisterArithmeticInfo)
537
537
  return left_arg.size
538
- largest_bound = max(bounds, key=abs)
539
- integer_places = int(largest_bound).bit_length() + int(largest_bound < 0)
540
- extra_sign_bit = int(
541
- argument_utils.is_signed(left_arg)
542
- and argument_utils.is_signed(right_arg)
543
- and largest_bound > 0
544
- )
545
- return max(1, integer_places + fraction_places + extra_sign_bit)
538
+ if bounds[0] == 0 and bounds[1] == 0:
539
+ return max(1, fraction_places)
540
+
541
+ integer_part_size = number_utils.bounds_to_integer_part_size(*bounds)
542
+ return integer_part_size + fraction_places
546
543
 
547
544
  def garbage_output_size(self) -> pydantic.NonNegativeInt:
548
545
  return max(
@@ -610,7 +607,7 @@ class Power(BinaryOpParams[RegisterArithmeticInfo, pydantic.PositiveInt]):
610
607
  )
611
608
  for bound in self.left_arg.bounds
612
609
  ]
613
- if (self.right_arg % 2) or min(bounds) >= 0:
610
+ if (self.right_arg % 2) or min(bounds) >= 0 or max(bounds) <= 0:
614
611
  return (
615
612
  number_utils.limit_fraction_places(
616
613
  bounds[0] ** self.right_arg,
@@ -633,8 +630,8 @@ class Power(BinaryOpParams[RegisterArithmeticInfo, pydantic.PositiveInt]):
633
630
  fraction_places = min(self.machine_precision, self.expected_fraction_places())
634
631
  bounds = self._get_result_bounds()
635
632
  size = number_utils.bounds_to_integer_part_size(*bounds) + fraction_places
636
- if bounds[0] == bounds[1]:
637
- size = 1
633
+ if bounds[0] == 0 and bounds[1] == 0:
634
+ size = max(1, fraction_places)
638
635
  return RegisterArithmeticInfo(
639
636
  size=size,
640
637
  is_signed=self.left_arg.is_signed and (self.right_arg % 2 == 1),
@@ -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) -> "AnyClassicalValue":
41
- return self.len
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