classiq 0.47.0__py3-none-any.whl → 0.48.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 (56) hide show
  1. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -7
  2. classiq/applications/grover/grover_model_constructor.py +2 -1
  3. classiq/execution/execution_session.py +40 -9
  4. classiq/execution/jobs.py +18 -6
  5. classiq/interface/_version.py +1 -1
  6. classiq/interface/execution/primitives.py +9 -1
  7. classiq/interface/executor/iqae_result.py +3 -3
  8. classiq/interface/executor/result.py +3 -1
  9. classiq/interface/generator/expressions/expression.py +8 -0
  10. classiq/interface/generator/functions/type_name.py +1 -3
  11. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +17 -3
  12. classiq/interface/ide/visual_model.py +3 -4
  13. classiq/interface/model/bind_operation.py +0 -3
  14. classiq/interface/model/port_declaration.py +1 -12
  15. classiq/interface/model/quantum_expressions/arithmetic_operation.py +34 -6
  16. classiq/interface/model/quantum_lambda_function.py +4 -1
  17. classiq/interface/model/quantum_statement.py +16 -1
  18. classiq/interface/model/quantum_variable_declaration.py +0 -22
  19. classiq/interface/server/global_versions.py +4 -4
  20. classiq/model_expansions/capturing/propagated_var_stack.py +5 -2
  21. classiq/model_expansions/closure.py +7 -2
  22. classiq/model_expansions/evaluators/quantum_type_utils.py +0 -7
  23. classiq/model_expansions/generative_functions.py +146 -28
  24. classiq/model_expansions/interpreter.py +11 -5
  25. classiq/model_expansions/quantum_operations/classicalif.py +27 -10
  26. classiq/model_expansions/quantum_operations/control.py +22 -15
  27. classiq/model_expansions/quantum_operations/emitter.py +60 -5
  28. classiq/model_expansions/quantum_operations/expression_operation.py +25 -16
  29. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +167 -95
  30. classiq/model_expansions/quantum_operations/invert.py +12 -6
  31. classiq/model_expansions/quantum_operations/phase.py +15 -3
  32. classiq/model_expansions/quantum_operations/power.py +9 -8
  33. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +20 -5
  34. classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -1
  35. classiq/model_expansions/quantum_operations/repeat.py +32 -13
  36. classiq/model_expansions/quantum_operations/within_apply.py +19 -6
  37. classiq/model_expansions/scope.py +16 -5
  38. classiq/model_expansions/scope_initialization.py +11 -1
  39. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +23 -1
  40. classiq/model_expansions/visitors/variable_references.py +11 -7
  41. classiq/qmod/builtins/__init__.py +10 -0
  42. classiq/qmod/builtins/constants.py +10 -0
  43. classiq/qmod/builtins/functions/state_preparation.py +4 -1
  44. classiq/qmod/builtins/operations.py +43 -163
  45. classiq/qmod/create_model_function.py +1 -1
  46. classiq/qmod/generative.py +14 -5
  47. classiq/qmod/native/pretty_printer.py +9 -5
  48. classiq/qmod/pretty_print/pretty_printer.py +8 -4
  49. classiq/qmod/qmod_constant.py +28 -18
  50. classiq/qmod/qmod_variable.py +43 -23
  51. classiq/qmod/quantum_expandable.py +14 -1
  52. classiq/qmod/semantics/static_semantics_visitor.py +10 -0
  53. classiq/qmod/semantics/validation/constants_validation.py +16 -0
  54. {classiq-0.47.0.dist-info → classiq-0.48.0.dist-info}/METADATA +3 -1
  55. {classiq-0.47.0.dist-info → classiq-0.48.0.dist-info}/RECORD +56 -54
  56. {classiq-0.47.0.dist-info → classiq-0.48.0.dist-info}/WHEEL +0 -0
@@ -44,10 +44,5 @@ def _pauli_terms_to_qmod(hamiltonian: List[PauliTerm]) -> str:
44
44
  return ", ".join(qmod_strings)
