classiq 0.37.0__py3-none-any.whl → 0.38.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 (228) hide show
  1. classiq/__init__.py +2 -2
  2. classiq/_analyzer_extras/_ipywidgets_async_extension.py +1 -1
  3. classiq/_analyzer_extras/interactive_hardware.py +3 -3
  4. classiq/_internals/api_wrapper.py +24 -16
  5. classiq/_internals/async_utils.py +1 -74
  6. classiq/_internals/authentication/device.py +9 -4
  7. classiq/_internals/authentication/password_manager.py +25 -10
  8. classiq/_internals/authentication/token_manager.py +2 -2
  9. classiq/_internals/client.py +13 -5
  10. classiq/_internals/jobs.py +10 -7
  11. classiq/analyzer/analyzer.py +26 -28
  12. classiq/analyzer/analyzer_utilities.py +5 -5
  13. classiq/analyzer/rb.py +4 -5
  14. classiq/analyzer/show_interactive_hack.py +6 -6
  15. classiq/applications/benchmarking/mirror_benchmarking.py +9 -6
  16. classiq/applications/combinatorial_optimization/__init__.py +5 -0
  17. classiq/applications/qnn/circuit_utils.py +2 -2
  18. classiq/applications/qnn/gradients/quantum_gradient.py +2 -2
  19. classiq/applications/qnn/types.py +2 -2
  20. classiq/applications/qsvm/qsvm.py +4 -7
  21. classiq/applications/qsvm/qsvm_data_generation.py +2 -5
  22. classiq/applications_model_constructors/__init__.py +9 -1
  23. classiq/applications_model_constructors/chemistry_model_constructor.py +9 -16
  24. classiq/applications_model_constructors/combinatorial_helpers/__init__.py +0 -0
  25. classiq/applications_model_constructors/combinatorial_helpers/allowed_constraints.py +20 -0
  26. classiq/applications_model_constructors/combinatorial_helpers/arithmetic/__init__.py +0 -0
  27. classiq/applications_model_constructors/combinatorial_helpers/arithmetic/arithmetic_expression.py +35 -0
  28. classiq/applications_model_constructors/combinatorial_helpers/arithmetic/isolation.py +42 -0
  29. classiq/applications_model_constructors/combinatorial_helpers/combinatorial_problem_utils.py +130 -0
  30. classiq/applications_model_constructors/combinatorial_helpers/encoding_mapping.py +107 -0
  31. classiq/applications_model_constructors/combinatorial_helpers/encoding_utils.py +122 -0
  32. classiq/applications_model_constructors/combinatorial_helpers/memory.py +79 -0
  33. classiq/applications_model_constructors/combinatorial_helpers/multiple_comp_basis_sp.py +34 -0
  34. classiq/applications_model_constructors/combinatorial_helpers/optimization_model.py +166 -0
  35. classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/__init__.py +0 -0
  36. classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +31 -0
  37. classiq/applications_model_constructors/combinatorial_helpers/pauli_helpers/pauli_utils.py +65 -0
  38. classiq/applications_model_constructors/combinatorial_helpers/py.typed +0 -0
  39. classiq/applications_model_constructors/combinatorial_helpers/pyomo_utils.py +243 -0
  40. classiq/applications_model_constructors/combinatorial_helpers/sympy_utils.py +22 -0
  41. classiq/applications_model_constructors/combinatorial_helpers/transformations/__init__.py +0 -0
  42. classiq/applications_model_constructors/combinatorial_helpers/transformations/encoding.py +194 -0
  43. classiq/applications_model_constructors/combinatorial_helpers/transformations/fixed_variables.py +144 -0
  44. classiq/applications_model_constructors/combinatorial_helpers/transformations/ising_converter.py +124 -0
  45. classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty.py +32 -0
  46. classiq/applications_model_constructors/combinatorial_helpers/transformations/penalty_support.py +41 -0
  47. classiq/applications_model_constructors/combinatorial_helpers/transformations/sign_seperation.py +75 -0
  48. classiq/applications_model_constructors/combinatorial_helpers/transformations/slack_variables.py +90 -0
  49. classiq/applications_model_constructors/combinatorial_optimization_model_constructor.py +48 -91
  50. classiq/applications_model_constructors/finance_model_constructor.py +4 -17
  51. classiq/applications_model_constructors/grover_model_constructor.py +20 -91
  52. classiq/applications_model_constructors/libraries/qmci_library.py +17 -19
  53. classiq/builtin_functions/standard_gates.py +1 -1
  54. classiq/exceptions.py +43 -1
  55. classiq/executor.py +10 -9
  56. classiq/interface/_version.py +1 -1
  57. classiq/interface/analyzer/analysis_params.py +6 -3
  58. classiq/interface/analyzer/result.py +12 -4
  59. classiq/interface/applications/qsvm.py +13 -1
  60. classiq/interface/backend/backend_preferences.py +4 -2
  61. classiq/interface/backend/pydantic_backend.py +3 -1
  62. classiq/interface/backend/quantum_backend_providers.py +1 -0
  63. classiq/interface/chemistry/fermionic_operator.py +15 -13
  64. classiq/interface/chemistry/ground_state_problem.py +18 -3
  65. classiq/interface/chemistry/molecule.py +8 -6
  66. classiq/interface/chemistry/operator.py +20 -14
  67. classiq/interface/combinatorial_optimization/examples/ascending_sequence.py +1 -1
  68. classiq/interface/combinatorial_optimization/examples/greater_than_ilp.py +1 -1
  69. classiq/interface/combinatorial_optimization/examples/ilp.py +2 -1
  70. classiq/interface/combinatorial_optimization/examples/integer_portfolio_optimization.py +2 -2
  71. classiq/interface/combinatorial_optimization/examples/mds.py +2 -1
  72. classiq/interface/combinatorial_optimization/examples/mht.py +3 -3
  73. classiq/interface/combinatorial_optimization/examples/mis.py +4 -1
  74. classiq/interface/combinatorial_optimization/examples/mvc.py +2 -1
  75. classiq/interface/combinatorial_optimization/examples/set_cover.py +2 -1
  76. classiq/interface/combinatorial_optimization/examples/tsp.py +4 -3
  77. classiq/interface/combinatorial_optimization/examples/tsp_digraph.py +6 -2
  78. classiq/interface/combinatorial_optimization/mht_qaoa_input.py +9 -3
  79. classiq/interface/executor/aws_execution_cost.py +4 -3
  80. classiq/interface/executor/estimation.py +2 -2
  81. classiq/interface/executor/execution_preferences.py +5 -34
  82. classiq/interface/executor/execution_request.py +19 -17
  83. classiq/interface/executor/optimizer_preferences.py +22 -13
  84. classiq/interface/executor/{quantum_program.py → quantum_code.py} +21 -15
  85. classiq/interface/executor/quantum_instruction_set.py +2 -1
  86. classiq/interface/executor/register_initialization.py +1 -3
  87. classiq/interface/executor/result.py +41 -10
  88. classiq/interface/executor/vqe_result.py +1 -1
  89. classiq/interface/finance/function_input.py +17 -4
  90. classiq/interface/finance/gaussian_model_input.py +3 -1
  91. classiq/interface/finance/log_normal_model_input.py +3 -1
  92. classiq/interface/finance/model_input.py +2 -0
  93. classiq/interface/generator/amplitude_loading.py +6 -3
  94. classiq/interface/generator/application_apis/__init__.py +1 -0
  95. classiq/interface/generator/application_apis/arithmetic_declarations.py +14 -0
  96. classiq/interface/generator/arith/argument_utils.py +14 -4
  97. classiq/interface/generator/arith/arithmetic.py +3 -1
  98. classiq/interface/generator/arith/arithmetic_arg_type_validator.py +12 -13
  99. classiq/interface/generator/arith/arithmetic_expression_abc.py +4 -1
  100. classiq/interface/generator/arith/arithmetic_expression_parser.py +8 -2
  101. classiq/interface/generator/arith/arithmetic_expression_validator.py +16 -2
  102. classiq/interface/generator/arith/arithmetic_operations.py +5 -10
  103. classiq/interface/generator/arith/ast_node_rewrite.py +1 -1
  104. classiq/interface/generator/arith/binary_ops.py +202 -54
  105. classiq/interface/generator/arith/extremum_operations.py +5 -3
  106. classiq/interface/generator/arith/logical_ops.py +4 -2
  107. classiq/interface/generator/arith/machine_precision.py +3 -0
  108. classiq/interface/generator/arith/number_utils.py +34 -44
  109. classiq/interface/generator/arith/register_user_input.py +21 -1
  110. classiq/interface/generator/arith/unary_ops.py +16 -25
  111. classiq/interface/generator/chemistry_function_params.py +4 -4
  112. classiq/interface/generator/commuting_pauli_exponentiation.py +3 -1
  113. classiq/interface/generator/compiler_keywords.py +4 -0
  114. classiq/interface/generator/complex_type.py +3 -10
  115. classiq/interface/generator/control_state.py +5 -3
  116. classiq/interface/generator/credit_risk_example/linear_gci.py +10 -3
  117. classiq/interface/generator/credit_risk_example/weighted_adder.py +14 -4
  118. classiq/interface/generator/expressions/atomic_expression_functions.py +5 -3
  119. classiq/interface/generator/expressions/evaluated_expression.py +18 -4
  120. classiq/interface/generator/expressions/expression.py +1 -1
  121. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +33 -0
  122. classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
  123. classiq/interface/generator/finance.py +1 -1
  124. classiq/interface/generator/function_params.py +7 -6
  125. classiq/interface/generator/functions/__init__.py +1 -1
  126. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +505 -138
  127. classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +25 -99
  128. classiq/interface/generator/functions/foreign_function_definition.py +12 -4
  129. classiq/interface/generator/functions/function_implementation.py +8 -4
  130. classiq/interface/generator/functions/native_function_definition.py +4 -2
  131. classiq/interface/generator/functions/register.py +4 -2
  132. classiq/interface/generator/functions/register_mapping_data.py +14 -10
  133. classiq/interface/generator/generated_circuit_data.py +2 -2
  134. classiq/interface/generator/grover_operator.py +5 -3
  135. classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +5 -1
  136. classiq/interface/generator/hardware/hardware_data.py +6 -4
  137. classiq/interface/generator/hardware_efficient_ansatz.py +25 -8
  138. classiq/interface/generator/hartree_fock.py +3 -1
  139. classiq/interface/generator/linear_pauli_rotations.py +3 -1
  140. classiq/interface/generator/mcu.py +5 -3
  141. classiq/interface/generator/mcx.py +7 -5
  142. classiq/interface/generator/model/constraints.py +2 -1
  143. classiq/interface/generator/model/model.py +11 -19
  144. classiq/interface/generator/model/preferences/preferences.py +4 -3
  145. classiq/interface/generator/oracles/custom_oracle.py +4 -2
  146. classiq/interface/generator/oracles/oracle_abc.py +2 -2
  147. classiq/interface/generator/qpe.py +6 -4
  148. classiq/interface/generator/qsvm.py +5 -8
  149. classiq/interface/generator/quantum_function_call.py +21 -16
  150. classiq/interface/generator/{generated_circuit.py → quantum_program.py} +10 -14
  151. classiq/interface/generator/range_types.py +3 -1
  152. classiq/interface/generator/slice_parsing_utils.py +8 -3
  153. classiq/interface/generator/standard_gates/controlled_standard_gates.py +4 -2
  154. classiq/interface/generator/state_preparation/metrics.py +2 -1
  155. classiq/interface/generator/state_preparation/state_preparation.py +7 -5
  156. classiq/interface/generator/state_propagator.py +16 -5
  157. classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
  158. classiq/interface/generator/types/struct_declaration.py +8 -3
  159. classiq/interface/generator/ucc.py +6 -4
  160. classiq/interface/generator/unitary_gate.py +7 -3
  161. classiq/interface/generator/validations/flow_graph.py +6 -4
  162. classiq/interface/generator/validations/validator_functions.py +6 -4
  163. classiq/interface/hardware.py +2 -2
  164. classiq/interface/helpers/custom_encoders.py +3 -0
  165. classiq/interface/helpers/pydantic_model_helpers.py +0 -6
  166. classiq/interface/helpers/validation_helpers.py +1 -1
  167. classiq/interface/helpers/versioned_model.py +4 -1
  168. classiq/interface/ide/show.py +2 -2
  169. classiq/interface/jobs.py +72 -3
  170. classiq/interface/model/bind_operation.py +18 -11
  171. classiq/interface/model/call_synthesis_data.py +68 -0
  172. classiq/interface/model/inplace_binary_operation.py +2 -2
  173. classiq/interface/model/model.py +27 -21
  174. classiq/interface/model/native_function_definition.py +3 -5
  175. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +9 -4
  176. classiq/interface/model/quantum_expressions/control_state.py +2 -2
  177. classiq/interface/model/quantum_function_call.py +25 -139
  178. classiq/interface/model/quantum_function_declaration.py +8 -0
  179. classiq/interface/model/quantum_if_operation.py +2 -3
  180. classiq/interface/model/quantum_lambda_function.py +64 -0
  181. classiq/interface/model/quantum_type.py +57 -56
  182. classiq/interface/model/quantum_variable_declaration.py +1 -1
  183. classiq/interface/model/statement_block.py +32 -0
  184. classiq/interface/model/validations/handles_validator.py +14 -12
  185. classiq/interface/model/within_apply_operation.py +11 -0
  186. classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +4 -1
  187. classiq/interface/server/routes.py +5 -0
  188. classiq/model/function_handler.py +5 -9
  189. classiq/model/model.py +2 -19
  190. classiq/qmod/__init__.py +13 -6
  191. classiq/qmod/builtins/classical_execution_primitives.py +27 -36
  192. classiq/qmod/builtins/classical_functions.py +24 -14
  193. classiq/qmod/builtins/functions.py +162 -145
  194. classiq/qmod/builtins/operations.py +24 -35
  195. classiq/qmod/builtins/structs.py +15 -15
  196. classiq/qmod/cfunc.py +42 -0
  197. classiq/qmod/classical_function.py +6 -14
  198. classiq/qmod/declaration_inferrer.py +12 -21
  199. classiq/qmod/expression_query.py +23 -0
  200. classiq/qmod/model_state_container.py +2 -0
  201. classiq/qmod/native/__init__.py +0 -0
  202. classiq/qmod/native/expression_to_qmod.py +189 -0
  203. classiq/qmod/native/pretty_printer.py +311 -0
  204. classiq/qmod/qfunc.py +27 -0
  205. classiq/qmod/qmod_constant.py +76 -0
  206. classiq/qmod/qmod_parameter.py +34 -12
  207. classiq/qmod/qmod_struct.py +3 -3
  208. classiq/qmod/qmod_variable.py +102 -18
  209. classiq/qmod/quantum_expandable.py +16 -16
  210. classiq/qmod/quantum_function.py +37 -8
  211. classiq/qmod/symbolic.py +47 -4
  212. classiq/qmod/symbolic_expr.py +9 -0
  213. classiq/qmod/utilities.py +13 -0
  214. classiq/qmod/write_qmod.py +39 -0
  215. classiq/quantum_functions/__init__.py +2 -2
  216. classiq/quantum_functions/annotation_parser.py +9 -11
  217. classiq/quantum_functions/function_parser.py +1 -1
  218. classiq/quantum_functions/quantum_function.py +3 -3
  219. classiq/quantum_register.py +17 -9
  220. {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/METADATA +2 -1
  221. {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/RECORD +222 -186
  222. {classiq-0.37.0.dist-info → classiq-0.38.0.dist-info}/WHEEL +1 -1
  223. classiq/interface/generator/expressions/qmod_qnum_proxy.py +0 -22
  224. classiq/interface/generator/types/builtin_struct_declarations/qaoa_declarations.py +0 -23
  225. classiq/interface/generator/types/combinatorial_problem.py +0 -26
  226. classiq/interface/model/numeric_reinterpretation.py +0 -25
  227. classiq/interface/model/operator_synthesis_data.py +0 -48
  228. classiq/model/function_handler.pyi +0 -152
@@ -1,40 +1,37 @@
1
1
  from typing import Callable, List, Union
2
2
 
3
3
  from classiq.interface.generator.expressions.expression import Expression
4
- from classiq.interface.generator.functions.core_lib_declarations.quantum_operators import (
5
- OPERAND_FIELD_NAME,
6
- )
7
4
  from classiq.interface.model.bind_operation import BindOperation
8
5
  from classiq.interface.model.inplace_binary_operation import (
9
6
  BinaryOperation,
10
7
  InplaceBinaryOperation,
11
8
  )
12
- from classiq.interface.model.numeric_reinterpretation import (
13
- NumericReinterpretationOperation,
14
- )
9
+ from classiq.interface.model.quantum_function_call import ArgValue
15
10
  from classiq.interface.model.quantum_function_declaration import (
16
11
  QuantumOperandDeclaration,
17
12
  )
18
13
  from classiq.interface.model.quantum_if_operation import QuantumIfOperation
14
+ from classiq.interface.model.within_apply_operation import WithinApplyOperation
19
15
 
20
- from classiq.qmod.builtins.functions import (
21
- apply,
22
- compute as compute_operator,
23
- uncompute,
24
- )
25
- from classiq.qmod.qmod_parameter import QParam
26
16
  from classiq.qmod.qmod_variable import Input, Output, QNum, QVar
27
17
  from classiq.qmod.quantum_callable import QCallable
28
18
  from classiq.qmod.quantum_expandable import prepare_arg
29
19
  from classiq.qmod.symbolic_expr import SymbolicExpr
30
20
 
31
21
 
32
- def bind(source: Input[QVar], destination: Output[QVar]) -> None:
22
+ def bind(
23
+ source: Union[Input[QVar], List[Input[QVar]]],
24
+ destination: Union[Output[QVar], List[Output[QVar]]],
25
+ ) -> None:
33
26
  assert QCallable.CURRENT_EXPANDABLE is not None
27
+ if not isinstance(source, list):
28
+ source = [source]
29
+ if not isinstance(destination, list):
30
+ destination = [destination]
34
31
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
35
32
  BindOperation(
36
- in_handle=source.get_handle_binding(),
37
- out_handle=destination.get_handle_binding(),
33
+ in_handles=[src_var.get_handle_binding() for src_var in source],
34
+ out_handles=[dst_var.get_handle_binding() for dst_var in destination],
38
35
  )
39
36
  )
40
37
 
@@ -46,22 +43,7 @@ def quantum_if(
46
43
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
47
44
  QuantumIfOperation(
48
45
  expression=Expression(expr=str(condition)),
49
- then=prepare_arg(QuantumOperandDeclaration(name=OPERAND_FIELD_NAME), then),
50
- )
51
- )
52
-
53
-
54
- def reinterpret_num(
55
- is_signed: Union[QParam[bool], bool],
56
- fraction_digits: Union[QParam[int], int],
57
- target: QNum,
58
- ) -> None:
59
- assert QCallable.CURRENT_EXPANDABLE is not None
60
- QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
61
- NumericReinterpretationOperation(
62
- target=target.get_handle_binding(),
63
- is_signed=Expression(expr=str(is_signed)),
64
- fraction_digits=Expression(expr=str(fraction_digits)),
46
+ then=_to_operand(then),
65
47
  )
66
48
  )
