classiq 0.68.0__py3-none-any.whl → 0.70.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 (96) hide show
  1. classiq/_internals/api_wrapper.py +4 -8
  2. classiq/analyzer/analyzer.py +0 -18
  3. classiq/analyzer/url_utils.py +9 -4
  4. classiq/applications/combinatorial_helpers/pyomo_utils.py +2 -0
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -11
  6. classiq/applications/qnn/gradients/quantum_gradient.py +1 -1
  7. classiq/applications/qnn/gradients/simple_quantum_gradient.py +1 -1
  8. classiq/applications/qnn/torch_utils.py +1 -1
  9. classiq/execution/jobs.py +2 -5
  10. classiq/interface/_version.py +1 -1
  11. classiq/interface/backend/quantum_backend_providers.py +8 -3
  12. classiq/interface/chemistry/operator.py +12 -28
  13. classiq/interface/debug_info/back_ref_util.py +22 -0
  14. classiq/interface/debug_info/debug_info.py +11 -21
  15. classiq/interface/executor/optimizer_preferences.py +1 -0
  16. classiq/interface/executor/quantum_instruction_set.py +1 -0
  17. classiq/interface/generator/arith/arithmetic.py +21 -6
  18. classiq/interface/generator/arith/arithmetic_param_getters.py +3 -3
  19. classiq/interface/generator/circuit_code/circuit_code.py +4 -0
  20. classiq/interface/generator/circuit_code/types_and_constants.py +1 -0
  21. classiq/interface/generator/expressions/atomic_expression_functions.py +1 -2
  22. classiq/interface/generator/expressions/expression_types.py +8 -2
  23. classiq/interface/generator/expressions/proxies/__init__.py +0 -0
  24. classiq/interface/generator/expressions/proxies/classical/__init__.py +0 -0
  25. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +75 -0
  26. classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +26 -0
  27. classiq/interface/generator/expressions/proxies/classical/classical_scalar_proxy.py +32 -0
  28. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +31 -0
  29. classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
  30. classiq/interface/generator/expressions/{qmod_qarray_proxy.py → proxies/quantum/qmod_qarray_proxy.py} +3 -1
  31. classiq/interface/generator/expressions/{qmod_qscalar_proxy.py → proxies/quantum/qmod_qscalar_proxy.py} +3 -1
  32. classiq/interface/generator/expressions/{qmod_qstruct_proxy.py → proxies/quantum/qmod_qstruct_proxy.py} +3 -1
  33. classiq/interface/generator/functions/classical_type.py +34 -29
  34. classiq/interface/generator/functions/type_name.py +26 -2
  35. classiq/interface/generator/generated_circuit_data.py +84 -27
  36. classiq/interface/generator/model/preferences/preferences.py +1 -0
  37. classiq/interface/generator/quantum_program.py +0 -1
  38. classiq/interface/generator/types/builtin_enum_declarations.py +1 -0
  39. classiq/interface/generator/types/enum_declaration.py +12 -1
  40. classiq/interface/ide/visual_model.py +0 -2
  41. classiq/interface/model/native_function_definition.py +0 -10
  42. classiq/interface/model/quantum_statement.py +1 -1
  43. classiq/interface/model/quantum_type.py +15 -3
  44. classiq/interface/server/routes.py +0 -6
  45. classiq/model_expansions/atomic_expression_functions_defs.py +9 -3
  46. classiq/model_expansions/evaluators/arg_type_match.py +4 -2
  47. classiq/model_expansions/evaluators/classical_expression.py +2 -2
  48. classiq/model_expansions/evaluators/control.py +1 -1
  49. classiq/model_expansions/evaluators/parameter_types.py +58 -16
  50. classiq/model_expansions/evaluators/quantum_type_utils.py +7 -57
  51. classiq/model_expansions/expression_evaluator.py +3 -1
  52. classiq/model_expansions/generative_functions.py +67 -7
  53. classiq/model_expansions/quantum_operations/arithmetic/__init__.py +0 -0
  54. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +60 -0
  55. classiq/model_expansions/quantum_operations/assignment_result_processor.py +9 -0
  56. classiq/model_expansions/quantum_operations/call_emitter.py +0 -13
  57. classiq/model_expansions/quantum_operations/quantum_function_call.py +0 -22
  58. classiq/model_expansions/scope.py +7 -6
  59. classiq/model_expansions/scope_initialization.py +20 -33
  60. classiq/model_expansions/transformers/model_renamer.py +13 -4
  61. classiq/model_expansions/visitors/variable_references.py +8 -4
  62. classiq/open_library/functions/__init__.py +2 -0
  63. classiq/open_library/functions/amplitude_amplification.py +3 -7
  64. classiq/open_library/functions/discrete_sine_cosine_transform.py +4 -4
  65. classiq/open_library/functions/grover.py +2 -2
  66. classiq/open_library/functions/hea.py +3 -3
  67. classiq/open_library/functions/lookup_table.py +58 -0
  68. classiq/open_library/functions/modular_exponentiation.py +10 -20
  69. classiq/open_library/functions/qft_functions.py +2 -2
  70. classiq/open_library/functions/qsvt.py +8 -8
  71. classiq/open_library/functions/utility_functions.py +2 -2
  72. classiq/qmod/builtins/classical_functions.py +24 -7
  73. classiq/qmod/builtins/enums.py +1 -0
  74. classiq/qmod/builtins/functions/__init__.py +2 -0
  75. classiq/qmod/builtins/functions/exponentiation.py +24 -0
  76. classiq/qmod/builtins/operations.py +26 -11
  77. classiq/qmod/cparam.py +32 -5
  78. classiq/qmod/declaration_inferrer.py +3 -1
  79. classiq/qmod/python_classical_type.py +10 -4
  80. classiq/qmod/qmod_parameter.py +8 -0
  81. classiq/qmod/qmod_variable.py +11 -14
  82. classiq/qmod/quantum_callable.py +2 -1
  83. classiq/qmod/quantum_function.py +3 -2
  84. classiq/qmod/semantics/annotation/call_annotation.py +0 -28
  85. classiq/qmod/semantics/annotation/qstruct_annotator.py +21 -1
  86. classiq/qmod/semantics/error_manager.py +1 -1
  87. classiq/qmod/semantics/validation/main_validation.py +1 -1
  88. classiq/qmod/semantics/validation/type_hints.py +29 -0
  89. classiq/qmod/utilities.py +67 -2
  90. classiq/synthesis.py +9 -6
  91. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/METADATA +10 -12
  92. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/RECORD +95 -84
  93. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/WHEEL +1 -1
  94. classiq/interface/execution/jobs.py +0 -31
  95. /classiq/interface/generator/expressions/{qmod_struct_instance.py → proxies/classical/qmod_struct_instance.py} +0 -0
  96. /classiq/interface/generator/expressions/{qmod_sized_proxy.py → proxies/quantum/qmod_sized_proxy.py} +0 -0
