classiq 0.100.0__py3-none-any.whl → 0.104.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 (95) hide show
  1. classiq/__init__.py +3 -0
  2. classiq/_internals/api_wrapper.py +29 -4
  3. classiq/applications/chemistry/op_utils.py +63 -1
  4. classiq/applications/chemistry/problems.py +18 -6
  5. classiq/applications/chemistry/ucc.py +2 -2
  6. classiq/evaluators/parameter_types.py +1 -4
  7. classiq/evaluators/qmod_annotated_expression.py +1 -1
  8. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +1 -8
  9. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +1 -1
  10. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +2 -2
  11. classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +18 -29
  12. classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +1 -6
  13. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +1 -7
  14. classiq/evaluators/qmod_node_evaluators/utils.py +6 -3
  15. classiq/evaluators/qmod_type_inference/quantum_type_comparison.py +52 -0
  16. classiq/execution/__init__.py +11 -1
  17. classiq/execution/execution_session.py +1 -1
  18. classiq/execution/functions/__init__.py +3 -0
  19. classiq/execution/functions/_logging.py +19 -0
  20. classiq/execution/functions/constants.py +9 -0
  21. classiq/execution/functions/parse_provider_backend.py +90 -0
  22. classiq/execution/functions/sample.py +257 -0
  23. classiq/execution/jobs.py +122 -5
  24. classiq/interface/_version.py +1 -1
  25. classiq/interface/backend/backend_preferences.py +15 -0
  26. classiq/interface/backend/provider_config/providers/aqt.py +1 -1
  27. classiq/interface/backend/provider_config/providers/azure.py +1 -2
  28. classiq/interface/backend/provider_config/providers/ibm.py +1 -1
  29. classiq/interface/backend/quantum_backend_providers.py +3 -0
  30. classiq/interface/exceptions.py +0 -42
  31. classiq/interface/executor/execution_request.py +1 -0
  32. classiq/interface/executor/quantum_code.py +0 -6
  33. classiq/interface/executor/result.py +9 -5
  34. classiq/interface/generator/arith/binary_ops.py +38 -2
  35. classiq/interface/generator/function_param_list.py +4 -2
  36. classiq/interface/generator/functions/builtins/internal_operators.py +5 -9
  37. classiq/interface/generator/functions/classical_type.py +45 -0
  38. classiq/interface/generator/functions/type_name.py +23 -0
  39. classiq/interface/generator/generated_circuit_data.py +0 -2
  40. classiq/interface/generator/generation_request.py +9 -4
  41. classiq/interface/generator/quantum_program.py +8 -36
  42. classiq/interface/generator/types/compilation_metadata.py +9 -0
  43. classiq/interface/hardware.py +1 -0
  44. classiq/interface/helpers/model_normalizer.py +62 -2
  45. classiq/interface/helpers/text_utils.py +17 -6
  46. classiq/interface/interface_version.py +1 -1
  47. classiq/interface/model/invert.py +15 -0
  48. classiq/interface/model/model.py +42 -3
  49. classiq/interface/model/model_visitor.py +4 -2
  50. classiq/interface/model/quantum_function_call.py +17 -5
  51. classiq/interface/model/quantum_type.py +21 -0
  52. classiq/interface/model/statement_block.py +0 -4
  53. classiq/model_expansions/capturing/captured_vars.py +16 -12
  54. classiq/model_expansions/function_builder.py +9 -1
  55. classiq/model_expansions/interpreters/base_interpreter.py +12 -10
  56. classiq/model_expansions/interpreters/generative_interpreter.py +9 -24
  57. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -0
  58. classiq/model_expansions/quantum_operations/assignment_result_processor.py +132 -28
  59. classiq/model_expansions/quantum_operations/bind.py +4 -0
  60. classiq/model_expansions/quantum_operations/call_emitter.py +5 -35
  61. classiq/model_expansions/quantum_operations/emitter.py +1 -4
  62. classiq/model_expansions/quantum_operations/expression_evaluator.py +0 -3
  63. classiq/model_expansions/visitors/uncomputation_signature_inference.py +15 -47
  64. classiq/open_library/functions/__init__.py +42 -27
  65. classiq/open_library/functions/bit_operations.py +30 -0
  66. classiq/open_library/functions/modular_arithmetics.py +597 -0
  67. classiq/open_library/functions/qft_space_arithmetics.py +81 -0
  68. classiq/open_library/functions/state_preparation.py +6 -3
  69. classiq/open_library/functions/utility_functions.py +22 -3
  70. classiq/qmod/builtins/functions/__init__.py +9 -0
  71. classiq/qmod/builtins/functions/arithmetic.py +131 -0
  72. classiq/qmod/builtins/functions/exponentiation.py +34 -4
  73. classiq/qmod/builtins/operations.py +30 -41
  74. classiq/qmod/native/pretty_printer.py +12 -12
  75. classiq/qmod/pretty_print/pretty_printer.py +11 -15
  76. classiq/qmod/qmod_parameter.py +4 -0
  77. classiq/qmod/qmod_variable.py +38 -63
  78. classiq/qmod/quantum_callable.py +8 -2
  79. classiq/qmod/quantum_expandable.py +3 -1
  80. classiq/qmod/quantum_function.py +45 -8
  81. classiq/qmod/semantics/validation/function_name_collisions_validation.py +7 -4
  82. classiq/qmod/semantics/validation/model_validation.py +7 -2
  83. classiq/qmod/symbolic_type.py +4 -2
  84. classiq/qmod/utilities.py +7 -4
  85. classiq/synthesis_action/__init__.py +20 -0
  86. classiq/synthesis_action/actions.py +106 -0
  87. {classiq-0.100.0.dist-info → classiq-0.104.0.dist-info}/METADATA +1 -1
  88. {classiq-0.100.0.dist-info → classiq-0.104.0.dist-info}/RECORD +90 -84
  89. classiq/interface/executor/register_initialization.py +0 -36
  90. classiq/interface/generator/amplitude_loading.py +0 -103
  91. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +0 -77
  92. classiq/open_library/functions/modular_exponentiation.py +0 -272
  93. classiq/open_library/functions/qsvt_temp.py +0 -536
  94. {classiq-0.100.0.dist-info → classiq-0.104.0.dist-info}/WHEEL +0 -0
  95. {classiq-0.100.0.dist-info → classiq-0.104.0.dist-info}/licenses/LICENSE.txt +0 -0