67
49
 
@@ -98,15 +80,22 @@ def within_apply(
98
80
  compute: Callable[[], None],
99
81
  action: Callable[[], None],
100
82
  ) -> None:
101
- compute_operator(compute)
102
- apply(action)
103
- uncompute(compute)
83
+ assert QCallable.CURRENT_EXPANDABLE is not None
84
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
85
+ WithinApplyOperation(
86
+ compute=_to_operand(compute),
87
+ action=_to_operand(action),
88
+ )
89
+ )
90
+
91
+
92
+ def _to_operand(callable_: Union[QCallable, Callable[[], None]]) -> ArgValue:
93
+ return prepare_arg(QuantumOperandDeclaration(name=""), callable_)
104
94
 
105
95
 
106
96
  __all__ = [
107
97
  "bind",
108
98
  "quantum_if",
109
- "reinterpret_num",
110
99
  "inplace_add",
111
100
  "inplace_xor",
112
101
  "within_apply",
@@ -2,16 +2,16 @@
2
2
 
3
3
  from typing import List
4
4
 
5
- from classiq.qmod.qmod_struct import QStruct
5
+ from classiq.qmod.qmod_struct import struct
6
6
 
7
7
 
8
- @QStruct
8
+ @struct
9
9
  class PauliTerm:
10
10
  pauli: List[int]
11
11
  coefficient: float
12
12
 
13
13
 
14
- @QStruct
14
+ @struct
15
15
  class MoleculeProblem:
16
16
  mapping: int
17
17
  z2_symmetries: bool
@@ -20,27 +20,27 @@ class MoleculeProblem:
20
20
  remove_orbitals: List[int]
21
21
 
22
22
 
23
- @QStruct
23
+ @struct
24
24
  class Molecule:
25
25
  atoms: List["ChemistryAtom"]
26
26
  spin: int
27
27
  charge: int
28
28
 
29
29
 
30
- @QStruct
30
+ @struct
31
31
  class ChemistryAtom:
32
32
  element: int
33
33
  position: "Position"
34
34
 
35
35
 
36
- @QStruct
36
+ @struct
37
37
  class Position:
38
38
  x: float
39
39
  y: float
40
40
  z: float
41
41
 
42
42
 
43
- @QStruct
43
+ @struct
44
44
  class FockHamiltonianProblem:
45
45
  mapping: int
46
46
  z2_symmetries: bool
@@ -48,19 +48,19 @@ class FockHamiltonianProblem:
48
48
  num_particles: List[int]
49
49
 
50
50
 
51
- @QStruct
51
+ @struct
52
52
  class LadderTerm:
53
53
  coefficient: float
54
54
  ops: List["LadderOp"]
55
55
 
56
56
 
57
- @QStruct
57
+ @struct
58
58
  class LadderOp:
59
59
  op: int
60
60
  index: int
61
61
 
62
62
 
63
- @QStruct
63
+ @struct
64
64
  class CombinatorialOptimizationSolution:
65
65
  probability: float
66
66
  cost: float
@@ -68,7 +68,7 @@ class CombinatorialOptimizationSolution:
68
68
  count: int
69
69
 
70
70
 
71
- @QStruct
71
+ @struct
72
72
  class GaussianModel:
73
73
  num_qubits: int
74
74
  normal_max_value: float
@@ -78,14 +78,14 @@ class GaussianModel:
78
78
  min_loss: int
79
79
 
80
80
 
81
- @QStruct
81
+ @struct
82
82
  class LogNormalModel:
83
83
  num_qubits: int
84
84
  mu: float
85
85
  sigma: float
86
86
 
87
87
 
88
- @QStruct
88
+ @struct
89
89
  class FinanceFunction:
90
90
  f: int
91
91
  threshold: float
@@ -95,13 +95,13 @@ class FinanceFunction:
95
95
  tail_probability: float
96
96
 
97
97
 
98
- @QStruct
98
+ @struct
99
99
  class QsvmResult:
100
100
  test_score: float
101
101
  predicted_labels: List[float]
102
102
 
103
103
 
104
- @QStruct
104
+ @struct
105
105
  class QSVMFeatureMapPauli:
106
106
  feature_dimension: int
107
107
  reps: int
classiq/qmod/cfunc.py ADDED
@@ -0,0 +1,42 @@
1
+ from typing import Any, Callable, Dict, Optional, Union, overload
2
+
3
+ from classiq.qmod.classical_function import CFunc
4
+
5
+
6
+ def get_caller_locals() -> Dict[str, Any]:
7
+ """Print the local variables in the caller's frame."""
8
+ import inspect
9
+
10
+ frame = inspect.currentframe()
11
+ try:
12
+ assert frame is not None
13
+ cfunc_frame = frame.f_back
14
+ assert cfunc_frame is not None
15
+ caller_frame = cfunc_frame.f_back
16
+ assert caller_frame is not None
17
+
18
+ return caller_frame.f_locals
19
+ finally:
20
+ # See here for information about the `del`
21
+ # https://docs.python.org/3/library/inspect.html#the-interpreter-stack
22
+ del frame
23
+
24
+
25
+ @overload
26
+ def cfunc(func: Callable) -> CFunc: ...
27
+
28
+
29
+ @overload
30
+ def cfunc(func: None = None) -> Callable[[Callable], CFunc]: ...
31
+
32
+
33
+ def cfunc(func: Optional[Callable] = None) -> Union[Callable[[Callable], CFunc], CFunc]:
34
+ caller_locals = get_caller_locals()
35
+
36
+ def wrapper(func: Callable) -> CFunc:
37
+ return CFunc(func, caller_locals)
38
+
39
+ if func is not None:
40
+ return wrapper(func)
41
+
42
+ return wrapper
@@ -2,10 +2,9 @@ import ast
2
2
  import inspect
3
3
  import sys
4
4
  from textwrap import dedent
5
- from typing import Callable
5
+ from typing import Any, Callable, Dict
6
6
 
7
7
  from classiq.exceptions import ClassiqValueError
8
- from classiq.qmod.builtins.classical_execution_primitives import sample, save
9
8
 
10
9
 
11
10
  def _unparse_function_body(code: str, func: ast.FunctionDef) -> str:
@@ -18,23 +17,16 @@ def _unparse_function_body(code: str, func: ast.FunctionDef) -> str:
18
17
 
19
18
 
20
19
  class CFunc:
21
- @staticmethod
22
- def default_cmain() -> "CFunc":
23
- @CFunc
24
- def cmain() -> None:
25
- result = sample()
26
- save({"result": result})
27
-
28
- return cmain
29
-
30
- def __init__(self, py_callable: Callable[[], None]):
20
+ def __init__(self, py_callable: Callable[[], None], caller_locals: Dict[str, Any]):
31
21
  code = dedent(inspect.getsource(py_callable))
32
22
  func = ast.parse(code).body[0]
33
23
  if not isinstance(func, ast.FunctionDef):
34
- raise ClassiqValueError(f"Use @{CFunc.__name__} to decorate a function")
24
+ raise ClassiqValueError("Use @cfunc to decorate a function")
35
25
  if len(func.args.args) > 0:
36
- raise ClassiqValueError(f"A @{CFunc.__name__} must receive no arguments")
26
+ raise ClassiqValueError("A @cfunc must receive no arguments")
37
27
  if sys.version_info >= (3, 9):
38
28
  self.code = "\n".join([ast.unparse(statement) for statement in func.body])
39
29
  else:
40
30
  self.code = _unparse_function_body(code, func)
31
+
32
+ self._caller_constants = caller_locals
@@ -1,6 +1,5 @@
1
1
  import dataclasses
2
2
  import inspect
3
- import sys
4
3
  from typing import Any, Callable, Dict, List, Optional, Type, get_args, get_origin
5
4
 
6
5
  from typing_extensions import _AnnotatedAlias
@@ -26,25 +25,17 @@ from classiq.interface.model.quantum_function_declaration import (
26
25
  )
27
26
 
28
27
  from classiq import StructDeclaration
28
+ from classiq.exceptions import ClassiqValueError
29
29
  from classiq.qmod.model_state_container import ModelStateContainer
30
30
  from classiq.qmod.qmod_parameter import Array, QParam
31
31
  from classiq.qmod.qmod_variable import QVar, get_type_hint_expr
32
32
  from classiq.qmod.quantum_callable import QCallable, QCallableList
33
- from classiq.qmod.utilities import unmangle_keyword
33
+ from classiq.qmod.utilities import unmangle_keyword, version_portable_get_args
34
34
 
35
35
  OPERAND_ARG_NAME = "arg{i}"
36
36
 
37
37
 
38
- def _version_portable_get_args(py_type: type) -> tuple:
39
- if get_origin(py_type) is None:
40
- return tuple()
41
- if sys.version_info[0:2] < (3, 10):
42
- return get_args(py_type) # The result of __class_getitem__
43
- else:
44
- return get_args(py_type)[0]
45
-
46
-
47
- def _python_type_to_qmod(
38
+ def python_type_to_qmod(
48
39
  py_type: type, *, qmodule: ModelStateContainer
49
40
  ) -> Optional[ConcreteClassicalType]:
50
41
  if py_type == int:
@@ -55,16 +46,16 @@ def _python_type_to_qmod(
55
46
  return Bool()
56
47
  elif get_origin(py_type) == list:
57
48
  return ClassicalList(
58
- element_type=_python_type_to_qmod(get_args(py_type)[0], qmodule=qmodule)
49
+ element_type=python_type_to_qmod(get_args(py_type)[0], qmodule=qmodule)
59
50
  )
60
51
  elif get_origin(py_type) == Array:
61
- array_args = _version_portable_get_args(py_type)
52
+ array_args = version_portable_get_args(py_type)
62
53
  if len(array_args) != 2:
63
- raise ValueError(
54
+ raise ClassiqValueError(
64
55
  "Array accepts two generic parameters in the form 'Array[<element-type>, <size>]'"
65
56
  )
66
57
  return ClassicalArray(
67
- element_type=_python_type_to_qmod(array_args[0], qmodule=qmodule),
58
+ element_type=python_type_to_qmod(array_args[0], qmodule=qmodule),
68
59
  size=get_type_hint_expr(array_args[1]),
69
60
  )
70
61
  elif inspect.isclass(py_type) and issubclass(py_type, QStructBase):
@@ -78,14 +69,14 @@ def _add_qmod_struct(
78
69
  ) -> None:
79
70
  if (
80
71
  py_type.__name__ in StructDeclaration.BUILTIN_STRUCT_DECLARATIONS
81
- or py_type.__name__ in qmodule.type_decls.keys()
72
+ or py_type.__name__ in qmodule.type_decls
82
73
  ):
83
74
  return
84
75
 
85
76
  qmodule.type_decls[py_type.__name__] = StructDeclaration(
86
77
  name=py_type.__name__,
87
78
  variables={
88
- f.name: _python_type_to_qmod(f.type, qmodule=qmodule)
79
+ f.name: python_type_to_qmod(f.type, qmodule=qmodule)
89
80
  for f in dataclasses.fields(py_type)
90
81
  },
91
82
  )
@@ -95,10 +86,10 @@ def _extract_param_decl(
95
86
  name: str, py_type: Any, *, qmodule: ModelStateContainer
96
87
  ) -> ClassicalParameterDeclaration:
97
88
  if len(get_args(py_type)) != 1:
98
- raise ValueError("QParam takes exactly one generic argument")
89
+ raise ClassiqValueError("QParam takes exactly one generic argument")
99
90
  py_type = get_args(py_type)[0]
100
91
  return ClassicalParameterDeclaration(
101
- name=name, classical_type=_python_type_to_qmod(py_type, qmodule=qmodule)
92
+ name=name, classical_type=python_type_to_qmod(py_type, qmodule=qmodule)
102
93
  )
103
94
 
104
95
 
@@ -118,7 +109,7 @@ def _extract_port_decl(name: str, py_type: Any) -> PortDeclaration:
118
109
  def _extract_operand_decl(
119
110
  name: str, py_type: Any, qmodule: ModelStateContainer
120
111
  ) -> QuantumOperandDeclaration:
121
- qc_args = _version_portable_get_args(py_type)
112
+ qc_args = version_portable_get_args(py_type)
122
113
  arg_dict = {
123
114
  OPERAND_ARG_NAME.format(i=i): arg_type for i, arg_type in enumerate(qc_args)
124
115
  }
@@ -0,0 +1,23 @@
1
+ from typing import TYPE_CHECKING, List, Tuple
2
+
3
+ from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
4
+ from classiq.interface.generator.arith.number_utils import MAXIMAL_MACHINE_PRECISION
5
+ from classiq.interface.model.quantum_type import QuantumNumeric
6
+
7
+ from classiq.qmod.qmod_variable import QNum
8
+ from classiq.qmod.symbolic_type import SymbolicTypes
9
+
10
+
11
+ def get_expression_numeric_attributes(
12
+ vars: List[QNum],
13
+ expr: SymbolicTypes,
14
+ machine_precision: int = MAXIMAL_MACHINE_PRECISION,
15
+ ) -> Tuple[int, bool, int]:
16
+ res_type = compute_arithmetic_result_type(
17
+ expr_str=str(expr),
18
+ var_types={var._name: var.get_qmod_type() for var in vars},
19
+ machine_precision=machine_precision,
20
+ )
21
+ if TYPE_CHECKING:
22
+ assert isinstance(res_type, QuantumNumeric)
23
+ return res_type.size_in_bits, res_type.sign_value, res_type.fraction_digits_value
@@ -1,5 +1,6 @@
1
1
  from typing import Dict
2
2
 
3
+ from classiq.interface.generator.constant import Constant
3
4
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
4
5
 
5
6
  from classiq import StructDeclaration
@@ -8,6 +9,7 @@ from classiq import StructDeclaration
8
9
  class ModelStateContainer:
9
10
  type_decls: Dict[str, StructDeclaration]
10
11
  native_defs: Dict[str, NativeFunctionDefinition]
12
+ constants: Dict[str, Constant]
11
13
 
12
14
 
13
15
  QMODULE = ModelStateContainer()
File without changes
@@ -0,0 +1,189 @@
1
+ import ast
2
+ import re
3
+ from dataclasses import dataclass
4
+ from typing import Callable, Dict, List, Mapping, Type
5
+
6
+ import numpy as np
7
+
8
+ from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
9
+
10
+ IDENTIFIER = re.compile(r"[a-zA-Z_]\w*")
11
+ BINARY_OPS: Mapping[Type[ast.operator], str] = {
12
+ ast.Add: "+",
13
+ ast.Sub: "-",
14
+ ast.Mult: "*",
15
+ ast.Div: "/",
16
+ ast.Mod: "%",
17
+ ast.Pow: "**",
18
+ ast.BitAnd: "&",
19
+ ast.BitOr: "|",
20
+ ast.BitXor: "^",
21
+ ast.LShift: "<<",
22
+ ast.RShift: ">>",
23
+ }
24
+ BOOL_OPS: Mapping[Type[ast.boolop], str] = {ast.And: "and", ast.Or: "or"}
25
+ UNARY_OPS: Mapping[Type[ast.unaryop], str] = {
26
+ ast.UAdd: "+",
27
+ ast.USub: "-",
28
+ ast.Invert: "~",
29
+ ast.Not: "not",
30
+ }
31
+ COMPARE_OPS: Mapping[Type[ast.cmpop], str] = {
32
+ ast.Eq: "==",
33
+ ast.NotEq: "!=",
34
+ ast.Lt: "<",
35
+ ast.LtE: "<=",
36
+ ast.Gt: ">",
37
+ ast.GtE: ">=",
38
+ }
39
+ LIST_FORMAT_CHAR_LIMIT = 20
40
+
41
+
42
+ @dataclass
43
+ class ASTToQMODCode:
44
+ level: int
45
+ decimal_precision: int
46
+ indent_seq: str = " "
47
+
48
+ @property
49
+ def indent(self) -> str:
50
+ return self.level * self.indent_seq
51
+
52
+ def visit(self, node: ast.AST) -> str:
53
+ return self.ast_to_code(node)
54
+
55
+ def ast_to_code(self, node: ast.AST) -> str:
56
+ if isinstance(node, ast.Module):
57
+ return self.indent.join(self.ast_to_code(child) for child in node.body)
58
+ elif isinstance(node, ast.Attribute):
59
+ # Enum attribute access
60
+ if not isinstance(node.value, ast.Name) or not isinstance(node.attr, str):
61
+ raise AssertionError("Error parsing enum attribute access")
62
+ if not (IDENTIFIER.match(node.value.id) and IDENTIFIER.match(node.attr)):
63
+ raise AssertionError("Error parsing enum attribute access")
64
+ return f"{node.value.id!s}::{node.attr!s}"
65
+ elif isinstance(node, ast.Name):
66
+ return node.id
67
+ elif isinstance(node, ast.Num):
68
+ return str(np.round(node.n, self.decimal_precision))
69
+ elif isinstance(node, ast.Str):
70
+ return repr(node.s)
71
+ elif isinstance(node, ast.Constant):
72
+ return repr(node.value)
73
+ elif isinstance(node, ast.BinOp):
74
+ return "({} {} {})".format(
75
+ self.ast_to_code(node.left),
76
+ BINARY_OPS[type(node.op)],
77
+ self.ast_to_code(node.right),
78
+ )
79
+ elif isinstance(node, ast.UnaryOp):
80
+ unary_op = UNARY_OPS[type(node.op)]
81
+ space = " " if unary_op == "not" else ""
82
+ return f"({unary_op}{space}{self.ast_to_code(node.operand)})"
83
+ elif isinstance(node, ast.BoolOp):
84
+ return "({})".format(
85
+ (" " + BOOL_OPS[type(node.op)] + " ").join(
86
+ self.ast_to_code(value) for value in node.values
87
+ )
88
+ )
89
+ elif isinstance(node, ast.Compare):
90
+ if len(node.ops) != 1 or len(node.comparators) != 1:
91
+ raise AssertionError("Error parsing comparison expression.")
92
+ return "({} {} {})".format(
93
+ self.ast_to_code(node.left),
94
+ COMPARE_OPS[type(node.ops[0])],
95
+ self.ast_to_code(node.comparators[0]),
96
+ )
97
+ elif isinstance(node, ast.List):
98
+ elts = node.elts
99
+ elements = self.indent_items(
100
+ lambda: [self.ast_to_code(element) for element in elts]
101
+ )
102
+ return f"[{elements}]"
103
+ elif isinstance(node, ast.Subscript):
104
+ return f"{self.ast_to_code(node.value)}[{_remove_redundant_parentheses(self.ast_to_code(node.slice))}]"
105
+ elif isinstance(node, ast.Slice):
106
+ # A QMOD expression does not support slice step
107
+ if node.lower is None or node.upper is None or node.step is not None:
108
+ raise AssertionError("Error parsing slice expression.")
109
+ return f"{self.ast_to_code(node.lower)}:{self.ast_to_code(node.upper)}"
110
+ elif isinstance(node, ast.Call):
111
+ func = self.ast_to_code(node.func)
112
+ if func == "get_field":
113
+ if len(node.args) != 2:
114
+ raise AssertionError("Error parsing struct field access.")
115
+ field = str(self.ast_to_code(node.args[1])).replace("'", "")
116
+ if not IDENTIFIER.match(field):
117
+ raise AssertionError("Error parsing struct field access.")
118
+ return f"{self.ast_to_code(node.args[0])}.{field}"
119
+ elif func == "struct_literal":
120
+ if len(node.args) != 1 or not isinstance(node.args[0], ast.Name):
121
+ raise AssertionError("Error parsing struct literal.")
122
+ keywords = node.keywords
123
+ initializer_list = self.indent_items(
124
+ lambda: [
125
+ f"{keyword.arg} = {self._cleaned_ast_to_code(keyword.value)}"
126
+ for keyword in keywords
127
+ if keyword.arg is not None
128
+ ]
129
+ )
130
+ return f"{self.ast_to_code(node.args[0])} {{{initializer_list}}}"
131
+ else:
132
+ return "{}({})".format(
133
+ func, ", ".join(self._cleaned_ast_to_code(arg) for arg in node.args)
134
+ )
135
+ elif isinstance(node, ast.Expr):
136
+ return self._cleaned_ast_to_code(node.value)
137
+ else:
138
+ raise AssertionError("Error parsing expression: unsupported AST node.")
139
+
140
+ def indent_items(self, items: Callable[[], List[str]]) -> str:
141
+ should_indent = (
142
+ len("".join([i.strip() for i in items()])) >= LIST_FORMAT_CHAR_LIMIT
143
+ )
144
+ if should_indent:
145
+ self.level += 1
146
+ left_ws = "\n" + self.indent
147
+ inner_ws = ",\n" + self.indent
148
+ else:
149
+ left_ws = ""
150
+ inner_ws = ", "
151
+ items_ = items()
152
+ if should_indent:
153
+ self.level -= 1
154
+ right_ws = "\n" + self.indent
155
+ else:
156
+ right_ws = ""
157
+ return f"{left_ws}{inner_ws.join(items_)}{right_ws}"
158
+
159
+ def _cleaned_ast_to_code(self, node: ast.AST) -> str:
160
+ return _remove_redundant_parentheses(self.ast_to_code(node))
161
+
162
+
163
+ def _remove_redundant_parentheses(expr: str) -> str:
164
+ if not (expr.startswith("(") and expr.endswith(")")):
165
+ return expr
166
+ parentheses_map: Dict[int, int] = dict()
167
+ stack: List[int] = []
168
+ for index, char in enumerate(expr):
169
+ if char == "(":
170
+ stack.append(index)
171
+ elif char == ")":
172
+ parentheses_map[stack.pop()] = index
173
+ index = 0
174
+ original_length = len(expr)
175
+ while (
176
+ index in parentheses_map
177
+ and parentheses_map[index] == original_length - index - 1
178
+ ):
179
+ expr = expr[1:-1]
180
+ index += 1
181
+ return expr
182
+
183
+
184
+ def transform_expression(
185
+ expr: str, level: int = 0, decimal_precision: int = DEFAULT_DECIMAL_PRECISION
186
+ ) -> str:
187
+ return ASTToQMODCode(level=level, decimal_precision=decimal_precision).visit(
188
+ ast.parse(expr)
189
+ )