45
45
 
46
46
 
47
- def _pauli_dict_to_str(hamiltonian: List[QmodPyStruct]) -> str:
48
- res = []
49
- for struct in hamiltonian:
50
- pauli_str = ", ".join([pauli_enum_to_str(p) for p in struct["pauli"]])
51
- res.append(f'"pauli": [{pauli_str}], "coefficient": {struct["coefficient"]}')
52
-
53
- return f"{{{', '.join(res)}}}"
47
+ def _pauli_dict_to_pauli_terms(hamiltonian: List[QmodPyStruct]) -> List[PauliTerm]:
48
+ return [PauliTerm(**struct) for struct in hamiltonian]
@@ -11,6 +11,7 @@ from classiq.interface.model.native_function_definition import NativeFunctionDef
11
11
  from classiq.interface.model.port_declaration import PortDeclaration
12
12
  from classiq.interface.model.quantum_expressions.arithmetic_operation import (
13
13
  ArithmeticOperation,
14
+ ArithmeticOperationKind,
14
15
  )
15
16
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
16
17
  from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
@@ -110,7 +111,7 @@ def construct_grover_model(
110
111
  ArithmeticOperation(
111
112
  expression=Expression(expr=expression),
112
113
  result_var=HandleBinding(name="res"),
113
- inplace_result=True,
114
+ operation_kind=ArithmeticOperationKind.InplaceXor,
114
115
  ),
115
116
  ],
116
117
  ),
@@ -1,8 +1,9 @@
1
1
  import json
2
2
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union, cast
3
3
 
4
+ from classiq.interface.chemistry.operator import PauliOperator, pauli_integers_to_str
4
5
  from classiq.interface.exceptions import ClassiqValueError
5
- from classiq.interface.execution.primitives import PrimitivesInput
6
+ from classiq.interface.execution.primitives import EstimateInput, PrimitivesInput
6
7
  from classiq.interface.executor.execution_preferences import ExecutionPreferences
7
8
  from classiq.interface.executor.result import (
8
9
  EstimationResult,
@@ -12,7 +13,7 @@ from classiq.interface.generator.functions.qmod_python_interface import QmodPySt
12
13
  from classiq.interface.generator.quantum_program import QuantumProgram
13
14
 
14
15
  from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import (
15
- _pauli_dict_to_str,
16
+ _pauli_dict_to_pauli_terms,
16
17
  _pauli_terms_to_qmod,
17
18
  )
18
19
  from classiq.execution.jobs import ExecutionJob
@@ -51,12 +52,26 @@ def _deserialize_program(program: Program) -> QuantumProgram:
51
52
  )
52
53
 
53
54
 
55
+ def hamiltonian_to_pauli_terms(hamiltonian: Hamiltonian) -> List[PauliTerm]:
56
+ if isinstance(hamiltonian[0], PauliTerm):
57
+ return cast(List[PauliTerm], hamiltonian)
58
+ else:
59
+ return _pauli_dict_to_pauli_terms(cast(List[QmodPyStruct], hamiltonian))
60
+
61
+
54
62
  def to_hamiltonian_str(hamiltonian: Hamiltonian) -> str:
55
- return (
56
- _pauli_terms_to_qmod(cast(List[PauliTerm], hamiltonian))
57
- if isinstance(hamiltonian[0], PauliTerm)
58
- else _pauli_dict_to_str(cast(List[QmodPyStruct], hamiltonian))
59
- )
63
+ return _pauli_terms_to_qmod(hamiltonian_to_pauli_terms(hamiltonian))
64
+
65
+
66
+ def _hamiltonian_to_pauli_operator(hamiltonian: Hamiltonian) -> PauliOperator:
67
+ pauli_list = [
68
+ (
69
+ pauli_integers_to_str(elem.pauli), # type: ignore[arg-type]
70
+ elem.coefficient,
71
+ )
72
+ for elem in hamiltonian_to_pauli_terms(hamiltonian)
73
+ ]
74
+ return PauliOperator(pauli_list=pauli_list)
60
75
 
