classiq 0.86.1__py3-none-any.whl → 0.87.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 (96) hide show
  1. classiq/__init__.py +2 -0
  2. classiq/applications/chemistry/hartree_fock.py +5 -1
  3. classiq/applications/chemistry/op_utils.py +2 -2
  4. classiq/applications/combinatorial_helpers/encoding_mapping.py +11 -15
  5. classiq/applications/combinatorial_helpers/encoding_utils.py +6 -6
  6. classiq/applications/combinatorial_helpers/memory.py +4 -4
  7. classiq/applications/combinatorial_helpers/optimization_model.py +5 -5
  8. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +6 -10
  9. classiq/applications/combinatorial_helpers/pyomo_utils.py +27 -26
  10. classiq/applications/combinatorial_helpers/sympy_utils.py +2 -2
  11. classiq/applications/combinatorial_helpers/transformations/encoding.py +4 -6
  12. classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +4 -4
  13. classiq/applications/combinatorial_helpers/transformations/ising_converter.py +2 -2
  14. classiq/applications/combinatorial_helpers/transformations/penalty_support.py +3 -3
  15. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -0
  16. classiq/applications/hamiltonian/pauli_decomposition.py +33 -1
  17. classiq/evaluators/argument_types.py +15 -6
  18. classiq/evaluators/parameter_types.py +43 -39
  19. classiq/evaluators/qmod_annotated_expression.py +88 -11
  20. classiq/evaluators/qmod_expression_visitors/out_of_place_node_transformer.py +19 -0
  21. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +54 -11
  22. classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +40 -25
  23. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +29 -59
  24. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +12 -5
  25. classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +175 -28
  26. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +21 -14
  27. classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +9 -5
  28. classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +20 -1
  29. classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +97 -0
  30. classiq/evaluators/qmod_node_evaluators/name_evaluation.py +11 -26
  31. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +56 -0
  32. classiq/evaluators/qmod_node_evaluators/piecewise_evaluation.py +40 -0
  33. classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +2 -3
  34. classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +48 -21
  35. classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +53 -9
  36. classiq/evaluators/qmod_node_evaluators/utils.py +27 -5
  37. classiq/evaluators/qmod_type_inference/classical_type_inference.py +188 -0
  38. classiq/evaluators/qmod_type_inference/quantum_type_inference.py +292 -0
  39. classiq/evaluators/quantum_type_utils.py +0 -131
  40. classiq/execution/execution_session.py +1 -1
  41. classiq/execution/qnn.py +4 -1
  42. classiq/execution/user_budgets.py +1 -1
  43. classiq/interface/_version.py +1 -1
  44. classiq/interface/backend/backend_preferences.py +10 -30
  45. classiq/interface/backend/quantum_backend_providers.py +63 -52
  46. classiq/interface/generator/arith/binary_ops.py +107 -115
  47. classiq/interface/generator/arith/extremum_operations.py +33 -45
  48. classiq/interface/generator/arith/number_utils.py +4 -1
  49. classiq/interface/generator/circuit_code/types_and_constants.py +0 -9
  50. classiq/interface/generator/compiler_keywords.py +2 -0
  51. classiq/interface/generator/function_param_list.py +133 -5
  52. classiq/interface/generator/functions/classical_type.py +59 -2
  53. classiq/interface/generator/functions/qmod_python_interface.py +15 -0
  54. classiq/interface/generator/functions/type_name.py +6 -0
  55. classiq/interface/generator/model/preferences/preferences.py +1 -17
  56. classiq/interface/generator/quantum_program.py +1 -13
  57. classiq/interface/helpers/model_normalizer.py +2 -2
  58. classiq/interface/helpers/text_utils.py +7 -2
  59. classiq/interface/interface_version.py +1 -1
  60. classiq/interface/model/classical_if.py +40 -0
  61. classiq/interface/model/handle_binding.py +28 -16
  62. classiq/interface/model/quantum_type.py +61 -2
  63. classiq/interface/pretty_print/expression_to_qmod.py +24 -11
  64. classiq/interface/pyomo_extension/__init__.py +0 -4
  65. classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +2 -2
  66. classiq/model_expansions/arithmetic.py +43 -1
  67. classiq/model_expansions/arithmetic_compute_result_attrs.py +255 -0
  68. classiq/model_expansions/capturing/captured_vars.py +2 -5
  69. classiq/model_expansions/quantum_operations/allocate.py +22 -15
  70. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -3
  71. classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -71
  72. classiq/model_expansions/quantum_operations/bind.py +15 -7
  73. classiq/model_expansions/quantum_operations/call_emitter.py +2 -10
  74. classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -0
  75. classiq/open_library/functions/__init__.py +3 -0
  76. classiq/open_library/functions/lcu.py +117 -0
  77. classiq/qmod/builtins/enums.py +2 -2
  78. classiq/qmod/builtins/structs.py +33 -18
  79. classiq/qmod/pretty_print/expression_to_python.py +7 -9
  80. {classiq-0.86.1.dist-info → classiq-0.87.0.dist-info}/METADATA +3 -3
  81. {classiq-0.86.1.dist-info → classiq-0.87.0.dist-info}/RECORD +83 -89
  82. classiq/interface/generator/amplitude_estimation.py +0 -34
  83. classiq/interface/generator/function_param_list_without_self_reference.py +0 -160
  84. classiq/interface/generator/grover_diffuser.py +0 -93
  85. classiq/interface/generator/grover_operator.py +0 -106
  86. classiq/interface/generator/oracles/__init__.py +0 -3
  87. classiq/interface/generator/oracles/arithmetic_oracle.py +0 -82
  88. classiq/interface/generator/oracles/custom_oracle.py +0 -65
  89. classiq/interface/generator/oracles/oracle_abc.py +0 -76
  90. classiq/interface/generator/oracles/oracle_function_param_list.py +0 -6
  91. classiq/interface/generator/piecewise_linear_amplitude_loading.py +0 -165
  92. classiq/interface/generator/qpe.py +0 -169
  93. classiq/interface/grover/grover_modelling_params.py +0 -13
  94. classiq/model_expansions/transformers/var_splitter.py +0 -224
  95. /classiq/{interface/grover → evaluators/qmod_type_inference}/__init__.py +0 -0
  96. {classiq-0.86.1.dist-info → classiq-0.87.0.dist-info}/WHEEL +0 -0
