classiq 0.36.1__py3-none-any.whl → 0.37.1__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 (70) hide show
  1. classiq/__init__.py +1 -0
  2. classiq/_internals/api_wrapper.py +24 -6
  3. classiq/_internals/authentication/device.py +6 -3
  4. classiq/_internals/authentication/token_manager.py +21 -5
  5. classiq/_internals/client.py +7 -2
  6. classiq/_internals/config.py +12 -0
  7. classiq/_internals/host_checker.py +1 -1
  8. classiq/_internals/jobs.py +3 -1
  9. classiq/_internals/type_validation.py +3 -6
  10. classiq/analyzer/analyzer.py +1 -0
  11. classiq/analyzer/rb.py +3 -5
  12. classiq/applications_model_constructors/chemistry_model_constructor.py +0 -1
  13. classiq/applications_model_constructors/grover_model_constructor.py +27 -18
  14. classiq/execution/jobs.py +13 -4
  15. classiq/executor.py +3 -2
  16. classiq/interface/_version.py +1 -1
  17. classiq/interface/analyzer/analysis_params.py +0 -6
  18. classiq/interface/analyzer/result.py +0 -4
  19. classiq/interface/backend/backend_preferences.py +2 -2
  20. classiq/interface/backend/quantum_backend_providers.py +1 -1
  21. classiq/interface/execution/resource_estimator.py +7 -0
  22. classiq/interface/execution/result.py +5 -0
  23. classiq/interface/generator/ansatz_library.py +3 -3
  24. classiq/interface/generator/arith/binary_ops.py +1 -3
  25. classiq/interface/generator/expressions/atomic_expression_functions.py +2 -0
  26. classiq/interface/generator/expressions/qmod_qnum_proxy.py +22 -0
  27. classiq/interface/generator/expressions/qmod_sized_proxy.py +2 -12
  28. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +140 -14
  29. classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +3 -20
  30. classiq/interface/generator/functions/native_function_definition.py +3 -3
  31. classiq/interface/generator/model/constraints.py +3 -3
  32. classiq/interface/generator/model/preferences/preferences.py +10 -8
  33. classiq/interface/generator/noise_properties.py +5 -5
  34. classiq/interface/generator/qpe.py +5 -5
  35. classiq/interface/generator/quantum_function_call.py +5 -3
  36. classiq/interface/generator/visitor.py +1 -2
  37. classiq/interface/hardware.py +1 -1
  38. classiq/interface/model/native_function_definition.py +2 -24
  39. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +2 -2
  40. classiq/interface/model/quantum_expressions/arithmetic_operation.py +2 -2
  41. classiq/interface/model/quantum_expressions/control_state.py +38 -0
  42. classiq/interface/model/quantum_expressions/quantum_expression.py +12 -9
  43. classiq/interface/model/quantum_function_call.py +3 -0
  44. classiq/interface/model/quantum_function_declaration.py +3 -3
  45. classiq/interface/model/quantum_if_operation.py +95 -0
  46. classiq/interface/model/validations/handles_validator.py +7 -15
  47. classiq/interface/server/routes.py +10 -6
  48. classiq/model/function_handler.pyi +84 -84
  49. classiq/model/model.py +1 -0
  50. classiq/qmod/__init__.py +4 -1
  51. classiq/qmod/builtins/__init__.py +13 -1
  52. classiq/qmod/builtins/classical_execution_primitives.py +109 -0
  53. classiq/qmod/builtins/classical_functions.py +68 -0
  54. classiq/qmod/builtins/functions.py +47 -21
  55. classiq/qmod/builtins/operations.py +15 -29
  56. classiq/qmod/classical_function.py +40 -0
  57. classiq/qmod/declaration_inferrer.py +5 -2
  58. classiq/qmod/qmod_variable.py +15 -3
  59. classiq/qmod/quantum_callable.py +24 -3
  60. classiq/qmod/quantum_expandable.py +99 -17
  61. classiq/qmod/quantum_function.py +12 -2
  62. classiq/qmod/symbolic.py +109 -107
  63. classiq/qmod/symbolic_expr.py +1 -4
  64. classiq/qmod/symbolic_type.py +8 -0
  65. classiq/quantum_functions/decorators.py +2 -4
  66. classiq/quantum_functions/function_library.py +1 -0
  67. {classiq-0.36.1.dist-info → classiq-0.37.1.dist-info}/METADATA +1 -1
  68. {classiq-0.36.1.dist-info → classiq-0.37.1.dist-info}/RECORD +69 -61
  69. classiq/interface/model/local_variable_declaration.py +0 -7
  70. {classiq-0.36.1.dist-info → classiq-0.37.1.dist-info}/WHEEL +0 -0
