classiq 0.102.0__py3-none-any.whl → 0.104.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. classiq/applications/chemistry/op_utils.py +32 -0
  2. classiq/evaluators/qmod_annotated_expression.py +1 -1
  3. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +1 -8
  4. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +1 -1
  5. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +2 -2
  6. classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +18 -29
  7. classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +1 -6
  8. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +1 -7
  9. classiq/evaluators/qmod_type_inference/quantum_type_comparison.py +52 -0
  10. classiq/execution/execution_session.py +1 -1
  11. classiq/execution/functions/__init__.py +3 -0
  12. classiq/execution/functions/_logging.py +19 -0
  13. classiq/execution/functions/constants.py +9 -0
  14. classiq/execution/functions/parse_provider_backend.py +90 -0
  15. classiq/execution/functions/sample.py +257 -0
  16. classiq/interface/_version.py +1 -1
  17. classiq/interface/backend/backend_preferences.py +15 -0
  18. classiq/interface/backend/provider_config/providers/aqt.py +1 -1
  19. classiq/interface/backend/provider_config/providers/azure.py +1 -2
  20. classiq/interface/backend/provider_config/providers/ibm.py +1 -1
  21. classiq/interface/backend/quantum_backend_providers.py +3 -0
  22. classiq/interface/executor/result.py +9 -5
  23. classiq/interface/generator/arith/binary_ops.py +38 -2
  24. classiq/interface/generator/function_param_list.py +4 -2
  25. classiq/interface/generator/functions/builtins/internal_operators.py +5 -9
  26. classiq/interface/generator/functions/classical_type.py +45 -0
  27. classiq/interface/generator/functions/type_name.py +23 -0
  28. classiq/interface/generator/generated_circuit_data.py +0 -2
  29. classiq/interface/generator/types/compilation_metadata.py +9 -0
  30. classiq/interface/hardware.py +1 -0
  31. classiq/interface/helpers/model_normalizer.py +42 -6
  32. classiq/interface/interface_version.py +1 -1
  33. classiq/interface/model/invert.py +8 -0
  34. classiq/interface/model/model_visitor.py +4 -2
  35. classiq/interface/model/quantum_type.py +21 -0
  36. classiq/interface/model/statement_block.py +0 -4
  37. classiq/model_expansions/capturing/captured_vars.py +16 -12
  38. classiq/model_expansions/function_builder.py +9 -1
  39. classiq/model_expansions/interpreters/base_interpreter.py +9 -8
  40. classiq/model_expansions/interpreters/generative_interpreter.py +9 -24
  41. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -0
  42. classiq/model_expansions/quantum_operations/assignment_result_processor.py +132 -28
  43. classiq/model_expansions/quantum_operations/bind.py +4 -0
  44. classiq/model_expansions/quantum_operations/call_emitter.py +5 -35
  45. classiq/model_expansions/quantum_operations/emitter.py +1 -4
  46. classiq/model_expansions/quantum_operations/expression_evaluator.py +0 -3
  47. classiq/model_expansions/visitors/uncomputation_signature_inference.py +0 -9
  48. classiq/qmod/builtins/functions/__init__.py +9 -0
  49. classiq/qmod/builtins/functions/arithmetic.py +131 -0
  50. classiq/qmod/builtins/functions/exponentiation.py +32 -2
  51. classiq/qmod/builtins/operations.py +2 -38
  52. classiq/qmod/native/pretty_printer.py +1 -12
  53. classiq/qmod/pretty_print/pretty_printer.py +1 -17
  54. classiq/qmod/qmod_parameter.py +4 -0
  55. classiq/qmod/qmod_variable.py +38 -63
  56. classiq/qmod/quantum_function.py +43 -7
  57. classiq/qmod/semantics/validation/function_name_collisions_validation.py +7 -4
  58. classiq/qmod/semantics/validation/model_validation.py +7 -2
  59. classiq/qmod/symbolic_type.py +4 -2
  60. {classiq-0.102.0.dist-info → classiq-0.104.0.dist-info}/METADATA +1 -1
  61. {classiq-0.102.0.dist-info → classiq-0.104.0.dist-info}/RECORD +63 -59
  62. classiq/interface/generator/amplitude_loading.py +0 -103
  63. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +0 -77
  64. {classiq-0.102.0.dist-info → classiq-0.104.0.dist-info}/WHEEL +0 -0
  65. {classiq-0.102.0.dist-info → classiq-0.104.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -1,7 +1,15 @@
1
- from typing import TYPE_CHECKING
1
+ from typing import TYPE_CHECKING, NoReturn, cast
2
2
 
3
- from classiq.interface.exceptions import ClassiqExpansionError
3
+ from classiq.interface.exceptions import (
4
+ ClassiqExpansionError,
5
+ )
4
6
  from classiq.interface.generator.expressions.expression import Expression
7
+ from classiq.interface.generator.functions.classical_type import (
8
+ Bool,
9
+ ClassicalArray,
10
+ ClassicalTuple,
11
+ Integer,
12
+ )
5
13
  from classiq.interface.model.allocate import Allocate
6
14
  from classiq.interface.model.bind_operation import BindOperation
7
15
  from classiq.interface.model.block import Block
@@ -20,9 +28,11 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
20
28
  )