classiq/__init__.py CHANGED
@@ -68,6 +68,8 @@ from classiq.synthesis import (
68
68
  update_execution_preferences,
69
69
  update_preferences,
70
70
  )
71
+ from classiq.synthesis_action import * # noqa: F403
72
+ from classiq.synthesis_action import __all__ as _synthesis_all
71
73
 
72
74
  _application_constructors_all = [
73
75
  "construct_combinatorial_optimization_model",
@@ -124,6 +126,7 @@ __all__ = (
124
126
  + _application_constructors_all
125
127
  + _qmod_all
126
128
  + _execution_all
129
+ + _synthesis_all
127
130
  + _open_library_all
128
131
  + _be_all
129
132
  )
@@ -20,6 +20,7 @@ from classiq.interface.executor.quantum_program_params import (
20
20
  )
21
21
  from classiq.interface.executor.user_budget import UserBudget
22
22
  from classiq.interface.generator import quantum_program as generator_result
23
+ from classiq.interface.generator.generation_request import SynthesisActionsQueryResults
23
24
  from classiq.interface.generator.preferences.qasm_to_qmod_params import QasmToQmodParams
24
25
  from classiq.interface.hardware import HardwareInformation, Provider
25
26
  from classiq.interface.ide.visual_model import ProgramVisualModel
@@ -321,18 +322,42 @@ class ApiWrapper:
321
322
  offset: int,
322
323
  limit: int,
323
324
  http_client: httpx.AsyncClient | None = None,
325
+ **extra_query_params: Any,
324
326
  ) -> execution_request.ExecutionJobsQueryResults:
327
+ params: dict[str, Any] = {
328
+ "offset": offset,
329
+ "limit": limit,
330
+ }
331
+ params.update(extra_query_params)
325
332
  data = await cls._call_task(
326
333
  http_method=HTTPMethod.GET,
327
334
  url=f"{routes.EXECUTION_JOBS_FULL_PATH}",
328
- params={
329
- "offset": offset,
330
- "limit": limit,
331
- },
335
+ params=params,
332
336
  http_client=http_client,
333
337
  )
334
338
  return execution_request.ExecutionJobsQueryResults.model_validate(data)
335
339
 
340
+ @classmethod
341
+ async def call_query_synthesis_actions(
342
+ cls,
343
+ offset: int,
344
+ limit: int,
345
+ http_client: httpx.AsyncClient | None = None,
346
+ **extra_query_params: Any | None,
347
+ ) -> SynthesisActionsQueryResults:
348
+ params: dict[str, Any] = {
349
+ "offset": offset,
350
+ "limit": limit,
351
+ }
352
+ params.update(extra_query_params)
353
+ data = await cls._call_task(
354
+ http_method=HTTPMethod.GET,
355
+ url=f"{routes.TASKS_GENERATE_FULL_PATH}",
356
+ params=params,
357
+ http_client=http_client,
358
+ )
359
+ return SynthesisActionsQueryResults.model_validate(data)
360
+
336
361
  @classmethod