@@ -4,7 +4,7 @@ from typing import List, Literal
4
4
 
5
5
  from classiq.qmod.qmod_parameter import QParam
6
6
  from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QNum
7
- from classiq.qmod.quantum_callable import QCallable
7
+ from classiq.qmod.quantum_callable import QCallable, QCallableList
8
8
  from classiq.qmod.quantum_function import ExternalQFunc
9
9
 
10
10
  from .structs import *
@@ -256,6 +256,22 @@ def add(
256
256
  pass
257
257
 
258
258
 
259
+ @ExternalQFunc
260
+ def modular_add(
261
+ left: QArray[QBit],
262
+ right: QArray[QBit],
263
+ ) -> None:
264
+ pass
265
+
266
+
267
+ @ExternalQFunc
268
+ def integer_xor(
269
+ left: QArray[QBit],
270
+ right: QArray[QBit],
271
+ ) -> None:
272
+ pass
273
+
274
+
259
275
  @ExternalQFunc
260
276
  def U(
261
277
  theta: QParam[float],
@@ -371,19 +387,26 @@ def qft(
371
387
 
372
388
 
373
389
  @ExternalQFunc
374
- def standard_qpe(
375
- precision: QParam[int],
376
- unitary: QCallable,
377
- phase: QArray[QBit, Literal["precision"]],
390
+ def qpe_flexible(
391
+ unitary_with_power: QCallable[QParam[int]],
392
+ phase: QNum,
378
393
  ) -> None:
379
394
  pass
380
395
 
381
396
 
382
397
  @ExternalQFunc
383
398
  def qpe(
399
+ unitary: QCallable,
400
+ phase: QNum,
401
+ ) -> None:
402
+ pass
403
+
404
+
405
+ @ExternalQFunc
406
+ def standard_qpe(
384
407
  precision: QParam[int],
385
408
  unitary: QCallable,
386
- phase: Output[QArray[QBit, Literal["precision"]]],
409
+ phase: QArray[QBit, Literal["precision"]],
387
410
  ) -> None:
388
411
  pass
389
412
 
@@ -545,13 +568,22 @@ def full_hea(
545
568
  angle_params: QParam[List[float]],
546
569
  connectivity_map: QParam[List[List[int]]],
547
570
  reps: QParam[int],
548
- operands_1qubit: QCallable[QParam[float], QBit],
549
- operands_2qubit: QCallable[QParam[float], QBit, QBit],
571
+ operands_1qubit: QCallableList[QParam[float], QBit],
572
+ operands_2qubit: QCallableList[QParam[float], QBit, QBit],
550
573
  x: QArray[QBit, Literal["num_qubits"]],
551
574
  ) -> None:
552
575
  pass
553
576
 
554
577
 
578
+ @ExternalQFunc
579
+ def swap_test(
580
+ state1: QArray[QBit],
581
+ state2: QArray[QBit],
582
+ test: Output[QArray[QBit]],
583
+ ) -> None:
584
+ pass
585
+
586
+
555
587
  @ExternalQFunc
556
588
  def repeat(
557
589
  count: QParam[int],
@@ -587,7 +619,7 @@ def if_(
587
619
  @ExternalQFunc
588
620
  def switch(
589
621
  selector: QParam[int],
590
- cases: QCallable,
622
+ cases: QCallableList,
591
623
  ) -> None:
592
624
  pass
593
625
 
@@ -614,7 +646,7 @@ def split(
614
646
 
615
647
  @ExternalQFunc
616
648
  def permute(
617
- functions: QCallable,
649
+ functions: QCallableList,
618
650
  ) -> None:
619
651
  pass
620
652
 
@@ -627,15 +659,6 @@ def power(
627
659
  pass
628
660
 
629
661
 
630
- @ExternalQFunc
631
- def control_with_value(
632
- ctrl_val: QParam[int],
633
- operand: QCallable,
634
- ctrl: QNum,
635
- ) -> None:
636
- pass
637
-
638
-
639
662
  @ExternalQFunc
640
663
  def apply(
641
664
  operand: QCallable,
@@ -812,6 +835,8 @@ __all__ = [
812
835
  "prepare_amplitudes",
813
836
  "unitary",
814
837
  "add",
838
+ "modular_add",
839
+ "integer_xor",
815
840
  "U",
816
841
  "CCX",
817
842
  "allocate",
@@ -825,8 +850,9 @@ __all__ = [
825
850
  "exponentiation_with_depth_constraint",
826
851
  "qft_step",
827
852
  "qft",
828
- "standard_qpe",
853
+ "qpe_flexible",
829
854
  "qpe",
855
+ "standard_qpe",
830
856
  "single_pauli",
831
857
  "linear_pauli_rotations",
832
858
  "amplitude_estimation",
@@ -844,6 +870,7 @@ __all__ = [
844
870
  "qaoa_init",
845
871
  "qaoa_penalty",
846
872
  "full_hea",
873
+ "swap_test",
847
874
  "repeat",
848
875
  "invert",
849
876
  "control",
@@ -853,7 +880,6 @@ __all__ = [
853
880
  "split",
854
881
  "permute",
855
882
  "power",
856
- "control_with_value",
857
883
  "apply",
858
884
  "compute",
859
885
  "uncompute",
@@ -1,6 +1,9 @@
1
1
  from typing import Callable, List, Union
2
2
 
3
3
  from classiq.interface.generator.expressions.expression import Expression
4
+ from classiq.interface.generator.functions.core_lib_declarations.quantum_operators import (
5
+ OPERAND_FIELD_NAME,
6
+ )
4
7
  from classiq.interface.model.bind_operation import BindOperation
5
8
  from classiq.interface.model.inplace_binary_operation import (
6
9
  BinaryOperation,
@@ -9,18 +12,21 @@ from classiq.interface.model.inplace_binary_operation import (
9
12
  from classiq.interface.model.numeric_reinterpretation import (
10
13
  NumericReinterpretationOperation,
11
14
  )
15
+ from classiq.interface.model.quantum_function_declaration import (
16
+ QuantumOperandDeclaration,
17
+ )
18
+ from classiq.interface.model.quantum_if_operation import QuantumIfOperation
12
19
 
13
- from classiq.exceptions import ClassiqValueError
14
20
  from classiq.qmod.builtins.functions import (
15
21
  apply,
16
22
  compute as compute_operator,
17
- control_with_value,
18
23
  uncompute,
19
24
  )
20
25
  from classiq.qmod.qmod_parameter import QParam
21
26
  from classiq.qmod.qmod_variable import Input, Output, QNum, QVar
22
27
  from classiq.qmod.quantum_callable import QCallable
23
- from classiq.qmod.symbolic_expr import SymbolicEquality, SymbolicExpr
28
+ from classiq.qmod.quantum_expandable import prepare_arg
29
+ from classiq.qmod.symbolic_expr import SymbolicExpr
24
30
 
25
31
 
26
32
  def bind(source: Input[QVar], destination: Output[QVar]) -> None:
@@ -33,36 +39,16 @@ def bind(source: Input[QVar], destination: Output[QVar]) -> None:
33
39
  )
34
40
 
35
41
 
36
- QUANTUM_IF_CONDITION_ARG_ERROR_MESSAGE_PREFIX = (
37
- "quantum_if condition must be of the form '<quantum-variable> == "
38
- "<classical-integer-expression>', but "
39
- )
40
-
41
-
42
42
  def quantum_if(
43
43
  condition: SymbolicExpr, then: Union[QCallable, Callable[[], None]]
44
44
  ) -> None:
45
- if not isinstance(condition, SymbolicEquality):
46
- raise ClassiqValueError(
47
- f"quantum_if condition must be an equality, was " f"{condition!r}"
48
- )
49
- ctrl, ctrl_val = condition.lhs, condition.rhs
50
- if isinstance(ctrl, (int, SymbolicExpr)) and isinstance(ctrl_val, QNum):
51
- ctrl, ctrl_val = ctrl_val, ctrl
52
-
53
- if not isinstance(ctrl, QNum):
54
- raise ClassiqValueError(
55
- QUANTUM_IF_CONDITION_ARG_ERROR_MESSAGE_PREFIX
56
- + f"condition's left-hand side was {ctrl!r} of type "
57
- f"{type(ctrl)}"
58
- )
59
- if not isinstance(ctrl_val, (int, SymbolicExpr)):
60
- raise ClassiqValueError(
61
- QUANTUM_IF_CONDITION_ARG_ERROR_MESSAGE_PREFIX
62
- + f"condition's right-hand side was {ctrl_val!r} of type "
63
- f"{type(ctrl_val)}"
45
+ assert QCallable.CURRENT_EXPANDABLE is not None
46
+ QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
47
+ QuantumIfOperation(
48
+ expression=Expression(expr=str(condition)),
49
+ then=prepare_arg(QuantumOperandDeclaration(name=OPERAND_FIELD_NAME), then),
64
50
  )
65
- control_with_value(ctrl_val, then, ctrl)
51
+ )
66
52
 
67
53
 
68
54
  def reinterpret_num(
@@ -0,0 +1,40 @@
1
+ import ast
2
+ import inspect
3
+ import sys
4
+ from textwrap import dedent
5
+ from typing import Callable
6
+
7
+ from classiq.exceptions import ClassiqValueError
8
+ from classiq.qmod.builtins.classical_execution_primitives import sample, save
9
+
10
+
11
+ def _unparse_function_body(code: str, func: ast.FunctionDef) -> str:
12
+ first_statement = func.body[0]
13
+ body_lines = list(code.split("\n"))[first_statement.lineno - 1 :]
14
+ body_lines[0] = body_lines[0][first_statement.col_offset :]
15
+ if len(body_lines) > 1:
16
+ body_lines = [body_lines[0], dedent("\n".join(body_lines[1:]))]
17
+ return "\n".join(body_lines).strip()
18
+
19
+
20
+ class CFunc:
21
+ @staticmethod
22
+ def default_cmain() -> "CFunc":
23
+ @CFunc
24
+ def cmain() -> None:
25
+ result = sample()
26
+ save({"result": result})
27
+
28
+ return cmain
29
+
30
+ def __init__(self, py_callable: Callable[[], None]):
31
+ code = dedent(inspect.getsource(py_callable))
32
+ func = ast.parse(code).body[0]
33
+ if not isinstance(func, ast.FunctionDef):
34
+ raise ClassiqValueError(f"Use @{CFunc.__name__} to decorate a function")
35
+ if len(func.args.args) > 0:
36
+ raise ClassiqValueError(f"A @{CFunc.__name__} must receive no arguments")
37
+ if sys.version_info >= (3, 9):
38
+ self.code = "\n".join([ast.unparse(statement) for statement in func.body])
39
+ else:
40
+ self.code = _unparse_function_body(code, func)
@@ -29,7 +29,7 @@ from classiq import StructDeclaration
29
29
  from classiq.qmod.model_state_container import ModelStateContainer
30
30
  from classiq.qmod.qmod_parameter import Array, QParam
31
31
  from classiq.qmod.qmod_variable import QVar, get_type_hint_expr
32
- from classiq.qmod.quantum_callable import QCallable
32
+ from classiq.qmod.quantum_callable import QCallable, QCallableList
33
33
  from classiq.qmod.utilities import unmangle_keyword
34
34
 
35
35
  OPERAND_ARG_NAME = "arg{i}"
@@ -125,6 +125,7 @@ def _extract_operand_decl(
125
125
  return QuantumOperandDeclaration(
126
126
  name=name,
127
127
  positional_arg_declarations=_extract_positional_args(arg_dict, qmodule=qmodule),
128
+ is_list=(get_origin(py_type) or py_type) is QCallableList,
128
129
  )
129
130
 
130
131
 
@@ -141,7 +142,9 @@ def _extract_positional_args(
141
142
  elif QVar.from_type_hint(py_type) is not None:
142
143
  result.append(_extract_port_decl(name, py_type))
143
144
  else:
144
- assert get_origin(py_type) or py_type is QCallable
145
+ assert (get_origin(py_type) or py_type) is QCallable or (
146
+ get_origin(py_type) or py_type
147
+ ) is QCallableList
145
148
  result.append(_extract_operand_decl(name, py_type, qmodule=qmodule))
146
149
  return result
147
150
 
@@ -40,7 +40,8 @@ from classiq.interface.model.quantum_type import (
40
40
  from classiq.exceptions import ClassiqValueError
41
41
  from classiq.qmod.qmod_parameter import ArrayBase, QParam, QParamScalar
42
42
  from classiq.qmod.quantum_callable import QCallable
43
- from classiq.qmod.symbolic_expr import SymbolicExpr, SymbolicTypes
43
+ from classiq.qmod.symbolic_expr import SymbolicExpr
44
+ from classiq.qmod.symbolic_type import SymbolicTypes
44
45
 
45
46
  ILLEGAL_SLICING_STEP_MSG = "Slicing with a step of a quantum variable is not supported"
46
47
  SLICE_OUT_OF_BOUNDS_MSG = "Slice end index out of bounds"
@@ -199,6 +200,18 @@ class QNum(Generic[_T], QScalar):
199
200
  def get_qmod_type(self) -> QuantumType:
200
201
  return self.QMOD_TYPE()
201
202
 
203
+ @property
204
+ def size(self) -> QParamScalar:
205
+ return QParamScalar(f"len({self._name})")
206
+
207
+ @property
208
+ def fraction_digits(self) -> QParamScalar:
209
+ return QParamScalar(f"fraction_digits({self._name})")
210
+
211
+ @property
212
+ def is_signed(self) -> QParamScalar:
213
+ return QParamScalar(f"is_signed({self._name})")
214
+
202
215
 
203
216
  _P = ParamSpec("_P")
204
217
 
@@ -240,8 +253,7 @@ class QArray(ArrayBase[_P], QVar):
240
253
 
241
254
  if TYPE_CHECKING:
242
255
 
243
- def len(self) -> int:
244
- ...
256
+ def len(self) -> int: ...
245
257
 
246
258
  else:
247
259
 
@@ -1,7 +1,14 @@
1
1
  import sys
2
2
  from abc import ABC, abstractmethod
3
- from typing import _GenericAlias # type: ignore[attr-defined]
4
- from typing import Any, ClassVar, Generic, Optional
3
+ from typing import ( # type: ignore[attr-defined]
4
+ TYPE_CHECKING,
5
+ Any,
6
+ ClassVar,
7
+ Generic,
8
+ Optional,
9
+ Union,
10
+ _GenericAlias,
11
+ )
5
12
 
6
13
  from typing_extensions import ParamSpec
7
14
 
@@ -12,6 +19,11 @@ from classiq.interface.model.quantum_function_declaration import (
12
19
  from classiq.interface.model.quantum_statement import QuantumStatement
13
20
  from classiq.interface.model.quantum_type import QuantumType
14
21
 
22
+ from classiq.qmod.qmod_parameter import QParam
23
+
24
+ if TYPE_CHECKING:
25
+ from classiq.qmod.quantum_expandable import QTerminalCallable
26
+
15
27
  P = ParamSpec("P")
16
28
 
17
29
 
@@ -25,7 +37,7 @@ class QExpandableInterface(ABC):
25
37
  raise NotImplementedError()
26
38
 
27
39
 
28
- class QCallable(Generic[P]):
40
+ class QCallable(Generic[P], ABC):
29
41
  CURRENT_EXPANDABLE: ClassVar[Optional[QExpandableInterface]] = None
30
42
 
31
43
  def __call__(self, *args: Any, **kwargs: Any) -> None:
@@ -51,3 +63,12 @@ class QCallable(Generic[P]):
51
63
  self, *args: Any, **kwargs: Any
52
64
  ) -> QuantumFunctionCall:
53
65
  raise NotImplementedError()
66
+
67
+
68
+ class QCallableList(QCallable, Generic[P], ABC):
69
+ if TYPE_CHECKING:
70
+
71
+ def len(self) -> int: ...
72
+
73
+ def __getitem__(self, key: Union[slice, int, QParam]) -> "QTerminalCallable":
74
+ raise NotImplementedError()
@@ -1,6 +1,18 @@
1
1
  from abc import ABC
2
2
  from types import TracebackType
3
- from typing import Any, Callable, ClassVar, Dict, List, Optional, Type, Union
3
+ from typing import (
4
+ TYPE_CHECKING,
5
+ Any,
6
+ Callable,
7
+ ClassVar,
8
+ Dict,
9
+ List,
10
+ Optional,
11
+ Type,
12
+ Union,
13
+ cast,
14
+ overload,
15
+ )
4
16
 
5
17
  from typing_extensions import Self
6
18
 
@@ -11,8 +23,10 @@ from classiq.interface.model.classical_parameter_declaration import (
11
23
  from classiq.interface.model.port_declaration import PortDeclaration
12
24
  from classiq.interface.model.quantum_function_call import (
13
25
  ArgValue,
26
+ OperandIdentifier,
14
27
  QuantumFunctionCall,
15
28
  QuantumLambdaFunction,
29
+ QuantumOperand,
16
30
  )
17
31
  from classiq.interface.model.quantum_function_declaration import (
18
32
  PositionalArg,
@@ -27,7 +41,7 @@ from classiq.interface.model.variable_declaration_statement import (
27
41
 
28
42
  from classiq.exceptions import ClassiqValueError
29
43
  from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
30
- from classiq.qmod.qmod_parameter import QParam, create_param
44
+ from classiq.qmod.qmod_parameter import QParam, QParamScalar, create_param
31
45
  from classiq.qmod.qmod_variable import QVar, create_qvar_for_port_decl
32
46
  from classiq.qmod.quantum_callable import QCallable, QExpandableInterface
33
47
  from classiq.qmod.utilities import mangle_keyword
@@ -98,7 +112,7 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
98
112
  def create_quantum_function_call(
99
113
  self, *args: Any, **kwargs: Any
100
114
  ) -> QuantumFunctionCall:
101
- return _create_quantum_function_call(self.func_decl, *args, **kwargs)
115
+ return _create_quantum_function_call(self.func_decl, None, *args, **kwargs)
102
116
 
103
117
 
104
118
  class QLambdaFunction(QExpandable):
@@ -127,8 +141,42 @@ class QLambdaFunction(QExpandable):
127
141
 
128
142
 
129
143
  class QTerminalCallable(QCallable):
130
- def __init__(self, decl: QuantumFunctionDeclaration) -> None:
144
+ def __init__(
145
+ self,
146
+ decl: QuantumFunctionDeclaration,
147
+ index_: Optional[Union[int, QParamScalar]] = None,
148
+ ) -> None:
131
149
  self._decl = decl
150
+ self._index = index_
151
+
152
+ @property
153
+ def is_list(self) -> bool:
154
+ return isinstance(self._decl, QuantumOperandDeclaration) and self._decl.is_list
155
+
156
+ def __getitem__(self, key: Union[slice, int, QParam]) -> "QTerminalCallable":
157
+ if not self.is_list:
158
+ raise ClassiqValueError("Cannot index a non-list operand")
159
+ if isinstance(key, slice):
160
+ raise NotImplementedError("Operand lists don't support slicing")
161
+ if isinstance(key, QParam) and not isinstance(key, QParamScalar):
162
+ raise ClassiqValueError("Non-classical parameter for slicing")
163
+ return QTerminalCallable(self._decl, key)
164
+
165
+ def __len__(self) -> int:
166
+ raise ValueError(
167
+ "len(<func>) is not supported for quantum callables - use <func>.len() instead (Only if it is an operand list)"
168
+ )
169
+
170
+ if TYPE_CHECKING:
171
+
172
+ def len(self) -> int: ...
173
+
174
+ else:
175
+
176
+ def len(self) -> QParamScalar:
177
+ if not self.is_list:
178
+ raise ClassiqValueError("Cannot get length of a non-list operand")
179
+ return QParamScalar(f"len({self.func_decl.name})")
132
180
 
133
181
  @property
134
182
  def func_decl(self) -> QuantumFunctionDeclaration:
@@ -137,15 +185,39 @@ class QTerminalCallable(QCallable):
137
185
  def create_quantum_function_call(
138
186
  self, *args: Any, **kwargs: Any
139
187
  ) -> QuantumFunctionCall:
140
- return _create_quantum_function_call(self.func_decl, *args, **kwargs)
188
+ if self.is_list and self._index is None:
189
+ raise ClassiqValueError(
190
+ f"Quantum operand {self.func_decl.name!r} is a list and must be indexed"
191
+ )
192
+ return _create_quantum_function_call(
193
+ self.func_decl, self._index, *args, **kwargs
194
+ )
195
+
196
+
197
+ @overload
198
+ def prepare_arg(
199
+ arg_decl: PositionalArg, val: Union[QCallable, Callable[[Any], None]]
200
+ ) -> QuantumOperand: ...
201
+
141
202
 
203
+ @overload
204
+ def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue: ...
142
205
 
143
- def _prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue:
206
+
207
+ def prepare_arg(arg_decl: PositionalArg, val: Any) -> ArgValue:
144
208
  if isinstance(arg_decl, ClassicalParameterDeclaration):
145
209
  return Expression(expr=str(val))
146
210
  elif isinstance(arg_decl, PortDeclaration):
147
211
  return val.get_handle_binding()
148
212
  else:
213
+ if isinstance(val, list):
214
+ if not all(isinstance(v, QCallable) or callable(v) for v in val):
215
+ raise ClassiqValueError(
216
+ f"Quantum operand {arg_decl.name!r} cannot be initialized with a list of non-callables"
217
+ )
218
+ val = cast(List[Union[QCallable, Callable[[Any], None]]], val)
219
+ return [prepare_arg(arg_decl, v) for v in val]
220
+
149
221
  if not isinstance(val, QCallable):
150
222
  val = QLambdaFunction(arg_decl, val)
151
223
 
@@ -163,9 +235,11 @@ def _get_operand_hint_args(
163
235
  ) -> str:
164
236
  return ", ".join(
165
237
  [
166
- f"{decl.name}={param_value}"
167
- if decl.name == param.name
168
- else f"{decl.name}=..."
238
+ (
239
+ f"{decl.name}={param_value}"
240
+ if decl.name == param.name
241
+ else f"{decl.name}=..."
242
+ )
169
243
  for decl in func.get_positional_arg_decls()
170
244
  ]
171
245
  )
@@ -198,31 +272,39 @@ def _prepare_args(
198
272
  if isinstance(arg_decl, QuantumOperandDeclaration):
199
273
  error_message += _get_operand_hint(decl, arg_decl)
200
274
  raise ClassiqValueError(error_message)
201
- result.append(_prepare_arg(arg_decl, arg))
275
+ result.append(prepare_arg(arg_decl, arg))
202
276
 
203
277
  return result
204
278
 
205
279
 
206
280
  def _create_quantum_function_call(
207
- decl: QuantumFunctionDeclaration, *args: Any, **kwargs: Any
281
+ decl_: QuantumFunctionDeclaration,
282
+ index_: Optional[Union[QParamScalar, int]] = None,
283
+ *args: Any,
284
+ **kwargs: Any,
208
285
  ) -> QuantumFunctionCall:
209
- arg_decls = decl.get_positional_arg_decls()
286
+ arg_decls = decl_.get_positional_arg_decls()
210
287
  arg_list = list(args)
211
- prepared_args = _prepare_args(decl, arg_list, kwargs)
288
+ prepared_args = _prepare_args(decl_, arg_list, kwargs)
212
289
 
213
290
  if kwargs:
214
291
  bad_kwarg = next(iter(kwargs))
215
292
  if not all(arg_decl.name == bad_kwarg for arg_decl in arg_decls):
216
293
  raise ClassiqValueError(
217
- f"{decl.name}() got an unexpected keyword argument {bad_kwarg!r}"
294
+ f"{decl_.name}() got an unexpected keyword argument {bad_kwarg!r}"
218
295
  )
219
296
  else:
220
297
  raise ClassiqValueError(
221
- f"{decl.name}() got multiple values for argument {bad_kwarg!r}"
298
+ f"{decl_.name}() got multiple values for argument {bad_kwarg!r}"
222
299
  )
223
300
  if arg_list:
224
301
  raise ClassiqValueError(
225
- f"{decl.name}() takes {len(arg_decls)} arguments but {len(args)} were given"
302
+ f"{decl_.name}() takes {len(arg_decls)} arguments but {len(args)} were given"
303
+ )
304
+ function_ident: Union[str, OperandIdentifier] = decl_.name
305
+ if index_ is not None:
306
+ function_ident = OperandIdentifier(
307
+ index=Expression(expr=str(index_)), name=function_ident
226
308
  )
227
309
 
228
- return QuantumFunctionCall(function=decl.name, positional_args=prepared_args)
310
+ return QuantumFunctionCall(function=function_ident, positional_args=prepared_args)
@@ -11,10 +11,11 @@ from classiq.interface.model.quantum_function_declaration import (
11
11
  )
12
12
 
13
13
  from classiq.exceptions import ClassiqError
14
+ from classiq.qmod.classical_function import CFunc
14
15
  from classiq.qmod.declaration_inferrer import infer_func_decl
15
16
  from classiq.qmod.qmod_parameter import QParam
16
17
  from classiq.qmod.qmod_variable import QVar
17
- from classiq.qmod.quantum_callable import QCallable
18
+ from classiq.qmod.quantum_callable import QCallable, QCallableList
18
19
  from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
19
20
  from classiq.qmod.utilities import mangle_keyword, unmangle_keyword
20
21
 
@@ -29,9 +30,10 @@ def create_model(
29
30
  constraints: Optional[Constraints] = None,
30
31
  execution_preferences: Optional[ExecutionPreferences] = None,
31
32
  preferences: Optional[Preferences] = None,
33
+ classical_execution_function: Optional[CFunc] = None,
32
34
  ) -> SerializedModel:
33
35
  return entry_point.create_model(
34
- constraints, execution_preferences, preferences
36
+ constraints, execution_preferences, preferences, classical_execution_function
35
37
  ).get_model()
36
38
 
37
39
 
@@ -57,6 +59,7 @@ class QFunc(QExpandable):
57
59
  constraints: Optional[Constraints] = None,
58
60
  execution_preferences: Optional[ExecutionPreferences] = None,
59
61
  preferences: Optional[Preferences] = None,
62
+ classical_execution_function: Optional[CFunc] = None,
60
63
  ) -> Model:
61
64
  self._qmodule.type_decls = dict()
62
65
  self._qmodule.native_defs = dict()
@@ -66,9 +69,15 @@ class QFunc(QExpandable):
66
69
  ("execution_preferences", execution_preferences),
67
70
  ("preferences", preferences),
68
71
  ]
72
+ classical_execution_function = (
73
+ CFunc.default_cmain()
74
+ if classical_execution_function is None
75
+ else classical_execution_function
76
+ )
69
77
  return Model(
70
78
  functions=list(self._qmodule.native_defs.values()),
71
79
  types=list(self._qmodule.type_decls.values()),
80
+ classical_execution_code=classical_execution_function.code,
72
81
  **{key: value for key, value in model_extra_settings if value},
73
82
  )
74
83
 
@@ -116,6 +125,7 @@ def _validate_no_gen_params(annotations: Dict[str, Any]) -> None:
116
125
  name == "return"
117
126
  or get_origin(annotation) is QParam
118
127
  or (get_origin(annotation) or annotation) is QCallable
128
+ or (get_origin(annotation) or annotation) is QCallableList
119
129
  or QVar.from_type_hint(annotation) is not None
120
130
  )
121
131
  }