61
76
 
62
77
  def serialize(
@@ -196,7 +211,7 @@ class ExecutionSession:
196
211
  SupportedPrimitives.SAMPLE, parameters=format_parameters(parameters)
197
212
  )
198
213
  self.program.execution_primitives_input = PrimitivesInput(
199
- sample=[parameters] if parameters is not None else [{}]
214
+ sample=[parse_params(parameters)] if parameters is not None else [{}]
200
215
  )
201
216
  return execute(SerializedQuantumProgram(self.qprog))
202
217
 
@@ -229,7 +244,9 @@ class ExecutionSession:
229
244
  self.program.model.classical_execution_code = generate_code_snippet(
230
245
  SupportedPrimitives.BATCH_SAMPLE, parameters=format_parameters(parameters)
231
246
  )
232
- self.program.execution_primitives_input = PrimitivesInput(sample=parameters)
247
+ self.program.execution_primitives_input = PrimitivesInput(
248
+ sample=[parse_params(params) for params in parameters]
249
+ )
233
250
  return execute(SerializedQuantumProgram(self.qprog))
234
251
 
235
252
  def estimate(
@@ -269,6 +286,14 @@ class ExecutionSession:
269
286
  parameters=format_parameters(parameters),
270
287
  hamiltonian=to_hamiltonian_str(hamiltonian),
271
288
  )
289
+ self.program.execution_primitives_input = PrimitivesInput(
290
+ estimate=EstimateInput(
291
+ hamiltonian=_hamiltonian_to_pauli_operator(hamiltonian),
292
+ parameters=(
293
+ [parse_params(parameters)] if parameters is not None else [{}]
294
+ ),
295
+ )
296
+ )
272
297
  return execute(SerializedQuantumProgram(self.qprog))
273
298
 
274
299
  def batch_estimate(
@@ -308,4 +333,10 @@ class ExecutionSession:
308
333
  parameters=format_parameters(parameters),
309
334
  hamiltonian=to_hamiltonian_str(hamiltonian),
310
335
  )
336
+ self.program.execution_primitives_input = PrimitivesInput(
337
+ estimate=EstimateInput(
338
+ hamiltonian=_hamiltonian_to_pauli_operator(hamiltonian),
339
+ parameters=[parse_params(params) for params in parameters],
340
+ )
341
+ )
311
342
  return execute(SerializedQuantumProgram(self.qprog))
classiq/execution/jobs.py CHANGED
@@ -162,11 +162,16 @@ class ExecutionJob:
162
162
  ClassiqAPIError: In case the job has failed.
163
163
  """
164
164
  results = self.result()
165
- if len(results) != 1 or not isinstance(
166
- results[0].value, MultipleExecutionDetails
167
- ):
165
+ if len(results) != 1:
168
166
  raise ClassiqExecutionResultError("batch_sample")
169
- return results[0].value.details
167
+
168
+ result = results[0].value
169
+ if isinstance(result, ExecutionDetails):
170
+ return [result]
171
+ if isinstance(result, MultipleExecutionDetails):
172
+ return result.details
173
+
174
+ raise ClassiqExecutionResultError("batch_sample")
170
175
 
171
176
  def get_estimate_result(self) -> EstimationResult:
172
177
  """
@@ -202,9 +207,16 @@ class ExecutionJob:
202
207
  ClassiqAPIError: In case the job has failed.
