classiq 0.87.0__py3-none-any.whl → 0.89.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.
Potentially problematic release.
This version of classiq might be problematic. Click here for more details.
- classiq/_internals/config.py +1 -1
- classiq/applications/__init__.py +1 -2
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +1 -1
- classiq/applications/hamiltonian/pauli_decomposition.py +1 -1
- classiq/evaluators/qmod_annotated_expression.py +37 -15
- classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +0 -5
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +20 -13
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +15 -8
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +39 -16
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +67 -6
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +18 -8
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +8 -0
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +4 -1
- classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +1 -1
- classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +1 -1
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +3 -3
- classiq/evaluators/qmod_node_evaluators/utils.py +1 -1
- classiq/evaluators/qmod_type_inference/classical_type_inference.py +4 -4
- classiq/evaluators/qmod_type_inference/quantum_type_inference.py +38 -0
- classiq/evaluators/type_type_match.py +1 -1
- classiq/execution/execution_session.py +17 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +1 -1
- classiq/interface/backend/backend_preferences.py +1 -1
- classiq/interface/execution/primitives.py +1 -0
- classiq/interface/generator/application_apis/__init__.py +0 -1
- classiq/interface/generator/arith/register_user_input.py +1 -1
- classiq/interface/generator/expressions/atomic_expression_functions.py +0 -2
- classiq/interface/generator/function_param_list.py +0 -4
- classiq/interface/generator/functions/classical_type.py +32 -3
- classiq/interface/generator/functions/function_declaration.py +0 -4
- classiq/interface/generator/functions/type_name.py +31 -0
- classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
- classiq/interface/generator/quantum_function_call.py +3 -3
- classiq/interface/generator/transpiler_basis_gates.py +5 -1
- classiq/interface/generator/types/builtin_enum_declarations.py +0 -8
- classiq/interface/generator/user_defined_function_params.py +0 -3
- classiq/interface/ide/ide_data.py +1 -1
- classiq/interface/ide/visual_model.py +2 -2
- classiq/interface/model/block.py +5 -1
- classiq/interface/model/classical_if.py +16 -8
- classiq/interface/model/classical_parameter_declaration.py +4 -0
- classiq/interface/model/handle_binding.py +1 -1
- classiq/interface/model/port_declaration.py +12 -0
- classiq/interface/model/quantum_function_declaration.py +12 -0
- classiq/interface/model/quantum_lambda_function.py +1 -1
- classiq/interface/model/quantum_statement.py +2 -4
- classiq/interface/model/quantum_type.py +102 -3
- classiq/interface/pretty_print/expression_to_qmod.py +3 -17
- classiq/model_expansions/quantum_operations/allocate.py +1 -1
- classiq/model_expansions/quantum_operations/handle_evaluator.py +2 -8
- classiq/open_library/functions/__init__.py +3 -0
- classiq/open_library/functions/lcu.py +2 -2
- classiq/open_library/functions/state_preparation.py +182 -5
- classiq/qmod/builtins/__init__.py +0 -3
- classiq/qmod/builtins/classical_functions.py +0 -28
- classiq/qmod/builtins/enums.py +24 -18
- classiq/qmod/builtins/functions/__init__.py +0 -5
- classiq/qmod/builtins/operations.py +142 -0
- classiq/qmod/builtins/structs.py +0 -11
- classiq/qmod/native/pretty_printer.py +1 -1
- classiq/qmod/pretty_print/expression_to_python.py +3 -6
- classiq/qmod/pretty_print/pretty_printer.py +4 -1
- classiq/qmod/qmod_variable.py +141 -3
- classiq/qmod/semantics/annotation/call_annotation.py +4 -2
- classiq/qmod/semantics/annotation/qstruct_annotator.py +20 -5
- {classiq-0.87.0.dist-info → classiq-0.89.0.dist-info}/METADATA +4 -4
- {classiq-0.87.0.dist-info → classiq-0.89.0.dist-info}/RECORD +69 -81
- classiq/applications/finance/__init__.py +0 -15
- classiq/interface/finance/__init__.py +0 -0
- classiq/interface/finance/finance_modelling_params.py +0 -11
- classiq/interface/finance/function_input.py +0 -102
- classiq/interface/finance/gaussian_model_input.py +0 -50
- classiq/interface/finance/log_normal_model_input.py +0 -40
- classiq/interface/finance/model_input.py +0 -22
- classiq/interface/generator/application_apis/finance_declarations.py +0 -108
- classiq/interface/generator/expressions/enums/__init__.py +0 -0
- classiq/interface/generator/expressions/enums/finance_functions.py +0 -12
- classiq/interface/generator/finance.py +0 -107
- classiq/qmod/builtins/functions/finance.py +0 -34
- {classiq-0.87.0.dist-info → classiq-0.89.0.dist-info}/WHEEL +0 -0
|
@@ -44,6 +44,18 @@ class AnonPortDeclaration(Parameter):
|
|
|
44
44
|
raise ClassiqInternalError
|
|
45
45
|
return PortDeclaration(**{**self.__dict__, "name": new_name})
|
|
46
46
|
|
|
47
|
+
@property
|
|
48
|
+
def qmod_type_name(self) -> str:
|
|
49
|
+
prefix = ""
|
|
50
|
+
suffix = ""
|
|
51
|
+
if self.type_modifier in (TypeModifier.Const, TypeModifier.Permutable):
|
|
52
|
+
prefix += f"{self.type_modifier.name}["
|
|
53
|
+
suffix += "]"
|
|
54
|
+
if self.direction != PortDeclarationDirection.Inout:
|
|
55
|
+
prefix += f"{self.direction.name}["
|
|
56
|
+
suffix += "]"
|
|
57
|
+
return f"{prefix}{self.quantum_type.qmod_type_name}{suffix}"
|
|
58
|
+
|
|
47
59
|
|
|
48
60
|
class PortDeclaration(AnonPortDeclaration):
|
|
49
61
|
name: str
|
|
@@ -190,6 +190,18 @@ class AnonQuantumOperandDeclaration(AnonQuantumFunctionDeclaration):
|
|
|
190
190
|
def is_generative(self) -> bool:
|
|
191
191
|
return self._is_generative
|
|
192
192
|
|
|
193
|
+
@property
|
|
194
|
+
def qmod_type_name(self) -> str:
|
|
195
|
+
if self.is_list:
|
|
196
|
+
type_name = "QCallableList"
|
|
197
|
+
else:
|
|
198
|
+
type_name = "QCallable"
|
|
199
|
+
if len(self.positional_arg_declarations) == 0:
|
|
200
|
+
params = ""
|
|
201
|
+
else:
|
|
202
|
+
params = f"[{', '.join(param.qmod_type_name for param in self.positional_arg_declarations)}]"
|
|
203
|
+
return f"{type_name}{params}"
|
|
204
|
+
|
|
193
205
|
|
|
194
206
|
AnonQuantumFunctionDeclaration.model_rebuild()
|
|
195
207
|
|
|
@@ -32,7 +32,7 @@ class QuantumLambdaFunction(ASTNode):
|
|
|
32
32
|
default=None
|
|
33
33
|
)
|
|
34
34
|
|
|
35
|
-
_py_callable: Callable = pydantic.PrivateAttr(default=None)
|
|
35
|
+
_py_callable: Callable = pydantic.PrivateAttr(default=None) # type: ignore[assignment]
|
|
36
36
|
|
|
37
37
|
@property
|
|
38
38
|
def py_callable(self) -> Callable:
|
|
@@ -4,7 +4,6 @@ from typing import Any, Callable, Optional
|
|
|
4
4
|
from uuid import UUID, uuid4
|
|
5
5
|
|
|
6
6
|
import pydantic
|
|
7
|
-
from pydantic import ConfigDict
|
|
8
7
|
from typing_extensions import Self
|
|
9
8
|
|
|
10
9
|
from classiq.interface.ast_node import ASTNode
|
|
@@ -21,7 +20,6 @@ from classiq.interface.model.handle_binding import (
|
|
|
21
20
|
|
|
22
21
|
class QuantumStatement(ASTNode):
|
|
23
22
|
kind: str
|
|
24
|
-
model_config = ConfigDict(extra="forbid")
|
|
25
23
|
uuid: UUID = pydantic.Field(
|
|
26
24
|
description="A unique identifier for this operation", default_factory=uuid4
|
|
27
25
|
)
|
|
@@ -29,12 +27,12 @@ class QuantumStatement(ASTNode):
|
|
|
29
27
|
def model_copy(
|
|
30
28
|
self,
|
|
31
29
|
*,
|
|
32
|
-
update: Optional[
|
|
30
|
+
update: Optional[Mapping[str, Any]] = None,
|
|
33
31
|
deep: bool = False,
|
|
34
32
|
keep_uuid: bool = False,
|
|
35
33
|
) -> Self:
|
|
36
34
|
if not keep_uuid:
|
|
37
|
-
update = update
|
|
35
|
+
update = dict(update) if update is not None else dict()
|
|
38
36
|
update.setdefault("uuid", uuid4())
|
|
39
37
|
return super().model_copy(update=update, deep=deep)
|
|
40
38
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, Any, Literal, Optional
|
|
2
2
|
|
|
3
3
|
import pydantic
|
|
4
|
-
from pydantic import BaseModel,
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
5
|
from typing_extensions import Self
|
|
6
6
|
|
|
7
7
|
from classiq.interface.ast_node import HashableASTNode
|
|
@@ -35,8 +35,6 @@ if TYPE_CHECKING:
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class QuantumType(HashableASTNode):
|
|
38
|
-
model_config = ConfigDict(extra="forbid")
|
|
39
|
-
|
|
40
38
|
_size_in_bits: Optional[int] = pydantic.PrivateAttr(default=None)
|
|
41
39
|
|
|
42
40
|
def _update_size_in_bits_from_declaration(self) -> None:
|
|
@@ -84,10 +82,17 @@ class QuantumType(HashableASTNode):
|
|
|
84
82
|
def is_evaluated(self) -> bool:
|
|
85
83
|
raise NotImplementedError
|
|
86
84
|
|
|
85
|
+
@property
|
|
86
|
+
def is_constant(self) -> bool:
|
|
87
|
+
raise NotImplementedError
|
|
88
|
+
|
|
87
89
|
@property
|
|
88
90
|
def expressions(self) -> list[Expression]:
|
|
89
91
|
return []
|
|
90
92
|
|
|
93
|
+
def without_symbolic_attributes(self) -> Self:
|
|
94
|
+
return self
|
|
95
|
+
|
|
91
96
|
|
|
92
97
|
class QuantumScalar(QuantumType):
|
|
93
98
|
def get_proxy(self, handle: "HandleBinding") -> QmodQScalarProxy:
|
|
@@ -109,6 +114,9 @@ class QuantumScalar(QuantumType):
|
|
|
109
114
|
def fraction_digits_value(self) -> int:
|
|
110
115
|
raise NotImplementedError
|
|
111
116
|
|
|
117
|
+
def get_bounds(self) -> Optional[tuple[float, float]]:
|
|
118
|
+
return None
|
|
119
|
+
|
|
112
120
|
def get_effective_bounds(
|
|
113
121
|
self, machine_precision: Optional[int] = None
|
|
114
122
|
) -> tuple[float, float]:
|
|
@@ -154,6 +162,10 @@ class QuantumBit(QuantumScalar):
|
|
|
154
162
|
def is_evaluated(self) -> bool:
|
|
155
163
|
return True
|
|
156
164
|
|
|
165
|
+
@property
|
|
166
|
+
def is_constant(self) -> bool:
|
|
167
|
+
return True
|
|
168
|
+
|
|
157
169
|
@property
|
|
158
170
|
def has_sign(self) -> bool:
|
|
159
171
|
return True
|
|
@@ -257,6 +269,15 @@ class QuantumBitvector(QuantumType):
|
|
|
257
269
|
and self.element_type.is_evaluated
|
|
258
270
|
)
|
|
259
271
|
|
|
272
|
+
@property
|
|
273
|
+
def is_constant(self) -> bool:
|
|
274
|
+
return (
|
|
275
|
+
self.length is not None
|
|
276
|
+
and self.length.is_evaluated()
|
|
277
|
+
and self.length.is_constant()
|
|
278
|
+
and self.element_type.is_constant
|
|
279
|
+
)
|
|
280
|
+
|
|
260
281
|
@property
|
|
261
282
|
def expressions(self) -> list[Expression]:
|
|
262
283
|
exprs = self.element_type.expressions
|
|
@@ -272,6 +293,18 @@ class QuantumBitvector(QuantumType):
|
|
|
272
293
|
length = 1
|
|
273
294
|
return length * self.element_type.minimal_size_in_bits
|
|
274
295
|
|
|
296
|
+
def without_symbolic_attributes(self) -> "QuantumBitvector":
|
|
297
|
+
length = (
|
|
298
|
+
None
|
|
299
|
+
if self.length is None
|
|
300
|
+
or not self.length.is_evaluated()
|
|
301
|
+
or not self.length.is_constant()
|
|
302
|
+
else self.length
|
|
303
|
+
)
|
|
304
|
+
return QuantumBitvector(
|
|
305
|
+
element_type=self.element_type.without_symbolic_attributes(), length=length
|
|
306
|
+
)
|
|
307
|
+
|
|
275
308
|
|
|
276
309
|
class QuantumNumeric(QuantumScalar):
|
|
277
310
|
kind: Literal["qnum"]
|
|
@@ -311,6 +344,14 @@ class QuantumNumeric(QuantumScalar):
|
|
|
311
344
|
def has_sign(self) -> bool:
|
|
312
345
|
return self.is_signed is not None
|
|
313
346
|
|
|
347
|
+
@property
|
|
348
|
+
def has_constant_sign(self) -> bool:
|
|
349
|
+
return (
|
|
350
|
+
self.is_signed is not None
|
|
351
|
+
and self.is_signed.is_evaluated()
|
|
352
|
+
and self.is_signed.is_constant()
|
|
353
|
+
)
|
|
354
|
+
|
|
314
355
|
@property
|
|
315
356
|
def sign_value(self) -> bool:
|
|
316
357
|
return False if self.is_signed is None else self.is_signed.to_bool_value()
|
|
@@ -319,6 +360,14 @@ class QuantumNumeric(QuantumScalar):
|
|
|
319
360
|
def has_fraction_digits(self) -> bool:
|
|
320
361
|
return self.fraction_digits is not None
|
|
321
362
|
|
|
363
|
+
@property
|
|
364
|
+
def has_constant_fraction_digits(self) -> bool:
|
|
365
|
+
return (
|
|
366
|
+
self.fraction_digits is not None
|
|
367
|
+
and self.fraction_digits.is_evaluated()
|
|
368
|
+
and self.fraction_digits.is_constant()
|
|
369
|
+
)
|
|
370
|
+
|
|
322
371
|
@property
|
|
323
372
|
def fraction_digits_value(self) -> int:
|
|
324
373
|
return (
|
|
@@ -388,6 +437,26 @@ class QuantumNumeric(QuantumScalar):
|
|
|
388
437
|
self.fraction_digits is not None and not self.fraction_digits.is_evaluated()
|
|
389
438
|
)
|
|
390
439
|
|
|
440
|
+
@property
|
|
441
|
+
def is_constant(self) -> bool:
|
|
442
|
+
if (
|
|
443
|
+
self.size is None
|
|
444
|
+
or not self.size.is_evaluated()
|
|
445
|
+
or not self.size.is_constant()
|
|
446
|
+
):
|
|
447
|
+
return False
|
|
448
|
+
if self.is_signed is not None and (
|
|
449
|
+
not self.is_signed.is_evaluated() or not self.is_signed.is_constant()
|
|
450
|
+
):
|
|
451
|
+
return False
|
|
452
|
+
return not (
|
|
453
|
+
self.fraction_digits is not None
|
|
454
|
+
and (
|
|
455
|
+
not self.fraction_digits.is_evaluated()
|
|
456
|
+
or not self.fraction_digits.is_constant()
|
|
457
|
+
)
|
|
458
|
+
)
|
|
459
|
+
|
|
391
460
|
@property
|
|
392
461
|
def expressions(self) -> list[Expression]:
|
|
393
462
|
exprs = []
|
|
@@ -431,6 +500,36 @@ class QuantumNumeric(QuantumScalar):
|
|
|
431
500
|
def minimal_size_in_bits(self) -> int:
|
|
432
501
|
return self.size_in_bits if self.has_size_in_bits else 1
|
|
433
502
|
|
|
503
|
+
def without_symbolic_attributes(self) -> "QuantumNumeric":
|
|
504
|
+
size = (
|
|
505
|
+
None
|
|
506
|
+
if self.size is None
|
|
507
|
+
or not self.size.is_evaluated()
|
|
508
|
+
or not self.size.is_constant()
|
|
509
|
+
else self.size
|
|
510
|
+
)
|
|
511
|
+
is_signed = (
|
|
512
|
+
None
|
|
513
|
+
if self.is_signed is None
|
|
514
|
+
or not self.is_signed.is_evaluated()
|
|
515
|
+
or not self.is_signed.is_constant()
|
|
516
|
+
else self.is_signed
|
|
517
|
+
)
|
|
518
|
+
fraction_digits = (
|
|
519
|
+
None
|
|
520
|
+
if self.fraction_digits is None
|
|
521
|
+
or not self.fraction_digits.is_evaluated()
|
|
522
|
+
or not self.fraction_digits.is_constant()
|
|
523
|
+
else self.fraction_digits
|
|
524
|
+
)
|
|
525
|
+
if size is None or is_signed is None or fraction_digits is None:
|
|
526
|
+
is_signed = fraction_digits = None
|
|
527
|
+
qnum = QuantumNumeric(
|
|
528
|
+
size=size, is_signed=is_signed, fraction_digits=fraction_digits
|
|
529
|
+
)
|
|
530
|
+
qnum.set_bounds(self.get_bounds())
|
|
531
|
+
return qnum
|
|
532
|
+
|
|
434
533
|
|
|
435
534
|
class RegisterQuantumType(BaseModel):
|
|
436
535
|
quantum_types: "ConcreteQuantumType"
|
|
@@ -141,24 +141,10 @@ class ASTToQMODCode:
|
|
|
141
141
|
if len(node.args) != 2:
|
|
142
142
|
raise PrettyPrinterError("Error parsing array access.")
|
|
143
143
|
return f"{self.ast_to_code(node.args[0])}[{self.ast_to_code(node.args[1])}]"
|
|
144
|
-
elif len(node.keywords) > 0:
|
|
145
|
-
# struct instance
|
|
146
|
-
keywords = node.keywords
|
|
147
|
-
initializer_list = self.indent_items(
|
|
148
|
-
lambda: [
|
|
149
|
-
f"{keyword.arg}={self._cleaned_ast_to_code(keyword.value)}"
|
|
150
|
-
for keyword in keywords
|
|
151
|
-
if keyword.arg is not None
|
|
152
|
-
]
|
|
153
|
-
)
|
|
154
|
-
return f"{func} {{{initializer_list}}}"
|
|
155
144
|
else:
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
for kwarg in node.keywords
|
|
160
|
-
]
|
|
161
|
-
return "{}({})".format(func, ", ".join(args + kwargs))
|
|
145
|
+
return "{}({})".format(
|
|
146
|
+
func, ", ".join(self._cleaned_ast_to_code(arg) for arg in node.args)
|
|
147
|
+
)
|
|
162
148
|
elif isinstance(node, ast.Expr):
|
|
163
149
|
return self._cleaned_ast_to_code(node.value)
|
|
164
150
|
else:
|
|
@@ -78,7 +78,7 @@ class AllocateEmitter(Emitter[Allocate]):
|
|
|
78
78
|
target: QuantumSymbol,
|
|
79
79
|
op_update_dict: dict[str, Expression],
|
|
80
80
|
) -> None:
|
|
81
|
-
if target.quantum_type.
|
|
81
|
+
if target.quantum_type.has_size_in_bits:
|
|
82
82
|
expr = str(target.quantum_type.size_in_bits)
|
|
83
83
|
elif self._allow_symbolic_attrs:
|
|
84
84
|
expr = f"{target.handle}.size"
|
|
@@ -19,11 +19,5 @@ class HandleEvaluator(Emitter[QuantumOperation]):
|
|
|
19
19
|
if not isinstance(handle, HandleBinding):
|
|
20
20
|
return False
|
|
21
21
|
evaluated_handle = self._interpreter.evaluate(handle).value.handle.collapse()
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
op = op.model_copy(
|
|
25
|
-
update={self._handle_name: evaluated_handle, "back_ref": op.uuid}
|
|
26
|
-
)
|
|
27
|
-
self._interpreter.add_to_debug_info(op)
|
|
28
|
-
self._interpreter.emit(op)
|
|
29
|
-
return True
|
|
22
|
+
setattr(op, self._handle_name, evaluated_handle)
|
|
23
|
+
return False
|
|
@@ -101,6 +101,7 @@ __all__ = [
|
|
|
101
101
|
"inplace_c_modular_multiply",
|
|
102
102
|
"inplace_prepare_complex_amplitudes",
|
|
103
103
|
"inplace_prepare_int",
|
|
104
|
+
"inplace_prepare_sparse_amplitudes",
|
|
104
105
|
"lcu",
|
|
105
106
|
"lcu_pauli",
|
|
106
107
|
"linear_pauli_rotations",
|
|
@@ -116,6 +117,8 @@ __all__ = [
|
|
|
116
117
|
"prepare_exponential_state",
|
|
117
118
|
"prepare_ghz_state",
|
|
118
119
|
"prepare_int",
|
|
120
|
+
"prepare_linear_amplitudes",
|
|
121
|
+
"prepare_sparse_amplitudes",
|
|
119
122
|
"prepare_uniform_interval_state",
|
|
120
123
|
"prepare_uniform_trimmed_state",
|
|
121
124
|
"projector_controlled_double_phase",
|
|
@@ -3,7 +3,7 @@ from typing import Literal
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
|
|
5
5
|
from classiq.open_library.functions.state_preparation import (
|
|
6
|
-
|
|
6
|
+
apply_phase_table,
|
|
7
7
|
)
|
|
8
8
|
from classiq.open_library.functions.utility_functions import switch
|
|
9
9
|
from classiq.qmod.builtins.functions import IDENTITY, X, Y, Z, inplace_prepare_state
|
|
@@ -73,7 +73,7 @@ def lcu(
|
|
|
73
73
|
iteration=lambda i: control(block == i, lambda: unitaries[i]()),
|
|
74
74
|
),
|
|
75
75
|
# TODO: replace to sparse constant phase
|
|
76
|
-
|
|
76
|
+
apply_phase_table(phases, block),
|
|
77
77
|
],
|
|
78
78
|
)
|
|
79
79
|
|
|
@@ -13,8 +13,8 @@ from classiq.open_library.functions.utility_functions import (
|
|
|
13
13
|
from classiq.qmod.builtins.functions import (
|
|
14
14
|
CX,
|
|
15
15
|
IDENTITY,
|
|
16
|
+
PHASE,
|
|
16
17
|
RY,
|
|
17
|
-
RZ,
|
|
18
18
|
H,
|
|
19
19
|
X,
|
|
20
20
|
inplace_prepare_amplitudes,
|
|
@@ -24,6 +24,7 @@ from classiq.qmod.builtins.operations import (
|
|
|
24
24
|
control,
|
|
25
25
|
if_,
|
|
26
26
|
inplace_add,
|
|
27
|
+
inplace_xor,
|
|
27
28
|
repeat,
|
|
28
29
|
within_apply,
|
|
29
30
|
)
|
|
@@ -324,7 +325,7 @@ def _classical_hadamard_transform(arr: list[float]) -> np.ndarray:
|
|
|
324
325
|
|
|
325
326
|
|
|
326
327
|
@qfunc
|
|
327
|
-
def
|
|
328
|
+
def apply_phase_table(
|
|
328
329
|
phases: list[float],
|
|
329
330
|
target: QArray[QBit, Literal["log(get_field(phases, 'len'), 2)"]],
|
|
330
331
|
) -> None:
|
|
@@ -333,10 +334,10 @@ def _load_phases(
|
|
|
333
334
|
for i in range(1, len(alphas) - 1):
|
|
334
335
|
gray = _graycode(i)
|
|
335
336
|
next_gray = _graycode(i + 1)
|
|
336
|
-
|
|
337
|
+
PHASE(alphas[gray], target[_msb(gray)])
|
|
337
338
|
CX(target[_control_qubit(i)], target[_msb(next_gray)])
|
|
338
339
|
|
|
339
|
-
|
|
340
|
+
PHASE(alphas[_graycode(len(phases) - 1)], target[target.len - 1])
|
|
340
341
|
|
|
341
342
|
|
|
342
343
|
@qfunc
|
|
@@ -358,7 +359,8 @@ def inplace_prepare_complex_amplitudes(
|
|
|
358
359
|
target: The quantum variable to act upon.
|
|
359
360
|
"""
|
|
360
361
|
inplace_prepare_amplitudes(magnitudes, 0, target)
|
|
361
|
-
|
|
362
|
+
if not np.allclose(phases, 0, atol=1e-12):
|
|
363
|
+
apply_phase_table(phases, target)
|
|
362
364
|
|
|
363
365
|
|
|
364
366
|
@qfunc
|
|
@@ -469,3 +471,178 @@ def prepare_basis_state(state: list[bool], arr: Output[QArray]) -> None:
|
|
|
469
471
|
for idx, value in enumerate(state):
|
|
470
472
|
if value:
|
|
471
473
|
X(arr[idx])
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def linear_hadamard_walsh_coefficients(n: int) -> np.ndarray:
|
|
477
|
+
coeffs = np.zeros(n + 1)
|
|
478
|
+
coeffs[0] = 2 ** (n / 2) * ((2**n - 1) / 2)
|
|
479
|
+
for k in range(1, n + 1):
|
|
480
|
+
coeffs[k] = -(2 ** (k - 1 + n / 2) / 2)
|
|
481
|
+
return coeffs / np.linalg.norm(coeffs)
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
@qfunc
|
|
485
|
+
def _zero_ctrl_rot(ctrl: QNum, target: QBit, theta: CReal) -> None:
|
|
486
|
+
control(ctrl == 0, lambda: RY(theta, target))
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
@qfunc
|
|
490
|
+
def prepare_linear_amplitudes(x: QArray) -> None:
|
|
491
|
+
"""
|
|
492
|
+
[Qmod Classiq-library function]
|
|
493
|
+
|
|
494
|
+
Initializes a quantum variable in a state with linear amplitudes:
|
|
495
|
+
$$|\\psi\rangle = \frac{1}{Z}\\sum_{x=0}^{2^n-1}{x|x\rangle}$$
|
|
496
|
+
Where $Z$ is a normalization constant.
|
|
497
|
+
|
|
498
|
+
Based on the paper https://quantum-journal.org/papers/q-2024-03-21-1297/pdf/
|
|
499
|
+
|
|
500
|
+
Args:
|
|
501
|
+
x: The quantum register to prepare.
|
|
502
|
+
"""
|
|
503
|
+
coeffs = linear_hadamard_walsh_coefficients(x.size) # type: ignore[arg-type]
|
|
504
|
+
thetas = np.zeros(x.size + 1) # type: ignore[arg-type]
|
|
505
|
+
for i in range(x.size): # type: ignore[arg-type]
|
|
506
|
+
thetas[i] = 2 * np.arcsin(
|
|
507
|
+
coeffs[i + 1] / np.sqrt(1 - np.linalg.norm(coeffs[1 : i + 1]) ** 2)
|
|
508
|
+
)
|
|
509
|
+
for k in range(x.len):
|
|
510
|
+
if k == 0:
|
|
511
|
+
RY(thetas[k], x[k])
|
|
512
|
+
else:
|
|
513
|
+
_zero_ctrl_rot(x[0:k], x[k], thetas[k])
|
|
514
|
+
|
|
515
|
+
hadamard_transform(x)
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
@qfunc
|
|
519
|
+
def swap_states(a: int, b: int, target: QArray) -> None:
|
|
520
|
+
"""
|
|
521
|
+
Swap 2 computational basis states a and b, leave all other states untouched.
|
|
522
|
+
|
|
523
|
+
Args:
|
|
524
|
+
a: 1st state number.
|
|
525
|
+
b: 2nd state number.
|
|
526
|
+
target: The quantum variable to act upon.
|
|
527
|
+
"""
|
|
528
|
+
assert a != b, "a and b should be different"
|
|
529
|
+
diff = a ^ b
|
|
530
|
+
diff_indices = [i for i in range(target.len) if (diff >> i) & 1]
|
|
531
|
+
anchor = diff_indices[0]
|
|
532
|
+
anchor_bit = (a >> anchor) & 1
|
|
533
|
+
|
|
534
|
+
# a hack for the binding (should be improved after we have cast
|
|
535
|
+
target_without_anchor = []
|
|
536
|
+
if anchor > 0:
|
|
537
|
+
target_without_anchor.append(target[0:anchor])
|
|
538
|
+
if anchor < target.len - 1:
|
|
539
|
+
target_without_anchor.append(target[anchor + 1 : target.len])
|
|
540
|
+
|
|
541
|
+
@qfunc
|
|
542
|
+
def _xor_if_equal(n: CInt, ctrl: QNum, target: QBit) -> None:
|
|
543
|
+
target ^= ctrl == n
|
|
544
|
+
|
|
545
|
+
def _remove_bit(x: int, j: int) -> int:
|
|
546
|
+
"""
|
|
547
|
+
Remove bit j from integer x (0 = least significant bit)
|
|
548
|
+
and shift higher bits down.
|
|
549
|
+
"""
|
|
550
|
+
low = x & ((1 << j) - 1) # bits below j
|
|
551
|
+
high = x >> (j + 1) # bits above j
|
|
552
|
+
return low | (high << j)
|
|
553
|
+
|
|
554
|
+
within_apply(
|
|
555
|
+
# make the states equal except the anchor qubit
|
|
556
|
+
lambda: [
|
|
557
|
+
inplace_xor(target[anchor] != anchor_bit, target[i])
|
|
558
|
+
for i in diff_indices[1:]
|
|
559
|
+
],
|
|
560
|
+
# do the actual swapping
|
|
561
|
+
lambda: _xor_if_equal(
|
|
562
|
+
_remove_bit(a, anchor), target_without_anchor, target[anchor]
|
|
563
|
+
),
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
@qfunc
|
|
568
|
+
def inplace_prepare_sparse_amplitudes(
|
|
569
|
+
states: list[int], amplitudes: list[complex], target: QArray
|
|
570
|
+
) -> None:
|
|
571
|
+
"""
|
|
572
|
+
[Qmod Classiq-library function]
|
|
573
|
+
|
|
574
|
+
Prepares a quantum state with the given (complex) amplitudes. The input is given sparse format, as a list of non-zero states and their corresponding amplitudes.
|
|
575
|
+
Notice that the function is only suitable sparse states. Inspired by https://arxiv.org/abs/2310.19309.
|
|
576
|
+
|
|
577
|
+
For example, `inplace_prepare_sparse_amplitudes([1, 8], [np.sqrt(0.5), np.sqrt(0.5)], target)` will prepare the state sqrt(0.5)|1> + sqrt(0.5)|8>
|
|
578
|
+
on the target variable, assuming it starts in the |0> state.
|
|
579
|
+
|
|
580
|
+
Complexity: Asymptotic gate complexity is $O(dn)$ where d is the number of states and n is the target number of qubits.
|
|
581
|
+
|
|
582
|
+
Args:
|
|
583
|
+
states: A list of distinct computational basis indices to populate. Each integer corresponds to the basis state in the computational basis.
|
|
584
|
+
amplitudes: A list of complex amplitudes for the corresponding entries in `states`. Must have the same length as `states`.
|
|
585
|
+
target: The quantum variable on which the state is to be prepared. Its size must be sufficient to represent all states in `states`.
|
|
586
|
+
"""
|
|
587
|
+
assert len(amplitudes) == len(
|
|
588
|
+
states
|
|
589
|
+
), "amplitudes and states should have the same size"
|
|
590
|
+
assert (
|
|
591
|
+
max(list(states)) <= 2**target.len
|
|
592
|
+
), "the target quantum variable is not large enough to populate all states"
|
|
593
|
+
assert len(set(states)) == len(states), "all states should be distinct"
|
|
594
|
+
|
|
595
|
+
# prepare a dense state
|
|
596
|
+
dense_size = max(int(np.ceil(np.log2(len(states)))), 1)
|
|
597
|
+
dense_amplitudes = np.zeros(2**dense_size, dtype=complex)
|
|
598
|
+
|
|
599
|
+
## make sure the states with number smaller than the dense state size are located in their place already
|
|
600
|
+
for i, state in enumerate(states):
|
|
601
|
+
if state < len(dense_amplitudes):
|
|
602
|
+
dense_amplitudes[state] = amplitudes[i]
|
|
603
|
+
|
|
604
|
+
## make a swap list for all other states
|
|
605
|
+
swap_list = []
|
|
606
|
+
free_index = 0
|
|
607
|
+
for i, state in enumerate(states):
|
|
608
|
+
if state < len(dense_amplitudes):
|
|
609
|
+
continue # already populated
|
|
610
|
+
while dense_amplitudes[free_index]:
|
|
611
|
+
free_index += 1 # index is not free
|
|
612
|
+
|
|
613
|
+
dense_amplitudes[free_index] = amplitudes[i]
|
|
614
|
+
swap_list.append((free_index, state))
|
|
615
|
+
free_index += 1
|
|
616
|
+
|
|
617
|
+
inplace_prepare_complex_amplitudes(
|
|
618
|
+
np.abs(dense_amplitudes), np.angle(dense_amplitudes), target[0:dense_size]
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
# swap all required states
|
|
622
|
+
sparse_size = max(int(np.ceil(np.log2(max(list(states) + [1])))), 1)
|
|
623
|
+
for a, b in swap_list:
|
|
624
|
+
swap_states(a, b, target[0:sparse_size])
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
@qfunc
|
|
628
|
+
def prepare_sparse_amplitudes(
|
|
629
|
+
states: list[int], amplitudes: list[complex], out: Output[QArray]
|
|
630
|
+
) -> None:
|
|
631
|
+
"""
|
|
632
|
+
[Qmod Classiq-library function]
|
|
633
|
+
|
|
634
|
+
Initializes and prepares a quantum state with the given (complex) amplitudes. The input is given sparse format, as a list of non-zero states and their corresponding amplitudes.
|
|
635
|
+
Notice that the function is only suitable sparse states. Inspired by https://arxiv.org/abs/2310.19309.
|
|
636
|
+
|
|
637
|
+
For example, `prepare_sparse_amplitudes([1, 8], [np.sqrt(0.5), np.sqrt(0.5)], out)` will and allocate it to be of size 4 qubits, and
|
|
638
|
+
prepare it in the state sqrt(0.5)|1> + sqrt(0.5)|8>.
|
|
639
|
+
|
|
640
|
+
Complexity: Asymptotic gate complexity is $O(dn)$ where d is the number of states and n is the required number of qubits.
|
|
641
|
+
|
|
642
|
+
Args:
|
|
643
|
+
states: A list of distinct computational basis indices to populate. Each integer corresponds to the basis state in the computational basis.
|
|
644
|
+
amplitudes: A list of complex amplitudes for the corresponding entries in `states`. Must have the same length as `states`.
|
|
645
|
+
out: The allocated quantum variable.
|
|
646
|
+
"""
|
|
647
|
+
allocate(max(int(np.ceil(np.log2(max(states)))), 1), out)
|
|
648
|
+
inplace_prepare_sparse_amplitudes(states, amplitudes, out)
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from classiq.interface.finance.function_input import FinanceFunctionInput
|
|
2
|
-
|
|
3
1
|
from .classical_execution_primitives import * # noqa: F403
|
|
4
2
|
from .classical_execution_primitives import (
|
|
5
3
|
__all__ as _builtin_classical_execution_primitives,
|
|
@@ -17,7 +15,6 @@ from .operations import __all__ as _builtin_operations
|
|
|
17
15
|
from .structs import * # noqa: F403
|
|
18
16
|
from .structs import __all__ as _builtin_structs
|
|
19
17
|
|
|
20
|
-
FinanceFunctionInput.model_rebuild()
|
|
21
18
|
BUILTIN_CONSTANTS = [
|
|
22
19
|
constant._get_constant_node()
|
|
23
20
|
for constant in [
|
|
@@ -54,38 +54,10 @@ def hypercube_entangler_graph(
|
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def gaussian_finance_post_process(
|
|
58
|
-
finance_model: GaussianModel,
|
|
59
|
-
estimation_method: FinanceFunction,
|
|
60
|
-
probability: CReal,
|
|
61
|
-
) -> CReal:
|
|
62
|
-
return symbolic_function(
|
|
63
|
-
finance_model,
|
|
64
|
-
estimation_method,
|
|
65
|
-
probability,
|
|
66
|
-
return_type=CReal, # type:ignore[type-abstract]
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def log_normal_finance_post_process(
|
|
71
|
-
finance_model: LogNormalModel,
|
|
72
|
-
estimation_method: FinanceFunction,
|
|
73
|
-
probability: CReal,
|
|
74
|
-
) -> CReal:
|
|
75
|
-
return symbolic_function(
|
|
76
|
-
finance_model,
|
|
77
|
-
estimation_method,
|
|
78
|
-
probability,
|
|
79
|
-
return_type=CReal, # type:ignore[type-abstract]
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
|
|
83
57
|
__all__ = [
|
|
84
58
|
"qft_const_adder_phase",
|
|
85
59
|
"fock_hamiltonian_problem_to_hamiltonian",
|
|
86
60
|
"molecule_problem_to_hamiltonian",
|
|
87
61
|
"grid_entangler_graph",
|
|
88
62
|
"hypercube_entangler_graph",
|
|
89
|
-
"gaussian_finance_post_process",
|
|
90
|
-
"log_normal_finance_post_process",
|
|
91
63
|
]
|