@@ -1,20 +1,17 @@
1
1
  from collections.abc import Sequence
2
- from typing import Any
3
2
 
4
- from classiq.interface.exceptions import ClassiqError
3
+ from classiq.interface.exceptions import ClassiqError, ClassiqInternalExpansionError
5
4
  from classiq.interface.generator.constant import Constant
6
- from classiq.interface.generator.expressions.expression_constants import (
7
- CPARAM_EXECUTION_SUFFIX,
8
- RESERVED_EXPRESSIONS,
5
+ from classiq.interface.generator.functions.classical_type import (
6
+ ClassicalArray,
7
+ ClassicalList,
9
8
  )
10
9
  from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
11
10
  from classiq.interface.model.handle_binding import HandleBinding
12
11
  from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
13
12
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
14
13
  from classiq.interface.model.port_declaration import PortDeclaration
15
- from classiq.interface.model.quantum_function_declaration import (
16
- PositionalArg,
17
- )
14
+ from classiq.interface.model.quantum_function_declaration import PositionalArg
18
15
 
19
16
  from classiq.model_expansions.closure import FunctionClosure, GenerativeFunctionClosure
20
17
  from classiq.model_expansions.evaluators.classical_expression import (
@@ -129,37 +126,27 @@ def init_builtin_types() -> None:
129
126
  QMODULE.type_decls |= BUILTIN_STRUCT_DECLARATIONS
130
127
 
131
128
 
132
- def _rename_exec_param(param_name: str) -> str:
133
- if param_name in RESERVED_EXPRESSIONS:
134
- return param_name
135
- return param_name + CPARAM_EXECUTION_SUFFIX
136
-
137
-
138
- def _add_exec_param_parts_to_scope(param_val: Any, scope: Scope) -> None:
139
- if not isinstance(param_val, list):
140
- scope[str(param_val)] = Evaluated(value=param_val)
141
- return
142
- for param_part in param_val:
143
- _add_exec_param_parts_to_scope(param_part, scope)
144
-
145
-
146
129
  def init_exec_params(model: Model, scope: Scope) -> dict[str, ConcreteClassicalType]:
147
130
  if model.execution_parameters is not None:
148
- exec_params = {
149
- param_name: (param_name, param_type)
150
- for param_name, param_type in model.execution_parameters.items()
151
- }
131
+ exec_params = model.execution_parameters
152
132
  else:
153
133
  exec_params = {
154
- param.name: (_rename_exec_param(param.name), param.classical_type)
134
+ param.name: param.classical_type
155
135
  for param in model.function_dict.get(
156
136
  "_dec_main", model.main_func
157
137
  ).param_decls
158
138
  }
159
- for param_name, (param_rename, param_type) in exec_params.items():
160
- param_val = param_type.as_symbolic(param_rename)
139
+ for param_name, param_type in exec_params.items():
140
+ param_val = param_type.get_classical_proxy(
141
+ handle=HandleBinding(name=param_name)
142
+ )
161
143
  scope[param_name] = Evaluated(value=param_val)
162
- scope[param_rename] = Evaluated(value=param_val)
163
- if isinstance(param_val, list):
164
- _add_exec_param_parts_to_scope(param_val, scope)
165
- return dict(exec_params.values())
144
+ return exec_params
145
+
146
+
147
+ def _get_shape(classical_type: ConcreteClassicalType) -> tuple[int, ...]:
148
+ if isinstance(classical_type, ClassicalList):
149
+ raise ClassiqInternalExpansionError("Unexpected classical list")
150
+ if isinstance(classical_type, ClassicalArray):
151
+ return classical_type.size, *_get_shape(classical_type.element_type)
152
+ return ()
@@ -1,4 +1,5 @@
1
1
  import ast
2
+ import re
2
3
  from collections.abc import Mapping, Sequence
3
4
  from dataclasses import dataclass
4
5
  from typing import TypeVar, cast
@@ -16,6 +17,12 @@ from classiq.model_expansions.visitors.variable_references import VarRefCollecto
16
17
  AST_NODE = TypeVar("AST_NODE", bound=NodeType)
17
18
 
18
19
 
20
+ def _replace_full_word(pattern: str, substitution: str, target: str) -> str:
21
+ return re.sub(
22
+ rf"(^|\b|\W)({re.escape(pattern)})($|\b|\W)", rf"\1{substitution}\3", target
23
+ )
24
+
25
+
19
26
  @dataclass(frozen=True)
20
27
  class HandleRenaming:
21
28
  source_handle: HandleBinding
@@ -74,7 +81,7 @@ class ModelRenamer:
74
81
  symbol_mapping: SymbolRenaming,
75
82
  expression: Expression,
76
83
  ) -> Expression:
77
- vrc = VarRefCollector(ignore_duplicated_handles=True)
84
+ vrc = VarRefCollector(ignore_duplicated_handles=True, ignore_sympy_symbols=True)
78
85
  vrc.visit(ast.parse(expression.expr))
79
86
 
80
87
  handle_names = {
@@ -87,10 +94,12 @@ class ModelRenamer:
87
94
  new_handle = handle.collapse()
88
95
  for handle_to_replace, replacement in handle_names.items():
89
96
  new_handle = new_handle.replace_prefix(handle_to_replace, replacement)
90
- new_expr_str = new_expr_str.replace(str(handle), str(new_handle))
97
+ new_expr_str = _replace_full_word(
98
+ str(handle), str(new_handle), new_expr_str
99
+ )
91
100
  if handle.qmod_expr != str(handle):
92
- new_expr_str = new_expr_str.replace(
93
- handle.qmod_expr, new_handle.qmod_expr
101
+ new_expr_str = _replace_full_word(
102
+ str(handle.qmod_expr), str(new_handle.qmod_expr), new_expr_str
94
103
  )
95
104
 
96
105
  new_expr = Expression(expr=new_expr_str)
@@ -24,10 +24,14 @@ from classiq.interface.model.handle_binding import (
24
24
 
25
25
  class VarRefCollector(ast.NodeVisitor):
26
26
  def __init__(
27
- self, ignore_duplicated_handles: bool = False, unevaluated: bool = False
27
+ self,
28
+ ignore_duplicated_handles: bool = False,
29
+ unevaluated: bool = False,
30
+ ignore_sympy_symbols: bool = False,
28
31
  ) -> None:
29
32
  self._var_handles: dict[HandleBinding, bool] = {}
30
33
  self._ignore_duplicated_handles = ignore_duplicated_handles
34
+ self._ignore_sympy_symbols = ignore_sympy_symbols
31
35
  self._unevaluated = unevaluated
32
36
  self._is_nested = False
33
37
 
@@ -117,9 +121,9 @@ class VarRefCollector(ast.NodeVisitor):
117
121
  return handle
118
122
 
119
123
  def visit_Name(self, node: ast.Name) -> Optional[HandleBinding]:
120
- if node.id in set(SYMPY_SUPPORTED_EXPRESSIONS) | set(
121
- DEFAULT_SUPPORTED_FUNC_NAMES
122
- ):
124
+ if not self._ignore_sympy_symbols and node.id in set(
125
+ SYMPY_SUPPORTED_EXPRESSIONS
126
+ ) | set(DEFAULT_SUPPORTED_FUNC_NAMES):
123
127
  return None
124
128
  handle = HandleBinding(name=node.id)
125
129
  if not self._is_nested:
@@ -9,6 +9,7 @@ from .grover import *
9
9
  from .hea import *
10
10
  from .linear_pauli_rotation import *
11
11
  from .linear_pauli_rotation import _single_pauli
12
+ from .lookup_table import span_lookup_table
12
13
  from .modular_exponentiation import *
13
14
  from .modular_exponentiation import _check_msb
14
15
  from .qaoa_penalty import *
@@ -131,6 +132,7 @@ __all__ = [
131
132
  "qsvt_lcu_step",
132
133
  "qsvt_step",
133
134
  "reflect_about_zero",
135
+ "span_lookup_table",
134
136
  "suzuki_trotter",
135
137
  "swap_test",
136
138
  "switch",
@@ -73,11 +73,9 @@ def exact_amplitude_amplification(
73
73
 
74
74
  extended_qvars: QArray = QArray()
75
75
  within_apply(
76
- lambda: [ # type:ignore[arg-type]
76
+ lambda: [
77
77
  allocate(aux),
78
- bind(
79
- [aux, packed_qvars], extended_qvars
80
- ), # type:ignore[func-returns-value]
78
+ bind([aux, packed_qvars], extended_qvars),
81
79
  ],
82
80
  lambda: amplitude_amplification(
83
81
  k,
@@ -85,9 +83,7 @@ def exact_amplitude_amplification(
85
83
  qvars_[0] == 0, lambda: oracle(qvars_[1 : qvars_.size])
86
84
  ),
87
85
  lambda qvars_: [
88
- space_transform( # type:ignore[func-returns-value]
89
- qvars_[1 : qvars_.size]
90
- ),
86
+ space_transform(qvars_[1 : qvars_.size]),
91
87
  RY(rot_phase, qvars_[0]),
92
88
  ],
93
89
  extended_qvars,
@@ -30,9 +30,9 @@ def _qct_d_operator(x: QNum, q: QBit) -> None:
30
30
  def _qct_pi_operator(x: QArray[QBit], q: QBit) -> None:
31
31
  control(
32
32
  q == 1,
33
- lambda: [ # type:ignore[arg-type]
33
+ lambda: [
34
34
  apply_to_all(X, x),
35
- inplace_add(1, x), # type:ignore[arg-type, func-returns-value]
35
+ inplace_add(1, x), # type:ignore[arg-type]
36
36
  ],
37
37
  )
38
38
 
@@ -70,7 +70,7 @@ def _pi2_operator(x: QArray[QBit], q: QBit) -> None:
70
70
 
71
71
 
72
72
  def _j_operator(q: QBit) -> None:
73
- within_apply(lambda: Z(q), lambda: (S(q), H(q), S(q))) # type:ignore[arg-type]
73
+ within_apply(lambda: Z(q), lambda: (S(q), H(q), S(q)))
74
74
 
75
75
 
76
76
  def _b_t_operator(q: QBit) -> None:
@@ -179,6 +179,6 @@ def qst_type2(x: QArray[QBit]) -> None:
179
179
  """
180
180
  q = QBit()
181
181
  within_apply(
182
- lambda: (allocate(q), X(q)), # type:ignore[arg-type]
182
+ lambda: (allocate(q), X(q)),
183
183
  lambda: qct_qst_type2(x, q),
184
184
  )
@@ -42,7 +42,7 @@ def phase_oracle(
42
42
  """
43
43
  aux = QBit()
44
44
  within_apply(
45
- within=lambda: (allocate(aux), X(aux), H(aux)), # type:ignore[arg-type]
45
+ within=lambda: (allocate(aux), X(aux), H(aux)),
46
46
  apply=lambda: predicate(target, aux),
47
47
  )
48
48
 
@@ -68,7 +68,7 @@ def reflect_about_zero(packed_vars: QArray[QBit]) -> None:
68
68
  lsb = QBit()
69
69
  bind(packed_vars, [msbs, lsb])
70
70
  within_apply(
71
- lambda: (X(lsb), H(lsb)), # type:ignore[arg-type]
71
+ lambda: (X(lsb), H(lsb)),
72
72
  lambda: control(msbs == 0, lambda: X(lsb)),
73
73
  )
74
74
  bind([msbs, lsb], packed_vars)
@@ -58,8 +58,8 @@ def full_hea(
58
58
  """
59
59
  repeat(
60
60
  reps,
61
- lambda r: [ # type:ignore[arg-type]
62
- repeat( # type:ignore[func-returns-value]
61
+ lambda r: [
62
+ repeat(
63
63
  operands_1qubit.len,
64
64
  lambda i1: repeat(
65
65
  num_qubits,
@@ -77,7 +77,7 @@ def full_hea(
77
77
  ),
78
78
  ),
79
79
  ),
80
- repeat( # type:ignore[func-returns-value]
80
+ repeat(
81
81
  operands_2qubit.len,
82
82
  lambda i2: repeat(
83
83
  connectivity_map.len,
@@ -0,0 +1,58 @@
1
+ from itertools import product
2
+
3
+ from classiq.interface.exceptions import ClassiqValueError
4
+
5
+ from classiq.qmod.builtins.operations import assign, bind, within_apply
6
+ from classiq.qmod.qmod_variable import QNum
7
+ from classiq.qmod.symbolic import subscript
8
+ from classiq.qmod.utilities import RealFunction, get_temp_var_name, qnum_values
9
+
10
+
11
+ def _get_qnum_values(num: QNum) -> list[float]:
12
+ size = num.size
13
+ is_signed = num.is_signed
14
+ fraction_digits = num.fraction_digits
15
+ if (
16
+ not isinstance(size, int)
17
+ or not isinstance(is_signed, bool)
18
+ or not isinstance(fraction_digits, int)
19
+ ):
20
+ raise ClassiqValueError(
21
+ "Must call 'span_lookup_table' inside a generative qfunc"
22
+ )
23
+
24
+ return qnum_values(size, is_signed, fraction_digits)
25
+
26
+
27
+ def span_lookup_table(func: RealFunction, *targets: QNum) -> QNum:
28
+ """
29
+ Applies a classical function to quantum numbers.
30
+
31
+ Works by reducing the function into a lookup table over all the possible values
32
+ of the quantum numbers.
33
+
34
+ Args:
35
+ func: A Python function
36
+ *targets: One or more initialized quantum numbers
37
+
38
+ Returns:
39
+ The quantum result of applying func to targets
40
+
41
+ Notes:
42
+ Must be called inside a generative function (`@qfunc(generative=True)`)
43
+ """
44
+ if len(targets) == 0:
45
+ raise ClassiqValueError("No targets specified")
46
+
47
+ target_vals = [_get_qnum_values(target) for target in targets]
48
+ lookup_table = [func(*vals[::-1]) for vals in product(*target_vals[::-1])]
49
+
50
+ index_size = sum(target.size for target in targets)
51
+ index: QNum = QNum(get_temp_var_name(), size=index_size)
52
+ result: QNum = QNum(get_temp_var_name("result"))
53
+
54
+ within_apply(
55
+ lambda: bind(list(targets), index),
56
+ lambda: assign(subscript(lookup_table, index), result),
57
+ )
58
+ return result
@@ -70,23 +70,17 @@ def cc_modular_add(n: CInt, a: CInt, phi_b: QArray[QBit], c1: QBit, c2: QBit) ->
70
70
  aux = QBit()
71
71
 
72
72
  within_apply(
73
- lambda: ( # type:ignore[arg-type]
73
+ lambda: (
74
74
  allocate(aux),
75
- bind([c1, c2], ctrl), # type:ignore[func-returns-value]
75
+ bind([c1, c2], ctrl),
76
76
  ),
77
- lambda: ( # type:ignore[arg-type]
78
- control( # type:ignore[func-returns-value]
79
- ctrl, lambda: qft_space_add_const(a, phi_b)
80
- ),
81
- invert( # type:ignore[func-returns-value]
82
- lambda: qft_space_add_const(n, phi_b)
83
- ),
77
+ lambda: (
78
+ control(ctrl, lambda: qft_space_add_const(a, phi_b)),
79
+ invert(lambda: qft_space_add_const(n, phi_b)),
84
80
  _check_msb(1, phi_b, aux),
85
- control( # type:ignore[func-returns-value]
86
- aux, lambda: qft_space_add_const(n, phi_b)
87
- ),
81
+ control(aux, lambda: qft_space_add_const(n, phi_b)),
88
82
  within_apply(
89
- lambda: invert( # type:ignore[func-returns-value]
83
+ lambda: invert(
90
84
  lambda: control(ctrl, lambda: qft_space_add_const(a, phi_b))
91
85
  ),
92
86
  lambda: _check_msb(0, phi_b, aux),
@@ -160,14 +154,10 @@ def inplace_c_modular_multiply(n: CInt, a: CInt, x: QArray[QBit], ctrl: QBit) ->
160
154
 
161
155
  within_apply(
162
156
  lambda: allocate(x.len + 1, b),
163
- lambda: ( # type:ignore[arg-type]
157
+ lambda: (
164
158
  c_modular_multiply(n, a, b, x, ctrl),
165
- control( # type:ignore[func-returns-value]
166
- ctrl, lambda: multiswap(x, b)
167
- ),
168
- invert( # type:ignore[func-returns-value]
169
- lambda: c_modular_multiply(n, mod_inverse(a, n), b, x, ctrl)
170
- ),
159
+ control(ctrl, lambda: multiswap(x, b)),
160
+ invert(lambda: c_modular_multiply(n, mod_inverse(a, n), b, x, ctrl)),
171
161
  ),
172
162
  )
173
163
 
@@ -18,9 +18,9 @@ def qft_no_swap(qbv: QArray[QBit]) -> None:
18
18
  """
19
19
  repeat(
20
20
  qbv.len,
21
- lambda i: ( # type:ignore[arg-type]
21
+ lambda i: (
22
22
  H(qbv[i]),
23
- repeat( # type:ignore[func-returns-value]
23
+ repeat(
24
24
  qbv.len - i - 1,
25
25
  lambda j: CPHASE(
26
26
  theta=pi / (2 ** (j + 1)),
@@ -97,8 +97,8 @@ def qsvt(
97
97
  if_(
98
98
  condition=phase_seq.len % 2 == 1,
99
99
  then=lambda: IDENTITY(qvar),
100
- else_=lambda: ( # type:ignore[arg-type]
101
- u(qvar), # type:ignore[func-returns-value]
100
+ else_=lambda: (
101
+ u(qvar),
102
102
  projector_controlled_phase(
103
103
  phase_seq[phase_seq.len - 1],
104
104
  proj_cnot_2,
@@ -297,8 +297,8 @@ def qsvt_lcu(
297
297
  condition=phase_seq_odd.len > phase_seq_even.len,
298
298
  then=lambda: control(
299
299
  lcu == 0,
300
- lambda: [ # type:ignore[arg-type]
301
- u(qvar), # type:ignore[func-returns-value]
300
+ lambda: [
301
+ u(qvar),
302
302
  projector_controlled_phase(
303
303
  phase_seq_odd[phase_seq_odd.len - 1], proj_cnot_2, qvar, aux
304
304
  ),
@@ -307,8 +307,8 @@ def qsvt_lcu(
307
307
  )
308
308
  if_(
309
309
  condition=phase_seq_odd.len < phase_seq_even.len,
310
- then=lambda: ( # type:ignore[arg-type]
311
- u(qvar), # type:ignore[func-returns-value]
310
+ then=lambda: (
311
+ u(qvar),
312
312
  projector_controlled_double_phase(
313
313
  phase_seq_even[phase_seq_even.len - 1],
314
314
  phase_seq_odd[phase_seq_odd.len - 1],
@@ -319,8 +319,8 @@ def qsvt_lcu(
319
319
  ),
320
320
  control(
321
321
  lcu,
322
- lambda: [ # type:ignore[arg-type]
323
- invert(lambda: u(qvar)), # type:ignore[func-returns-value]
322
+ lambda: [
323
+ invert(lambda: u(qvar)),
324
324
  projector_controlled_phase(
325
325
  phase_seq_even[phase_seq_even.len - 1], proj_cnot_1, qvar, aux
326
326
  ),
@@ -79,8 +79,8 @@ def modular_increment(a: CInt, x: QNum) -> None:
79
79
  )
80
80
  array_cast: QArray = QArray()
81
81
  within_apply(
82
- lambda: ( # type:ignore[arg-type]
83
- bind(x, array_cast), # type:ignore[func-returns-value]
82
+ lambda: (
83
+ bind(x, array_cast),
84
84
  qft(array_cast),
85
85
  ),
86
86
  lambda: repeat(
@@ -12,19 +12,25 @@ def qft_const_adder_phase(
12
12
  value: CInt,
13
13
  reg_len: CInt,
14
14
  ) -> CReal:
15
- return symbolic_function(bit_index, value, reg_len, return_type=CReal)
15
+ return symbolic_function(
16
+ bit_index, value, reg_len, return_type=CReal # type:ignore[type-abstract]
17
+ )
16
18
 
17
19
 
18
20
  def fock_hamiltonian_problem_to_hamiltonian(
19
21
  problem: FockHamiltonianProblem,
20
22
  ) -> CArray[PauliTerm]:
21
- return symbolic_function(problem, return_type=CArray[PauliTerm])
23
+ return symbolic_function(
24
+ problem, return_type=CArray[PauliTerm] # type:ignore[type-abstract]
25
+ )
22
26
 
23
27
 
24
28
  def molecule_problem_to_hamiltonian(
25
29
  problem: MoleculeProblem,
26
30
  ) -> CArray[PauliTerm]:
27
- return symbolic_function(problem, return_type=CArray[PauliTerm])
31
+ return symbolic_function(
32
+ problem, return_type=CArray[PauliTerm] # type:ignore[type-abstract]
33
+ )
28
34
 
29
35
 
30
36
  def grid_entangler_graph(
@@ -33,14 +39,19 @@ def grid_entangler_graph(
33
39
  grid_randomization: CBool,
34
40
  ) -> CArray[CArray[CInt]]:
35
41
  return symbolic_function(
36
- num_qubits, schmidt_rank, grid_randomization, return_type=CArray[CArray[CInt]]
42
+ num_qubits,
43
+ schmidt_rank,
44
+ grid_randomization,
45
+ return_type=CArray[CArray[CInt]], # type:ignore[type-abstract]
37
46
  )
38
47
 
39
48
 
40
49
  def hypercube_entangler_graph(
41
50
  num_qubits: CInt,
42
51
  ) -> CArray[CArray[CInt]]:
43
- return symbolic_function(num_qubits, return_type=CArray[CArray[CInt]])
52
+ return symbolic_function(
53
+ num_qubits, return_type=CArray[CArray[CInt]] # type:ignore[type-abstract]
54
+ )
44
55
 
45
56
 
46
57
  def gaussian_finance_post_process(
@@ -49,7 +60,10 @@ def gaussian_finance_post_process(
49
60
  probability: CReal,
50
61
  ) -> CReal:
51
62
  return symbolic_function(
52
- finance_model, estimation_method, probability, return_type=CReal
63
+ finance_model,
64
+ estimation_method,
65
+ probability,
66
+ return_type=CReal, # type:ignore[type-abstract]
53
67
  )
54
68
 
55
69
 
@@ -59,7 +73,10 @@ def log_normal_finance_post_process(
59
73
  probability: CReal,
60
74
  ) -> CReal:
61
75
  return symbolic_function(
62
- finance_model, estimation_method, probability, return_type=CReal
76
+ finance_model,
77
+ estimation_method,
78
+ probability,
79
+ return_type=CReal, # type:ignore[type-abstract]
63
80
  )
64
81
 
65
82
 
@@ -149,6 +149,7 @@ class Optimizer(IntEnum):
149
149
  L_BFGS_B = 3
150
150
  NELDER_MEAD = 4
151
151
  ADAM = 5
152
+ SLSQP = 6
152
153
 
153
154
 
154
155
  class Pauli(IntEnum):
@@ -64,6 +64,7 @@ CORE_LIB_DECLS = [
64
64
  inplace_prepare_state,
65
65
  inplace_prepare_amplitudes,
66
66
  single_pauli_exponent,
67
+ commuting_paulis_exponent,
67
68
  suzuki_trotter,
68
69
  qdrift,
69
70
  exponentiation_with_depth_constraint,
@@ -129,6 +130,7 @@ __all__ = [ # noqa: RUF022
129
130
  "randomized_benchmarking",
130
131
  "real_xor_constant",
131
132
  "single_pauli_exponent",
133
+ "commuting_paulis_exponent",
132
134
  "suzuki_trotter",
133
135
  "unitary",
134
136
  "RESET",
@@ -28,6 +28,30 @@ def single_pauli_exponent(
28
28
  pass
29
29
 
30
30
 
31
+ @qfunc(external=True)
32
+ def commuting_paulis_exponent(
33
+ pauli_operator: CArray[PauliTerm],
34
+ evolution_coefficient: CReal,
35
+ qbv: QArray[
36
+ QBit, Literal["get_field(get_field(pauli_operator[0], 'pauli'), 'len')"]
37
+ ],
38
+ ) -> None:
39
+ """
40
+ [Qmod core-library function]
41
+
42
+ Exponentiates the specified commutative Pauli operator.
43
+ As all the Pauli operator's terms commute, the exponential of the whole operator
44
+ is exactly the product of exponentials of each term.
45
+ Calling this funciton with a non-commutative Pauli operator will issue an error.
46
+
47
+ Args:
48
+ pauli_operator: The Pauli operator to be exponentiated.
49
+ evolution_coefficient: A global evolution coefficient multiplying the Pauli operator.
50
+ qbv: The target quantum variable of the exponentiation.
51
+ """
52
+ pass
53
+
54
+
31
55
  @qfunc(external=True)
32
56
  def suzuki_trotter(
33
57
  pauli_operator: CArray[PauliTerm],