21
29
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
22
30
  from classiq.interface.model.quantum_type import (
31
+ QuantumBit,
23
32
  QuantumBitvector,
24
33
  QuantumNumeric,
25
34
  QuantumScalar,
35
+ QuantumType,
26
36
  )
27
37
  from classiq.interface.model.statement_block import StatementBlock
28
38
  from classiq.interface.model.variable_declaration_statement import (
@@ -31,6 +41,18 @@ from classiq.interface.model.variable_declaration_statement import (
31
41
  from classiq.interface.model.within_apply_operation import WithinApply
32
42
 
33
43
  from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
44
+ from classiq.evaluators.qmod_node_evaluators.utils import (
45
+ QmodType,
46
+ array_len,
47
+ element_types,
48
+ is_numeric_type,
49
+ )
50
+ from classiq.evaluators.qmod_type_inference.classical_type_inference import (
51
+ infer_classical_type,
52
+ )
53
+ from classiq.evaluators.qmod_type_inference.quantum_type_comparison import (
54
+ compare_quantum_types,
55
+ )
34
56
  from classiq.evaluators.qmod_type_inference.quantum_type_inference import (
35
57
  inject_quantum_type_attributes_inplace,
36
58
  )
@@ -43,13 +65,22 @@ from classiq.model_expansions.quantum_operations.arithmetic.explicit_boolean_exp
43
65
  validate_assignment_bool_expression,
44
66
  )
45
67
  from classiq.model_expansions.quantum_operations.emitter import Emitter
46
- from classiq.model_expansions.scope import ClassicalSymbol
68
+ from classiq.model_expansions.scope import ClassicalSymbol, QuantumSymbol
47
69
  from classiq.qmod.builtins.functions.standard_gates import CX
48
70
 
49
71
  if TYPE_CHECKING:
50
72
  from classiq.model_expansions.interpreters.base_interpreter import BaseInterpreter
51
73
 
52
74
 
75
+ def _get_expr_type(op: QuantumAssignmentOperation) -> QmodType:
76
+ expr_val = op.expression.value.value
77
+ if isinstance(expr_val, QmodAnnotatedExpression):
78
+ return expr_val.get_type(expr_val.root)
79
+ else:
80
+ expr_type = infer_classical_type(expr_val)
81
+ return expr_type
82
+
83
+
53
84
  class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
54
85
  def __init__(
55
86
  self, interpreter: "BaseInterpreter", replace_assignment_if_needed: bool = False
@@ -60,14 +91,26 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
60
91
  def emit(self, op: QuantumAssignmentOperation, /) -> bool:
61
92
  result_symbol = self._interpreter.evaluate(op.result_var).value
62
93
  if isinstance(result_symbol, ClassicalSymbol):
63
- return False
94
+ return self._emit_classical_assignment(op)
95
+ if isinstance(result_symbol.quantum_type, QuantumScalar):
96
+ return self._emit_scalar_assignment(op)
97
+ expr_val = op.expression.value.value
98
+ if isinstance(expr_val, QmodAnnotatedExpression) and isinstance(
99
+ expr_val.get_type(expr_val.root), QuantumType
100
+ ):
101
+ return self._emit_quantum_var_quantum_expr_assignment(op)
102
+ return self._emit_quantum_var_classical_expr_assignment(op)
103
+
104
+ def _emit_classical_assignment(self, _: QuantumAssignmentOperation, /) -> bool:
105
+ return False
106
+
107
+ def _emit_scalar_assignment(self, op: QuantumAssignmentOperation, /) -> bool:
108
+ result_symbol = self._interpreter.evaluate(op.result_var).value
64
109
  result_type = result_symbol.quantum_type
65
- if not isinstance(result_type, QuantumScalar):
66
- raise ClassiqExpansionError(
67
- f"Cannot assign into a non-scalar quantum variable "
68
- f"{str(result_symbol.handle)!r} of type "
69
- f"{result_type.raw_qmod_type_name}"
70
- )
110
+
111
+ expr_type = _get_expr_type(op)
112
+ if not is_numeric_type(expr_type) and not isinstance(expr_type, Bool):
113
+ self._raise_type_error(result_symbol, expr_type)
71
114
 
72
115
  if not (
73
116
  isinstance(op, ArithmeticOperation)
@@ -82,36 +125,73 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
82
125
  )
83
126
  convert_assignment_bool_expression(op)
84
127
 
85
- expression_type = self._infer_expression_type(op)
86
- if expression_type is None:
128
+ quantum_expr_type = self._infer_scalar_expression_type(op)
129
+ if quantum_expr_type is None:
87
130
  return False
88
131
 
89
132
  if not result_type.is_instantiated:
90
- if not inject_quantum_type_attributes_inplace(expression_type, result_type):
91
- raise ClassiqExpansionError(
92
- f"Cannot assign expression of type "
93
- f"{expression_type.qmod_type_name} to variable "
94
- f"{str(result_symbol)!r} of type "
95
- f"{result_symbol.quantum_type.qmod_type_name}"
96
- )
133
+ if not inject_quantum_type_attributes_inplace(
134
+ quantum_expr_type, result_type
135
+ ):
136
+ self._raise_type_error(result_symbol, expr_type)
97
137
  return False
98
138
 
99
- if isinstance(result_type, QuantumNumeric) and isinstance(
100
- expression_type, QuantumNumeric
139
+ if not isinstance(result_type, QuantumScalar) or not isinstance(
140
+ quantum_expr_type, QuantumScalar
101
141
  ):
102
- result_type.set_bounds(expression_type.get_bounds())
103
- if self._same_numeric_attributes(result_type, expression_type):
142
+ return False
143
+ if isinstance(result_type, QuantumNumeric):
144
+ result_type.set_bounds(quantum_expr_type.get_bounds())
145
+ if self._same_numeric_attributes(result_type, quantum_expr_type):
104
146
  return False
105
147
  self._validate_declared_attributes(
106
- result_type, expression_type, str(op.result_var)
148
+ result_type, quantum_expr_type, str(op.result_var)
107
149
  )
108
150
  if not self._replace_assignment_if_needed:
109
151
  return False
110
152
 
111
- self._assign_to_inferred_var_and_bind(op, result_type, expression_type)
153
+ self._assign_to_inferred_var_and_bind(op, result_type, quantum_expr_type)
112
154
  return True
113
155
 
114
- def _infer_expression_type(self, op: ArithmeticOperation) -> QuantumScalar | None:
156
+ def _emit_quantum_var_quantum_expr_assignment(
157
+ self, op: QuantumAssignmentOperation, /
158
+ ) -> bool:
159
+ result_symbol = self._interpreter.evaluate(op.result_var).value
160
+ result_type = result_symbol.quantum_type
161
+ expr_type = cast(QuantumType, _get_expr_type(op))
162
+ if not inject_quantum_type_attributes_inplace(
163
+ expr_type, result_type
164
+ ) or not compare_quantum_types(result_type, expr_type):
165
+ self._raise_type_error(result_symbol, expr_type)
166
+ return False
167
+
168
+ def _emit_quantum_var_classical_expr_assignment(
169
+ self, op: QuantumAssignmentOperation, /
170
+ ) -> bool:
171
+ result_symbol = self._interpreter.evaluate(op.result_var).value
172
+ result_type = result_symbol.quantum_type
173
+
174
+ quantum_expr_type = self._infer_classical_array_expression_type(op)
175
+ if quantum_expr_type is None or not inject_quantum_type_attributes_inplace(
176
+ quantum_expr_type, result_type
177
+ ):
178
+ self._raise_type_error(result_symbol, _get_expr_type(op))
179
+
180
+ return False
181
+
182
+ def _raise_type_error(
183
+ self, result_symbol: QuantumSymbol, expr_type: QmodType
184
+ ) -> NoReturn:
185
+ raise ClassiqExpansionError(
186
+ f"Cannot assign expression of type "
187
+ f"{expr_type.qmod_type_name} to variable "
188
+ f"{str(result_symbol)!r} of type "
189
+ f"{result_symbol.quantum_type.qmod_type_name}"
190
+ )
191
+
192
+ def _infer_scalar_expression_type(
193
+ self, op: QuantumAssignmentOperation
194
+ ) -> QuantumType | None:
115
195
  expr = self._evaluate_expression(op.expression)
116
196
  expr_val = expr.value.value
117
197
  if isinstance(expr_val, QmodAnnotatedExpression):
@@ -129,9 +209,33 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
129
209
  ).to_quantum_numeric()
130
210
  return None
131
211
 
212
+ def _infer_classical_array_expression_type(
213
+ self, op: QuantumAssignmentOperation
214
+ ) -> QuantumType | None:
215
+ expr = self._evaluate_expression(op.expression)
216
+ expr_val = expr.value.value
217
+ if isinstance(expr_val, QmodAnnotatedExpression):
218
+ root_type = expr_val.get_type(expr_val.root)
219
+ if isinstance(root_type, (ClassicalArray, ClassicalTuple)) and all(
220
+ isinstance(element_type, Integer)
221
+ for element_type in element_types(root_type)
222
+ ):
223
+ list_len = array_len(root_type)
224
+ len_expr = (
225
+ Expression(expr=str(list_len)) if list_len is not None else None
226
+ )
227
+ return QuantumBitvector(element_type=QuantumBit(), length=len_expr)
228
+ return None
229
+ if isinstance(expr_val, list) and all(
230
+ element in (0, 1) for element in expr_val
231
+ ):
232
+ length_expr = Expression(expr=str(len(expr_val)))
233
+ return QuantumBitvector(element_type=QuantumBit(), length=length_expr)
234
+ return None
235
+
132
236
  @staticmethod
133
237
  def _same_numeric_attributes(
134
- result_type: QuantumNumeric, expression_type: QuantumScalar
238
+ result_type: QuantumScalar, expression_type: QuantumScalar
135
239
  ) -> bool:
136
240
  return (
137
241
  result_type.size_in_bits == expression_type.size_in_bits
@@ -142,7 +246,7 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
142
246
 
143
247
  @classmethod
144
248
  def _validate_declared_attributes(
145
- cls, result_type: QuantumNumeric, expression_type: QuantumScalar, var: str
249
+ cls, result_type: QuantumScalar, expression_type: QuantumScalar, var: str
146
250
  ) -> None:
147
251
  result_size, result_sign, result_fractions = (
148
252
  result_type.size_in_bits,
@@ -97,6 +97,8 @@ class BindEmitter(Emitter[BindOperation]):
97
97
  state = self._builder.current_block.captured_vars.get_state(
98
98
  var_name, inp.defining_function
99
99
  )
100
+ if state is None:
101
+ continue
100
102
  if not state:
101
103
  raise ClassiqExpansionError(
102
104
  f"Cannot use uninitialized quantum variable "
@@ -112,6 +114,8 @@ class BindEmitter(Emitter[BindOperation]):
112
114
  state = self._builder.current_block.captured_vars.get_state(
113
115
  var_name, out.defining_function
114
116
  )
117
+ if state is None:
118
+ continue
115
119
  if state:
116
120
  raise ClassiqExpansionError(
117
121
  f"Cannot use initialized quantum variable "
@@ -12,9 +12,6 @@ from classiq.interface.debug_info.debug_info import (
12
12
  new_function_debug_info_by_node,
13
13
  )
14
14
  from classiq.interface.exceptions import ClassiqExpansionError
15
- from classiq.interface.generator.functions.port_declaration import (
16
- PortDeclarationDirection,
17
- )
18
15
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
19
16
  from classiq.interface.helpers.text_utils import are, readable_list, s
20
17
  from classiq.interface.model.block import Block
@@ -45,8 +42,6 @@ from classiq.evaluators.parameter_types import (
45
42
  )
46
43
  from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
47
44
  from classiq.model_expansions.capturing.captured_vars import (
48
- INITIALIZED_VAR_MESSAGE,
49
- UNINITIALIZED_VAR_MESSAGE,
50
45
  validate_args_are_not_propagated,
51
46
  )
52
47
  from classiq.model_expansions.closure import Closure, FunctionClosure
@@ -109,12 +104,14 @@ def _validate_gen_args(
109
104
  if (
110
105
  isinstance(param, ClassicalParameterDeclaration)
111
106
  and not param.classical_type.is_purely_declarative
112
- and isinstance(arg.value, QmodAnnotatedExpression)
107
+ and isinstance(arg_val := arg.value, QmodAnnotatedExpression)
113
108
  ):
114
109
  readable_expr = transform_expression(str(arg.value), {}, {}, one_line=True)
110
+ expr_type = arg_val.get_type(arg_val.root)
115
111
  raise ClassiqExpansionError(
116
- f"Cannot pass symbolic expression {readable_expr!r} as Python-type "
117
- f"parameter {param.name!r}"
112
+ f"Cannot pass {readable_expr!r} of type {expr_type.qmod_type_name} as "
113
+ f"parameter {param.name!r} of Python-type "
114
+ f"{param.classical_type.python_type_name}"
118
115
  )
119
116
 
120
117
 
@@ -195,7 +192,6 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], ModelR
195
192
  ) -> QuantumFunctionCall:
196
193
  function = function.clone()
197
194
  function = function.set_depth(self._builder.current_function.depth + 1)
198
- self._validate_call_args(function.positional_arg_declarations, args)
199
195
  evaluated_args = [self._interpreter.evaluate(arg) for arg in args]
200
196
  _validate_cloning(evaluated_args)
201
197
  _validate_runtime_args(evaluated_args, self._current_scope)
@@ -391,32 +387,6 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], ModelR
391
387
  permutation=function.permutation,
392
388
  )
393
389
 
394
- def _validate_call_args(
395
- self, params: Sequence[PositionalArg], args: list[ArgValue]
396
- ) -> None:
397
- for param, arg in zip(params, args):
398
- if not isinstance(param, PortDeclaration) or not isinstance(
399
- arg, HandleBinding
400
- ):
401
- continue
402
- var_name = arg.name
403
- symbol = self._interpreter.evaluate(var_name)
404
- if (
405
- not isinstance(symbol.value, QuantumSymbol)
406
- or symbol.defining_function is None
407
- ):
408
- continue
409
- var_state = self._builder.current_block.captured_vars.get_state(
410
- var_name, symbol.defining_function
411
- )
412
- if not var_state and param.direction in (
413
- PortDeclarationDirection.Inout,
414
- PortDeclarationDirection.Input,
415
- ):
416
- raise ClassiqExpansionError(UNINITIALIZED_VAR_MESSAGE.format(var_name))
417
- if var_state and param.direction == PortDeclarationDirection.Output:
418
- raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
419
-
420
390
  def _validate_type_modifiers(
421
391
  self, func_context: FunctionContext, func_def: NativeFunctionDefinition
422
392
  ) -> None:
@@ -144,11 +144,8 @@ class Emitter(Generic[QuantumStatementT], ABC):
144
144
  expression: Expression,
145
145
  *,
146
146
  simplify: bool = False,
147
- treat_qnum_as_float: bool = False,
148
147
  ) -> Expression:
149
- expr_val = self._interpreter.evaluate(
150
- expression, simplify=simplify, treat_qnum_as_float=treat_qnum_as_float
151
- ).value
148
+ expr_val = self._interpreter.evaluate(expression, simplify=simplify).value
152
149
  new_expr = Expression(expr=str(expr_val))
153
150
  new_expr._evaluated_expr = EvaluatedExpression(value=expr_val)
154
151
  return new_expr
@@ -26,14 +26,12 @@ class ExpressionEvaluator(Emitter[QuantumOperation]):
26
26
  *,
27
27
  readable_expression_name: str | None = None,
28
28
  simplify: bool = False,
29
- treat_qnum_as_float: bool = False,
30
29
  allow_link_time_vars: bool = True,
31
30
  allow_runtime_vars: bool = True,
32
31
  ) -> None:
33
32
  super().__init__(interpreter)
34
33
  self._expression_name = expression_name
35
34
  self._simplify = simplify
36
- self._treat_qnum_as_float = treat_qnum_as_float
37
35
  self._allow_link_time_vars = allow_link_time_vars
38
36
  self._allow_runtime_vars = allow_runtime_vars
39
37
  if (
@@ -49,7 +47,6 @@ class ExpressionEvaluator(Emitter[QuantumOperation]):
49
47
  evaluated_expression = self._evaluate_expression(
50
48
  expression,
51
49
  simplify=self._simplify,
52
- treat_qnum_as_float=self._treat_qnum_as_float,
53
50
  )
54
51
  for symbol in self._get_symbols_in_expression(evaluated_expression):
55
52
  self._capture_handle(symbol.handle, PortDeclarationDirection.Inout)
@@ -21,9 +21,6 @@ from classiq.interface.model.invert import Invert
21
21
  from classiq.interface.model.model_visitor import ModelStatementsVisitor
22
22
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
23
23
  from classiq.interface.model.power import Power
24
- from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
25
- AmplitudeLoadingOperation,
26
- )
27
24
  from classiq.interface.model.quantum_expressions.arithmetic_operation import (
28
25
  ArithmeticOperation,
29
26
  )
@@ -147,12 +144,6 @@ class UncomputationSignatureInference(ModelStatementsVisitor):
147
144
  else:
148
145
  self._mark_as_non_const(arith.result_var.name, True)
149
146
 
150
- def visit_AmplitudeLoadingOperation(
151
- self, amp_load: AmplitudeLoadingOperation
152
- ) -> None:
153
- self._mark_as_non_permutation()
154
- self._mark_as_non_const(amp_load.result_var.name, False)
155
-
156
147
  def visit_Control(self, control: Control) -> None:
157
148
  self.visit(control.body)
158
149
  if control.else_block is not None:
@@ -48,6 +48,10 @@ CORE_LIB_DECLS = [
48
48
  integer_xor,
49
49
  modular_add_constant,
50
50
  real_xor_constant,
51
+ multiply,
52
+ multiply_constant,
53
+ canonical_multiply,
54
+ canonical_multiply_constant,
51
55
  U,
52
56
  CCX,
53
57
  free,
@@ -61,6 +65,7 @@ CORE_LIB_DECLS = [
61
65
  commuting_paulis_exponent,
62
66
  suzuki_trotter,
63
67
  unscheduled_suzuki_trotter,
68
+ sequential_suzuki_trotter,
64
69
  exponentiate,
65
70
  multi_suzuki_trotter,
66
71
  parametric_suzuki_trotter,
@@ -111,6 +116,10 @@ __all__ = [ # noqa: RUF022
111
116
  "add_inplace_right",
112
117
  "apply",
113
118
  "exponentiation_with_depth_constraint",
119
+ "multiply",
120
+ "multiply_constant",
121
+ "canonical_multiply",
122
+ "canonical_multiply_constant",
114
123
  "free",
115
124
  "drop",
116
125
  "inplace_prepare_amplitudes",
@@ -1,5 +1,6 @@
1
1
  from typing import Literal
2
2
 
3
+ from classiq.qmod.cparam import CInt
3
4
  from classiq.qmod.qfunc import qfunc, qperm
4
5
  from classiq.qmod.qmod_parameter import CArray, CBool, CReal
5
6
  from classiq.qmod.qmod_variable import (
@@ -83,3 +84,133 @@ def integer_xor(left: Const[QArray[QBit]], right: QArray[QBit]) -> None:
83
84
  @qperm(external=True)
84
85
  def real_xor_constant(left: CReal, right: QNum) -> None:
85
86
  pass
87
+
88
+
89
+ @qperm(external=True)
90
+ def multiply(left: Const[QNum], right: Const[QNum], result: Output[QNum]) -> None:
91
+ """
92
+ [Qmod core-library function]
93
+
94
+ Multiplies two quantum numeric variables:
95
+
96
+ $$
97
+ \\left|\\text{left}\\right\\rangle \\left|\\text{right}\\right\\rangle
98
+ \\mapsto
99
+ \\left|\\text{left}\\right\\rangle \\left|\\text{right}\\right\\rangle
100
+ \\left|\\text{left} \\cdot \\text{right} \\right\\rangle
101
+ $$
102
+
103
+ Args:
104
+ left: The first argument for the multiplication.
105
+ right: The second argument for the multiplication.
106
+ result: The quantum variable to hold the multiplication result.
107
+ """
108
+ pass
109
+
110
+
111
+ @qperm(external=True)
112
+ def multiply_constant(left: CReal, right: Const[QNum], result: Output[QNum]) -> None:
113
+ """
114
+ [Qmod core-library function]
115
+
116
+ Multiplies a quantum numeric variable with a constant:
117
+
118
+ $$
119
+ \\left|\\text{right}\\right\\rangle
120
+ \\mapsto
121
+ \\left|\\text{right}\\right\\rangle
122
+ \\left|\\text{left} \\cdot \\text{right} \\right\\rangle
123
+ $$
124
+
125
+ Args:
126
+ left: The constant argument for the multiplication.
127
+ right: The variable argument for the multiplication.
128
+ result: The quantum variable to hold the multiplication result.
129
+ """
130
+ pass
131
+
132
+
133
+ @qperm(external=True)
134
+ def canonical_multiply(
135
+ left: Const[QArray],
136
+ extend_left: CBool,
137
+ right: Const[QArray],
138
+ extend_right: CBool,
139
+ result: QArray,
140
+ trim_result_lsb: CBool,
141
+ ) -> None:
142
+ """
143
+ [Qmod core-library function]
144
+
145
+ Multiplies two quantum variables representing integers (signed or unsigned) into the
146
+ result variable which is assumed to start in the $|0\\rangle$ state.
147
+
148
+ If `trim_result_lsb` is `False`, applies the transformation:
149
+
150
+ $$
151
+ \\left|\\text{left}\\right\\rangle \\left|\\text{right}\\right\\rangle
152
+ \\left|0\\right\\rangle \\mapsto \\left|\\text{left}\\right\\rangle
153
+ \\left|\\text{right}\\right\\rangle \\left|\\left( \\text{left} \\cdot
154
+ \\text{right} \\right) \\bmod 2^{\\text{result.size}} \\right\\rangle
155
+ $$
156
+
157
+ If `trim_result_lsb` is `True`, the function avoids computing the result's LSB and
158
+ applies the transformation:
159
+
160
+ $$
161
+ \\left|\\text{left}\\right\\rangle \\left|\\text{right}\\right\\rangle
162
+ \\left|0\\right\\rangle \\mapsto \\left|\\text{left}\\right\\rangle
163
+ \\left|\\text{right}\\right\\rangle \\left|\\left( \\text{left} \\cdot
164
+ \\text{right} \\right) \\gg 1 \\bmod 2^{\\text{result.size}} \\right\\rangle
165
+ $$
166
+
167
+ Args:
168
+ left: The first argument for the multiplication.
169
+ extend_left: Whether to sign-extend the left argument.
170
+ right: The second argument for the multiplication.
171
+ extend_right: Whether to sign-extend the right argument.
172
+ result: The quantum variable to hold the multiplication result.
173
+ trim_result_lsb: Whether to avoid computing the result's LSB.
174
+ """
175
+ pass
176
+
177
+
178
+ @qperm(external=True)
179
+ def canonical_multiply_constant(
180
+ left: CInt,
181
+ right: Const[QArray],
182
+ extend_right: CBool,
183
+ result: QArray,
184
+ trim_result_lsb: CBool,
185
+ ) -> None:
186
+ """
187
+ [Qmod core-library function]
188
+
189
+ Multiplies a quantum variable representing an integer (signed or unsigned) with a
190
+ constant, into the result variable which is assumed to start in the $|0\\rangle$ state.
191
+
192
+ If `trim_result_lsb` is `False`, applies the transformation:
193
+
194
+ $$
195
+ \\left|\\text{right}\\right\\rangle \\left|0\\right\\rangle \\mapsto
196
+ \\left|\\text{right}\\right\\rangle \\left|\\left( \\text{left} \\cdot
197
+ \\text{right} \\right) \\bmod 2^{\\text{result.size}} \\right\\rangle
198
+ $$
199
+
200
+ If `trim_result_lsb` is `True`, the function avoids computing the result's LSB and
201
+ applies the transformation:
202
+
203
+ $$
204
+ \\left|\\text{right}\\right\\rangle \\left|0\\right\\rangle \\mapsto
205
+ \\left|\\text{right}\\right\\rangle \\left|\\left( \\text{left} \\cdot
206
+ \\text{right} \\right) \\gg 1 \\bmod 2^{\\text{result.size}} \\right\\rangle
207
+ $$
208
+
209
+ Args:
210
+ left: The constant argument for the multiplication.
211
+ right: The variable argument for the multiplication.
212
+ extend_right: Whether to sign-extend the right argument.
213
+ result: The quantum variable to hold the multiplication result.
214
+ trim_result_lsb: Whether to avoid computing the result's LSB.
215
+ """
216
+ pass
@@ -99,7 +99,7 @@ def multi_suzuki_trotter(
99
99
  The error of a Suzuki-Trotter decomposition decreases as the order and number of repetitions increase.
100
100
 
101
101
  Args:
102
- hamiltonians: The hamitonians to be exponentiated, in sparse representation.
102
+ hamiltonians: The hamiltonians to be exponentiated, in sparse representation.
103
103
  evolution_coefficients: The hamiltonian coefficients (can be link-time).
104
104
  order: The order of the Suzuki-Trotter decomposition.
105
105
  repetitions: The number of repetitions of the Suzuki-Trotter decomposition.
@@ -129,7 +129,37 @@ def unscheduled_suzuki_trotter(
129
129
  The error of a Suzuki-Trotter decomposition decreases as the order and number of repetitions increase.
130
130
 
131
131
  Args:
132
- hamiltonians: The hamitonians to be exponentiated, in sparse representation.
132
+ hamiltonians: The hamiltonians to be exponentiated, in sparse representation.
133
+ evolution_coefficients: The hamiltonian coefficients (can be link-time).
134
+ order: The order of the Suzuki-Trotter decomposition.
135
+ repetitions: The number of repetitions of the Suzuki-Trotter decomposition.
136
+ qbv: The target quantum variable of the exponentiation.
137
+ """
138
+ pass
139
+
140
+
141
+ @qfunc(external=True)
142
+ def sequential_suzuki_trotter(
143
+ hamiltonians: CArray[SparsePauliOp],
144
+ evolution_coefficients: CArray[CReal],
145
+ order: CInt,
146
+ repetitions: CInt,
147
+ qbv: QArray,
148
+ ) -> None:
149
+ """
150
+ [Qmod core-library function]
151
+
152
+ Applies the Suzuki-Trotter decomposition jointly to a sum of Hamiltonians
153
+ (represented as Pauli operators), each with its separate evolution coefficient,
154
+ approximating $\\exp{-iH_1t_1+H_2t_2+\\dots}$ with a specified order and number of
155
+ repetitions. Does not reorder the Pauli terms.
156
+
157
+ The Suzuki-Trotter decomposition is a method for approximating the exponential of a sum of operators by a product of exponentials of each operator.
158
+ The Suzuki-Trotter decomposition of a given order nullifies the error of the Taylor series expansion of the product of exponentials up to that order.
159
+ The error of a Suzuki-Trotter decomposition decreases as the order and number of repetitions increase.
160
+
161
+ Args:
162
+ hamiltonians: The hamiltonians to be exponentiated, in sparse representation.
133
163
  evolution_coefficients: The hamiltonian coefficients (can be link-time).
134
164
  order: The order of the Suzuki-Trotter decomposition.
135
165
  repetitions: The number of repetitions of the Suzuki-Trotter decomposition.