classiq 0.46.1__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.
- classiq/_internals/api_wrapper.py +45 -8
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -7
- classiq/applications/grover/grover_model_constructor.py +2 -1
- classiq/execution/execution_session.py +133 -45
- classiq/execution/jobs.py +120 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +0 -1
- classiq/interface/debug_info/debug_info.py +23 -1
- classiq/interface/execution/primitives.py +17 -0
- classiq/interface/executor/iqae_result.py +3 -3
- classiq/interface/executor/result.py +3 -1
- classiq/interface/generator/arith/arithmetic_operations.py +5 -2
- classiq/interface/generator/arith/binary_ops.py +21 -14
- classiq/interface/generator/arith/extremum_operations.py +9 -1
- classiq/interface/generator/arith/number_utils.py +6 -0
- classiq/interface/generator/arith/register_user_input.py +30 -21
- classiq/interface/generator/arith/unary_ops.py +13 -1
- classiq/interface/generator/expressions/expression.py +8 -0
- classiq/interface/generator/functions/type_name.py +1 -3
- classiq/interface/generator/generated_circuit_data.py +47 -2
- classiq/interface/generator/quantum_program.py +10 -2
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +17 -3
- classiq/interface/ide/visual_model.py +10 -5
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/bind_operation.py +0 -3
- classiq/interface/model/phase_operation.py +11 -0
- classiq/interface/model/port_declaration.py +1 -12
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +34 -6
- classiq/interface/model/quantum_lambda_function.py +4 -1
- classiq/interface/model/quantum_statement.py +16 -1
- classiq/interface/model/quantum_variable_declaration.py +0 -22
- classiq/interface/model/statement_block.py +3 -0
- classiq/interface/server/global_versions.py +4 -4
- classiq/interface/server/routes.py +0 -3
- classiq/model_expansions/capturing/propagated_var_stack.py +5 -2
- classiq/model_expansions/closure.py +7 -2
- classiq/model_expansions/evaluators/quantum_type_utils.py +0 -7
- classiq/model_expansions/generative_functions.py +146 -28
- classiq/model_expansions/interpreter.py +17 -5
- classiq/model_expansions/quantum_operations/classicalif.py +27 -10
- classiq/model_expansions/quantum_operations/control.py +22 -15
- classiq/model_expansions/quantum_operations/emitter.py +68 -7
- classiq/model_expansions/quantum_operations/expression_operation.py +25 -16
- classiq/model_expansions/quantum_operations/inplace_binary_operation.py +167 -95
- classiq/model_expansions/quantum_operations/invert.py +12 -6
- classiq/model_expansions/quantum_operations/phase.py +189 -0
- classiq/model_expansions/quantum_operations/power.py +9 -8
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +20 -5
- classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -1
- classiq/model_expansions/quantum_operations/repeat.py +32 -13
- classiq/model_expansions/quantum_operations/within_apply.py +19 -6
- classiq/model_expansions/scope.py +16 -5
- classiq/model_expansions/scope_initialization.py +11 -1
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +23 -1
- classiq/model_expansions/visitors/variable_references.py +11 -7
- classiq/qmod/builtins/__init__.py +10 -0
- classiq/qmod/builtins/constants.py +10 -0
- classiq/qmod/builtins/functions/state_preparation.py +4 -1
- classiq/qmod/builtins/operations.py +55 -161
- classiq/qmod/create_model_function.py +1 -1
- classiq/qmod/generative.py +14 -5
- classiq/qmod/native/pretty_printer.py +14 -4
- classiq/qmod/pretty_print/pretty_printer.py +14 -4
- classiq/qmod/qmod_constant.py +28 -18
- classiq/qmod/qmod_variable.py +43 -23
- classiq/qmod/quantum_expandable.py +14 -1
- classiq/qmod/semantics/static_semantics_visitor.py +10 -0
- classiq/qmod/semantics/validation/constants_validation.py +16 -0
- {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/METADATA +9 -4
- {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/RECORD +71 -66
- {classiq-0.46.1.dist-info → classiq-0.48.0.dist-info}/WHEEL +0 -0
@@ -21,6 +21,7 @@ from classiq.interface.exceptions import ClassiqValueError
|
|
21
21
|
from classiq.interface.generator.arith import argument_utils, number_utils
|
22
22
|
from classiq.interface.generator.arith.argument_utils import RegisterOrConst
|
23
23
|
from classiq.interface.generator.arith.arithmetic_operations import (
|
24
|
+
MODULO_WITH_FRACTION_PLACES_ERROR_MSG,
|
24
25
|
ArithmeticOperationParams,
|
25
26
|
)
|
26
27
|
from classiq.interface.generator.arith.ast_node_rewrite import (
|
@@ -391,17 +392,20 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
391
392
|
arg=self.effective_right_arg,
|
392
393
|
output_size=self.negation_output_size,
|
393
394
|
inplace=self.should_inplace_negation,
|
395
|
+
bypass_bounds_validation=True,
|
394
396
|
)
|
395
397
|
negation_result = negation_params.result_register
|
396
398
|
if self.output_size is None and max(self.effective_right_arg.bounds) > 0:
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
399
|
+
bounds = (
|
400
|
+
-max(self.effective_right_arg.bounds),
|
401
|
+
-min(self.effective_right_arg.bounds),
|
402
|
+
)
|
403
|
+
negation_result = RegisterArithmeticInfo(
|
404
|
+
size=negation_result.size,
|
405
|
+
fraction_places=negation_result.fraction_places,
|
406
|
+
is_signed=True,
|
407
|
+
bounds=bounds,
|
408
|
+
bypass_bounds_validation=True,
|
405
409
|
)
|
406
410
|
adder_params = Adder(
|
407
411
|
left_arg=self.effective_left_arg,
|
@@ -465,12 +469,8 @@ class Multiplier(BinaryOpWithFloatInputs):
|
|
465
469
|
for right in argument_utils.bounds(args[1])
|
466
470
|
]
|
467
471
|
return (
|
468
|
-
number_utils.limit_fraction_places(
|
469
|
-
|
470
|
-
),
|
471
|
-
number_utils.limit_fraction_places(
|
472
|
-
max(extremal_values), machine_precision=machine_precision
|
473
|
-
),
|
472
|
+
number_utils.limit_fraction_places(min(extremal_values), machine_precision),
|
473
|
+
number_utils.limit_fraction_places(max(extremal_values), machine_precision),
|
474
474
|
)
|
475
475
|
|
476
476
|
def _get_result_register(self) -> RegisterArithmeticInfo:
|
@@ -482,6 +482,13 @@ class Multiplier(BinaryOpWithFloatInputs):
|
|
482
482
|
self.right_arg, self.machine_precision
|
483
483
|
)
|
484
484
|
bounds = self._get_bounds((left_arg, right_arg), self.machine_precision)
|
485
|
+
if self.output_size:
|
486
|
+
if fraction_places:
|
487
|
+
raise ValueError(MODULO_WITH_FRACTION_PLACES_ERROR_MSG)
|
488
|
+
max_bounds = RegisterArithmeticInfo.get_maximal_bounds(
|
489
|
+
size=self.output_size, is_signed=False, fraction_places=0
|
490
|
+
)
|
491
|
+
bounds = number_utils.bounds_cut(bounds, max_bounds)
|
485
492
|
|
486
493
|
return RegisterArithmeticInfo(
|
487
494
|
size=self.output_size
|
@@ -4,9 +4,10 @@ from typing import Any, Dict, Iterable
|
|
4
4
|
import pydantic
|
5
5
|
|
6
6
|
from classiq.interface.exceptions import ClassiqValueError
|
7
|
-
from classiq.interface.generator.arith import argument_utils
|
7
|
+
from classiq.interface.generator.arith import argument_utils, number_utils
|
8
8
|
from classiq.interface.generator.arith.argument_utils import RegisterOrConst
|
9
9
|
from classiq.interface.generator.arith.arithmetic_operations import (
|
10
|
+
MODULO_WITH_FRACTION_PLACES_ERROR_MSG,
|
10
11
|
ArithmeticOperationParams,
|
11
12
|
)
|
12
13
|
from classiq.interface.generator.arith.binary_ops import (
|
@@ -108,6 +109,13 @@ class Extremum(ArithmeticOperationParams):
|
|
108
109
|
argument_utils.upper_bound(eff_right_arg),
|
109
110
|
),
|
110
111
|
)
|
112
|
+
if self.output_size:
|
113
|
+
if fraction_places:
|
114
|
+
raise ValueError(MODULO_WITH_FRACTION_PLACES_ERROR_MSG)
|
115
|
+
max_bounds = RegisterArithmeticInfo.get_maximal_bounds(
|
116
|
+
size=self.output_size, is_signed=False, fraction_places=0
|
117
|
+
)
|
118
|
+
bounds = number_utils.bounds_cut(bounds, max_bounds)
|
111
119
|
return RegisterArithmeticInfo(
|
112
120
|
size=self.output_size or required_size,
|
113
121
|
fraction_places=fraction_places,
|
@@ -110,3 +110,9 @@ def limit_fraction_places(number: float, machine_precision: int) -> float:
|
|
110
110
|
fraction_part_size=orig_fractions - removed_fractions,
|
111
111
|
is_signed=number < 0,
|
112
112
|
)
|
113
|
+
|
114
|
+
|
115
|
+
def bounds_cut(
|
116
|
+
bounds1: Tuple[float, float], bounds2: Tuple[float, float]
|
117
|
+
) -> Tuple[float, float]:
|
118
|
+
return max(min(bounds1), min(bounds2)), min(max(bounds1), max(bounds2))
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Any, Dict, Optional
|
1
|
+
from typing import Any, Dict, Optional, Tuple
|
2
2
|
|
3
3
|
import pydantic
|
4
4
|
|
@@ -14,6 +14,7 @@ class RegisterArithmeticInfo(HashablePydanticBaseModel):
|
|
14
14
|
size: pydantic.PositiveInt
|
15
15
|
is_signed: bool = pydantic.Field(default=False)
|
16
16
|
fraction_places: pydantic.NonNegativeInt = pydantic.Field(default=0)
|
17
|
+
bypass_bounds_validation: bool = pydantic.Field(default=False)
|
17
18
|
bounds: PydanticFloatTuple = pydantic.Field(default=None)
|
18
19
|
|
19
20
|
@pydantic.root_validator(pre=True)
|
@@ -22,32 +23,39 @@ class RegisterArithmeticInfo(HashablePydanticBaseModel):
|
|
22
23
|
values.pop("name")
|
23
24
|
return values
|
24
25
|
|
26
|
+
@staticmethod
|
27
|
+
def get_maximal_bounds(
|
28
|
+
*, size: int, is_signed: bool, fraction_places: int
|
29
|
+
) -> Tuple[float, float]:
|
30
|
+
lb = 0 if not is_signed else -(2 ** (size - 1))
|
31
|
+
ub = 2**size - 1 if not is_signed else 2 ** (size - 1) - 1
|
32
|
+
fraction_factor = float(2**-fraction_places)
|
33
|
+
return (lb * fraction_factor, ub * fraction_factor)
|
34
|
+
|
25
35
|
@pydantic.validator("bounds", always=True)
|
26
36
|
def _validate_bounds(
|
27
37
|
cls, bounds: Optional[PydanticFloatTuple], values: Dict[str, Any]
|
28
38
|
) -> PydanticFloatTuple:
|
29
|
-
if bounds is not None:
|
30
|
-
if min(bounds) < 0:
|
31
|
-
assert values.get("is_signed")
|
32
|
-
fraction_places = values.get("fraction_places")
|
33
|
-
if not isinstance(fraction_places, int):
|
34
|
-
raise ClassiqValueError(
|
35
|
-
"RegisterUserInput must have an integer fraction_places"
|
36
|
-
)
|
37
|
-
return number_utils.limit_fraction_places(
|
38
|
-
min(bounds), machine_precision=fraction_places
|
39
|
-
), number_utils.limit_fraction_places(
|
40
|
-
max(bounds), machine_precision=fraction_places
|
41
|
-
)
|
42
|
-
|
43
39
|
size = values.get("size")
|
40
|
+
is_signed = values.get("is_signed", False)
|
41
|
+
fraction_places = values.get("fraction_places", 0)
|
44
42
|
if not isinstance(size, int):
|
45
|
-
raise ClassiqValueError("
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
43
|
+
raise ClassiqValueError("RegisterArithmeticInfo must have an integer size")
|
44
|
+
|
45
|
+
maximal_bounds = cls.get_maximal_bounds(
|
46
|
+
size=size, is_signed=is_signed, fraction_places=fraction_places
|
47
|
+
)
|
48
|
+
if bounds is None:
|
49
|
+
return maximal_bounds
|
50
|
+
|
51
|
+
trimmed_bounds = (
|
52
|
+
number_utils.limit_fraction_places(min(bounds), fraction_places),
|
53
|
+
number_utils.limit_fraction_places(max(bounds), fraction_places),
|
54
|
+
)
|
55
|
+
if not values.get("bypass_bounds_validation", False):
|
56
|
+
assert min(trimmed_bounds) >= min(maximal_bounds), "Illegal bound min"
|
57
|
+
assert max(trimmed_bounds) <= max(maximal_bounds), "Illegal bound max"
|
58
|
+
return trimmed_bounds
|
51
59
|
|
52
60
|
def limit_fraction_places(self, machine_precision: int) -> "RegisterArithmeticInfo":
|
53
61
|
truncated_bits: int = max(self.fraction_places - machine_precision, 0)
|
@@ -56,6 +64,7 @@ class RegisterArithmeticInfo(HashablePydanticBaseModel):
|
|
56
64
|
is_signed=self.is_signed,
|
57
65
|
fraction_places=self.fraction_places - truncated_bits,
|
58
66
|
bounds=self.bounds,
|
67
|
+
bypass_bounds_validation=self.bypass_bounds_validation,
|
59
68
|
)
|
60
69
|
|
61
70
|
@property
|
@@ -5,6 +5,7 @@ import pydantic
|
|
5
5
|
from classiq.interface.exceptions import ClassiqValueError
|
6
6
|
from classiq.interface.generator.arith import argument_utils, number_utils
|
7
7
|
from classiq.interface.generator.arith.arithmetic_operations import (
|
8
|
+
MODULO_WITH_FRACTION_PLACES_ERROR_MSG,
|
8
9
|
ArithmeticOperationParams,
|
9
10
|
)
|
10
11
|
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
@@ -74,6 +75,9 @@ class BitwiseInvert(UnaryOpParams):
|
|
74
75
|
|
75
76
|
|
76
77
|
class Negation(UnaryOpParams):
|
78
|
+
bypass_bounds_validation: bool = pydantic.Field(
|
79
|
+
default=False
|
80
|
+
) # True for efficient subtraction
|
77
81
|
output_name = "negated"
|
78
82
|
|
79
83
|
@staticmethod
|
@@ -88,11 +92,19 @@ class Negation(UnaryOpParams):
|
|
88
92
|
eff_arg = self.arg.limit_fraction_places(self.machine_precision)
|
89
93
|
is_signed = max(eff_arg.bounds) > 0 and self._include_sign
|
90
94
|
bounds = (-max(eff_arg.bounds), -min(eff_arg.bounds))
|
95
|
+
if self.output_size and not self.bypass_bounds_validation:
|
96
|
+
if eff_arg.fraction_places:
|
97
|
+
raise ValueError(MODULO_WITH_FRACTION_PLACES_ERROR_MSG)
|
98
|
+
max_bounds = RegisterArithmeticInfo.get_maximal_bounds(
|
99
|
+
size=self.output_size, is_signed=False, fraction_places=0
|
100
|
+
)
|
101
|
+
bounds = number_utils.bounds_cut(bounds, max_bounds)
|
91
102
|
return RegisterArithmeticInfo(
|
92
103
|
size=self.output_size or self._expected_result_size(eff_arg),
|
93
104
|
fraction_places=eff_arg.fraction_places,
|
94
105
|
is_signed=is_signed,
|
95
|
-
|
106
|
+
bypass_bounds_validation=self.bypass_bounds_validation,
|
107
|
+
bounds=bounds,
|
96
108
|
)
|
97
109
|
|
98
110
|
def zero_input_for_extension(self) -> pydantic.NonNegativeInt:
|
@@ -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
|
-
|
71
|
-
|
72
|
-
QMODULE.qstruct_decls[self.name].fields = fields
|
70
|
+
self._assigned_fields = fields
|
73
71
|
|
74
72
|
|
75
73
|
class Enum(TypeName):
|
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
import logging
|
2
|
+
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
|
2
3
|
|
3
4
|
import pydantic
|
4
5
|
from typing_extensions import TypeAlias
|
@@ -10,10 +11,10 @@ from classiq.interface.generator.synthesis_metadata.synthesis_execution_data imp
|
|
10
11
|
)
|
11
12
|
from classiq.interface.ide.visual_model import OperationParameter
|
12
13
|
|
14
|
+
_logger = logging.getLogger(__name__)
|
13
15
|
ParameterName = str
|
14
16
|
IOQubitMapping: TypeAlias = Dict[str, Tuple[int, ...]]
|
15
17
|
|
16
|
-
|
17
18
|
CLASSIQ_HIERARCHY_SEPARATOR: Literal["."] = "."
|
18
19
|
|
19
20
|
VISUALIZATION_HIDE_LIST = [
|
@@ -102,6 +103,7 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
102
103
|
absolute_qubits: Optional[Tuple[int, ...]]
|
103
104
|
is_basis_gate: Optional[bool]
|
104
105
|
parameters: List[OperationParameter] = list()
|
106
|
+
port_to_passed_variable_map: Dict[str, str] = pydantic.Field(default_factory=dict)
|
105
107
|
|
106
108
|
@property
|
107
109
|
def registers(self) -> List[GeneratedRegister]:
|
@@ -120,3 +122,46 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
120
122
|
if self.generated_function is None:
|
121
123
|
return list()
|
122
124
|
return self.generated_function.control_states
|
125
|
+
|
126
|
+
@staticmethod
|
127
|
+
def create_parameters_from_dict(
|
128
|
+
parameters: Dict[str, str]
|
129
|
+
) -> List[OperationParameter]:
|
130
|
+
return [
|
131
|
+
OperationParameter(label=key, value=value)
|
132
|
+
for key, value in parameters.items()
|
133
|
+
]
|
134
|
+
|
135
|
+
def update(self, **kwargs: Any) -> None:
|
136
|
+
for key, value in kwargs.items():
|
137
|
+
setattr(self, key, value)
|
138
|
+
|
139
|
+
def propagate_absolute_qubits(self) -> None:
|
140
|
+
if self.absolute_qubits is None:
|
141
|
+
return
|
142
|
+
|
143
|
+
for register in self.registers:
|
144
|
+
register.qubit_indexes_absolute = list(
|
145
|
+
_get_absolute_from_relative(
|
146
|
+
self.absolute_qubits, tuple(register.qubit_indexes_relative)
|
147
|
+
)
|
148
|
+
)
|
149
|
+
|
150
|
+
for child in self.children:
|
151
|
+
child.absolute_qubits = _get_absolute_from_relative(
|
152
|
+
self.absolute_qubits, child.relative_qubits
|
153
|
+
)
|
154
|
+
child.propagate_absolute_qubits()
|
155
|
+
|
156
|
+
|
157
|
+
def _get_absolute_from_relative(
|
158
|
+
absolute_qubits: Tuple[int, ...], relative_qubits: Tuple[int, ...]
|
159
|
+
) -> Tuple[int, ...]:
|
160
|
+
if max(relative_qubits) >= len(absolute_qubits):
|
161
|
+
_logger.warning(
|
162
|
+
"Invalid qubit computation (relative qubits: %s, absolute qubits: %s)",
|
163
|
+
relative_qubits,
|
164
|
+
absolute_qubits,
|
165
|
+
)
|
166
|
+
return tuple()
|
167
|
+
return tuple(absolute_qubits[relative_qubit] for relative_qubit in relative_qubits)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import uuid
|
2
|
-
from datetime import datetime
|
2
|
+
from datetime import datetime, timezone
|
3
3
|
from pathlib import Path
|
4
4
|
from typing import Dict, List, Optional, Tuple, Union
|
5
5
|
|
@@ -10,6 +10,7 @@ from classiq.interface.exceptions import (
|
|
10
10
|
ClassiqMissingOutputFormatError,
|
11
11
|
ClassiqStateInitializationError,
|
12
12
|
)
|
13
|
+
from classiq.interface.execution.primitives import PrimitivesInput
|
13
14
|
from classiq.interface.executor import quantum_code
|
14
15
|
from classiq.interface.executor.quantum_instruction_set import QuantumInstructionSet
|
15
16
|
from classiq.interface.executor.register_initialization import RegisterInitialization
|
@@ -50,16 +51,23 @@ def get_uuid_as_str() -> str:
|
|
50
51
|
return str(uuid.uuid4())
|
51
52
|
|
52
53
|
|
54
|
+
def _get_formatted_utc_current_time() -> str:
|
55
|
+
# The purpose of this method is to replicate the behavior of
|
56
|
+
# datetime.utcnow().isoformat(), since `utcnow` is now deprecated
|
57
|
+
return datetime.now(timezone.utc).isoformat().split("+")[0]
|
58
|
+
|
59
|
+
|
53
60
|
class QuantumProgram(VersionedModel, CircuitCodeInterface):
|
54
61
|
hardware_data: SynthesisHardwareData
|
55
62
|
initial_values: Optional[InitialConditions]
|
56
63
|
data: GeneratedCircuitData
|
57
64
|
model: ExecutionModel
|
58
65
|
transpiled_circuit: Optional[TranspiledCircuitData]
|
59
|
-
creation_time: str = pydantic.Field(default_factory=
|
66
|
+
creation_time: str = pydantic.Field(default_factory=_get_formatted_utc_current_time)
|
60
67
|
synthesis_duration: Optional[SynthesisStepDurations]
|
61
68
|
debug_info: Optional[List[FunctionDebugInfoInterface]]
|
62
69
|
program_id: str = pydantic.Field(default_factory=get_uuid_as_str)
|
70
|
+
execution_primitives_input: Optional[PrimitivesInput] = pydantic.Field(default=None)
|
63
71
|
|
64
72
|
def _hardware_agnostic_program_code(self) -> CodeAndSyntax:
|
65
73
|
circuit_code = self.program_circuit.get_code_by_priority()
|
@@ -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[
|
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.
|
36
|
+
function_execution_data.power_var
|
23
37
|
for function_execution_data in self.function_execution.values()
|
24
|
-
if function_execution_data.
|
38
|
+
if function_execution_data.power_var is not None
|
25
39
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Dict, List, Optional, Tuple
|
1
|
+
from typing import Any, Dict, List, Optional, Tuple
|
2
2
|
|
3
3
|
import pydantic
|
4
4
|
|
@@ -38,6 +38,7 @@ class OperationParameter(pydantic.BaseModel):
|
|
38
38
|
|
39
39
|
class OperationLink(pydantic.BaseModel):
|
40
40
|
label: str
|
41
|
+
inner_label: Optional[str] = None
|
41
42
|
qubits: Tuple[int, ...]
|
42
43
|
type: str
|
43
44
|
|
@@ -47,6 +48,11 @@ class OperationLink(pydantic.BaseModel):
|
|
47
48
|
def __hash__(self) -> int:
|
48
49
|
return hash((type(self), self.label, self.qubits, self.type))
|
49
50
|
|
51
|
+
def __eq__(self, other: Any) -> bool:
|
52
|
+
if not isinstance(other, OperationLink):
|
53
|
+
return False
|
54
|
+
return hash(self) == hash(other)
|
55
|
+
|
50
56
|
|
51
57
|
class OperationLinks(pydantic.BaseModel):
|
52
58
|
inputs: List[OperationLink]
|
@@ -63,10 +69,9 @@ class Operation(pydantic.BaseModel):
|
|
63
69
|
target_qubits: Tuple[int, ...]
|
64
70
|
parameters: List[OperationParameter] = pydantic.Field(default_factory=list)
|
65
71
|
operation_level: OperationLevel
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
operation_type: OperationType = pydantic.Field(default=OperationType.REGULAR)
|
72
|
+
operation_type: OperationType = pydantic.Field(
|
73
|
+
description="Identifies unique operations that are visualized differently"
|
74
|
+
)
|
70
75
|
|
71
76
|
|
72
77
|
class ProgramVisualModel(VersionedModel):
|
@@ -1 +1 @@
|
|
1
|
-
INTERFACE_VERSION = "
|
1
|
+
INTERFACE_VERSION = "3"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
from typing import Literal
|
2
|
+
|
3
|
+
from classiq.interface.generator.expressions.expression import Expression
|
4
|
+
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
5
|
+
QuantumExpressionOperation,
|
6
|
+
)
|
7
|
+
|
8
|
+
|
9
|
+
class PhaseOperation(QuantumExpressionOperation):
|
10
|
+
kind: Literal["PhaseOperation"]
|
11
|
+
theta: Expression
|
@@ -1,25 +1,20 @@
|
|
1
|
-
from typing import Any, Dict, Literal, Mapping
|
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.
|
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.
|
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.
|
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.
|
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=
|
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
|