classiq 0.66.1__py3-none-any.whl → 0.67.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 (50) hide show
  1. classiq/applications/finance/finance_model_constructor.py +9 -0
  2. classiq/applications/grover/grover_model_constructor.py +10 -0
  3. classiq/applications/qnn/qlayer.py +8 -2
  4. classiq/applications/qsvm/qsvm_model_constructor.py +9 -0
  5. classiq/interface/_version.py +1 -1
  6. classiq/interface/debug_info/debug_info.py +12 -0
  7. classiq/interface/exceptions.py +2 -5
  8. classiq/interface/generator/arith/argument_utils.py +1 -1
  9. classiq/interface/generator/arith/arithmetic.py +3 -1
  10. classiq/interface/generator/arith/binary_ops.py +3 -0
  11. classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
  12. classiq/interface/generator/functions/type_name.py +2 -2
  13. classiq/interface/generator/generated_circuit_data.py +34 -1
  14. classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
  15. classiq/interface/generator/hva.py +1 -1
  16. classiq/interface/generator/model/preferences/preferences.py +8 -1
  17. classiq/interface/generator/reset.py +14 -0
  18. classiq/interface/generator/ucc.py +1 -1
  19. classiq/interface/interface_version.py +1 -1
  20. classiq/interface/model/quantum_statement.py +13 -0
  21. classiq/model_expansions/atomic_expression_functions_defs.py +2 -1
  22. classiq/model_expansions/capturing/captured_vars.py +184 -54
  23. classiq/model_expansions/closure.py +6 -3
  24. classiq/model_expansions/evaluators/control.py +14 -38
  25. classiq/model_expansions/function_builder.py +19 -14
  26. classiq/model_expansions/generative_functions.py +7 -11
  27. classiq/model_expansions/interpreters/base_interpreter.py +14 -5
  28. classiq/model_expansions/interpreters/generative_interpreter.py +9 -8
  29. classiq/model_expansions/quantum_operations/allocate.py +6 -2
  30. classiq/model_expansions/quantum_operations/bind.py +65 -13
  31. classiq/model_expansions/quantum_operations/call_emitter.py +79 -10
  32. classiq/model_expansions/quantum_operations/classicalif.py +5 -2
  33. classiq/model_expansions/quantum_operations/emitter.py +8 -1
  34. classiq/model_expansions/quantum_operations/repeat.py +7 -2
  35. classiq/model_expansions/quantum_operations/shallow_emitter.py +1 -1
  36. classiq/model_expansions/quantum_operations/variable_decleration.py +11 -1
  37. classiq/open_library/functions/amplitude_amplification.py +4 -4
  38. classiq/open_library/functions/grover.py +5 -5
  39. classiq/qmod/builtins/functions/__init__.py +3 -0
  40. classiq/qmod/builtins/functions/mid_circuit_measurement.py +15 -0
  41. classiq/qmod/quantum_function.py +4 -0
  42. classiq/qmod/semantics/annotation/call_annotation.py +8 -2
  43. classiq/qmod/semantics/annotation/model_annotation.py +9 -0
  44. classiq/qmod/semantics/error_manager.py +0 -6
  45. classiq/qmod/semantics/static_semantics_visitor.py +0 -347
  46. {classiq-0.66.1.dist-info → classiq-0.67.0.dist-info}/METADATA +1 -1
  47. {classiq-0.66.1.dist-info → classiq-0.67.0.dist-info}/RECORD +48 -47
  48. classiq/qmod/semantics/validation/func_call_validation.py +0 -99
  49. classiq/qmod/semantics/validation/handle_validation.py +0 -85
  50. {classiq-0.66.1.dist-info → classiq-0.67.0.dist-info}/WHEEL +0 -0
@@ -1,3 +1,4 @@
1
+ import warnings
1
2
  from math import floor, log
2
3
  from typing import Union
3
4
 
@@ -32,6 +33,14 @@ def construct_finance_model(
32
33
  finance_function_input: FinanceFunctionInput,
33
34
  phase_port_size: int,
34
35
  ) -> SerializedModel:
36
+ warnings.warn(
37
+ "Function 'construct_finance_model' has been deprecated and will no longer"
38
+ "be supported starting on 03/02/2025 the earliest\nHint: It is now possible to "
39
+ "implement Option Pricing in pure Qmod. For example, see the Option Pricing notebook on the "
40
+ "Classiq library at https://github.com/Classiq/classiq-library/blob/main/applications/finance/option_pricing/option_pricing.ipynb",
41
+ category=DeprecationWarning,
42
+ stacklevel=2,
43
+ )
35
44
  if isinstance(finance_model_input, LogNormalModelInput):
