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.

Files changed (81) hide show
  1. classiq/_internals/config.py +1 -1
  2. classiq/applications/__init__.py +1 -2
  3. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +1 -1
  4. classiq/applications/hamiltonian/pauli_decomposition.py +1 -1
  5. classiq/evaluators/qmod_annotated_expression.py +37 -15
  6. classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +0 -5
  7. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +20 -13
  8. classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +15 -8
  9. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +39 -16
  10. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +67 -6
  11. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +18 -8
  12. classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +8 -0
  13. classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +4 -1
  14. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +1 -1
  15. classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +1 -1
  16. classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +3 -3
  17. classiq/evaluators/qmod_node_evaluators/utils.py +1 -1
  18. classiq/evaluators/qmod_type_inference/classical_type_inference.py +4 -4
  19. classiq/evaluators/qmod_type_inference/quantum_type_inference.py +38 -0
  20. classiq/evaluators/type_type_match.py +1 -1
  21. classiq/execution/execution_session.py +17 -2
  22. classiq/interface/_version.py +1 -1
  23. classiq/interface/analyzer/analysis_params.py +1 -1
  24. classiq/interface/backend/backend_preferences.py +1 -1
  25. classiq/interface/execution/primitives.py +1 -0
  26. classiq/interface/generator/application_apis/__init__.py +0 -1
  27. classiq/interface/generator/arith/register_user_input.py +1 -1
  28. classiq/interface/generator/expressions/atomic_expression_functions.py +0 -2
  29. classiq/interface/generator/function_param_list.py +0 -4
  30. classiq/interface/generator/functions/classical_type.py +32 -3
  31. classiq/interface/generator/functions/function_declaration.py +0 -4
  32. classiq/interface/generator/functions/type_name.py +31 -0
  33. classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
  34. classiq/interface/generator/quantum_function_call.py +3 -3
  35. classiq/interface/generator/transpiler_basis_gates.py +5 -1
  36. classiq/interface/generator/types/builtin_enum_declarations.py +0 -8
  37. classiq/interface/generator/user_defined_function_params.py +0 -3
  38. classiq/interface/ide/ide_data.py +1 -1
  39. classiq/interface/ide/visual_model.py +2 -2
  40. classiq/interface/model/block.py +5 -1
  41. classiq/interface/model/classical_if.py +16 -8
  42. classiq/interface/model/classical_parameter_declaration.py +4 -0
  43. classiq/interface/model/handle_binding.py +1 -1
  44. classiq/interface/model/port_declaration.py +12 -0
  45. classiq/interface/model/quantum_function_declaration.py +12 -0
  46. classiq/interface/model/quantum_lambda_function.py +1 -1
  47. classiq/interface/model/quantum_statement.py +2 -4
  48. classiq/interface/model/quantum_type.py +102 -3
  49. classiq/interface/pretty_print/expression_to_qmod.py +3 -17
  50. classiq/model_expansions/quantum_operations/allocate.py +1 -1
  51. classiq/model_expansions/quantum_operations/handle_evaluator.py +2 -8
  52. classiq/open_library/functions/__init__.py +3 -0
  53. classiq/open_library/functions/lcu.py +2 -2
  54. classiq/open_library/functions/state_preparation.py +182 -5
  55. classiq/qmod/builtins/__init__.py +0 -3
  56. classiq/qmod/builtins/classical_functions.py +0 -28
  57. classiq/qmod/builtins/enums.py +24 -18
  58. classiq/qmod/builtins/functions/__init__.py +0 -5
  59. classiq/qmod/builtins/operations.py +142 -0
  60. classiq/qmod/builtins/structs.py +0 -11
  61. classiq/qmod/native/pretty_printer.py +1 -1
  62. classiq/qmod/pretty_print/expression_to_python.py +3 -6
  63. classiq/qmod/pretty_print/pretty_printer.py +4 -1
  64. classiq/qmod/qmod_variable.py +141 -3
  65. classiq/qmod/semantics/annotation/call_annotation.py +4 -2
  66. classiq/qmod/semantics/annotation/qstruct_annotator.py +20 -5
  67. {classiq-0.87.0.dist-info → classiq-0.89.0.dist-info}/METADATA +4 -4
  68. {classiq-0.87.0.dist-info → classiq-0.89.0.dist-info}/RECORD +69 -81
  69. classiq/applications/finance/__init__.py +0 -15
  70. classiq/interface/finance/__init__.py +0 -0
  71. classiq/interface/finance/finance_modelling_params.py +0 -11
  72. classiq/interface/finance/function_input.py +0 -102
  73. classiq/interface/finance/gaussian_model_input.py +0 -50
  74. classiq/interface/finance/log_normal_model_input.py +0 -40
  75. classiq/interface/finance/model_input.py +0 -22
  76. classiq/interface/generator/application_apis/finance_declarations.py +0 -108
  77. classiq/interface/generator/expressions/enums/__init__.py +0 -0
  78. classiq/interface/generator/expressions/enums/finance_functions.py +0 -12
  79. classiq/interface/generator/finance.py +0 -107
  80. classiq/qmod/builtins/functions/finance.py +0 -34
  81. {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[dict[str, Any]] = None,
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 or dict()
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, ConfigDict, Field
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
- args = [self._cleaned_ast_to_code(arg) for arg in node.args]
157
- kwargs = [
158
- f"{kwarg.arg}={self._cleaned_ast_to_code(kwarg.value)}"
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.is_evaluated:
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
- if handle == evaluated_handle:
23
- return False
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
- _load_phases,
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
- _load_phases(phases, block),
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 _load_phases(
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
- RZ(alphas[gray], target[_msb(gray)])
337
+ PHASE(alphas[gray], target[_msb(gray)])
337
338
  CX(target[_control_qubit(i)], target[_msb(next_gray)])
338
339
 
339
- RZ(alphas[_graycode(len(phases) - 1)], target[target.len - 1])
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
- _load_phases(phases, target)
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
  ]