classiq 0.83.0__py3-none-any.whl → 0.85.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 (103) hide show
  1. classiq/_internals/api_wrapper.py +27 -0
  2. classiq/applications/chemistry/chemistry_model_constructor.py +0 -2
  3. classiq/applications/chemistry/hartree_fock.py +68 -0
  4. classiq/applications/chemistry/mapping.py +85 -0
  5. classiq/applications/chemistry/op_utils.py +79 -0
  6. classiq/applications/chemistry/problems.py +195 -0
  7. classiq/applications/chemistry/ucc.py +109 -0
  8. classiq/applications/chemistry/z2_symmetries.py +368 -0
  9. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +30 -1
  10. classiq/applications/combinatorial_optimization/combinatorial_problem.py +20 -42
  11. classiq/{model_expansions/evaluators → evaluators}/arg_type_match.py +12 -4
  12. classiq/{model_expansions/evaluators → evaluators}/argument_types.py +1 -1
  13. classiq/evaluators/classical_expression.py +53 -0
  14. classiq/{model_expansions/evaluators → evaluators}/classical_type_inference.py +3 -4
  15. classiq/{model_expansions/evaluators → evaluators}/parameter_types.py +17 -15
  16. classiq/execution/__init__.py +12 -1
  17. classiq/execution/execution_session.py +238 -49
  18. classiq/execution/jobs.py +26 -1
  19. classiq/execution/qnn.py +2 -2
  20. classiq/execution/user_budgets.py +39 -0
  21. classiq/interface/_version.py +1 -1
  22. classiq/interface/constants.py +1 -0
  23. classiq/interface/debug_info/debug_info.py +0 -4
  24. classiq/interface/execution/primitives.py +29 -1
  25. classiq/interface/executor/estimate_cost.py +35 -0
  26. classiq/interface/executor/execution_result.py +13 -0
  27. classiq/interface/executor/result.py +116 -1
  28. classiq/interface/executor/user_budget.py +26 -33
  29. classiq/interface/generator/expressions/atomic_expression_functions.py +10 -1
  30. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +0 -6
  31. classiq/interface/generator/functions/builtins/internal_operators.py +2 -0
  32. classiq/interface/generator/functions/classical_type.py +2 -35
  33. classiq/interface/generator/functions/concrete_types.py +20 -3
  34. classiq/interface/generator/functions/type_modifier.py +0 -19
  35. classiq/interface/generator/generated_circuit_data.py +5 -18
  36. classiq/interface/generator/types/compilation_metadata.py +0 -3
  37. classiq/interface/ide/operation_registry.py +45 -0
  38. classiq/interface/ide/visual_model.py +68 -3
  39. classiq/interface/model/bounds.py +12 -2
  40. classiq/interface/model/model.py +12 -7
  41. classiq/interface/model/port_declaration.py +2 -24
  42. classiq/interface/model/quantum_expressions/arithmetic_operation.py +7 -4
  43. classiq/interface/model/variable_declaration_statement.py +33 -6
  44. classiq/interface/pretty_print/__init__.py +0 -0
  45. classiq/{qmod/native → interface/pretty_print}/expression_to_qmod.py +18 -11
  46. classiq/interface/server/routes.py +4 -0
  47. classiq/model_expansions/atomic_expression_functions_defs.py +47 -6
  48. classiq/model_expansions/function_builder.py +4 -1
  49. classiq/model_expansions/interpreters/base_interpreter.py +3 -3
  50. classiq/model_expansions/interpreters/generative_interpreter.py +16 -1
  51. classiq/model_expansions/quantum_operations/allocate.py +1 -1
  52. classiq/model_expansions/quantum_operations/assignment_result_processor.py +64 -22
  53. classiq/model_expansions/quantum_operations/bind.py +2 -2
  54. classiq/model_expansions/quantum_operations/bounds.py +7 -1
  55. classiq/model_expansions/quantum_operations/call_emitter.py +26 -20
  56. classiq/model_expansions/quantum_operations/classical_var_emitter.py +16 -0
  57. classiq/model_expansions/quantum_operations/variable_decleration.py +31 -11
  58. classiq/model_expansions/scope.py +7 -0
  59. classiq/model_expansions/scope_initialization.py +3 -3
  60. classiq/model_expansions/transformers/model_renamer.py +6 -4
  61. classiq/model_expansions/transformers/type_modifier_inference.py +81 -43
  62. classiq/model_expansions/transformers/var_splitter.py +1 -1
  63. classiq/model_expansions/visitors/symbolic_param_inference.py +2 -3
  64. classiq/open_library/functions/__init__.py +3 -2
  65. classiq/open_library/functions/amplitude_amplification.py +10 -18
  66. classiq/open_library/functions/discrete_sine_cosine_transform.py +5 -5
  67. classiq/open_library/functions/grover.py +14 -6
  68. classiq/open_library/functions/modular_exponentiation.py +22 -20
  69. classiq/open_library/functions/qaoa_penalty.py +8 -1
  70. classiq/open_library/functions/state_preparation.py +18 -32
  71. classiq/qmod/__init__.py +2 -0
  72. classiq/qmod/builtins/enums.py +23 -0
  73. classiq/qmod/builtins/functions/__init__.py +2 -0
  74. classiq/qmod/builtins/functions/exponentiation.py +32 -4
  75. classiq/qmod/builtins/operations.py +65 -1
  76. classiq/qmod/builtins/structs.py +55 -3
  77. classiq/qmod/classical_variable.py +74 -0
  78. classiq/qmod/declaration_inferrer.py +3 -2
  79. classiq/qmod/native/pretty_printer.py +20 -20
  80. classiq/qmod/pretty_print/expression_to_python.py +2 -1
  81. classiq/qmod/pretty_print/pretty_printer.py +35 -21
  82. classiq/qmod/python_classical_type.py +12 -5
  83. classiq/qmod/qfunc.py +2 -19
  84. classiq/qmod/qmod_constant.py +2 -5
  85. classiq/qmod/qmod_parameter.py +2 -5
  86. classiq/qmod/qmod_variable.py +61 -23
  87. classiq/qmod/quantum_expandable.py +5 -3
  88. classiq/qmod/quantum_function.py +49 -4
  89. classiq/qmod/semantics/annotation/qstruct_annotator.py +1 -1
  90. classiq/qmod/semantics/validation/main_validation.py +1 -9
  91. classiq/qmod/symbolic_type.py +2 -1
  92. classiq/qmod/utilities.py +0 -2
  93. classiq/qmod/write_qmod.py +1 -1
  94. {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/METADATA +4 -1
  95. {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/RECORD +101 -90
  96. classiq/interface/model/quantum_variable_declaration.py +0 -7
  97. classiq/model_expansions/evaluators/classical_expression.py +0 -36
  98. /classiq/{model_expansions/evaluators → evaluators}/__init__.py +0 -0
  99. /classiq/{model_expansions/evaluators → evaluators}/control.py +0 -0
  100. /classiq/{model_expansions → evaluators}/expression_evaluator.py +0 -0
  101. /classiq/{model_expansions/evaluators → evaluators}/quantum_type_utils.py +0 -0
  102. /classiq/{model_expansions/evaluators → evaluators}/type_type_match.py +0 -0
  103. {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/WHEEL +0 -0
@@ -1,7 +1,11 @@
1
1
  from enum import IntEnum
2
+ from typing import TYPE_CHECKING
2
3
 
3
4
  from classiq.interface.generator.types.enum_declaration import EnumDeclaration
4
5
 
6
+ if TYPE_CHECKING:
7
+ from classiq.qmod.builtins.structs import SparsePauliOp
8
+
5
9
 
6
10
  class Element(IntEnum):
7
11
  H = 0
@@ -176,6 +180,25 @@ class Pauli(IntEnum):
176
180
  """Z (int): Pauli-Z matrix, represented by the integer 3. \n
177
181
  $Z = \\begin{bmatrix} 1 & 0 \\\\ 0 & -1 \\end{bmatrix}$"""
178
182
 
183
+ def __call__(self, index: int) -> "SparsePauliOp":
184
+ from classiq.qmod.builtins.structs import (
185
+ IndexedPauli,
186
+ SparsePauliOp,
187
+ SparsePauliTerm,
188
+ )
189
+
190
+ return SparsePauliOp(
191
+ terms=[ # type:ignore[arg-type]
192
+ SparsePauliTerm(
193
+ paulis=[ # type:ignore[arg-type]
194
+ IndexedPauli(pauli=self, index=index) # type:ignore[arg-type]
195
+ ],
196
+ coefficient=1.0, # type:ignore[arg-type]
197
+ )
198
+ ],
199
+ num_qubits=index + 1, # type:ignore[arg-type]
200
+ )
201
+
179
202
 
180
203
  class QSVMFeatureMapEntanglement(IntEnum):
181
204
  FULL = 0
@@ -71,6 +71,7 @@ CORE_LIB_DECLS = [
71
71
  single_pauli_exponent,
72
72
  commuting_paulis_exponent,
73
73
  suzuki_trotter,
74
+ multi_suzuki_trotter,
74
75
  parametric_suzuki_trotter,
75
76
  sparse_suzuki_trotter,
76
77
  qdrift,
@@ -132,6 +133,7 @@ __all__ = [ # noqa: RUF022
132
133
  "molecule_hartree_fock",
133
134
  "molecule_hva",
134
135
  "molecule_ucc",
136
+ "multi_suzuki_trotter",
135
137
  "parametric_suzuki_trotter",
136
138
  "pauli_feature_map",
137
139
  "permute",
@@ -55,13 +55,11 @@ def commuting_paulis_exponent(
55
55
 
56
56
  @qfunc(external=True)
57
57
  def suzuki_trotter(
58
- pauli_operator: CArray[PauliTerm],
58
+ pauli_operator: SparsePauliOp, # FIXME: Rename to hamiltonian (CLS-2912)
59
59
  evolution_coefficient: CReal,
60
60
  order: CInt,
61
61
  repetitions: CInt,
62
- qbv: QArray[
63
- QBit, Literal["get_field(get_field(pauli_operator[0], 'pauli'), 'len')"]
64
- ],
62
+ qbv: QArray[QBit], # FIXME: Add length expr (CLS-2912)
65
63
  ) -> None:
66
64
  """
67
65
  [Qmod core-library function]
@@ -82,6 +80,36 @@ def suzuki_trotter(
82
80
  pass
83
81
 
84
82
 
83
+ @qfunc(external=True)
84
+ def multi_suzuki_trotter(
85
+ hamiltonians: CArray[SparsePauliOp],
86
+ evolution_coefficients: CArray[CReal],
87
+ order: CInt,
88
+ repetitions: CInt,
89
+ qbv: QArray,
90
+ ) -> None:
91
+ """
92
+ [Qmod core-library function]
93
+
94
+ Applies the Suzuki-Trotter decomposition jointly to a sum of Hamiltonians
95
+ (represented as Pauli operators), each with its separate evolution coefficient,
96
+ approximating $\\exp{-iH_1t_1+H_2t_2+\\dots}$ with a specified order and number of
97
+ repetitions.
98
+
99
+ The Suzuki-Trotter decomposition is a method for approximating the exponential of a sum of operators by a product of exponentials of each operator.
100
+ The Suzuki-Trotter decomposition of a given order nullifies the error of the Taylor series expansion of the product of exponentials up to that order.
101
+ The error of a Suzuki-Trotter decomposition decreases as the order and number of repetitions increase.
102
+
103
+ Args:
104
+ hamiltonians: The hamitonians to be exponentiated, in sparse representation.
105
+ evolution_coefficients: The hamiltonian coefficients (can be link-time).
106
+ order: The order of the Suzuki-Trotter decomposition.
107
+ repetitions: The number of repetitions of the Suzuki-Trotter decomposition.
108
+ qbv: The target quantum variable of the exponentiation.
109
+ """
110
+ pass
111
+
112
+
85
113
  @qfunc(external=True)
86
114
  def parametric_suzuki_trotter(
87
115
  paulis: CArray[CArray[Pauli]],
@@ -7,6 +7,7 @@ from typing import (
7
7
  Callable,
8
8
  Final,
9
9
  NoReturn,
10
+ Optional,
10
11
  Union,
11
12
  overload,
12
13
  )
@@ -20,6 +21,8 @@ from classiq.interface.generator.functions.classical_type import Integer
20
21
  from classiq.interface.helpers.text_utils import s
21
22
  from classiq.interface.model.allocate import Allocate
22
23
  from classiq.interface.model.bind_operation import BindOperation
24
+ from classiq.interface.model.block import Block
25
+ from classiq.interface.model.bounds import SetBoundsStatement
23
26
  from classiq.interface.model.classical_if import ClassicalIf
24
27
  from classiq.interface.model.classical_parameter_declaration import (
25
28
  ClassicalParameterDeclaration,
@@ -46,7 +49,7 @@ from classiq.interface.model.within_apply_operation import WithinApply
46
49
 
47
50
  from classiq.qmod.generative import is_generative_mode
48
51
  from classiq.qmod.qmod_constant import QConstant
49
- from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QScalar, QVar
52
+ from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QNum, QScalar, QVar
50
53
  from classiq.qmod.quantum_callable import QCallable
51
54
  from classiq.qmod.quantum_expandable import prepare_arg
52
55
  from classiq.qmod.symbolic_expr import SymbolicExpr
@@ -398,6 +401,65 @@ def phase(expr: SymbolicExpr, theta: float = 1.0) -> None:
398
401
  )
399
402
 
400
403
 
404
+ @suppress_return_value
405
+ def block(
406
+ statements: Union[QCallable, Callable[[], Statements]],
407
+ ) -> None:
408
+ _validate_operand(statements)
409
+ assert QCallable.CURRENT_EXPANDABLE is not None
410
+ source_ref = get_source_ref(sys._getframe(1))
411
+
412
+ block_stmt = Block(
413
+ statements=_operand_to_body(statements, "statements"),
414
+ source_ref=source_ref,
415
+ )
416
+ if is_generative_mode():
417
+ block_stmt.set_generative_block("statements", statements)
418
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(block_stmt)
419
+
420
+
421
+ @overload
422
+ def reset_bounds(
423
+ target_var: QNum,
424
+ ) -> None:
425
+ pass
426
+
427
+
428
+ @overload
429
+ def reset_bounds(
430
+ target_var: QNum,
431
+ lower_bound: Union[float, SymbolicExpr],
432
+ upper_bound: Union[float, SymbolicExpr],
433
+ ) -> None:
434
+ pass
435
+
436
+
437
+ @suppress_return_value
438
+ def reset_bounds(
439
+ target_var: QNum,
440
+ lower_bound: Optional[Union[float, SymbolicExpr]] = None,
441
+ upper_bound: Optional[Union[float, SymbolicExpr]] = None,
442
+ ) -> None:
443
+ assert QCallable.CURRENT_EXPANDABLE is not None
444
+ source_ref = get_source_ref(sys._getframe(1))
445
+
446
+ lower_bound_expr = (
447
+ None if lower_bound is None else Expression(expr=str(lower_bound))
448
+ )
449
+ upper_bound_expr = (
450
+ None if upper_bound is None else Expression(expr=str(upper_bound))
451
+ )
452
+
453
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
454
+ SetBoundsStatement(
455
+ target=target_var.get_handle_binding(),
456
+ lower_bound=lower_bound_expr,
457
+ upper_bound=upper_bound_expr,
458
+ source_ref=source_ref,
459
+ )
460
+ )
461
+
462
+
401
463
  def _validate_operand(stmt_block: Any, num_params: int = 0) -> None:
402
464
  if stmt_block is None:
403
465
  _raise_operand_error(
@@ -502,6 +564,7 @@ __all__ = [
502
564
  "assign",
503
565
  "assign_amplitude",
504
566
  "bind",
567
+ "block",
505
568
  "control",
506
569
  "if_",
507
570
  "inplace_add",
@@ -510,6 +573,7 @@ __all__ = [
510
573
  "phase",
511
574
  "power",
512
575
  "repeat",
576
+ "reset_bounds",
513
577
  "within_apply",
514
578
  ]
515
579
 
@@ -1,6 +1,9 @@
1
1
  from dataclasses import dataclass, fields, is_dataclass
2
+ from typing import Union
2
3
 
4
+ from classiq.interface.exceptions import ClassiqValueError
3
5
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
6
+ from classiq.interface.helpers.text_utils import are, readable_list, s
4
7
 
5
8
  from classiq.qmod.builtins.enums import LadderOperator, Pauli
6
9
  from classiq.qmod.cparam import CArray, CBool, CInt, CReal
@@ -56,13 +59,62 @@ class SparsePauliOp:
56
59
  Represents a collection of sparse Pauli operators.
57
60
 
58
61
  Attributes:
59
- paulis (CArray[SparsePauliTerm]): The list of chosen sparse Pauli operators in the term, corresponds to a product of them. (See: SparsePauliTerm)
62
+ terms (CArray[SparsePauliTerm]): The list of chosen sparse Pauli terms, corresponds to a product of them. (See: SparsePauliTerm)
60
63
  num_qubits (CInt): The number of qubits in the Hamiltonian.
61
64
  """
62
65
 
63
- paulis: CArray[SparsePauliTerm]
66
+ terms: CArray[SparsePauliTerm]
64
67
  num_qubits: CInt
65
68
 
69
+ def __mul__(self, obj: Union[float, "SparsePauliOp"]) -> "SparsePauliOp":
70
+ if isinstance(obj, (int, float, complex)):
71
+ return SparsePauliOp(
72
+ terms=[ # type:ignore[arg-type]
73
+ SparsePauliTerm(
74
+ paulis=term.paulis,
75
+ coefficient=obj * term.coefficient,
76
+ )
77
+ for term in self.terms # type:ignore[attr-defined]
78
+ ],
79
+ num_qubits=self.num_qubits,
80
+ )
81
+ if len(self.terms) != 1 or len(obj.terms) != 1: # type:ignore[arg-type]
82
+ raise ClassiqValueError("Cannot attach a pauli to multiple pauli terms")
83
+ existing_indices = {
84
+ indexed_pauli.index for indexed_pauli in self.terms[0].paulis
85
+ }
86
+ added_indices = {indexed_pauli.index for indexed_pauli in obj.terms[0].paulis}
87
+ overlapping_indices = sorted(existing_indices.intersection(added_indices))
88
+ if len(overlapping_indices):
89
+ raise ClassiqValueError(
90
+ f"Pauli{s(overlapping_indices)} at "
91
+ f"{'indices' if len(overlapping_indices) > 1 else 'index'} "
92
+ f"{readable_list(overlapping_indices)} {are(overlapping_indices)} "
93
+ f"already assigned"
94
+ )
95
+ return SparsePauliOp(
96
+ terms=[ # type:ignore[arg-type]
97
+ SparsePauliTerm(
98
+ paulis=self.terms[0].paulis + obj.terms[0].paulis,
99
+ coefficient=self.terms[0].coefficient * obj.terms[0].coefficient,
100
+ )
101
+ ],
102
+ num_qubits=max(
103
+ self.num_qubits, obj.num_qubits # type:ignore[call-overload]
104
+ ),
105
+ )
106
+
107
+ def __rmul__(self, obj: Union[float, "SparsePauliOp"]) -> "SparsePauliOp":
108
+ return self.__mul__(obj)
109
+
110
+ def __add__(self, other: "SparsePauliOp") -> "SparsePauliOp":
111
+ return SparsePauliOp(
112
+ terms=self.terms + other.terms, # type:ignore[arg-type]
113
+ num_qubits=max(
114
+ self.num_qubits, other.num_qubits # type:ignore[call-overload]
115
+ ),
116
+ )
117
+
66
118
 
67
119
  @dataclass
68
120
  class Position:
@@ -167,7 +219,7 @@ BUILTIN_STRUCT_DECLARATIONS = {
167
219
  struct_decl.__name__: StructDeclaration(
168
220
  name=struct_decl.__name__,
169
221
  variables={
170
- field.name: PythonClassicalType().convert(field.type)
222
+ field.name: PythonClassicalType().convert(field.type, nested=True)
171
223
  for field in fields(struct_decl)
172
224
  },
173
225
  )
@@ -0,0 +1,74 @@
1
+ import sys
2
+ from typing import TYPE_CHECKING, Any
3
+
4
+ from classiq.interface.exceptions import ClassiqInternalError
5
+ from classiq.interface.generator.expressions.expression import Expression
6
+ from classiq.interface.generator.functions.classical_type import Bool, ClassicalType
7
+ from classiq.interface.model.handle_binding import HandleBinding
8
+ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
9
+ ArithmeticOperation,
10
+ ArithmeticOperationKind,
11
+ )
12
+ from classiq.interface.model.variable_declaration_statement import (
13
+ VariableDeclarationStatement,
14
+ )
15
+
16
+ from classiq.qmod.cparam import CBool, CParam, CParamScalar
17
+ from classiq.qmod.qmod_variable import QBit, _infer_variable_name
18
+ from classiq.qmod.quantum_callable import QCallable
19
+ from classiq.qmod.symbolic import symbolic_function
20
+ from classiq.qmod.symbolic_type import SYMBOLIC_TYPES
21
+ from classiq.qmod.utilities import get_source_ref
22
+
23
+
24
+ def declare_classical_variable(
25
+ name: str, classical_type: ClassicalType, frame_depth: int
26
+ ) -> None:
27
+ if TYPE_CHECKING:
28
+ assert QCallable.CURRENT_EXPANDABLE is not None
29
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
30
+ VariableDeclarationStatement(
31
+ name=name,
32
+ qmod_type=classical_type,
33
+ source_ref=get_source_ref(sys._getframe(frame_depth)),
34
+ )
35
+ )
36
+
37
+
38
+ def assign_classical_variable(target: CParam, value: Any, frame_depth: int) -> None:
39
+ if not isinstance(value, SYMBOLIC_TYPES):
40
+ raise TypeError(f"Invalid argument {value!r} for classical variable assignment")
41
+
42
+ if TYPE_CHECKING:
43
+ assert QCallable.CURRENT_EXPANDABLE is not None
44
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
45
+ ArithmeticOperation(
46
+ expression=Expression(expr=str(value)),
47
+ result_var=HandleBinding(name=str(target)),
48
+ operation_kind=ArithmeticOperationKind.Assignment,
49
+ source_ref=get_source_ref(sys._getframe(frame_depth)),
50
+ )
51
+ )
52
+
53
+
54
+ def measure(var: QBit) -> CParamScalar:
55
+ """
56
+ Measures the given qubit. `measure` is a non-unitary operation.
57
+
58
+ Args:
59
+ var: a qubit variable
60
+
61
+ Returns:
62
+ the measurement result (a symbolic boolean variable)
63
+ """
64
+ name = _infer_variable_name(None, 2)
65
+ if name is None:
66
+ raise ClassiqInternalError("Could not infer measure var name")
67
+ declare_classical_variable(name, Bool(), 2)
68
+ res_var = CParamScalar(name)
69
+ res_val = symbolic_function(
70
+ var,
71
+ return_type=CBool, # type:ignore[type-abstract]
72
+ )
73
+ assign_classical_variable(res_var, res_val, 2)
74
+ return res_var
@@ -75,14 +75,15 @@ class _PythonClassicalType(PythonClassicalType):
75
75
  all_decls = BUILTIN_STRUCT_DECLARATIONS | self.qmodule.type_decls
76
76
  if py_type.__name__ in all_decls:
77
77
  classical_type.set_classical_struct_decl(
78
- all_decls[py_type.__name__].model_copy()
78
+ all_decls[py_type.__name__].model_copy(deep=True)
79
79
  )
80
80
  return classical_type
81
81
 
82
82
  struct_decl = StructDeclaration(
83
83
  name=py_type.__name__,
84
84
  variables={
85
- f.name: self.convert(f.type) for f in dataclasses.fields(py_type)
85
+ f.name: self.convert(f.type, nested=True)
86
+ for f in dataclasses.fields(py_type)
86
87
  },
87
88
  )
88
89
  check_duplicate_types([struct_decl, *self.qmodule.user_types()])
@@ -1,13 +1,13 @@
1
1
  from collections.abc import Mapping
2
2
  from typing import Optional, Union
3
3
 
4
+ from classiq.interface.constants import DEFAULT_DECIMAL_PRECISION
4
5
  from classiq.interface.exceptions import ClassiqInternalError
5
6
  from classiq.interface.generator.constant import Constant
6
7
  from classiq.interface.generator.expressions.expression import Expression
7
8
  from classiq.interface.generator.functions.classical_type import (
8
9
  Bool,
9
10
  ClassicalArray,
10
- ClassicalList,
11
11
  ClassicalTuple,
12
12
  Integer,
13
13
  Real,
@@ -29,6 +29,7 @@ from classiq.interface.generator.visitor import NodeType
29
29
  from classiq.interface.model.allocate import Allocate
30
30
  from classiq.interface.model.bind_operation import BindOperation
31
31
  from classiq.interface.model.block import Block
32
+ from classiq.interface.model.bounds import SetBoundsStatement
32
33
  from classiq.interface.model.classical_if import ClassicalIf
33
34
  from classiq.interface.model.classical_parameter_declaration import (
34
35
  AnonClassicalParameterDeclaration,
@@ -76,20 +77,16 @@ from classiq.interface.model.quantum_type import (
76
77
  QuantumBitvector,
77
78
  QuantumNumeric,
78
79
  )
79
- from classiq.interface.model.quantum_variable_declaration import (
80
- QuantumVariableDeclaration,
81
- )
82
80
  from classiq.interface.model.repeat import Repeat
83
81
  from classiq.interface.model.statement_block import StatementBlock
84
82
  from classiq.interface.model.variable_declaration_statement import (
85
83
  VariableDeclarationStatement,
86
84
  )
87
85
  from classiq.interface.model.within_apply_operation import WithinApply
86
+ from classiq.interface.pretty_print.expression_to_qmod import transform_expression
88
87
 
89
88
  from classiq.open_library.functions import OPEN_LIBRARY_FUNCTIONS
90
- from classiq.qmod.native.expression_to_qmod import transform_expression
91
89
  from classiq.qmod.semantics.annotation.call_annotation import resolve_function_calls
92
- from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
93
90
 
94
91
 
95
92
  class DSLPrettyPrinter(ModelVisitor):
@@ -190,11 +187,6 @@ class DSLPrettyPrinter(ModelVisitor):
190
187
  self._level -= 1
191
188
  return variables_str
192
189
 
193
- def visit_QuantumVariableDeclaration(
194
- self, var_decl: QuantumVariableDeclaration
195
- ) -> str:
196
- return f"{var_decl.name}: {self.visit(var_decl.quantum_type)}"
197
-
198
190
  def visit_AnonPortDeclaration(self, port_decl: AnonPortDeclaration) -> str:
199
191
  modifier_str = (
200
192
  f"{port_decl.type_modifier} "
@@ -248,9 +240,6 @@ class DSLPrettyPrinter(ModelVisitor):
248
240
  def visit_Bool(self, ctbool: Bool) -> str:
249
241
  return "bool"
250
242
 
251
- def visit_ClassicalList(self, ctlist: ClassicalList) -> str:
252
- return f"{self.visit(ctlist.element_type)}[]"
253
-
254
243
  def visit_ClassicalArray(self, ctarray: ClassicalArray) -> str:
255
244
  element_type = self.visit(ctarray.element_type)
256
245
  if ctarray.length is not None:
@@ -267,9 +256,9 @@ class DSLPrettyPrinter(ModelVisitor):
267
256
  return type_.name
268
257
 
269
258
  def visit_VariableDeclarationStatement(
270
- self, local_decl: VariableDeclarationStatement
259
+ self, var_decl: VariableDeclarationStatement
271
260
  ) -> str:
272
- return f"{self._indent}{self.visit_QuantumVariableDeclaration(local_decl)};\n"
261
+ return f"{self._indent}{var_decl.name}: {self.visit(var_decl.qmod_type)};\n"
273
262
 
274
263
  def visit_AnonQuantumOperandDeclaration(
275
264
  self, op_decl: AnonQuantumOperandDeclaration
@@ -365,10 +354,10 @@ class DSLPrettyPrinter(ModelVisitor):
365
354
  return invert_code
366
355
 
367
356
  def visit_Block(self, block: Block) -> str:
368
- invert_code = f"{self._indent}block {{\n"
369
- invert_code += self._visit_body(block.statements)
370
- invert_code += f"{self._indent}}}\n"
371
- return invert_code
357
+ block_code = f"{self._indent}{{\n"
358
+ block_code += self._visit_body(block.statements)
359
+ block_code += f"{self._indent}}}\n"
360
+ return block_code
372
361
 
373
362
  def _visit_body(self, body: StatementBlock) -> str:
374
363
  code = ""
@@ -447,6 +436,17 @@ class DSLPrettyPrinter(ModelVisitor):
447
436
  def visit_OperandIdentifier(self, op: OperandIdentifier) -> str:
448
437
  return str(op)
449
438
 
439
+ def visit_SetBoundsStatement(self, op: SetBoundsStatement) -> str:
440
+ target = self.visit(op.target)
441
+ if op.lower_bound is None or op.upper_bound is None:
442
+ return f"{self._indent}reset_bounds({target});\n"
443
+ else:
444
+ lower_bound = self.visit(op.lower_bound)
445
+ upper_bound = self.visit(op.upper_bound)
446
+ return (
447
+ f"{self._indent}reset_bounds({target}, {lower_bound}, {upper_bound});\n"
448
+ )
449
+
450
450
  @property
451
451
  def _indent(self) -> str:
452
452
  return " " * self._level
@@ -6,8 +6,9 @@ from typing import Callable
6
6
 
7
7
  import numpy as np
8
8
 
9
+ from classiq.interface.constants import DEFAULT_DECIMAL_PRECISION
10
+
9
11
  import classiq
10
- from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
11
12
 
12
13
  IDENTIFIER = re.compile(r"[a-zA-Z_]\w*")
13
14
  BINARY_OPS: Mapping[type[ast.operator], str] = {
@@ -3,13 +3,13 @@ from typing import Optional, Union, cast
3
3
 
4
4
  import black
5
5
 
6
+ from classiq.interface.constants import DEFAULT_DECIMAL_PRECISION
6
7
  from classiq.interface.exceptions import ClassiqInternalError
7
8
  from classiq.interface.generator.constant import Constant
8
9
  from classiq.interface.generator.expressions.expression import Expression
9
10
  from classiq.interface.generator.functions.classical_type import (
10
11
  Bool,
11
12
  ClassicalArray,
12
- ClassicalList,
13
13
  ClassicalTuple,
14
14
  Integer,
15
15
  Real,
@@ -30,6 +30,8 @@ from classiq.interface.generator.types.struct_declaration import StructDeclarati
30
30
  from classiq.interface.generator.visitor import NodeType, Visitor
31
31
  from classiq.interface.model.allocate import Allocate
32
32
  from classiq.interface.model.bind_operation import BindOperation
33
+ from classiq.interface.model.block import Block
34
+ from classiq.interface.model.bounds import SetBoundsStatement
33
35
  from classiq.interface.model.classical_if import ClassicalIf
34
36
  from classiq.interface.model.classical_parameter_declaration import (
35
37
  AnonClassicalParameterDeclaration,
@@ -75,9 +77,6 @@ from classiq.interface.model.quantum_type import (
75
77
  QuantumBitvector,
76
78
  QuantumNumeric,
77
79
  )
78
- from classiq.interface.model.quantum_variable_declaration import (
79
- QuantumVariableDeclaration,
80
- )
81
80
  from classiq.interface.model.repeat import Repeat
82
81
  from classiq.interface.model.statement_block import StatementBlock
83
82
  from classiq.interface.model.variable_declaration_statement import (
@@ -88,10 +87,11 @@ from classiq.interface.model.within_apply_operation import WithinApply
88
87
  import classiq
89
88
  from classiq.qmod.builtins.functions import BUILTIN_FUNCTION_DECLARATIONS
90
89
  from classiq.qmod.pretty_print.expression_to_python import transform_expression
91
- from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
92
90
 
93
91
 
94
92
  class VariableDeclarationAssignment(Visitor):
93
+ # FIXME: Support classical variable types
94
+
95
95
  def __init__(self, pretty_printer: "PythonPrettyPrinter") -> None:
96
96
  self.pretty_printer = pretty_printer
97
97
 
@@ -252,11 +252,6 @@ class PythonPrettyPrinter(ModelVisitor):
252
252
  self._level -= 1
253
253
  return variables_str
254
254
 
255
- def visit_QuantumVariableDeclaration(
256
- self, var_decl: QuantumVariableDeclaration
257
- ) -> str:
258
- return f"{var_decl.name}: {self.visit(var_decl.quantum_type)}"
259
-
260
255
  def visit_AnonPortDeclaration(self, port_decl: AnonPortDeclaration) -> str:
261
256
  var_type = self._extract_port_type(port_decl)
262
257
  return f"{port_decl.name}: {var_type}"
@@ -338,10 +333,6 @@ class PythonPrettyPrinter(ModelVisitor):
338
333
  self._imports["CBool"] = 1
339
334
  return "CBool"
340
335
 
341
- def visit_ClassicalList(self, ctlist: ClassicalList) -> str:
342
- self._imports["CArray"] = 1
343
- return f"CArray[{self.visit(ctlist.element_type)}]"
344
-
345
336
  def visit_ClassicalArray(self, ctarray: ClassicalArray) -> str:
346
337
  self._imports["CArray"] = 1
347
338
  element_type = self.visit(ctarray.element_type)
@@ -366,14 +357,23 @@ class PythonPrettyPrinter(ModelVisitor):
366
357
  self._imports[type_.name] = 1
367
358
 
368
359
  def visit_VariableDeclarationStatement(
369
- self, local_decl: VariableDeclarationStatement
360
+ self,
361
+ local_decl: VariableDeclarationStatement,
362
+ walrus: bool = False,
370
363
  ) -> str:
371
364
  type_name, params = VariableDeclarationAssignment(self).visit(
372
- local_decl.quantum_type
365
+ local_decl.qmod_type
373
366
  )
374
367
  params = [f'"{local_decl.name}"'] + params
375
368
  param_args = ", ".join(params)
376
- return f"{self._indent}{self.visit_QuantumVariableDeclaration(local_decl)} = {type_name}({param_args})\n"
369
+
370
+ res = f"{self._indent}{local_decl.name}"
371
+ if walrus:
372
+ res += " := "
373
+ else:
374
+ res += f": {self.visit(local_decl.qmod_type)} = "
375
+ res += f"{type_name}({param_args})\n"
376
+ return res
377
377
 
378
378
  def _visit_operand_arg_decl(self, arg_decl: AnonPositionalArg) -> str:
379
379
  if isinstance(arg_decl, AnonPortDeclaration):
@@ -481,6 +481,10 @@ class PythonPrettyPrinter(ModelVisitor):
481
481
  self._imports["invert"] = 1
482
482
  return f"{self._indent}invert({self._visit_body(invert.body)})\n"
483
483
 
484
+ def visit_Block(self, block: Block) -> str:
485
+ self._imports["block"] = 1
486
+ return f"{self._indent}block({self._visit_body(block.statements)})\n"
487
+
484
488
  def _visit_body(
485
489
  self, body: StatementBlock, operand_arguments: Optional[list[str]] = None
486
490
  ) -> str:
@@ -493,10 +497,8 @@ class PythonPrettyPrinter(ModelVisitor):
493
497
  self._level += 1
494
498
  for i, statement in enumerate(body):
495
499
  if isinstance(statement, VariableDeclarationStatement):
496
- raise AssertionError(
497
- "pretty printing variable declaration statements in quantum lambda function is unsupported."
498
- )
499
- if isinstance(statement, AmplitudeLoadingOperation):
500
+ code += self.visit_VariableDeclarationStatement(statement, walrus=True)
501
+ elif isinstance(statement, AmplitudeLoadingOperation):
500
502
  code += self.visit_AmplitudeLoadingOperation(statement, in_lambda=True)
501
503
  elif isinstance(statement, ArithmeticOperation):
502
504
  code += self.visit_ArithmeticOperation(statement, in_lambda=True)
@@ -579,6 +581,18 @@ class PythonPrettyPrinter(ModelVisitor):
579
581
  def visit_OperandIdentifier(self, op: OperandIdentifier) -> str:
580
582
  return str(op)
581
583
 
584
+ def visit_SetBoundsStatement(self, op: SetBoundsStatement) -> str:
585
+ self._imports["reset_bounds"] = 1
586
+ target = self.visit(op.target)
587
+ if op.lower_bound is None or op.upper_bound is None:
588
+ return f"{self._indent}reset_bounds({target})\n"
589
+ else:
590
+ lower_bound = self.visit(op.lower_bound)
591
+ upper_bound = self.visit(op.upper_bound)
592
+ return (
593
+ f"{self._indent}reset_bounds({target}, {lower_bound}, {upper_bound})\n"
594
+ )
595
+
582
596
  @property
583
597
  def _indent(self) -> str:
584
598
  return " " * self._level