36
45
  finance_model = f"struct_literal(LogNormalModel, num_qubits={finance_model_input.num_qubits}, mu={finance_model_input.mu}, sigma={finance_model_input.sigma})"
37
46
  finance_function = "log_normal_finance"
@@ -1,3 +1,5 @@
1
+ import warnings
2
+
1
3
  from classiq.interface.generator.expressions.expression import Expression
2
4
  from classiq.interface.generator.functions.port_declaration import (
3
5
  PortDeclarationDirection,
@@ -90,6 +92,14 @@ def construct_grover_model(
90
92
  expression: str,
91
93
  num_reps: int = 1,
92
94
  ) -> SerializedModel:
95
+ warnings.warn(
96
+ "Function 'construct_grover_model' has been deprecated and will no longer"
97
+ "be supported starting on 03/02/2025 the earliest\nHint: It is now possible to "
98
+ "implement the Grover algorithm in pure Qmod. For example, see the Grover notebook on the "
99
+ "Classiq library at https://github.com/Classiq/classiq-library/blob/main/algorithms/grover/3_sat_grover/3_sat_grover.ipynb",
100
+ category=DeprecationWarning,
101
+ stacklevel=2,
102
+ )
93
103
  predicate_port_decls = grover_main_port_declarations(
94
104
  definitions, PortDeclarationDirection.Inout
95
105
  )
@@ -244,9 +244,11 @@ class QLayer(nn.Module):
244
244
 
245
245
  self.weight = Parameter(value)
246
246
 
247
- @staticmethod
248
- def _make_execute(quantum_program: SerializedQuantumProgram) -> ExecuteFunction:
247
+ def _make_execute(
248
+ self, quantum_program: SerializedQuantumProgram
249
+ ) -> ExecuteFunction:
249
250
  session = ExecutionSession(quantum_program)
251
+ self._session = session
250
252
 
251
253
  def execute(_ignored: Any, arguments: MultipleArguments) -> ResultsCollection:
252
254
  result: ResultsCollection = []
@@ -266,3 +268,7 @@ class QLayer(nn.Module):
266
268
  self._post_process,
267
269
  self._epsilon,
268
270
  )
271
+
272
+ def _cleanup(self) -> None:
273
+ if hasattr(self, "_session"):
274
+ self._session.close()
@@ -1,3 +1,4 @@
1
+ import warnings
1
2
  from typing import Any
2
3
 
3
4
  from classiq.interface.applications.qsvm import DataList, LabelsInt
@@ -92,6 +93,14 @@ def construct_qsvm_model(
92
93
  feature_map_function_name: str,
93
94
  **kwargs: Any,
94
95
  ) -> SerializedModel:
96
+ warnings.warn(
97
+ "Function 'construct_qsvm_model' has been deprecated and will no longer"
98
+ "be supported starting on 03/02/2025 the earliest\nHint: It is now possible to "
99
+ "implement QSVM in pure Qmod. For example, see the QSVM notebook on the "
100
+ "Classiq library at https://github.com/Classiq/classiq-library/blob/main/algorithms/qml/qsvm/qsvm.ipynb",
101
+ category=DeprecationWarning,
102
+ stacklevel=2,
103
+ )
95
104
  qsvm_qmod = Model(
96
105
  functions=[
97
106
  NativeFunctionDefinition(
@@ -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.66.1'
6
+ SEMVER_VERSION = '0.67.0'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -32,6 +32,7 @@ class FunctionDebugInfo(BaseModel):
32
32
  statement_type: Union[StatementType, None] = None
33
33
  is_allocate_or_free: bool = Field(default=False)
34
34
  is_inverse: bool = Field(default=False)
35
+ release_by_inverse: bool = Field(default=False)
35
36
  port_to_passed_variable_map: dict[str, str] = Field(default_factory=dict)
36
37
  node: Optional[ConcreteQuantumStatement] = None
37
38
 
@@ -86,3 +87,14 @@ class DebugInfoCollection(BaseModel):
86
87
  if (debug_info := self.get(key)) is None:
87
88
  return None
88
89
  return self.blackbox_data.get(debug_info.name)
90
+
91
+
92
+ def new_function_debug_info_by_node(
93
+ node: ConcreteQuantumStatement,
94
+ ) -> FunctionDebugInfo:
95
+ return FunctionDebugInfo(
96
+ name="",
97
+ parameters=dict(),
98
+ level=OperationLevel.QMOD_STATEMENT,
99
+ node=node._as_back_ref(),
100
+ )
@@ -6,7 +6,8 @@ _logger = logging.getLogger(__name__)
6
6
 
7
7
  CLASSIQ_SLACK_COMMUNITY_LINK = (
8
8
  "\nIf you need further assistance, please reach out on our Community Slack channel "
9
- "at: https://short.classiq.io/join-slack"
9
+ "at: https://short.classiq.io/join-slack or open a support ticket at: "
10
+ "https://classiq-community.freshdesk.com/support/tickets/new"
10
11
  )
11
12
 
12
13
 
@@ -177,10 +178,6 @@ class ClassiqExecutorInvalidHamiltonianError(ClassiqCombOptError):
177
178
  super().__init__("Invalid hamiltonian")
178
179
 
179
180
 
180
- class ClassiqSemanticError(ClassiqError):
181
- pass
182
-
183
-
184
181
  class ClassiqDeprecationWarning(FutureWarning):
185
182
  pass
186
183
 
@@ -91,7 +91,7 @@ def unsigned_integer_interpretation(
91
91
  int_val <<= fraction_digits_diff
92
92
 
93
93
  # extend sign bit
94
- if int(value) < 0:
94
+ if value < 0:
95
95
  bin_val = number_utils.binary_string(int_val)
96
96
  bin_val += "1" * (register.size - len(bin_val))
97
97
  int_val = number_utils.binary_to_int(bin_val[::-1])
@@ -32,6 +32,8 @@ ARITHMETIC_EXPRESSION_TARGET_NAME: Final[str] = "arithmetic_target"
32
32
  ARITHMETIC_EXPRESSION_RESULT_NAME: Final[str] = "expression_result"
33
33
  ARITHMETIC_EXPRESSION_GARBAGE_NAME: Final[str] = "expression_garbage"
34
34
 
35
+ TARGET_ASSIGNMENT_ERROR = "Expression does not support target assignment"
36
+
35
37
 
36
38
  def is_zero(expr: str) -> bool:
37
39
  return is_constant(expr) and float(expr) == 0
@@ -60,7 +62,7 @@ class Arithmetic(ArithmeticExpressionABC):
60
62
  degree or operation_allows_target(id2op(node))
61
63
  for node, degree in graph.out_degree
62
64
  ):
63
- raise ClassiqValueError("Expression does not support target assignment")
65
+ raise ClassiqValueError(TARGET_ASSIGNMENT_ERROR)
64
66
 
65
67
  def _create_ios(self) -> None:
66
68
  self._inputs = {
@@ -398,6 +398,7 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
398
398
  right_arg=-self.effective_right_arg,
399
399
  output_size=self.output_size,
400
400
  inplace_arg=self.inplace_arg,
401
+ machine_precision=self.machine_precision,
401
402
  )
402
403
  return adder_params.garbage_output_size()
403
404
 
@@ -406,6 +407,7 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
406
407
  output_size=self.negation_output_size,
407
408
  inplace=self.should_inplace_negation,
408
409
  bypass_bounds_validation=True,
410
+ machine_precision=self.machine_precision,
409
411
  )
410
412
  negation_result = negation_params.result_register
411
413
  if self.output_size is None and max(self.effective_right_arg.bounds) > 0:
@@ -425,6 +427,7 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
425
427
  right_arg=negation_result,
426
428
  output_size=self.output_size,
427
429
  inplace_arg=self.arg_to_inplace_adder,
430
+ machine_precision=self.machine_precision,
428
431
  )
429
432
  negation_garbage_size = negation_params.garbage_output_size() * int(
430
433
  not self.should_uncompute_negation
@@ -65,6 +65,7 @@ from classiq.interface.generator.piecewise_linear_amplitude_loading import (
65
65
  from classiq.interface.generator.qft import QFT
66
66
  from classiq.interface.generator.qsvm import QSVMFeatureMap
67
67
  from classiq.interface.generator.randomized_benchmarking import RandomizedBenchmarking
68
+ from classiq.interface.generator.reset import Reset
68
69
  from classiq.interface.generator.standard_gates.standard_gates_param_list import (
69
70
  standard_gate_function_param_library,
70
71
  )
@@ -150,6 +151,7 @@ function_param_library_without_self_reference: FunctionParamLibrary = (
150
151
  PiecewiseLinearRotationAmplitudeLoading,
151
152
  HadamardTransform,
152
153
  Copy,
154
+ Reset,
153
155
  },
154
156
  standard_gate_function_param_library.param_list,
155
157
  oracle_function_param_library.param_list,
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Any, Literal, Optional
3
3
 
4
4
  import pydantic
5
5
 
6
- from classiq.interface.exceptions import ClassiqInternalError
6
+ from classiq.interface.exceptions import ClassiqExpansionError
7
7
  from classiq.interface.generator.expressions.qmod_qstruct_proxy import QmodQStructProxy
8
8
  from classiq.interface.generator.functions.classical_type import (
9
9
  ClassicalType,
@@ -59,7 +59,7 @@ class TypeName(ClassicalType, QuantumType):
59
59
  @property
60
60
  def fields(self) -> Mapping[str, "ConcreteQuantumType"]:
61
61
  if self._assigned_fields is None:
62
- raise ClassiqInternalError("Fields not set")
62
+ raise ClassiqExpansionError(f"Type {self.name!r} is undefined")
63
63
  return self._assigned_fields
64
64
 
65
65
  @property
@@ -135,6 +135,7 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
135
135
  level: OperationLevel = pydantic.Field(default=OperationLevel.UNKNOWN)
136
136
  parameters: list[OperationParameter] = list()
137
137
  port_to_passed_variable_map: dict[str, str] = pydantic.Field(default={})
138
+ release_by_inverse: bool = pydantic.Field(default=False)
138
139
 
139
140
  model_config = ConfigDict(extra="allow")
140
141
  # Temporary field to store the override debug info for parallel old/new visualization
@@ -227,14 +228,46 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
227
228
  )
228
229
 
229
230
  def inverse(self) -> "FunctionDebugInfoInterface":
230
- inverted_children = [child.inverse() for child in self.children[::-1]]
231
+ if self.override_debug_info is not None:
232
+ self.override_debug_info = self.override_debug_info.inverse()
233
+ return self
234
+ inverse_generated_function = (
235
+ self.generated_function.model_copy(
236
+ update=dict(registers=self._inverse_registers)
237
+ )
238
+ if self.generated_function
239
+ else None
240
+ )
241
+ inverted_children = [child.inverse() for child in reversed(self.children)]
231
242
  return self.model_copy(
232
243
  update=dict(
233
244
  is_inverse=not self.is_inverse,
234
245
  children=inverted_children,
246
+ generated_function=inverse_generated_function,
235
247
  )
236
248
  )
237
249
 
250
+ @property
251
+ def _inverse_registers(self) -> list[GeneratedRegister]:
252
+ return [
253
+ reg.model_copy(update=dict(role=self._inverse_register_role(reg.role)))
254
+ for reg in self.registers
255
+ ]
256
+
257
+ def _inverse_register_role(self, role: RegisterRole) -> RegisterRole:
258
+ if role is RegisterRole.INPUT:
259
+ return RegisterRole.OUTPUT
260
+ if role is RegisterRole.EXPLICIT_ZERO_INPUT or role is RegisterRole.ZERO_INPUT:
261
+ if self.release_by_inverse:
262
+ return RegisterRole.ZERO_OUTPUT
263
+ return RegisterRole.OUTPUT
264
+ if role is RegisterRole.AUXILIARY:
265
+ return RegisterRole.AUXILIARY
266
+ if role is RegisterRole.OUTPUT or role is RegisterRole.GARBAGE_OUTPUT:
267
+ return RegisterRole.INPUT
268
+ if role is RegisterRole.ZERO_OUTPUT:
269
+ return RegisterRole.ZERO_INPUT
270
+
238
271
 
239
272
  def _get_absolute_from_relative(
240
273
  absolute_qubits: tuple[int, ...], relative_qubits: tuple[int, ...]
@@ -60,7 +60,7 @@ class HardwareEfficientAnsatz(function_params.FunctionParams):
60
60
  description='List of gates for the two qubit gates entangling layer, e.g. ["cx", "cry"]',
61
61
  )
62
62
  parameter_prefix: str = pydantic.Field(
63
- default="param_",
63
+ default="hea_param_",
64
64
  description="Prefix for the generated parameters",
65
65
  )
66
66
 
@@ -17,6 +17,6 @@ class HVA(ChemistryFunctionParams):
17
17
  default=False, description="Whether to evolve the operator naively"
18
18
  )
19
19
  parameter_prefix: str = pydantic.Field(
20
- default="param_",
20
+ default="hva_param_",
21
21
  description="Prefix for the generated parameters",
22
22
  )
@@ -36,7 +36,7 @@ if TYPE_CHECKING:
36
36
  PydanticBackendName = str
37
37
  else:
38
38
  PydanticBackendName = Annotated[
39
- str, Field(strict=True, min_length=1, pattern="^([.A-Za-z0-9_-]*)$")
39
+ str, Field(strict=True, pattern="^([.A-Za-z0-9_-][ .A-Za-z0-9_-]*)$")
40
40
  ]
41
41
 
42
42
 
@@ -262,6 +262,13 @@ class Preferences(pydantic.BaseModel, extra="forbid"):
262
262
 
263
263
  return output_format
264
264
 
265
+ @pydantic.field_validator("backend_name")
266
+ @classmethod
267
+ def validate_backend_name(cls, backend_name: Optional[str]) -> Optional[str]:
268
+ if backend_name is None:
269
+ return backend_name
270
+ return backend_name.rstrip()
271
+
265
272
  @pydantic.model_validator(mode="after")
266
273
  def validate_backend(self) -> Self:
267
274
  backend_name = self.backend_name
@@ -0,0 +1,14 @@
1
+ from classiq.interface.generator.arith.register_user_input import (
2
+ RegisterArithmeticInfo,
3
+ RegisterUserInput,
4
+ )
5
+ from classiq.interface.generator.function_params import FunctionParams
6
+
7
+
8
+ class Reset(FunctionParams):
9
+ target: RegisterUserInput
10
+
11
+ def _create_ios(self) -> None:
12
+ mapping: dict[str, RegisterArithmeticInfo] = {self.target.name: self.target}
13
+ self._inputs = mapping
14
+ self._outputs = mapping
@@ -38,7 +38,7 @@ class UCC(ChemistryFunctionParams):
38
38
  description="Maximum depth of the generated quantum circuit ansatz",
39
39
  )
40
40
  parameter_prefix: str = pydantic.Field(
41
- default="param_",
41
+ default="ucc_param_",
42
42
  description="Prefix for the generated parameters",
43
43
  )
44
44
 
@@ -1 +1 @@
1
- INTERFACE_VERSION = "7"
1
+ INTERFACE_VERSION = "8"
@@ -5,6 +5,7 @@ from uuid import UUID, uuid4
5
5
 
6
6
  import pydantic
7
7
  from pydantic import ConfigDict
8
+ from typing_extensions import Self
8
9
 
9
10
  from classiq.interface.ast_node import ASTNode
10
11
  from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
@@ -21,6 +22,18 @@ class QuantumStatement(ASTNode):
21
22
  description="A unique identifier for this operation", default_factory=uuid4
22
23
  )