@@ -1,169 +0,0 @@
1
- from typing import Any, Optional
2
-
3
- import pydantic
4
- from pydantic import ConfigDict
5
- from pydantic_core.core_schema import ValidationInfo
6
- from typing_extensions import Self
7
-
8
- from classiq.interface.exceptions import ClassiqMismatchIOsError, ClassiqValueError
9
- from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
10
- from classiq.interface.generator.function_param_list_without_self_reference import (
11
- function_param_library_without_self_reference,
12
- )
13
- from classiq.interface.generator.function_params import (
14
- DEFAULT_ZERO_NAME,
15
- FunctionParams,
16
- IOName,
17
- parse_function_params_values,
18
- )
19
- from classiq.interface.generator.hamiltonian_evolution.exponentiation import (
20
- Exponentiation,
21
- )
22
- from classiq.interface.generator.user_defined_function_params import CustomFunction
23
-
24
- PHASE_ESTIMATION_DEFAULT_OUTPUT_NAME = "PHASE_ESTIMATION"
25
- CUSTOM_FUNCTIONS_IO_MISMATCH_ERROR = (
26
- "Custom function provided to the QPE has different input and output names."
27
- )
28
-
29
-
30
- class ExponentiationScaling(pydantic.BaseModel):
31
- """
32
- Details of exponentiation scaling for phase estimation.
33
- """
34
-
35
- max_depth: pydantic.PositiveInt = pydantic.Field(
36
- description="The max_depth of the smallest exponentiation",
37
- )
38
- max_depth_scaling_factor: pydantic.NonNegativeFloat = pydantic.Field(
39
- default=2.0,
40
- description="The scaling factor of the exponentiation max_depth; defaults to 2.",
41
- )
42
- model_config = ConfigDict(frozen=True)
43
-
44
-
45
- class ExponentiationSpecification(pydantic.BaseModel):
46
- """
47
- Specifications of individual Exponentiation details for each qubit; only valid if Exponentiation is given as unitary_params for PhaseEstimation.
48
- This sets the optimization to ExponentiationOptimization.MINIMIZE_ERROR and overrides the max_depth constraints.
49
- """
50
-
51
- scaling: Optional[ExponentiationScaling] = pydantic.Field(
52
- default=None,
53
- description="The scaling of the exponentiation functions.",
54
- )
55
- max_depths: Optional[tuple[pydantic.NonNegativeInt, ...]] = pydantic.Field(
56
- default=None,
57
- description="The max_depth of each exponentiation function; overrides scaling.",
58
- )
59
- model_config = ConfigDict(frozen=True)
60
-
61
- @pydantic.model_validator(mode="after")
62
- def _validate_exponentiation_specification(self) -> Self:
63
- if self.scaling is None and self.max_depths is None:
64
- raise ClassiqValueError("At least one specification must be provided.")
65
- return self
66
-
67
-
68
- class PhaseEstimation(FunctionParams):
69
- """
70
- Quantum phase estimation of a given unitary function.
71
- """
72
-
73
- size: pydantic.PositiveInt = pydantic.Field(
74
- description="The number of qubits storing the estimated phase."
75
- )
76
- unitary: str = pydantic.Field(
77
- description="The unitary function for phase estimation.",
78
- )
79
- unitary_params: FunctionParams = pydantic.Field(
80
- description="The unitary function parameters.",
81
- default_factory=CustomFunction,
82
- )
83
- exponentiation_specification: Optional[ExponentiationSpecification] = (
84
- pydantic.Field(
85
- default=None,
86
- description="The specifications for phase estimation of exponentiation functions.",
87
- )
88
- )
89
-
90
- _output_name: IOName = pydantic.PrivateAttr(
91
- default=PHASE_ESTIMATION_DEFAULT_OUTPUT_NAME
92
- )
93
-
94
- @property
95
- def output_name(self) -> str:
96
- return self._output_name
97
-
98
- def _create_ios(self) -> None:
99
- self._inputs = {**self.unitary_params.inputs}
100
- self._outputs = {**self.unitary_params.outputs}
101
- self._outputs[self._output_name] = RegisterArithmeticInfo(size=self.size)
102
- self._create_zero_input_registers({DEFAULT_ZERO_NAME: self.size})
103
-
104
- @pydantic.model_validator(mode="before")
105
- @classmethod
106
- def _validate_composite_name(cls, values: Any) -> dict[str, Any]:
107
- if not isinstance(values, dict):
108
- return values
109
- unitary_params = values.get("unitary_params")
110
- unitary = values.get("unitary")
111
-
112
- if isinstance(unitary_params, CustomFunction) and not unitary:
113
- raise ClassiqValueError(
114
- "`PhaseEstimation` of a user define function (`CustomFunction`) must receive the function name from the `unitary` field"
115
- )
116
- return values
117
-
118
- @pydantic.model_validator(mode="before")
119
- @classmethod
120
- def _parse_function_params(
121
- cls, values: Any, info: ValidationInfo
122
- ) -> dict[str, Any]:
123
- vals = info.data.copy() if info.data else {}
124
- if isinstance(values, dict):
125
- vals = values
126
- elif isinstance(values, PhaseEstimation):
127
- vals = values.model_dump()
128
-
129
- parse_function_params_values(
130
- values=vals,
131
- params_key="unitary_params",
132
- discriminator_key="unitary",
133
- param_classes=function_param_library_without_self_reference.param_list,
134
- default_parser_class=CustomFunction,
135
- )
136
- return vals
137
-
138
- @pydantic.field_validator("unitary_params")
139
- @classmethod
140
- def _validate_unitary_params(cls, unitary_params: FunctionParams) -> FunctionParams:
141
- if not unitary_params.is_powerable():
142
- if isinstance(unitary_params, CustomFunction):
143
- raise ClassiqMismatchIOsError(CUSTOM_FUNCTIONS_IO_MISMATCH_ERROR)
144
- raise ClassiqValueError(
145
- f"Phase estimation of {unitary_params.discriminator()} is currently not supported."
146
- )
147
- return unitary_params
148
-
149
- @pydantic.field_validator("exponentiation_specification")
150
- @classmethod
151
- def _validate_exponentiation_specification(
152
- cls,
153
- exponentiation_specification: Optional[ExponentiationSpecification],
154
- validation_info: ValidationInfo,
155
- ) -> Optional[ExponentiationSpecification]:
156
- if exponentiation_specification is None:
157
- return exponentiation_specification
158
- unitary_params = validation_info.data.get("unitary_params")
159
- if not isinstance(unitary_params, Exponentiation):
160
- raise ClassiqValueError(
161
- "exponentiation_specification is only valid for Exponentiation unitary_params."
162
- )
163
- if exponentiation_specification.max_depths is not None and len(
164
- exponentiation_specification.max_depths
165
- ) != validation_info.data.get("size"):
166
- raise ClassiqValueError(
167
- "Length of max_depths must match the provided size."
168
- )
169
- return exponentiation_specification
@@ -1,13 +0,0 @@
1
- import pydantic
2
- from pydantic import BaseModel
3
-
4
- from classiq.interface.generator.oracles import ArithmeticOracle
5
-
6
-
7
- class GroverParams(BaseModel):
8
- oracle: ArithmeticOracle = pydantic.Field(
9
- description="An arithmatic oracle for the grover search."
10
- )
11
- num_reps: int = pydantic.Field(
12
- default=1, description="The number of repetitions of the " "grover block."
13
- )
@@ -1,224 +0,0 @@
1
- import ast
2
- from dataclasses import dataclass
3
- from itertools import chain
4
- from typing import TYPE_CHECKING, Callable, Optional
5
-
6
- from classiq.interface.exceptions import (
7
- ClassiqExpansionError,
8
- ClassiqInternalExpansionError,
9
- )
10
- from classiq.interface.generator.expressions.expression import Expression
11
- from classiq.interface.model.bind_operation import BindOperation
12
- from classiq.interface.model.handle_binding import (
13
- HandleBinding,
14
- NestedHandleBinding,
15
- SlicedHandleBinding,
16
- SubscriptHandleBinding,
17
- )
18
- from classiq.interface.model.quantum_type import (
19
- QuantumBitvector,
20
- QuantumScalar,
21
- QuantumType,
22
- )
23
- from classiq.interface.model.variable_declaration_statement import (
24
- VariableDeclarationStatement,
25
- )
26
-
27
- from classiq.model_expansions.scope import QuantumSymbol, Scope
28
- from classiq.model_expansions.transformers.model_renamer import (
29
- HandleRenaming,
30
- ModelRenamer,
31
- )
32
- from classiq.model_expansions.visitors.variable_references import VarRefCollector
33
-
34
-
35
- @dataclass(frozen=True)
36
- class SymbolPart(HandleRenaming):
37
- target_var_type: QuantumType
38
-
39
-
40
- SymbolParts = dict[HandleBinding, list[SymbolPart]]
41
- PartNamer = Callable[[str], str]
42
-
43
-
44
- class VarSplitter(ModelRenamer):
45
- def __init__(self, scope: Scope):
46
- self._scope = scope
47
-
48
- def split_symbols(self, expression: Expression, namer: PartNamer) -> SymbolParts:
49
- vrc = VarRefCollector(ignore_duplicated_handles=True)
50
- vrc.visit(ast.parse(expression.expr))
51
- symbol_names_to_split = dict.fromkeys(
52
- handle.name
53
- for handle in vrc.var_handles
54
- if isinstance(self._scope[handle.name].value, QuantumSymbol)
55
- and isinstance(handle, NestedHandleBinding)
56
- )
57
-
58
- symbol_handles = {
59
- symbol: list(
60
- dict.fromkeys(
61
- handle.collapse()
62
- for handle in vrc.var_handles
63
- if handle.name == symbol.handle.name
64
- )
65
- )
66
- for symbol_name in symbol_names_to_split
67
- if isinstance(
68
- symbol := self._scope[symbol_name].value,
69
- QuantumSymbol,
70
- )
71
- }
72
-
73
- return {
74
- symbol.handle: [
75
- SymbolPart(
76
- source_handle=part.handle,
77
- target_var_name=namer(part.handle.identifier),
78
- target_var_type=part.quantum_type,
79
- )
80
- for part in self._get_symbol_parts(symbol, handles)
81
- ]
82
- for symbol, handles in symbol_handles.items()
83
- }
84
-
85
- def _get_symbol_parts(
86
- self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
87
- ) -> list[QuantumSymbol]:
88
- for i in range(len(target_parts)):
89
- for j in range(i + 1, len(target_parts)):
90
- if target_parts[i].overlaps(target_parts[j]):
91
- raise ClassiqInternalExpansionError(
92
- f"Handles {str(target_parts[i])!r} and "
93
- f"{str(target_parts[j])!r} overlapping in expression"
94
- )
95
- return self._get_symbol_parts_unsafe(symbol, target_parts)
96
-
97
- def _get_symbol_parts_unsafe(
98
- self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
99
- ) -> list[QuantumSymbol]:
100
- if all(
101
- symbol.handle == target_part or symbol.handle not in target_part.prefixes()
102
- for target_part in target_parts
103
- ) or isinstance(symbol.quantum_type, QuantumScalar):
104
- return [symbol]
105
-
106
- if isinstance(symbol.quantum_type, QuantumBitvector):
107
- return self._get_array_parts(symbol, target_parts)
108
-
109
- return self._get_struct_parts(symbol, target_parts)
110
-
111
- def _get_array_parts(
112
- self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
113
- ) -> list[QuantumSymbol]:
114
- if TYPE_CHECKING:
115
- assert isinstance(symbol.quantum_type, QuantumBitvector)
116
-
117
- if not symbol.quantum_type.has_length:
118
- raise ClassiqExpansionError(
119
- f"Could not determine the length of quantum array " f"{symbol.handle}."
120
- )
121
- target_slices = self._get_target_slices(symbol, target_parts)
122
-
123
- symbol_parts: list[QuantumSymbol] = []
124
- idx = 0
125
- while idx < symbol.quantum_type.length_value:
126
- if idx in target_slices:
127
- stop = target_slices[idx]
128
- if stop <= idx:
129
- raise ClassiqInternalExpansionError(
130
- f"Illegal sliced handle {str(symbol[idx: stop].handle)!r}"
131
- )
132
- symbol_parts.append(symbol[idx:stop])
133
- idx = stop
134
- else:
135
- symbol_parts.extend(
136
- self._get_symbol_parts_unsafe(symbol[idx], target_parts)
137
- )
138
- idx += 1
139
-
140
- return symbol_parts
141
-
142
- def _get_target_slices(
143
- self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
144
- ) -> dict[int, int]:
145
- if TYPE_CHECKING:
146
- assert isinstance(symbol.quantum_type, QuantumBitvector)
147
- target_items = {
148
- idx
149
- for idx in range(symbol.quantum_type.length_value)
150
- for target_part in target_parts
151
- if target_part
152
- in SubscriptHandleBinding(
153
- base_handle=symbol.handle, index=Expression(expr=str(idx))
154
- )
155
- }
156
- target_slices = {
157
- target_part.start.to_int_value(): target_part.end.to_int_value()
158
- for target_part in target_parts
159
- if isinstance(target_part, SlicedHandleBinding)
160
- and symbol.handle == target_part.base_handle
161
- }
162
- self._add_unused_indices_as_slices(symbol, target_items, target_slices)
163
- return target_slices
164
-
165
- @staticmethod
166
- def _add_unused_indices_as_slices(
167
- symbol: QuantumSymbol, target_items: set[int], target_slices: dict[int, int]
168
- ) -> None:
169
- if TYPE_CHECKING:
170
- assert isinstance(symbol.quantum_type, QuantumBitvector)
171
- last_unused_idx: Optional[int] = None
172
- array_length = symbol.quantum_type.length_value
173
- idx = 0
174
-
175
- while idx < array_length:
176
- if (
177
- idx in target_items or idx in target_slices
178
- ) and last_unused_idx is not None:
179
- target_slices[last_unused_idx] = idx
180
- last_unused_idx = None
181
-
182
- if idx in target_slices:
183
- if target_slices[idx] <= idx:
184
- raise ClassiqInternalExpansionError
185
- idx = target_slices[idx]
186
- continue
187
-
188
- if idx not in target_items and last_unused_idx is None:
189
- last_unused_idx = idx
190
-
191
- idx += 1
192
-
193
- if last_unused_idx is not None:
194
- target_slices[last_unused_idx] = array_length
195
-
196
- def _get_struct_parts(
197
- self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
198
- ) -> list[QuantumSymbol]:
199
- return list(
200
- chain.from_iterable(
201
- self._get_symbol_parts_unsafe(field_symbol, target_parts)
202
- for field_symbol in symbol.fields.values()
203
- )
204
- )
205
-
206
- @staticmethod
207
- def get_bind_ops(symbol_parts: SymbolParts) -> list[BindOperation]:
208
- return [
209
- BindOperation(
210
- in_handles=[handle],
211
- out_handles=[part.target_var_handle for part in parts],
212
- )
213
- for handle, parts in symbol_parts.items()
214
- ]
215
-
216
- @staticmethod
217
- def get_var_decls(symbol_parts: SymbolParts) -> list[VariableDeclarationStatement]:
218
- return [
219
- VariableDeclarationStatement(
220
- name=part.target_var_name,
221
- qmod_type=part.target_var_type,
222
- )
223
- for part in chain.from_iterable(symbol_parts.values())
224
- ]