203
208
  """
204
209
  results = self.result()
205
- if len(results) != 1 or not isinstance(results[0].value, EstimationResults):
210
+ if len(results) != 1:
206
211
  raise ClassiqExecutionResultError("batch_estimate")
207
- return results[0].value.results
212
+
213
+ result = results[0].value
214
+ if isinstance(result, EstimationResult):
215
+ return [result]
216
+ if isinstance(result, EstimationResults):
217
+ return result.results
218
+
219
+ raise ClassiqExecutionResultError("batch_estimate")
208
220
 
209
221
  async def poll_async(self, timeout_sec: Optional[float] = None) -> None:
210
222
  if not self.status.is_final():
@@ -3,5 +3,5 @@ from packaging.version import Version
3
3
  # This file was generated automatically
4
4
  # Please don't track in version control (DONTTRACK)
5
5
 
6
- SEMVER_VERSION = '0.47.0'
6
+ SEMVER_VERSION = '0.48.0'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -2,8 +2,16 @@ from typing import List, Optional
2
2
 
3
3
  from pydantic import BaseModel, Field
4
4
 
5
+ from classiq.interface.chemistry.operator import PauliOperator
5
6
  from classiq.interface.executor.quantum_code import Arguments
7
+ from classiq.interface.helpers.custom_encoders import CUSTOM_ENCODERS
6
8
 
7
9
 
8
- class PrimitivesInput(BaseModel):
10
+ class EstimateInput(BaseModel, json_encoders=CUSTOM_ENCODERS):
11
+ hamiltonian: PauliOperator
12
+ parameters: List[Arguments]
13
+
14
+
15
+ class PrimitivesInput(BaseModel, json_encoders=CUSTOM_ENCODERS):
9
16
  sample: Optional[List[Arguments]] = Field(default=None)
17
+ estimate: Optional[EstimateInput] = Field(default=None)
@@ -1,6 +1,6 @@
1
- from typing import List, Tuple
1
+ from typing import List
2
2
 
3
- from pydantic import BaseModel
3
+ from pydantic import BaseModel, Field
4
4
 
5
5
  from classiq.interface.executor.result import ExecutionDetails
6
6
  from classiq.interface.generator.functions.classical_type import QmodPyObject
@@ -14,6 +14,6 @@ class IQAEIterationData(BaseModel):
14
14
 
15
15
  class IQAEResult(VersionedModel, QmodPyObject):
16
16
  estimation: float
17
- confidence_interval: Tuple[float, float]
17
+ confidence_interval: List[float] = Field(min_items=2, max_items=2)
18
18
  iterations_data: List[IQAEIterationData]
19
19
  warnings: List[str]
@@ -305,7 +305,9 @@ class EstimationMetadata(BaseModel, extra=pydantic.Extra.allow):
305
305
 
306
306
  class EstimationResult(BaseModel, QmodPyObject):
307
307
  value: Complex = pydantic.Field(..., description="Estimation for the operator")
308
- variance: Complex = pydantic.Field(..., description="Variance of the estimation")
308
+ variance: Optional[Complex] = pydantic.Field(
309
+ description="Variance of the estimation", default=None
310
+ )
309
311
  metadata: EstimationMetadata = pydantic.Field(
310
312
  ..., description="Metadata for the estimation"
311
313
  )
@@ -69,6 +69,14 @@ class Expression(HashableASTNode):
69
69
  return self.as_constant(list)
70
70
 
71
71
  def _try_to_immediate_evaluate(self) -> None:
72
+ # FIXME remove special treatment (CAD-22999)
73
+ if self.expr == "SIGNED":
74
+ self._evaluated_expr = EvaluatedExpression(value=True)
75
+ return
76
+ if self.expr == "UNSIGNED":
77
+ self._evaluated_expr = EvaluatedExpression(value=False)
78
+ return
79
+
72
80
  try:
73
81
  result = ast.literal_eval(self.expr)
74
82
  if isinstance(result, (int, float, bool)):
@@ -67,9 +67,7 @@ class TypeName(ClassicalType, QuantumType): # type:ignore[misc]
67
67
  return self._assigned_fields
68
68
 
69
69
  def _set_fields(self, fields: Mapping[str, "ConcreteQuantumType"]) -> None:
70
- from classiq.qmod.model_state_container import QMODULE
71
-
72
- QMODULE.qstruct_decls[self.name].fields = fields
70
+ self._assigned_fields = fields
73
71
 
74
72
 
75
73
  class Enum(TypeName):
@@ -1,12 +1,26 @@
1
1
  from typing import Dict, Optional, Set
2
2
 
3
3
  import pydantic
4
+ import sympy
4
5
 
5
6
  from classiq.interface.backend.pydantic_backend import PydanticExecutionParameter
7
+ from classiq.interface.exceptions import ClassiqValueError
8
+ from classiq.interface.generator.parameters import ParameterType
6
9
 
7
10
 
8
11
  class FunctionExecutionData(pydantic.BaseModel):
9
- power_parameter: Optional[PydanticExecutionParameter] = pydantic.Field(default=None)
12
+ power_parameter: Optional[ParameterType] = pydantic.Field(default=None)
13
+
14
+ @property
15
+ def power_var(self) -> Optional[str]:
16
+ if self.power_parameter is None:
17
+ return None
18
+ power_vars = sympy.sympify(self.power_parameter).free_symbols
19
+ if len(power_vars) != 1:
20
+ raise ClassiqValueError(
21
+ f"Power parameter expression: {self.power_parameter} must contain exactly one variable"
22
+ )
23
+ return str(list(power_vars)[0])
10
24
 
11
25
 
12
26
  class ExecutionData(pydantic.BaseModel):
@@ -19,7 +33,7 @@ class ExecutionData(pydantic.BaseModel):
19
33
  self,
20
34
  ) -> Set[PydanticExecutionParameter]:
21
35
  return {
22
- function_execution_data.power_parameter
36
+ function_execution_data.power_var
23
37
  for function_execution_data in self.function_execution.values()
24
- if function_execution_data.power_parameter is not None
38
+ if function_execution_data.power_var is not None
25
39
  }
@@ -69,10 +69,9 @@ class Operation(pydantic.BaseModel):
69
69
  target_qubits: Tuple[int, ...]
70
70
  parameters: List[OperationParameter] = pydantic.Field(default_factory=list)
71
71
  operation_level: OperationLevel
72
- # This field is meant to identify unique operations, such as variable initialization
73
- # These will be visualized differently. We don't identify them yet, though, so
74
- # we always set this field to be REGULAR
75
- operation_type: OperationType = pydantic.Field(default=OperationType.REGULAR)
72
+ operation_type: OperationType = pydantic.Field(
73
+ description="Identifies unique operations that are visualized differently"
74
+ )
76
75
 
77
76
 
78
77
  class ProgramVisualModel(VersionedModel):
@@ -56,6 +56,3 @@ class BindOperation(QuantumOperation):
56
56
  raise ClassiqValueError(f"Cannot bind '{handle}'")
57
57
 
58
58
  return handles
59
-
60
- def reversed(self) -> "BindOperation":
61
- return BindOperation(in_handles=self.out_handles, out_handles=self.in_handles)
@@ -1,25 +1,20 @@
1
- from typing import Any, Dict, Literal, Mapping, Optional
1
+ from typing import Any, Dict, Literal, Mapping
2
2
 
3
3
  import pydantic
4
4
 
5
5
  from classiq.interface.exceptions import ClassiqInternalError, ClassiqValueError
6
- from classiq.interface.generator.expressions.expression import Expression
7
6
  from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
8
7
  from classiq.interface.generator.functions.port_declaration import (
9
8
  PortDeclarationDirection,
10
9
  )
11
10
  from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
12
11
  from classiq.interface.model.parameter import Parameter
13
- from classiq.interface.model.quantum_variable_declaration import (
14
- QuantumVariableDeclaration,
15
- )
16
12
 
17
13
 
18
14
  class AnonPortDeclaration(Parameter):
19
15
  kind: Literal["PortDeclaration"]
20
16
 
21
17
  quantum_type: ConcreteQuantumType
22
- size: Optional[Expression] = pydantic.Field(default=None, exclude=True)
23
18
  direction: PortDeclarationDirection
24
19
 
25
20
  @pydantic.root_validator(pre=True)
@@ -37,12 +32,6 @@ class AnonPortDeclaration(Parameter):
37
32
 
38
33
  return direction
39
34
 
40
- @pydantic.validator("size")
41
- def _propagate_size_to_type(
42
- cls, size: Optional[Expression], values: Mapping[str, Any]
43
- ) -> Optional[Expression]:
44
- return QuantumVariableDeclaration._propagate_size_to_type(size, values)
45
-
46
35
  def rename(self, new_name: str) -> "PortDeclaration":
47
36
  if type(self) not in (AnonPortDeclaration, PortDeclaration):
48
37
  raise ClassiqInternalError
@@ -1,7 +1,8 @@
1
- from typing import Dict, Literal, Mapping, Sequence
1
+ from typing import Dict, Literal, Mapping, Optional, Sequence
2
2
 
3
3
  import pydantic
4
4
 
5
+ from classiq.interface.enum_utils import StrEnum
5
6
  from classiq.interface.generator.arith.arithmetic import (
6
7
  ARITHMETIC_EXPRESSION_RESULT_NAME,
7
8
  compute_arithmetic_result_type,
@@ -17,13 +18,40 @@ from classiq.interface.model.quantum_statement import HandleMetadata
17
18
  from classiq.interface.model.quantum_type import QuantumType
18
19
 
19
20
 
21
+ class ArithmeticOperationKind(StrEnum):
22
+ InplaceAdd = "inplace_add"
23
+ Assignment = "assignment"
24
+ InplaceXor = "inplace_xor"
25
+
26
+
20
27
  class ArithmeticOperation(QuantumAssignmentOperation):
21
28
  kind: Literal["ArithmeticOperation"]
22
29
 
23
- inplace_result: bool = pydantic.Field(
30
+ inplace_result: Optional[bool] = pydantic.Field(
24
31
  description="Determines whether the result variable is initialized",
32
+ default=None,
33
+ exclude=True,
34
+ )
35
+
36
+ operation_kind: ArithmeticOperationKind = pydantic.Field(
37
+ default=ArithmeticOperationKind.InplaceXor,
25
38
  )
26
39
 
40
+ @property
41
+ def is_inplace(self) -> bool:
42
+ return self.get_operation_kind() in (
43
+ ArithmeticOperationKind.InplaceXor,
44
+ ArithmeticOperationKind.InplaceAdd,
45
+ )
46
+
47
+ def get_operation_kind(self) -> ArithmeticOperationKind:
48
+ if hasattr(self, "inplace_result"):
49
+ if self.inplace_result is False:
50
+ return ArithmeticOperationKind.Assignment
51
+ if self.inplace_result is True:
52
+ return ArithmeticOperationKind.InplaceXor
53
+ return self.operation_kind
54
+
27
55
  def initialize_var_types(
28
56
  self,
29
57
  var_types: Dict[str, QuantumType],
@@ -39,7 +67,7 @@ class ArithmeticOperation(QuantumAssignmentOperation):
39
67
  self,
40
68
  ) -> Mapping[str, ConcreteHandleBinding]:
41
69
  inouts = dict(super().wiring_inouts)
42
- if self.inplace_result:
70
+ if self.is_inplace:
43
71
  inouts[self.result_name()] = self.result_var
44
72
  return inouts
45
73
 
@@ -49,7 +77,7 @@ class ArithmeticOperation(QuantumAssignmentOperation):
49
77
  HandleMetadata(handle=handle, readable_location="in an expression")
50
78
  for handle in self.var_handles
51
79
  ]
52
- if self.inplace_result:
80
+ if self.is_inplace:
53
81
  inouts.append(
54
82
  HandleMetadata(
55
83
  handle=self.result_var,
@@ -60,13 +88,13 @@ class ArithmeticOperation(QuantumAssignmentOperation):
60
88
 
61
89
  @property
62
90
  def wiring_outputs(self) -> Mapping[str, HandleBinding]:
63
- if self.inplace_result:
91
+ if self.is_inplace:
64
92
  return {}
65
93
  return super().wiring_outputs
66
94
 
67
95
  @property
68
96
  def readable_outputs(self) -> Sequence[HandleMetadata]:
69
- if self.inplace_result:
97
+ if self.is_inplace:
70
98
  return []
71
99
  return [
72
100
  HandleMetadata(
@@ -19,7 +19,7 @@ class QuantumLambdaFunction(ASTNode):
19
19
 
20
20
  rename_params: Dict[str, str] = pydantic.Field(
21
21
  default_factory=dict,
22
- exclude=False,
22
+ exclude=True,
23
23
  )
24
24
 
25
25
  pos_rename_params: List[str] = pydantic.Field(
@@ -41,6 +41,9 @@ class QuantumLambdaFunction(ASTNode):
41
41
  def py_callable(self) -> Callable:
42
42
  return self._py_callable
43
43
 
44
+ def is_generative(self) -> bool:
45
+ return self.py_callable is not None
46
+
44
47
  def set_py_callable(self, py_callable: Callable) -> None:
45
48
  self._py_callable = py_callable
46
49
 
@@ -1,6 +1,7 @@
1
1
  from dataclasses import dataclass
2
- from typing import Any, Dict, Mapping, Optional, Sequence
2
+ from typing import Any, Callable, Dict, Mapping, Optional, Sequence
3
3
 
4
+ import pydantic
4
5
  from pydantic import Extra, root_validator
5
6
 
6
7
  from classiq.interface.ast_node import ASTNode
@@ -29,6 +30,8 @@ class HandleMetadata:
29
30
 
30
31
 
31
32
  class QuantumOperation(QuantumStatement):
33
+ _generative_blocks: Dict[str, Callable] = pydantic.PrivateAttr(default_factory=dict)
34
+
32
35
  @property
33
36
  def wiring_inputs(self) -> Mapping[str, HandleBinding]:
34
37
  return dict()
@@ -64,3 +67,15 @@ class QuantumOperation(QuantumStatement):
64
67
  @property
65
68
  def readable_outputs(self) -> Sequence[HandleMetadata]:
66
69
  return [HandleMetadata(handle=handle) for handle in self.outputs]
70
+
71
+ def set_generative_block(self, block_name: str, py_callable: Callable) -> None:
72
+ self._generative_blocks[block_name] = py_callable
73
+
74
+ def get_generative_block(self, block_name: str) -> Callable:
75
+ return self._generative_blocks[block_name]
76
+
77
+ def has_generative_block(self, block_name: str) -> bool:
78
+ return block_name in self._generative_blocks
79
+
80
+ def is_generative(self) -> bool:
81
+ return len(self._generative_blocks) > 0
@@ -1,29 +1,7 @@
1
- from typing import Any, Mapping, Optional
2
-
3
- import pydantic
4
-
5
1
  from classiq.interface.ast_node import ASTNode
6
- from classiq.interface.generator.expressions.expression import Expression
7
2
  from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
8
- from classiq.interface.model.quantum_type import (
9
- QuantumBitvector,
10
- QuantumNumeric,
11
- )
12
3
 
13
4
 
14
5
  class QuantumVariableDeclaration(ASTNode):
15
6
  name: str
16
7
  quantum_type: ConcreteQuantumType
17
- size: Optional[Expression] = pydantic.Field(default=None, exclude=True)
18
-
19
- @pydantic.validator("size")
20
- def _propagate_size_to_type(
21
- cls, size: Optional[Expression], values: Mapping[str, Any]
22
- ) -> Optional[Expression]:
23
- if size is not None:
24
- quantum_type = values.get("quantum_type")
25
- if isinstance(quantum_type, QuantumBitvector):
26
- quantum_type.length = size
27
- elif isinstance(quantum_type, QuantumNumeric):
28
- quantum_type.size = size
29
- return size
@@ -1,13 +1,13 @@
1
- from datetime import date, datetime
2
- from typing import Any, Dict, Union
1
+ from datetime import date
2
+ from typing import Any, Dict
3
3
 
4
4
  import pydantic
5
5
  from pydantic import BaseModel
6
6
 
7
7
 
8
8
  class DeprecationInfo(BaseModel):
9
- deprecation_date: Union[datetime, date] = pydantic.Field()
10
- removal_date: Union[datetime, date] = pydantic.Field()
9
+ deprecation_date: date = pydantic.Field()
10
+ removal_date: date = pydantic.Field()
11
11
 
12
12
 
13
13
  class GlobalVersions(BaseModel):
@@ -15,7 +15,7 @@ from classiq.interface.model.quantum_function_call import ArgValue
15
15
  from classiq.interface.model.quantum_statement import QuantumOperation
16
16
 
17
17
  from classiq.model_expansions.capturing.mangling_utils import mangle_captured_var_name
18
- from classiq.model_expansions.closure import FunctionClosure
18
+ from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
19
19
  from classiq.model_expansions.function_builder import OperationBuilder
20
20
  from classiq.model_expansions.scope import QuantumSymbol, Scope
21
21
 
@@ -144,7 +144,10 @@ class PropagatedVarStack:
144
144
  def _get_propagated_var_name(self, var: PropagatedVariable) -> str:
145
145
  if (
146
146
  var.defining_function == self._builder.current_function.name
147
- or self._no_name_conflict(var)
147
+ or not isinstance(
148
+ self._builder.current_function, GenerativeFunctionClosure
149
+ ) # FIXME doesn't work for all cases (CAD-22663)
150
+ and self._no_name_conflict(var)
148
151
  ):
149
152
  handle_name = var.name
150
153
  if var in self._to_mangle:
@@ -40,6 +40,11 @@ class Closure:
40
40
  }
41
41
 
42
42
 
43
+ @dataclass(frozen=True)
44
+ class GenerativeClosure(Closure):
45
+ generative_blocks: Dict[str, GenerativeQFunc] = None # type:ignore[assignment]
46
+
47
+
43
48
  @dataclass(frozen=True)
44
49
  class FunctionClosure(Closure):
45
50
  is_lambda: bool = False
@@ -101,8 +106,8 @@ class FunctionClosure(Closure):
101
106
 
102
107
 
103
108
  @dataclass(frozen=True)
104
- class GenerativeFunctionClosure(FunctionClosure):
105
- generative_function: GenerativeQFunc = None # type:ignore[assignment]
109
+ class GenerativeFunctionClosure(GenerativeClosure, FunctionClosure):
110
+ pass
106
111
 
107
112
 
108
113
  NestedFunctionClosureT = Union[FunctionClosure, List["NestedFunctionClosureT"]]
@@ -10,7 +10,6 @@ from classiq.interface.generator.functions.type_name import (
10
10
  TypeName,
11
11
  )
12
12
  from classiq.interface.model.bind_operation import BindOperation
13
- from classiq.interface.model.inplace_binary_operation import BinaryOperation
14
13
  from classiq.interface.model.quantum_type import (
15
14
  QuantumBit,
16
15
  QuantumBitvector,
@@ -227,9 +226,3 @@ def validate_inplace_binary_op_vars(
227
226
  raise ClassiqExpansionError(
228
227
  f"Cannot perform `{operation_name}` operation with non numeric target {target_var.handle}"
229
228
  )
230
- if operation_name != BinaryOperation.Xor.value and (
231
- value_var.quantum_type.sign_value or target_var.quantum_type.sign_value
232
- ):
233
- raise ClassiqExpansionError(
234
- f"Cannot perform `{operation_name}` operation with signed variables"
235
- )