23
24
 
25
+ def model_copy(
26
+ self,
27
+ *,
28
+ update: Optional[dict[str, Any]] = None,
29
+ deep: bool = False,
30
+ keep_uuid: bool = False
31
+ ) -> Self:
32
+ if not keep_uuid:
33
+ update = update or dict()
34
+ update.setdefault("uuid", uuid4())
35
+ return super().model_copy(update=update, deep=deep)
36
+
24
37
  @pydantic.model_validator(mode="before")
25
38
  @classmethod
26
39
  def _set_kind(cls, values: Any) -> dict[str, Any]:
@@ -21,6 +21,7 @@ from classiq.interface.generator.functions.classical_function_declaration import
21
21
  )
22
22
  from classiq.interface.generator.functions.classical_type import (
23
23
  Bool,
24
+ ClassicalArray,
24
25
  ClassicalList,
25
26
  ClassicalType,
26
27
  OpaqueHandle,
@@ -64,7 +65,7 @@ def qmod_val_to_python(val: ExpressionValue, qmod_type: ClassicalType) -> Any:
64
65
  if isinstance(val, (Enum, int)):
65
66
  return val
66
67
 
67
- elif isinstance(qmod_type, ClassicalList):
68
+ elif isinstance(qmod_type, (ClassicalArray, ClassicalList)):
68
69
  if isinstance(val, list):
69
70
  return [qmod_val_to_python(elem, qmod_type.element_type) for elem in val]
70
71