337
362
  async def call_analysis_task(
338
363
  cls,
@@ -1,10 +1,11 @@
1
+ import warnings
1
2
  from typing import cast
2
3
 
3
4
  import numpy as np
4
5
  from openfermion.ops.operators.qubit_operator import QubitOperator
5
6
  from openfermion.utils.operator_utils import count_qubits
6
7
 
7
- from classiq.interface.exceptions import ClassiqValueError
8
+ from classiq.interface.exceptions import ClassiqDeprecationWarning, ClassiqValueError
8
9
 
9
10
  from classiq.qmod.builtins.enums import Pauli
10
11
  from classiq.qmod.builtins.structs import IndexedPauli, SparsePauliOp, SparsePauliTerm
@@ -25,6 +26,13 @@ def _get_n_qubits(qubit_op: QubitOperator, n_qubits: int | None) -> int:
25
26
  def qubit_op_to_pauli_terms(
26
27
  qubit_op: QubitOperator, n_qubits: int | None = None
27
28
  ) -> SparsePauliOp:
29
+ warnings.warn(
30
+ "The function 'qubit_op_to_pauli_terms' is deprecated due to incorrect order "
31
+ "of qubits in its result. It will no longer be supported starting on 2026-01-22 "
32
+ "at the earliest. Please use 'qubit_op_to_qmod' instead.",
33
+ ClassiqDeprecationWarning,
34
+ stacklevel=2,
35
+ )
28
36
  n_qubits = _get_n_qubits(qubit_op, n_qubits)
29
37
  return SparsePauliOp(
30
38
  terms=[
@@ -44,6 +52,60 @@ def qubit_op_to_pauli_terms(
44
52
  )
45
53
 
46
54
 
55
+ def qubit_op_to_qmod(
56
+ qubit_op: QubitOperator, n_qubits: int | None = None
57
+ ) -> SparsePauliOp:
58
+ n_qubits = _get_n_qubits(qubit_op, n_qubits)
59
+ return SparsePauliOp(
60
+ terms=[
61
+ SparsePauliTerm(
62
+ paulis=[ # type:ignore[arg-type]
63
+ IndexedPauli(
64
+ pauli=getattr(Pauli, pauli),
65
+ index=qubit,
66
+ )
67
+ for qubit, pauli in term
68
+ ],
69
+ coefficient=coeff,
70
+ )
71
+ for term, coeff in qubit_op.terms.items()
72
+ ],
73
+ num_qubits=n_qubits,
74
+ )
75
+
76
+
77
+ def qmod_to_qubit_op(operator: SparsePauliOp) -> QubitOperator:
78
+ """
79
+ Transforms Qmod's SparsePauliOp data structure to OpenFermion's QubitOperator data structure.
80
+
81
+ Args:
82
+ operator (SparsePauliOp): The operator to be transformed
83
+
84
+ Returns:
85
+ QubitOperator: The operator in OpenFermion's data structure
86
+ """
87
+
88
+ # Initiating the QubitOperator as the 0 operator
89
+ qo = QubitOperator()
90
+ for sparse_pauli_term in operator.terms:
91
+ # loop over all the IndexedPaulis
92
+ coeff = sparse_pauli_term.coefficient
93
+ if sparse_pauli_term.paulis:
94
+ qo.terms[
95
+ tuple(
96
+ [
97
+ (p.index, p.pauli.name)
98
+ for p in sparse_pauli_term.paulis # type: ignore[attr-defined]
99
+ if p.pauli is not Pauli.I
100
+ ]
101
+ )
102
+ ] = coeff
103
+ # Operator is the identity
104
+ else:
105
+ qo.terms[()] = coeff
106
+ return qo
107
+
108
+
47
109
  _PAULIS_TO_XZ = {"I": (0, 0), "X": (1, 0), "Z": (0, 1), "Y": (1, 1)}
48
110
  _XZ_TO_PAULIS = {(0, 0): "I", (1, 0): "X", (0, 1): "Z", (1, 1): "Y"}
49
111
 
@@ -1,3 +1,4 @@
1
+ import warnings
1
2
  from collections.abc import Sequence
2
3
  from typing import cast
3
4
 
@@ -9,7 +10,7 @@ from openfermion.transforms import (
9
10
  )
10
11
  from openfermion.utils import count_qubits
11
12
 
12
- from classiq.interface.exceptions import ClassiqValueError
13
+ from classiq.interface.exceptions import ClassiqDeprecationWarning, ClassiqValueError
13
14
 
14
15
 
15
16
  class FermionHamiltonianProblem:
@@ -21,7 +22,7 @@ class FermionHamiltonianProblem:
21
22
  Attributes:
22
23
  fermion_hamiltonian (FermionOperator): The fermionic hamiltonian of the problem.
23
24
  Assumed to be in the block-spin labeling.
24
- n_orbitals (int): Number of spatial orbitlas.
25
+ n_orbitals (int): Number of spatial orbitals.
25
26
  n_alpha (int): Number of alpha particles.
26
27
  n_beta (int): Number of beta particles.
27
28
  n_particles (tuple[int, int]): Number of alpha and beta particles.
@@ -116,6 +117,7 @@ class FermionHamiltonianProblem:
116
117
  molecule: MolecularData,
117
118
  first_active_index: int = 0,
118
119
  remove_orbitlas: Sequence[int] | None = None,
120
+ remove_orbitals: Sequence[int] | None = None,
119
121
  op_compression_tol: float = 1e-13,
120
122
  ) -> "FermionHamiltonianProblem":
121
123
  """
@@ -125,12 +127,22 @@ class FermionHamiltonianProblem:
125
127
  molecule (MolecularData): The molecule data.
126
128
  first_active_index (int): The first active index, indicates all prior
127
129
  indices are freezed.
128
- remove_orbitlas (Sequence[int], optional): Active indices to be removed.
130
+ remove_orbitals (Sequence[int], optional): Active indices to be removed.
129
131
  op_compression_tol (float): Tolerance for trimming the fermion operator.
130
132
 
131
133
  Returns:
132
134
  The fermion hamiltonian problem.
133
135
  """
136
+ if remove_orbitlas is not None:
137
+ warnings.warn(
138
+ "The `remove_orbitlas` parameter is deprecated and will not longer be "
139
+ "supported starting on 2026-01-15 at the earliest. Use the "
140
+ "'remove_orbitals' parameter instead",
141
+ ClassiqDeprecationWarning,
142
+ stacklevel=2,
143
+ )
144
+ remove_orbitals = remove_orbitlas
145
+
134
146
  if molecule.n_orbitals is None:
135
147
  raise ClassiqValueError(
136
148
  "The molecular data is not populated. Hint: call `run_pyscf` with the molecule."
@@ -145,8 +157,8 @@ class FermionHamiltonianProblem:
145
157
 
146
158
  freezed_indices = list(range(first_active_index))
147
159
  active_indices = list(range(first_active_index, molecule.n_orbitals))
148
- if remove_orbitlas:
149
- active_indices = list(set(active_indices) - set(remove_orbitlas))
160
+ if remove_orbitals:
161
+ active_indices = list(set(active_indices) - set(remove_orbitals))
150
162
 
151
163
  molecular_hamiltonian = molecule.get_molecular_hamiltonian(
152
164
  occupied_indices=freezed_indices,
@@ -167,7 +179,7 @@ class FermionHamiltonianProblem:
167
179
  f"and {n_particles} electrons. "
168
180
  f"This can happen if too many orbitals were frozen."
169
181
  f"Before freezing number of particle was ({n_alpha, n_beta})."
170
- f"Consider adjusting `first_active_index` or `remove_orbitlas` "
182
+ f"Consider adjusting `first_active_index` or `remove_orbitals` "
171
183
  f"to ensure the active space is non-empty."
172
184
  )
173
185
 
@@ -6,7 +6,7 @@ from openfermion.ops.operators.fermion_operator import FermionOperator
6
6
  from openfermion.ops.operators.qubit_operator import QubitOperator
7
7
 
8
8
  from classiq.applications.chemistry.mapping import FermionToQubitMapper
9
- from classiq.applications.chemistry.op_utils import qubit_op_to_pauli_terms
9
+ from classiq.applications.chemistry.op_utils import qubit_op_to_qmod
10
10
  from classiq.applications.chemistry.problems import FermionHamiltonianProblem
11
11
  from classiq.qmod.builtins.structs import (
12
12
  SparsePauliOp,
@@ -44,7 +44,7 @@ def get_ucc_hamiltonians(
44
44
 
45
45
  n_qubits = mapper.get_num_qubits(problem)
46
46
  return [
47
- qubit_op_to_pauli_terms(q_op, n_qubits)
47
+ qubit_op_to_qmod(q_op, n_qubits)
48
48
  for f_op in f_ops
49
49
  if (q_op := mapper.map(f_op))
50
50
  not in (
@@ -133,9 +133,6 @@ def _evaluate_type_from_arg(
133
133
  # FIXME: Remove suzuki_trotter overloading (CLS-2912)
134
134
  if closure.name == "suzuki_trotter" and parameter.name == "pauli_operator":
135
135
  return parameter
136
- # FIXME: Remove qdrift overloading (CLS-4347)
137
- if closure.name == "qdrift" and parameter.name == "pauli_operator":
138
- return parameter
139
136
  if isinstance(parameter, ClassicalParameterDeclaration):
140
137
  return _evaluate_classical_type_from_arg(parameter, argument, closure)
141
138
  if isinstance(parameter, PortDeclaration):
@@ -257,7 +254,7 @@ def _evaluate_op_type_from_arg(
257
254
  first_lambda = arg_val[0]
258
255
  if isinstance(first_lambda, FunctionClosure):
259
256
  _raise_argument_type_error(
260
- arg_val,
257
+ f"[{', '.join(['<lambda>'] * len(arg_val))}]",
261
258
  first_lambda.as_operand_declaration(is_list=True),
262
259
  parameter.name,
263
260
  parameter,
@@ -207,7 +207,7 @@ class QmodAnnotatedExpression:
207
207
  node = id(node)
208
208
  return node in self._quantum_subscripts
209
209
 
210
- def get_quantum_subcript(
210
+ def get_quantum_subscript(
211
211
  self, node: ast.AST | QmodExprNodeId
212
212
  ) -> QuantumSubscriptAnnotation:
213
213
  if isinstance(node, ast.AST):
@@ -77,7 +77,6 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
77
77
  self,
78
78
  expr_val: QmodAnnotatedExpression,
79
79
  *,
80
- treat_qnum_as_float: bool = False,
81
80
  machine_precision: int = DEFAULT_MACHINE_PRECISION,
82
81
  classical_struct_declarations: Sequence[StructDeclaration] | None = None,
83
82
  enum_declarations: Sequence[EnumDeclaration] | None = None,
@@ -88,7 +87,6 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
88
87
  scope: Mapping[str, Any] | None = None,
89
88
  ) -> None:
90
89
  self._expr_val = expr_val
91
- self._treat_qnum_as_float = treat_qnum_as_float
92
90
  self._machine_precision = machine_precision
93
91
  self._classical_struct_decls = nameables_to_dict(
94
92
  classical_struct_declarations or []
@@ -114,9 +112,7 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
114
112
 
115
113
  def visit_BinOp(self, node: ast.BinOp) -> None:
116
114
  super().generic_visit(node)
117
- eval_binary_op(
118
- self._expr_val, node, self._treat_qnum_as_float, self._machine_precision
119
- )
115
+ eval_binary_op(self._expr_val, node, self._machine_precision)
120
116
 
121
117
  def visit_UnaryOp(self, node: ast.UnaryOp) -> None:
122
118
  super().generic_visit(node)
@@ -164,7 +160,6 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
164
160
  self._expr_val,
165
161
  node,
166
162
  func_name,
167
- self._treat_qnum_as_float,
168
163
  self._machine_precision,
169
164
  )
170
165
  return
@@ -256,7 +251,6 @@ class QmodExpressionEvaluator(ast.NodeVisitor):
256
251
  def evaluate_qmod_expression(
257
252
  expr: str,
258
253
  *,
259
- treat_qnum_as_float: bool = False,
260
254
  machine_precision: int = DEFAULT_MACHINE_PRECISION,
261
255
  classical_struct_declarations: Sequence[StructDeclaration] | None = None,
262
256
  enum_declarations: Sequence[EnumDeclaration] | None = None,
@@ -270,7 +264,6 @@ def evaluate_qmod_expression(
270
264
  expr_value = QmodAnnotatedExpression(expr_ast)
271
265
  QmodExpressionEvaluator(
272
266
  expr_value,
273
- treat_qnum_as_float=treat_qnum_as_float,
274
267
  machine_precision=machine_precision,
275
268
  classical_struct_declarations=classical_struct_declarations,
276
269
  enum_declarations=enum_declarations,
@@ -122,7 +122,7 @@ class _InverseVarMaskTransformer(OutOfPlaceNodeTransformer):
122
122
 
123
123
  class _SympyCompatibilityTransformer(OutOfPlaceNodeTransformer):
124
124
  def visit_BoolOp(self, node: ast.BoolOp) -> ast.Call:
125
- if len(node.values) != 2:
125
+ if len(node.values) < 2:
126
126
  raise ClassiqInternalExpansionError
127
127
  node = cast(ast.BoolOp, self.generic_visit(node))
128
128
  if isinstance(node.op, ast.Or):
@@ -107,7 +107,7 @@ def _eval_type_attribute(
107
107
  if isinstance(subject_type, QuantumNumeric):
108
108
  if attr == "is_signed":
109
109
  expr_val.set_type(node, Bool())
110
- if subject_type.has_sign:
110
+ if subject_type.has_constant_sign:
111
111
  expr_val.set_value(node, subject_type.sign_value)
112
112
  _remove_quantum_var(expr_val, subject)
113
113
  elif subject_type.has_size_in_bits:
@@ -120,7 +120,7 @@ def _eval_type_attribute(
120
120
  return
121
121
  if attr == "fraction_digits":
122
122
  expr_val.set_type(node, Integer())
123
- if subject_type.has_fraction_digits:
123
+ if subject_type.has_constant_fraction_digits:
124
124
  expr_val.set_value(node, subject_type.fraction_digits_value)
125
125
  _remove_quantum_var(expr_val, subject)
126
126
  elif subject_type.has_size_in_bits:
@@ -50,7 +50,6 @@ def _validate_binary_op(
50
50
  op: ast.AST,
51
51
  left_type: QmodType,
52
52
  right_type: QmodType,
53
- treat_qnum_as_float: bool,
54
53
  ) -> None:
55
54
  if not _binary_op_allowed(left_type, right_type, op):
56
55
  raise ClassiqExpansionError(
@@ -76,20 +75,19 @@ def _validate_binary_op(
76
75
  f"Binary operation {type(op).__name__!r} is not supported"
77
76
  )
78
77
 
79
- if not treat_qnum_as_float:
80
- if isinstance(op, ast.FloorDiv) and (
81
- not is_classical_type(left_type) or not is_classical_type(right_type)
82
- ):
83
- raise ClassiqExpansionError(
84
- f"{type(op).__name__!r} with quantum variables is not supported"
85
- )
78
+ if isinstance(op, ast.FloorDiv) and (
79
+ not is_classical_type(left_type) or not is_classical_type(right_type)
80
+ ):
81
+ raise ClassiqExpansionError(
82
+ f"{type(op).__name__!r} with quantum variables is not supported"
83
+ )
86
84
 
87
- if not is_classical_type(right_type) and isinstance(
88
- op, (ast.Div, ast.Mod, ast.Pow, ast.LShift, ast.RShift)
89
- ):
90
- raise ClassiqExpansionError(
91
- f"Right-hand side of binary operation {type(op).__name__!r} must be classical numeric value"
92
- )
85
+ if not is_classical_type(right_type) and isinstance(
86
+ op, (ast.Div, ast.Mod, ast.Pow, ast.LShift, ast.RShift)
87
+ ):
88
+ raise ClassiqExpansionError(
89
+ f"Right-hand side of binary operation {type(op).__name__!r} must be classical numeric value"
90
+ )
93
91
 
94
92
 
95
93
  def _infer_binary_op_type(
@@ -98,7 +96,6 @@ def _infer_binary_op_type(
98
96
  left_type: QmodType,
99
97
  right_type: QmodType,
100
98
  machine_precision: int,
101
- treat_qnum_as_float: bool,
102
99
  ) -> QmodType:
103
100
  op = node.op
104
101
 
@@ -113,19 +110,13 @@ def _infer_binary_op_type(
113
110
  return Integer()
114
111
  return Real()
115
112
 
116
- left_attrs = get_numeric_attrs(
117
- expr_val, node.left, left_type, machine_precision, treat_qnum_as_float
118
- )
119
- right_attrs = get_numeric_attrs(
120
- expr_val, node.right, right_type, machine_precision, treat_qnum_as_float
121
- )
113
+ left_attrs = get_numeric_attrs(expr_val, node.left, left_type, machine_precision)
114
+ right_attrs = get_numeric_attrs(expr_val, node.right, right_type, machine_precision)
122
115
 
123
116
  if left_attrs is None or right_attrs is None:
124
117
  return QuantumNumeric()
125
118
 
126
- right_value = get_classical_value_for_arithmetic(
127
- expr_val, node.right, right_type, treat_qnum_as_float
128
- )
119
+ right_value = get_classical_value_for_arithmetic(expr_val, node.right, right_type)
129
120
 
130
121
  if isinstance(op, ast.Add):
131
122
  result_attrs = compute_result_attrs_add(
@@ -157,14 +148,14 @@ def _infer_binary_op_type(
157
148
  return QuantumNumeric()
158
149
 
159
150
  elif isinstance(op, ast.Mod):
160
- if right_value is None or treat_qnum_as_float:
151
+ if right_value is None:
161
152
  return QuantumNumeric()
162
153
  result_attrs = compute_result_attrs_modulo(
163
154
  left_attrs, right_attrs, machine_precision
164
155
  )
165
156
 
166
157
  elif isinstance(op, ast.Pow):
167
- if right_value is None or treat_qnum_as_float:
158
+ if right_value is None:
168
159
  return QuantumNumeric()
169
160
  result_attrs = compute_result_attrs_power(
170
161
  left_attrs, right_attrs, machine_precision
@@ -252,7 +243,6 @@ def _eval_binary_op_constant(
252
243
  def eval_binary_op(
253
244
  expr_val: QmodAnnotatedExpression,
254
245
  node: ast.BinOp,
255
- treat_qnum_as_float: bool,
256
246
  machine_precision: int,
257
247
  ) -> None:
258
248
  left = node.left
@@ -261,7 +251,7 @@ def eval_binary_op(
261
251
 
262
252
  left_type = expr_val.get_type(left)
263
253
  right_type = expr_val.get_type(right)
264
- _validate_binary_op(op, left_type, right_type, treat_qnum_as_float)
254
+ _validate_binary_op(op, left_type, right_type)
265
255
 
266
256
  inferred_type = _infer_binary_op_type(
267
257
  expr_val,
@@ -269,7 +259,6 @@ def eval_binary_op(
269
259
  left_type,
270
260
  right_type,
271
261
  machine_precision,
272
- treat_qnum_as_float,
273
262
  )
274
263
  expr_val.set_type(node, inferred_type)
275
264
 
@@ -31,7 +31,6 @@ def _infer_min_max_op_type(
31
31
  node: ast.Call,
32
32
  func_name: str,
33
33
  args_types: list[QmodType],
34
- treat_qnum_as_float: bool,
35
34
  machine_precision: int,
36
35
  ) -> QmodType:
37
36
  if all(is_classical_type(arg_type) for arg_type in args_types):
@@ -41,9 +40,7 @@ def _infer_min_max_op_type(
41
40
 
42
41
  args_attrs: list[NumericAttributes] = []
43
42
  for arg, arg_type in zip(node.args, args_types):
44
- attrs = get_numeric_attrs(
45
- expr_val, arg, arg_type, machine_precision, treat_qnum_as_float
46
- )
43
+ attrs = get_numeric_attrs(expr_val, arg, arg_type, machine_precision)
47
44
  if attrs is None:
48
45
  return QuantumNumeric()
49
46
  args_attrs.append(attrs)
@@ -62,7 +59,6 @@ def eval_min_max_op(
62
59
  expr_val: QmodAnnotatedExpression,
63
60
  node: ast.Call,
64
61
  func_name: str,
65
- treat_qnum_as_float: bool,
66
62
  machine_precision: int,
67
63
  ) -> None:
68
64
  if len(node.args) < 1:
@@ -79,7 +75,6 @@ def eval_min_max_op(
79
75
  node,
80
76
  func_name,
81
77
  args_types,
82
- treat_qnum_as_float,
83
78
  machine_precision,
84
79
  )
85
80
  expr_val.set_type(node, inferred_type)
@@ -21,14 +21,11 @@ def get_numeric_attrs(
21
21
  node: ast.AST,
22
22
  qmod_type: QmodType,
23
23
  machine_precision: int,
24
- treat_qnum_as_float: bool,
25
24
  ) -> NumericAttributes | None:
26
25
  if isinstance(qmod_type, Bool):
27
26
  return NumericAttributes.from_bounds(0, 1, 0, machine_precision)
28
27
  if is_classical_type(qmod_type):
29
- value = get_classical_value_for_arithmetic(
30
- expr_val, node, qmod_type, treat_qnum_as_float
31
- )
28
+ value = get_classical_value_for_arithmetic(expr_val, node, qmod_type)
32
29
  if value is None:
33
30
  return None
34
31
  return NumericAttributes.from_constant(value, machine_precision)
@@ -44,7 +41,6 @@ def get_classical_value_for_arithmetic(
44
41
  expr_val: QmodAnnotatedExpression,
45
42
  node: ast.AST,
46
43
  qmod_type: QmodType,
47
- treat_qnum_as_float: bool,
48
44
  ) -> float | None:
49
45
  if not is_classical_type(qmod_type):
50
46
  return None
@@ -55,8 +51,6 @@ def get_classical_value_for_arithmetic(
55
51
  if isinstance(value, sympy.Basic):
56
52
  value = get_sympy_val(value)
57
53
  if not isinstance(value, (int, float)):
58
- if treat_qnum_as_float and isinstance(value, complex):
59
- return None
60
54
  raise ClassiqExpansionError(
61
55
  "Arithmetic of quantum variables and non-real values is not supported"
62
56
  )
@@ -2,7 +2,10 @@ from typing import Union, cast
2
2
 
3
3
  import sympy
4
4
 
5
- from classiq.interface.exceptions import ClassiqInternalExpansionError
5
+ from classiq.interface.exceptions import (
6
+ ClassiqExpansionError,
7
+ ClassiqInternalExpansionError,
8
+ )
6
9
  from classiq.interface.generator.functions.classical_type import (
7
10
  Bool,
8
11
  ClassicalArray,
@@ -108,7 +111,7 @@ def get_sympy_val(val: sympy.Basic) -> bool | int | float | complex:
108
111
  hasattr(val, "is_imaginary") and val.is_imaginary
109
112
  ):
110
113
  return complex(val)
111
- raise ClassiqInternalExpansionError("Unidentified sympy value")
114
+ raise ClassiqExpansionError(f"{str(val)!r} is not a number")
112
115
 
113
116
 
114
117
  def get_sympy_type(val: sympy.Basic) -> ClassicalType:
@@ -124,4 +127,4 @@ def get_sympy_type(val: sympy.Basic) -> ClassicalType:
124
127
  or (hasattr(val, "is_imaginary") and val.is_imaginary)
125
128
  ):
126
129
  return Real()
127
- raise ClassiqInternalExpansionError("Unidentified sympy value")
130
+ raise ClassiqExpansionError(f"{str(val)!r} is not a number")
@@ -0,0 +1,52 @@
1
+ from classiq.interface.exceptions import (
2
+ ClassiqInternalExpansionError,
3
+ )
4
+ from classiq.interface.generator.expressions.expression import Expression
5
+ from classiq.interface.generator.functions.type_name import TypeName
6
+ from classiq.interface.model.quantum_type import (
7
+ QuantumBit,
8
+ QuantumBitvector,
9
+ QuantumNumeric,
10
+ QuantumType,
11
+ )
12
+
13
+
14
+ def _compare_expressions(expr1: Expression | None, expr2: Expression | None) -> bool:
15
+ if expr1 is None:
16
+ return expr2 is None
17
+ if expr2 is None:
18
+ return False
19
+ return expr1.expr == expr2.expr
20
+
21
+
22
+ def compare_quantum_types(type_1: QuantumType, type_2: QuantumType) -> bool:
23
+ for qmod_type in (type_1, type_2):
24
+ if isinstance(qmod_type, TypeName) and not qmod_type.has_fields:
25
+ raise ClassiqInternalExpansionError("Quantum struct expected")
26
+ if isinstance(type_1, QuantumBit):
27
+ return isinstance(type_2, QuantumBit)
28
+ if isinstance(type_1, QuantumNumeric):
29
+ return (
30
+ isinstance(type_2, QuantumNumeric)
31
+ and _compare_expressions(type_1.size, type_2.size)
32
+ and _compare_expressions(type_1.is_signed, type_2.is_signed)
33
+ and _compare_expressions(type_1.fraction_digits, type_2.fraction_digits)
34
+ )
35
+ if isinstance(type_1, QuantumBitvector):
36
+ return (
37
+ isinstance(type_2, QuantumBitvector)
38
+ and _compare_expressions(type_1.length, type_2.length)
39
+ and compare_quantum_types(type_1.element_type, type_2.element_type)
40
+ )
41
+ if isinstance(type_1, TypeName):
42
+ return (
43
+ isinstance(type_2, TypeName)
44
+ and type_1.name == type_2.name
45
+ and all(
46
+ compare_quantum_types(field_type_1, field_type_2)
47
+ for field_type_1, field_type_2 in zip(
48
+ type_1.fields.values(), type_2.fields.values(), strict=True
49
+ )
50
+ )
51
+ )
52
+ raise ClassiqInternalExpansionError(f"Unexpected type {type(type_1).__name__}")
@@ -8,7 +8,14 @@ from ..interface.executor.execution_preferences import __all__ as _ep_all
8
8
  from ..interface.executor.result import ExecutionDetails
9
9
  from ..interface.executor.vqe_result import VQESolverResult
10
10
  from .execution_session import ExecutionSession
11
- from .jobs import ExecutionJob, get_execution_jobs, get_execution_jobs_async
11
+ from .jobs import (
12
+ ExecutionJob,
13
+ ExecutionJobFilters,
14
+ get_execution_actions,
15
+ get_execution_actions_async,
16
+ get_execution_jobs,
17
+ get_execution_jobs_async,
18
+ )
12
19
  from .qnn import execute_qnn
13
20
  from .user_budgets import (
14
21
  clear_budget_limit,
@@ -28,6 +35,9 @@ __all__ = (
28
35
  "VQESolverResult",
29
36
  "IQAEResult",
30
37
  "ExecutionJob",
38
+ "ExecutionJobFilters",
39
+ "get_execution_actions",
40
+ "get_execution_actions_async",
31
41
  "get_execution_jobs",
32
42
  "get_execution_jobs_async",
33
43
  "ExecutionSession",
@@ -365,7 +365,7 @@ class ExecutionSession:
365
365
  A list of tuples, each containing the estimated cost and the corresponding parameters for that iteration. `cost` is a float, and `parameters` is a dictionary matching the execution parameter format.
366
366
 
367
367
  See Also:
368
- The [Classiq Tutorial](https://docs.classiq.io/latest/getting-started/classiq_tutorial/execution_tutorial_part2/) has examples on using this method in variational quantum algorithms.
368
+ The [Execution Tutorial](https://docs.classiq.io/latest/getting-started/classiq_tutorial/execution_tutorial_part2/) has examples on using this method in variational quantum algorithms.
369
369
  More information about [Hamiltonians](https://docs.classiq.io/latest/qmod-reference/language-reference/classical-types/#hamiltonians).
370
370
  """
371
371
  _hamiltonian_deprecation_warning(cost_function)
@@ -0,0 +1,3 @@
1
+ from classiq.execution.functions.sample import _new_sample
2
+
3
+ __all__ = ["_new_sample"]