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.
- classiq/_internals/api_wrapper.py +27 -0
- classiq/applications/chemistry/chemistry_model_constructor.py +0 -2
- classiq/applications/chemistry/hartree_fock.py +68 -0
- classiq/applications/chemistry/mapping.py +85 -0
- classiq/applications/chemistry/op_utils.py +79 -0
- classiq/applications/chemistry/problems.py +195 -0
- classiq/applications/chemistry/ucc.py +109 -0
- classiq/applications/chemistry/z2_symmetries.py +368 -0
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +30 -1
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +20 -42
- classiq/{model_expansions/evaluators → evaluators}/arg_type_match.py +12 -4
- classiq/{model_expansions/evaluators → evaluators}/argument_types.py +1 -1
- classiq/evaluators/classical_expression.py +53 -0
- classiq/{model_expansions/evaluators → evaluators}/classical_type_inference.py +3 -4
- classiq/{model_expansions/evaluators → evaluators}/parameter_types.py +17 -15
- classiq/execution/__init__.py +12 -1
- classiq/execution/execution_session.py +238 -49
- classiq/execution/jobs.py +26 -1
- classiq/execution/qnn.py +2 -2
- classiq/execution/user_budgets.py +39 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/constants.py +1 -0
- classiq/interface/debug_info/debug_info.py +0 -4
- classiq/interface/execution/primitives.py +29 -1
- classiq/interface/executor/estimate_cost.py +35 -0
- classiq/interface/executor/execution_result.py +13 -0
- classiq/interface/executor/result.py +116 -1
- classiq/interface/executor/user_budget.py +26 -33
- classiq/interface/generator/expressions/atomic_expression_functions.py +10 -1
- classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +0 -6
- classiq/interface/generator/functions/builtins/internal_operators.py +2 -0
- classiq/interface/generator/functions/classical_type.py +2 -35
- classiq/interface/generator/functions/concrete_types.py +20 -3
- classiq/interface/generator/functions/type_modifier.py +0 -19
- classiq/interface/generator/generated_circuit_data.py +5 -18
- classiq/interface/generator/types/compilation_metadata.py +0 -3
- classiq/interface/ide/operation_registry.py +45 -0
- classiq/interface/ide/visual_model.py +68 -3
- classiq/interface/model/bounds.py +12 -2
- classiq/interface/model/model.py +12 -7
- classiq/interface/model/port_declaration.py +2 -24
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +7 -4
- classiq/interface/model/variable_declaration_statement.py +33 -6
- classiq/interface/pretty_print/__init__.py +0 -0
- classiq/{qmod/native → interface/pretty_print}/expression_to_qmod.py +18 -11
- classiq/interface/server/routes.py +4 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +47 -6
- classiq/model_expansions/function_builder.py +4 -1
- classiq/model_expansions/interpreters/base_interpreter.py +3 -3
- classiq/model_expansions/interpreters/generative_interpreter.py +16 -1
- classiq/model_expansions/quantum_operations/allocate.py +1 -1
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +64 -22
- classiq/model_expansions/quantum_operations/bind.py +2 -2
- classiq/model_expansions/quantum_operations/bounds.py +7 -1
- classiq/model_expansions/quantum_operations/call_emitter.py +26 -20
- classiq/model_expansions/quantum_operations/classical_var_emitter.py +16 -0
- classiq/model_expansions/quantum_operations/variable_decleration.py +31 -11
- classiq/model_expansions/scope.py +7 -0
- classiq/model_expansions/scope_initialization.py +3 -3
- classiq/model_expansions/transformers/model_renamer.py +6 -4
- classiq/model_expansions/transformers/type_modifier_inference.py +81 -43
- classiq/model_expansions/transformers/var_splitter.py +1 -1
- classiq/model_expansions/visitors/symbolic_param_inference.py +2 -3
- classiq/open_library/functions/__init__.py +3 -2
- classiq/open_library/functions/amplitude_amplification.py +10 -18
- classiq/open_library/functions/discrete_sine_cosine_transform.py +5 -5
- classiq/open_library/functions/grover.py +14 -6
- classiq/open_library/functions/modular_exponentiation.py +22 -20
- classiq/open_library/functions/qaoa_penalty.py +8 -1
- classiq/open_library/functions/state_preparation.py +18 -32
- classiq/qmod/__init__.py +2 -0
- classiq/qmod/builtins/enums.py +23 -0
- classiq/qmod/builtins/functions/__init__.py +2 -0
- classiq/qmod/builtins/functions/exponentiation.py +32 -4
- classiq/qmod/builtins/operations.py +65 -1
- classiq/qmod/builtins/structs.py +55 -3
- classiq/qmod/classical_variable.py +74 -0
- classiq/qmod/declaration_inferrer.py +3 -2
- classiq/qmod/native/pretty_printer.py +20 -20
- classiq/qmod/pretty_print/expression_to_python.py +2 -1
- classiq/qmod/pretty_print/pretty_printer.py +35 -21
- classiq/qmod/python_classical_type.py +12 -5
- classiq/qmod/qfunc.py +2 -19
- classiq/qmod/qmod_constant.py +2 -5
- classiq/qmod/qmod_parameter.py +2 -5
- classiq/qmod/qmod_variable.py +61 -23
- classiq/qmod/quantum_expandable.py +5 -3
- classiq/qmod/quantum_function.py +49 -4
- classiq/qmod/semantics/annotation/qstruct_annotator.py +1 -1
- classiq/qmod/semantics/validation/main_validation.py +1 -9
- classiq/qmod/symbolic_type.py +2 -1
- classiq/qmod/utilities.py +0 -2
- classiq/qmod/write_qmod.py +1 -1
- {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/METADATA +4 -1
- {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/RECORD +101 -90
- classiq/interface/model/quantum_variable_declaration.py +0 -7
- classiq/model_expansions/evaluators/classical_expression.py +0 -36
- /classiq/{model_expansions/evaluators → evaluators}/__init__.py +0 -0
- /classiq/{model_expansions/evaluators → evaluators}/control.py +0 -0
- /classiq/{model_expansions → evaluators}/expression_evaluator.py +0 -0
- /classiq/{model_expansions/evaluators → evaluators}/quantum_type_utils.py +0 -0
- /classiq/{model_expansions/evaluators → evaluators}/type_type_match.py +0 -0
- {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/WHEEL +0 -0
@@ -1,5 +1,8 @@
|
|
1
|
+
import json
|
1
2
|
from collections import Counter
|
3
|
+
from collections.abc import Iterator
|
2
4
|
from functools import cached_property
|
5
|
+
from itertools import count
|
3
6
|
from typing import Any, Optional
|
4
7
|
|
5
8
|
import pydantic
|
@@ -13,6 +16,26 @@ from classiq.interface.generator.hardware.hardware_data import SynthesisHardware
|
|
13
16
|
from classiq.interface.helpers.versioned_model import VersionedModel
|
14
17
|
|
15
18
|
|
19
|
+
class OperationIdCounter:
|
20
|
+
_op_id_counter: Iterator[int] = count()
|
21
|
+
|
22
|
+
def next_id(self) -> int:
|
23
|
+
return next(self._op_id_counter)
|
24
|
+
|
25
|
+
def reset_operation_counter(self) -> None:
|
26
|
+
self._op_id_counter = count()
|
27
|
+
|
28
|
+
|
29
|
+
_operation_id_counter = OperationIdCounter()
|
30
|
+
|
31
|
+
|
32
|
+
def reset_operation_counter() -> None:
|
33
|
+
"""
|
34
|
+
Call this at the start of every new task to restart ids at 0.
|
35
|
+
"""
|
36
|
+
_operation_id_counter.reset_operation_counter()
|
37
|
+
|
38
|
+
|
16
39
|
class OperationType(StrEnum):
|
17
40
|
REGULAR = "REGULAR"
|
18
41
|
INVISIBLE = "INVISIBLE"
|
@@ -108,9 +131,12 @@ class AtomicGate(StrEnum):
|
|
108
131
|
|
109
132
|
class Operation(pydantic.BaseModel):
|
110
133
|
name: str
|
134
|
+
_id: int = pydantic.PrivateAttr(default_factory=_operation_id_counter.next_id)
|
111
135
|
qasm_name: str = pydantic.Field(default="")
|
112
136
|
details: str = pydantic.Field(default="")
|
113
|
-
children: list["Operation"]
|
137
|
+
children: list["Operation"] = pydantic.Field(default_factory=list)
|
138
|
+
# children_ids is optional in order to support backwards compatibility.
|
139
|
+
children_ids: list[int] = pydantic.Field(default_factory=list)
|
114
140
|
operation_data: Optional[OperationData] = None
|
115
141
|
operation_links: OperationLinks
|
116
142
|
control_qubits: tuple[int, ...] = pydantic.Field(default_factory=tuple)
|
@@ -118,7 +144,7 @@ class Operation(pydantic.BaseModel):
|
|
118
144
|
target_qubits: tuple[int, ...]
|
119
145
|
operation_level: OperationLevel
|
120
146
|
operation_type: OperationType = pydantic.Field(
|
121
|
-
description="Identifies unique operations that are visualized differently"
|
147
|
+
description="Identifies unique operations that are visualized differently",
|
122
148
|
)
|
123
149
|
gate: AtomicGate = pydantic.Field(
|
124
150
|
default=AtomicGate.UNKNOWN, description="Gate type"
|
@@ -127,7 +153,46 @@ class Operation(pydantic.BaseModel):
|
|
127
153
|
expanded: bool = pydantic.Field(default=False)
|
128
154
|
show_expanded_label: bool = pydantic.Field(default=False)
|
129
155
|
|
156
|
+
@property
|
157
|
+
def id(self) -> int:
|
158
|
+
return self._id
|
159
|
+
|
160
|
+
def __hash__(self) -> int:
|
161
|
+
"""
|
162
|
+
using a custom hashable_dict in order to compare the operation
|
163
|
+
with the qubits in order
|
164
|
+
"""
|
165
|
+
js = json.dumps(
|
166
|
+
self._hashable_dict(),
|
167
|
+
sort_keys=True,
|
168
|
+
default=lambda o: o.value if hasattr(o, "value") else str(o),
|
169
|
+
)
|
170
|
+
return hash(js)
|
171
|
+
|
172
|
+
def __eq__(self, other: object) -> bool:
|
173
|
+
return (
|
174
|
+
isinstance(other, Operation)
|
175
|
+
and self._hashable_dict() == other._hashable_dict()
|
176
|
+
)
|
177
|
+
|
178
|
+
def _hashable_dict(self) -> dict:
|
179
|
+
data = self.model_dump(
|
180
|
+
exclude_none=True,
|
181
|
+
)
|
182
|
+
# force qubit order for equality
|
183
|
+
for key in ("target_qubits", "auxiliary_qubits", "control_qubits"):
|
184
|
+
data[key] = sorted(data[key])
|
185
|
+
return data
|
186
|
+
|
130
187
|
|
131
188
|
class ProgramVisualModel(VersionedModel):
|
132
|
-
main_operation: Operation
|
189
|
+
main_operation: Operation = pydantic.Field(default=None)
|
190
|
+
id_to_operations: dict[int, Operation] = pydantic.Field(default_factory=dict)
|
191
|
+
main_operation_id: int = pydantic.Field(default=None)
|
133
192
|
program_data: ProgramData
|
193
|
+
|
194
|
+
@property
|
195
|
+
def main_op_from_mapping(self) -> Operation:
|
196
|
+
if self.main_operation_id is None:
|
197
|
+
raise ValueError("Main operation ID is not set.")
|
198
|
+
return self.id_to_operations[self.main_operation_id]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import Literal, Optional
|
2
2
|
|
3
|
-
from classiq.interface.
|
3
|
+
from classiq.interface.generator.expressions.expression import Expression
|
4
4
|
from classiq.interface.model.handle_binding import ConcreteHandleBinding
|
5
5
|
from classiq.interface.model.quantum_statement import QuantumOperation
|
6
6
|
|
@@ -9,4 +9,14 @@ class SetBoundsStatement(QuantumOperation):
|
|
9
9
|
kind: Literal["SetBoundsStatement"]
|
10
10
|
|
11
11
|
target: ConcreteHandleBinding
|
12
|
-
|
12
|
+
lower_bound: Optional[Expression]
|
13
|
+
upper_bound: Optional[Expression]
|
14
|
+
|
15
|
+
@property
|
16
|
+
def expressions(self) -> list[Expression]:
|
17
|
+
exprs = []
|
18
|
+
if self.lower_bound is not None:
|
19
|
+
exprs.append(self.lower_bound)
|
20
|
+
if self.upper_bound is not None:
|
21
|
+
exprs.append(self.upper_bound)
|
22
|
+
return exprs
|
classiq/interface/model/model.py
CHANGED
@@ -185,11 +185,16 @@ class Model(VersionedModel, ASTNode):
|
|
185
185
|
return constants
|
186
186
|
|
187
187
|
def dump_no_metadata(self) -> dict[str, Any]:
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
188
|
+
compilation_metadata_with_unchecked = {
|
189
|
+
name: CompilationMetadata(unchecked=comp_metadata.unchecked)
|
190
|
+
for name, comp_metadata in self.functions_compilation_metadata.items()
|
191
|
+
if len(comp_metadata.unchecked) > 0
|
192
|
+
}
|
193
|
+
model = self.model_copy(
|
194
|
+
update={
|
195
|
+
"functions_compilation_metadata": compilation_metadata_with_unchecked,
|
196
|
+
}
|
197
|
+
)
|
198
|
+
return model.model_dump(
|
199
|
+
exclude={"constraints", "execution_preferences", "preferences"},
|
195
200
|
)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Any, Literal
|
1
|
+
from typing import Any, Literal
|
2
2
|
|
3
3
|
import pydantic
|
4
4
|
from pydantic_core.core_schema import ValidationInfo
|
@@ -10,7 +10,6 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
10
10
|
)
|
11
11
|
from classiq.interface.generator.functions.type_modifier import (
|
12
12
|
TypeModifier,
|
13
|
-
TypeQualifier,
|
14
13
|
)
|
15
14
|
from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
|
16
15
|
from classiq.interface.model.parameter import Parameter
|
@@ -19,35 +18,14 @@ from classiq.interface.model.parameter import Parameter
|
|
19
18
|
class AnonPortDeclaration(Parameter):
|
20
19
|
quantum_type: ConcreteQuantumType
|
21
20
|
direction: PortDeclarationDirection
|
22
|
-
type_qualifier: Optional[TypeQualifier] = pydantic.Field(
|
23
|
-
default=None, exclude=True
|
24
|
-
) # TODO remove after BWC breaking release https://classiq.atlassian.net/browse/CLS-2777
|
25
21
|
kind: Literal["PortDeclaration"]
|
26
|
-
type_modifier: TypeModifier
|
22
|
+
type_modifier: TypeModifier
|
27
23
|
|
28
24
|
@pydantic.model_validator(mode="before")
|
29
25
|
@classmethod
|
30
26
|
def _set_kind(cls, values: Any) -> dict[str, Any]:
|
31
27
|
return values_with_discriminator(values, "kind", "PortDeclaration")
|
32
28
|
|
33
|
-
@pydantic.model_validator(mode="before")
|
34
|
-
@classmethod
|
35
|
-
def _set_type_modifier(cls, values: Any) -> dict[str, Any]:
|
36
|
-
if values.get("type_modifier") is None:
|
37
|
-
type_qualifier = values.get("type_qualifier")
|
38
|
-
if type_qualifier is not None:
|
39
|
-
if isinstance(type_qualifier, TypeQualifier):
|
40
|
-
values["type_modifier"] = type_qualifier.to_modifier()
|
41
|
-
elif isinstance(type_qualifier, str):
|
42
|
-
values["type_modifier"] = TypeQualifier(
|
43
|
-
type_qualifier
|
44
|
-
).to_modifier()
|
45
|
-
else:
|
46
|
-
raise pydantic.ValidationError("Missing a type modifier")
|
47
|
-
else:
|
48
|
-
raise pydantic.ValidationError("Missing a type modifier")
|
49
|
-
return values
|
50
|
-
|
51
29
|
@pydantic.field_validator("direction", mode="before")
|
52
30
|
@classmethod
|
53
31
|
def _direction_validator(
|
@@ -1,6 +1,8 @@
|
|
1
1
|
from collections.abc import Mapping, Sequence
|
2
2
|
from typing import Literal
|
3
3
|
|
4
|
+
from pydantic import PrivateAttr
|
5
|
+
|
4
6
|
from classiq.interface.enum_utils import StrEnum
|
5
7
|
from classiq.interface.generator.arith.arithmetic import (
|
6
8
|
ARITHMETIC_EXPRESSION_RESULT_NAME,
|
@@ -27,6 +29,7 @@ class ArithmeticOperation(QuantumAssignmentOperation):
|
|
27
29
|
kind: Literal["ArithmeticOperation"]
|
28
30
|
|
29
31
|
operation_kind: ArithmeticOperationKind
|
32
|
+
_classical_assignment: bool = PrivateAttr(default=False)
|
30
33
|
|
31
34
|
@property
|
32
35
|
def is_inplace(self) -> bool:
|
@@ -50,7 +53,7 @@ class ArithmeticOperation(QuantumAssignmentOperation):
|
|
50
53
|
self,
|
51
54
|
) -> Mapping[str, ConcreteHandleBinding]:
|
52
55
|
inouts = dict(super().wiring_inouts)
|
53
|
-
if self.is_inplace:
|
56
|
+
if self.is_inplace and not self._classical_assignment:
|
54
57
|
inouts[self.result_name()] = self.result_var
|
55
58
|
return inouts
|
56
59
|
|
@@ -60,7 +63,7 @@ class ArithmeticOperation(QuantumAssignmentOperation):
|
|
60
63
|
HandleMetadata(handle=handle, readable_location="in an expression")
|
61
64
|
for handle in self.var_handles
|
62
65
|
]
|
63
|
-
if self.is_inplace:
|
66
|
+
if self.is_inplace and not self._classical_assignment:
|
64
67
|
inouts.append(
|
65
68
|
HandleMetadata(
|
66
69
|
handle=self.result_var,
|
@@ -71,13 +74,13 @@ class ArithmeticOperation(QuantumAssignmentOperation):
|
|
71
74
|
|
72
75
|
@property
|
73
76
|
def wiring_outputs(self) -> Mapping[str, HandleBinding]:
|
74
|
-
if self.is_inplace:
|
77
|
+
if self.is_inplace or self._classical_assignment:
|
75
78
|
return {}
|
76
79
|
return super().wiring_outputs
|
77
80
|
|
78
81
|
@property
|
79
82
|
def readable_outputs(self) -> Sequence[HandleMetadata]:
|
80
|
-
if self.is_inplace:
|
83
|
+
if self.is_inplace or self._classical_assignment:
|
81
84
|
return []
|
82
85
|
return [
|
83
86
|
HandleMetadata(
|
@@ -1,15 +1,42 @@
|
|
1
|
-
from typing import Literal
|
1
|
+
from typing import Any, Literal, Optional
|
2
|
+
|
3
|
+
import pydantic
|
2
4
|
|
3
5
|
from classiq.interface.generator.expressions.expression import Expression
|
4
|
-
from classiq.interface.
|
5
|
-
|
6
|
-
|
6
|
+
from classiq.interface.generator.functions.concrete_types import (
|
7
|
+
ConcreteQuantumType,
|
8
|
+
ConcreteType,
|
7
9
|
)
|
10
|
+
from classiq.interface.model.quantum_statement import QuantumStatement
|
11
|
+
from classiq.interface.model.quantum_type import QuantumType
|
8
12
|
|
9
13
|
|
10
|
-
class VariableDeclarationStatement(QuantumStatement
|
14
|
+
class VariableDeclarationStatement(QuantumStatement):
|
11
15
|
kind: Literal["VariableDeclarationStatement"]
|
12
16
|
|
17
|
+
name: str
|
18
|
+
quantum_type: Optional[ConcreteQuantumType] = None
|
19
|
+
qmod_type: ConcreteType
|
20
|
+
|
21
|
+
@pydantic.model_validator(mode="before")
|
22
|
+
@classmethod
|
23
|
+
def _set_qmod_type(cls, values: Any) -> dict[str, Any]:
|
24
|
+
if isinstance(values, dict):
|
25
|
+
if "quantum_type" in values and (
|
26
|
+
"qmod_type" not in values or values["qmod_type"] is None
|
27
|
+
):
|
28
|
+
values["qmod_type"] = values["quantum_type"]
|
29
|
+
values["quantum_type"] = None
|
30
|
+
return values
|
31
|
+
if values.quantum_type is not None and values.qmod_type is None:
|
32
|
+
values.qmod_type = values.quantum_type
|
33
|
+
values.quantum_type = None
|
34
|
+
return values
|
35
|
+
|
13
36
|
@property
|
14
37
|
def expressions(self) -> list[Expression]:
|
15
|
-
return self.
|
38
|
+
return self.qmod_type.expressions
|
39
|
+
|
40
|
+
@property
|
41
|
+
def is_quantum(self) -> bool:
|
42
|
+
return isinstance(self.qmod_type, QuantumType)
|
File without changes
|
@@ -6,7 +6,7 @@ from typing import Callable, Optional
|
|
6
6
|
|
7
7
|
import numpy as np
|
8
8
|
|
9
|
-
from classiq.
|
9
|
+
from classiq.interface.constants import DEFAULT_DECIMAL_PRECISION
|
10
10
|
|
11
11
|
IDENTIFIER = re.compile(r"[a-zA-Z_]\w*")
|
12
12
|
BINARY_OPS: Mapping[type[ast.operator], str] = {
|
@@ -40,6 +40,10 @@ COMPARE_OPS: Mapping[type[ast.cmpop], str] = {
|
|
40
40
|
LIST_FORMAT_CHAR_LIMIT = 20
|
41
41
|
|
42
42
|
|
43
|
+
class PrettyPrinterError(AssertionError):
|
44
|
+
pass
|
45
|
+
|
46
|
+
|
43
47
|
@dataclass
|
44
48
|
class ASTToQMODCode:
|
45
49
|
level: int
|
@@ -59,10 +63,13 @@ class ASTToQMODCode:
|
|
59
63
|
elif isinstance(node, ast.Attribute):
|
60
64
|
# Enum attribute access
|
61
65
|
if not isinstance(node.value, ast.Name) or not isinstance(node.attr, str):
|
62
|
-
raise
|
66
|
+
raise PrettyPrinterError("Error parsing enum attribute access")
|
63
67
|
if not (IDENTIFIER.match(node.value.id) and IDENTIFIER.match(node.attr)):
|
64
|
-
raise
|
65
|
-
|
68
|
+
raise PrettyPrinterError("Error parsing enum attribute access")
|
69
|
+
# FIXME: identify enum member accesses by type name (CLS-2858)
|
70
|
+
if len(node.value.id) > 0 and node.value.id[0].isupper():
|
71
|
+
return f"{node.value.id!s}::{node.attr!s}"
|
72
|
+
return f"{node.value.id!s}.{node.attr!s}"
|
66
73
|
elif isinstance(node, ast.Name):
|
67
74
|
return node.id
|
68
75
|
elif isinstance(node, ast.Num):
|
@@ -91,7 +98,7 @@ class ASTToQMODCode:
|
|
91
98
|
)
|
92
99
|
elif isinstance(node, ast.Compare):
|
93
100
|
if len(node.ops) != 1 or len(node.comparators) != 1:
|
94
|
-
raise
|
101
|
+
raise PrettyPrinterError("Error parsing comparison expression.")
|
95
102
|
return "({} {} {})".format(
|
96
103
|
self.ast_to_code(node.left),
|
97
104
|
COMPARE_OPS[type(node.ops[0])],
|
@@ -108,20 +115,20 @@ class ASTToQMODCode:
|
|
108
115
|
elif isinstance(node, ast.Slice):
|
109
116
|
# A QMOD expression does not support slice step
|
110
117
|
if node.lower is None or node.upper is None or node.step is not None:
|
111
|
-
raise
|
118
|
+
raise PrettyPrinterError("Error parsing slice expression.")
|
112
119
|
return f"{self.ast_to_code(node.lower)}:{self.ast_to_code(node.upper)}"
|
113
120
|
elif isinstance(node, ast.Call):
|
114
121
|
func = self.ast_to_code(node.func)
|
115
122
|
if func == "get_field":
|
116
123
|
if len(node.args) != 2:
|
117
|
-
raise
|
124
|
+
raise PrettyPrinterError("Error parsing struct field access.")
|
118
125
|
field = str(self.ast_to_code(node.args[1])).replace("'", "")
|
119
126
|
if not IDENTIFIER.match(field):
|
120
|
-
raise
|
127
|
+
raise PrettyPrinterError("Error parsing struct field access.")
|
121
128
|
return f"{self.ast_to_code(node.args[0])}.{field}"
|
122
129
|
elif func == "struct_literal":
|
123
130
|
if len(node.args) != 1 or not isinstance(node.args[0], ast.Name):
|
124
|
-
raise
|
131
|
+
raise PrettyPrinterError("Error parsing struct literal.")
|
125
132
|
keywords = node.keywords
|
126
133
|
initializer_list = self.indent_items(
|
127
134
|
lambda: [
|
@@ -133,7 +140,7 @@ class ASTToQMODCode:
|
|
133
140
|
return f"{self.ast_to_code(node.args[0])} {{{initializer_list}}}"
|
134
141
|
elif func == "do_subscript":
|
135
142
|
if len(node.args) != 2:
|
136
|
-
raise
|
143
|
+
raise PrettyPrinterError("Error parsing array access.")
|
137
144
|
return f"{self.ast_to_code(node.args[0])}[{self.ast_to_code(node.args[1])}]"
|
138
145
|
else:
|
139
146
|
return "{}({})".format(
|
@@ -142,7 +149,7 @@ class ASTToQMODCode:
|
|
142
149
|
elif isinstance(node, ast.Expr):
|
143
150
|
return self._cleaned_ast_to_code(node.value)
|
144
151
|
else:
|
145
|
-
raise
|
152
|
+
raise PrettyPrinterError("Error parsing expression: unsupported AST node.")
|
146
153
|
|
147
154
|
def indent_items(self, items: Callable[[], list[str]]) -> str:
|
148
155
|
should_indent = (
|
@@ -91,3 +91,7 @@ IQCC_LIST_AUTH_TARGETS_FULL_PATH = IQCC_PREFIX + IQCC_LIST_AUTH_TARGETS_SUFFIX
|
|
91
91
|
|
92
92
|
USER_BUDGETS_SUFFIX = "/all"
|
93
93
|
USER_BUDGETS_FULL_PATH = USER_BUDGETS_PREFIX + USER_BUDGETS_SUFFIX
|
94
|
+
USER_BUDGET_SET_LIMIT_SUFFIX = "/set_limit"
|
95
|
+
USER_BUDGET_SET_LIMIT_FULL_PATH = USER_BUDGETS_PREFIX + USER_BUDGET_SET_LIMIT_SUFFIX
|
96
|
+
USER_BUDGET_CLEAR_LIMIT_SUFFIX = "/clear_limit"
|
97
|
+
USER_BUDGET_CLEAR_LIMIT_FULL_PATH = USER_BUDGETS_PREFIX + USER_BUDGET_CLEAR_LIMIT_SUFFIX
|
@@ -9,6 +9,9 @@ from classiq.interface.exceptions import (
|
|
9
9
|
ClassiqExpansionError,
|
10
10
|
ClassiqInternalExpansionError,
|
11
11
|
)
|
12
|
+
from classiq.interface.generator.expressions.atomic_expression_functions import (
|
13
|
+
MEASUREMENT_FUNCTIONS,
|
14
|
+
)
|
12
15
|
from classiq.interface.generator.expressions.expression_types import (
|
13
16
|
ExpressionValue,
|
14
17
|
QmodStructInstance,
|
@@ -39,7 +42,6 @@ from classiq.interface.generator.functions.classical_function_declaration import
|
|
39
42
|
from classiq.interface.generator.functions.classical_type import (
|
40
43
|
Bool,
|
41
44
|
ClassicalArray,
|
42
|
-
ClassicalList,
|
43
45
|
ClassicalTuple,
|
44
46
|
ClassicalType,
|
45
47
|
OpaqueHandle,
|
@@ -91,7 +93,7 @@ def qmod_val_to_python(val: ExpressionValue, qmod_type: ClassicalType) -> Any:
|
|
91
93
|
if isinstance(val, (Enum, int)):
|
92
94
|
return val
|
93
95
|
|
94
|
-
elif isinstance(qmod_type,
|
96
|
+
elif isinstance(qmod_type, ClassicalArray):
|
95
97
|
if isinstance(val, list):
|
96
98
|
return [qmod_val_to_python(elem, qmod_type.element_type) for elem in val]
|
97
99
|
|
@@ -145,9 +147,9 @@ def python_val_to_qmod(val: Any, qmod_type: ClassicalType) -> ExpressionValue:
|
|
145
147
|
field_name: python_val_to_qmod(val[field_name], field_type)
|
146
148
|
for field_name, field_type in struct_decl.variables.items()
|
147
149
|
}
|
148
|
-
return QmodStructInstance(struct_decl.model_copy(), qmod_dict)
|
150
|
+
return QmodStructInstance(struct_decl.model_copy(deep=True), qmod_dict)
|
149
151
|
|
150
|
-
if isinstance(qmod_type,
|
152
|
+
if isinstance(qmod_type, ClassicalArray):
|
151
153
|
if not isinstance(val, list):
|
152
154
|
raise ClassiqInternalExpansionError("Bad value for list")
|
153
155
|
return [python_val_to_qmod(elem, qmod_type.element_type) for elem in val]
|
@@ -182,7 +184,7 @@ def python_call_wrapper(func: Callable, *args: ExpressionValue) -> Any:
|
|
182
184
|
|
183
185
|
def struct_literal(struct_type_symbol: Symbol, **kwargs: Any) -> QmodStructInstance:
|
184
186
|
return QmodStructInstance(
|
185
|
-
QMODULE.type_decls[struct_type_symbol.name].model_copy(),
|
187
|
+
QMODULE.type_decls[struct_type_symbol.name].model_copy(deep=True),
|
186
188
|
{field: sympy_to_python(field_value) for field, field_value in kwargs.items()},
|
187
189
|
)
|
188
190
|
|
@@ -330,6 +332,40 @@ def mod_inverse(a: Any, b: Any) -> Any:
|
|
330
332
|
return sympy.mod_inverse(a, b)
|
331
333
|
|
332
334
|
|
335
|
+
def min_wrapper(*vals: Any) -> Any:
|
336
|
+
try:
|
337
|
+
return sympy.Min(*vals)
|
338
|
+
except ValueError:
|
339
|
+
return AnyClassicalValue(f"Min({', '.join(map(str, vals))})")
|
340
|
+
|
341
|
+
|
342
|
+
min_wrapper.__name__ = "min"
|
343
|
+
|
344
|
+
|
345
|
+
def Min_wrapper(*vals: Any) -> Any: # noqa: N802
|
346
|
+
return min_wrapper(*vals)
|
347
|
+
|
348
|
+
|
349
|
+
Min_wrapper.__name__ = "Min"
|
350
|
+
|
351
|
+
|
352
|
+
def max_wrapper(*vals: Any) -> Any:
|
353
|
+
try:
|
354
|
+
return sympy.Max(*vals)
|
355
|
+
except ValueError:
|
356
|
+
return AnyClassicalValue(f"Max({', '.join(map(str, vals))})")
|
357
|
+
|
358
|
+
|
359
|
+
max_wrapper.__name__ = "max"
|
360
|
+
|
361
|
+
|
362
|
+
def Max_wrapper(*vals: Any) -> Any: # noqa: N802
|
363
|
+
return max_wrapper(*vals)
|
364
|
+
|
365
|
+
|
366
|
+
Max_wrapper.__name__ = "Max"
|
367
|
+
|
368
|
+
|
333
369
|
CORE_LIB_FUNCTIONS_LIST: list[Callable] = [
|
334
370
|
print,
|
335
371
|
do_sum,
|
@@ -347,6 +383,10 @@ CORE_LIB_FUNCTIONS_LIST: list[Callable] = [
|
|
347
383
|
RShift,
|
348
384
|
LShift,
|
349
385
|
mod_inverse,
|
386
|
+
min_wrapper,
|
387
|
+
Min_wrapper,
|
388
|
+
max_wrapper,
|
389
|
+
Max_wrapper,
|
350
390
|
]
|
351
391
|
|
352
392
|
|
@@ -361,7 +401,8 @@ def _symbolic_function(func: str) -> Callable:
|
|
361
401
|
|
362
402
|
|
363
403
|
QMOD_CLASSICAL_FUNCTIONS = [
|
364
|
-
_symbolic_function(func)
|
404
|
+
_symbolic_function(func)
|
405
|
+
for func in qmod_classical_functions + list(MEASUREMENT_FUNCTIONS)
|
365
406
|
]
|
366
407
|
|
367
408
|
ATOMIC_EXPRESSION_FUNCTIONS = {
|
@@ -9,6 +9,7 @@ from classiq.interface.generator.compiler_keywords import (
|
|
9
9
|
LAMBDA_KEYWORD,
|
10
10
|
)
|
11
11
|
from classiq.interface.generator.functions.builtins.internal_operators import (
|
12
|
+
BLOCK_OPERATOR_NAME,
|
12
13
|
WITHIN_APPLY_NAME,
|
13
14
|
)
|
14
15
|
from classiq.interface.model.model import MAIN_FUNCTION_NAME
|
@@ -35,6 +36,8 @@ from classiq.model_expansions.utils.counted_name_allocator import CountedNameAll
|
|
35
36
|
|
36
37
|
ClosureType = TypeVar("ClosureType", bound=Closure)
|
37
38
|
|
39
|
+
BLOCKS_ALLOWED_CAPTURING = (WITHIN_APPLY_NAME, BLOCK_OPERATOR_NAME)
|
40
|
+
|
38
41
|
|
39
42
|
@dataclass
|
40
43
|
class Block:
|
@@ -142,7 +145,7 @@ class OperationBuilder:
|
|
142
145
|
captured_vars = self.current_block.captured_vars
|
143
146
|
if (
|
144
147
|
not isinstance(self.current_operation, FunctionClosure)
|
145
|
-
and self.current_operation.name
|
148
|
+
and self.current_operation.name not in BLOCKS_ALLOWED_CAPTURING
|
146
149
|
):
|
147
150
|
validate_captured_directions(
|
148
151
|
captured_vars.filter_var_decls(
|
@@ -39,14 +39,14 @@ from classiq.interface.model.quantum_lambda_function import (
|
|
39
39
|
)
|
40
40
|
from classiq.interface.model.quantum_statement import QuantumStatement
|
41
41
|
|
42
|
+
from classiq.evaluators.classical_expression import (
|
43
|
+
evaluate_classical_expression,
|
44
|
+
)
|
42
45
|
from classiq.model_expansions.closure import (
|
43
46
|
Closure,
|
44
47
|
FunctionClosure,
|
45
48
|
)
|
46
49
|
from classiq.model_expansions.debug_flag import debug_mode
|
47
|
-
from classiq.model_expansions.evaluators.classical_expression import (
|
48
|
-
evaluate_classical_expression,
|
49
|
-
)
|
50
50
|
from classiq.model_expansions.function_builder import (
|
51
51
|
FunctionContext,
|
52
52
|
OperationBuilder,
|
@@ -7,6 +7,7 @@ from numpy.random import permutation
|
|
7
7
|
from classiq.interface.generator.constant import Constant
|
8
8
|
from classiq.interface.generator.expressions.expression import Expression
|
9
9
|
from classiq.interface.generator.functions.builtins.internal_operators import (
|
10
|
+
BLOCK_OPERATOR_NAME,
|
10
11
|
CLASSICAL_IF_OPERATOR_NAME,
|
11
12
|
CONTROL_OPERATOR_NAME,
|
12
13
|
INVERT_OPERATOR_NAME,
|
@@ -15,6 +16,7 @@ from classiq.interface.generator.functions.builtins.internal_operators import (
|
|
15
16
|
)
|
16
17
|
from classiq.interface.model.allocate import Allocate
|
17
18
|
from classiq.interface.model.bind_operation import BindOperation
|
19
|
+
from classiq.interface.model.block import Block
|
18
20
|
from classiq.interface.model.bounds import SetBoundsStatement
|
19
21
|
from classiq.interface.model.classical_if import ClassicalIf
|
20
22
|
from classiq.interface.model.control import Control
|
@@ -68,6 +70,9 @@ from classiq.model_expansions.quantum_operations.block_evaluator import (
|
|
68
70
|
RepeatElimination,
|
69
71
|
)
|
70
72
|
from classiq.model_expansions.quantum_operations.bounds import SetBoundsEmitter
|
73
|
+
from classiq.model_expansions.quantum_operations.classical_var_emitter import (
|
74
|
+
ClassicalVarEmitter,
|
75
|
+
)
|
71
76
|
from classiq.model_expansions.quantum_operations.composite_emitter import (
|
72
77
|
CompositeEmitter,
|
73
78
|
)
|
@@ -192,6 +197,7 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
192
197
|
[
|
193
198
|
HandleEvaluator(self, "result_var"),
|
194
199
|
ExpressionEvaluator(self, "expression"),
|
200
|
+
ClassicalVarEmitter(self),
|
195
201
|
AssignmentResultProcessor(self),
|
196
202
|
],
|
197
203
|
).emit(op)
|
@@ -303,9 +309,18 @@ class GenerativeInterpreter(BaseInterpreter):
|
|
303
309
|
def emit_set_bounds(self, op: SetBoundsStatement) -> None:
|
304
310
|
CompositeEmitter[SetBoundsStatement](
|
305
311
|
self,
|
306
|
-
[
|
312
|
+
[
|
313
|
+
ExpressionEvaluator(self, "lower_bound"),
|
314
|
+
ExpressionEvaluator(self, "upper_bound"),
|
315
|
+
HandleEvaluator(self, "target"),
|
316
|
+
SetBoundsEmitter(self),
|
317
|
+
],
|
307
318
|
).emit(op)
|
308
319
|
|
320
|
+
@emit.register
|
321
|
+
def emit_block(self, block: Block) -> None:
|
322
|
+
BlockEvaluator(self, BLOCK_OPERATOR_NAME, "statements").emit(block)
|
323
|
+
|
309
324
|
def _expand_body(self, operation: Closure) -> None:
|
310
325
|
if isinstance(operation, FunctionClosure) and operation.name == "permute":
|
311
326
|
# special expansion since permute is generative
|
@@ -12,7 +12,7 @@ from classiq.interface.model.quantum_type import (
|
|
12
12
|
QuantumNumeric,
|
13
13
|
)
|
14
14
|
|
15
|
-
from classiq.
|
15
|
+
from classiq.evaluators.quantum_type_utils import copy_type_information
|
16
16
|
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
17
17
|
from classiq.model_expansions.scope import QuantumSymbol
|
18
18
|
|