classiq 0.94.2__py3-none-any.whl → 0.96.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 (76) hide show
  1. classiq/_internals/api_wrapper.py +0 -83
  2. classiq/_internals/authentication/auth0.py +32 -3
  3. classiq/_internals/authentication/authorization_code.py +9 -0
  4. classiq/_internals/authentication/authorization_flow.py +41 -0
  5. classiq/_internals/authentication/device.py +31 -50
  6. classiq/_internals/authentication/hybrid_flow.py +19 -0
  7. classiq/_internals/authentication/token_manager.py +5 -4
  8. classiq/applications/__init__.py +2 -2
  9. classiq/applications/iqae/iqae.py +6 -3
  10. classiq/applications/qnn/gradients/simple_quantum_gradient.py +1 -1
  11. classiq/applications/qnn/qlayer.py +1 -1
  12. classiq/applications/qnn/torch_utils.py +2 -2
  13. classiq/applications/qsp/qsp.py +6 -5
  14. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +10 -0
  15. classiq/execution/__init__.py +0 -3
  16. classiq/execution/user_budgets.py +0 -1
  17. classiq/interface/_version.py +1 -1
  18. classiq/interface/backend/backend_preferences.py +11 -35
  19. classiq/interface/backend/quantum_backend_providers.py +0 -2
  20. classiq/interface/exceptions.py +0 -4
  21. classiq/interface/generator/application_apis/__init__.py +0 -1
  22. classiq/interface/generator/arith/register_user_input.py +1 -1
  23. classiq/interface/generator/function_param_list.py +0 -2
  24. classiq/interface/generator/generated_circuit_data.py +1 -6
  25. classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
  26. classiq/interface/generator/quantum_function_call.py +1 -1
  27. classiq/interface/generator/quantum_program.py +0 -4
  28. classiq/interface/generator/transpiler_basis_gates.py +3 -0
  29. classiq/interface/generator/types/builtin_enum_declarations.py +0 -9
  30. classiq/interface/hardware.py +0 -1
  31. classiq/interface/interface_version.py +1 -1
  32. classiq/interface/model/block.py +4 -0
  33. classiq/interface/model/classical_if.py +4 -0
  34. classiq/interface/model/control.py +7 -0
  35. classiq/interface/model/invert.py +4 -0
  36. classiq/interface/model/model_visitor.py +40 -1
  37. classiq/interface/model/power.py +4 -0
  38. classiq/interface/model/quantum_statement.py +8 -1
  39. classiq/interface/model/repeat.py +4 -0
  40. classiq/interface/model/skip_control.py +4 -0
  41. classiq/interface/model/within_apply_operation.py +4 -0
  42. classiq/interface/server/routes.py +0 -12
  43. classiq/model_expansions/generative_functions.py +6 -8
  44. classiq/model_expansions/interpreters/base_interpreter.py +1 -1
  45. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +2 -1
  46. classiq/model_expansions/visitors/symbolic_param_inference.py +3 -3
  47. classiq/model_expansions/visitors/uncomputation_signature_inference.py +14 -3
  48. classiq/open_library/functions/__init__.py +3 -2
  49. classiq/open_library/functions/amplitude_loading.py +85 -0
  50. classiq/open_library/functions/lcu.py +47 -18
  51. classiq/open_library/functions/modular_exponentiation.py +5 -8
  52. classiq/open_library/functions/qsvt.py +4 -4
  53. classiq/open_library/functions/state_preparation.py +7 -7
  54. classiq/qmod/builtins/classical_execution_primitives.py +0 -12
  55. classiq/qmod/builtins/enums.py +15 -17
  56. classiq/qmod/builtins/functions/__init__.py +5 -5
  57. classiq/qmod/builtins/functions/allocation.py +21 -0
  58. classiq/qmod/builtins/functions/mcx.py +7 -0
  59. classiq/qmod/builtins/operations.py +125 -23
  60. classiq/qmod/builtins/structs.py +22 -33
  61. classiq/qmod/semantics/annotation/call_annotation.py +3 -3
  62. classiq/qmod/semantics/error_manager.py +7 -8
  63. classiq/qmod/utilities.py +0 -10
  64. {classiq-0.94.2.dist-info → classiq-0.96.0.dist-info}/METADATA +1 -1
  65. {classiq-0.94.2.dist-info → classiq-0.96.0.dist-info}/RECORD +67 -71
  66. {classiq-0.94.2.dist-info → classiq-0.96.0.dist-info}/WHEEL +1 -1
  67. classiq/applications/qsvm/__init__.py +0 -8
  68. classiq/applications/qsvm/qsvm.py +0 -11
  69. classiq/execution/iqcc.py +0 -128
  70. classiq/interface/applications/qsvm.py +0 -114
  71. classiq/interface/execution/iqcc.py +0 -42
  72. classiq/interface/generator/application_apis/qsvm_declarations.py +0 -6
  73. classiq/interface/generator/qsvm.py +0 -96
  74. classiq/open_library/functions/lookup_table.py +0 -58
  75. classiq/qmod/builtins/functions/qsvm.py +0 -24
  76. {classiq-0.94.2.dist-info → classiq-0.96.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -20,7 +20,6 @@ class ProviderVendor(StrEnum):
20
20
  OQC = "OQC"
21
21
  INTEL = "Intel"
22
22
  AQT = "AQT"
23
- IQCC = "IQCC"
24
23
  CINECA = "CINECA"
25
24
 
26
25
 
@@ -35,7 +34,6 @@ class ProviderTypeVendor:
35
34
  OQC = Literal[ProviderVendor.OQC]
36
35
  INTEL = Literal[ProviderVendor.INTEL]
37
36
  AQT = Literal[ProviderVendor.AQT]
38
- IQCC = Literal[ProviderVendor.IQCC]
39
37
  CINECA = Literal[ProviderVendor.CINECA]
40
38
 
41
39
 
@@ -95,10 +95,6 @@ class ClassiqQFuncError(ClassiqValueError):
95
95
  pass
96
96
 
97
97
 
98
- class ClassiqQSVMError(ClassiqValueError):
99
- pass
100
-
101
-
102
98
  class ClassiqQNNError(ClassiqValueError):
103
99
  pass
104
100
 
@@ -4,6 +4,5 @@ from classiq.interface.generator.builtin_api_builder import (
4
4
 
5
5
  from .arithmetic_declarations import * # noqa: F403
6
6
  from .combinatorial_optimization_declarations import * # noqa: F403
7
- from .qsvm_declarations import * # noqa: F403
8
7
 
9
8
  populate_builtin_declarations(vars().values())
@@ -19,7 +19,7 @@ class RegisterArithmeticInfo(HashablePydanticBaseModel):
19
19
  is_signed: bool = pydantic.Field(default=False)
20
20
  fraction_places: pydantic.NonNegativeInt = pydantic.Field(default=0)
21
21
  bypass_bounds_validation: bool = pydantic.Field(default=False)
22
- bounds: PydanticFloatTuple = pydantic.Field( # type: ignore[assignment]
22
+ bounds: PydanticFloatTuple = pydantic.Field(
23
23
  default=None,
24
24
  validate_default=True,
25
25
  )
@@ -42,7 +42,6 @@ from classiq.interface.generator.hardware_efficient_ansatz import (
42
42
  from classiq.interface.generator.identity import Identity
43
43
  from classiq.interface.generator.mcu import Mcu
44
44
  from classiq.interface.generator.mcx import Mcx
45
- from classiq.interface.generator.qsvm import QSVMFeatureMap
46
45
  from classiq.interface.generator.randomized_benchmarking import RandomizedBenchmarking
47
46
  from classiq.interface.generator.reset import Reset
48
47
  from classiq.interface.generator.standard_gates.standard_gates_param_list import (
@@ -108,7 +107,6 @@ function_param_library: FunctionParamLibrary = FunctionParamLibrary(
108
107
  RandomizedBenchmarking,
109
108
  UGate,
110
109
  AmplitudeLoading,
111
- QSVMFeatureMap,
112
110
  HadamardTransform,
113
111
  Copy,
114
112
  Reset,
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import re
3
- from typing import Literal, Optional, TypeAlias
3
+ from typing import Literal, TypeAlias
4
4
  from uuid import UUID
5
5
 
6
6
  import pydantic
@@ -188,8 +188,6 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
188
188
  back_refs: StatementBlock = Field(default_factory=list)
189
189
 
190
190
  model_config = ConfigDict(extra="allow")
191
- # Temporary field to store the override debug info for parallel old/new visualization
192
- override_debug_info: Optional["FunctionDebugInfoInterface"] = None
193
191
 
194
192
  @property
195
193
  def is_allocate_or_free(self) -> bool:
@@ -317,9 +315,6 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
317
315
  )
318
316
 
319
317
  def inverse(self) -> "FunctionDebugInfoInterface":
320
- if self.override_debug_info is not None:
321
- self.override_debug_info = self.override_debug_info.inverse()
322
- return self
323
318
  inverse_generated_function = (
324
319
  self.generated_function.model_copy(
325
320
  update=dict(registers=self._inverse_registers)
@@ -42,7 +42,7 @@ class HardwareEfficientAnsatz(function_params.FunctionParams):
42
42
  "If none specified - use connectivity map from the model hardware settings. "
43
43
  "If none specified as well, all qubit pairs will be connected.",
44
44
  )
45
- num_qubits: pydantic.PositiveInt = pydantic.Field( # type: ignore[assignment]
45
+ num_qubits: pydantic.PositiveInt = pydantic.Field(
46
46
  default=None,
47
47
  description="Number of qubits in the ansatz.",
48
48
  validate_default=True,
@@ -144,7 +144,7 @@ class SynthesisQuantumFunctionCall(BaseModel):
144
144
  power: PydanticPowerType = pydantic.Field(
145
145
  default=1, description="Number of successive calls to the operation"
146
146
  )
147
- name: PydanticNonEmptyString = pydantic.Field( # type: ignore[assignment]
147
+ name: PydanticNonEmptyString = pydantic.Field(
148
148
  default=None,
149
149
  validate_default=True,
150
150
  description="The name of the function instance. "
@@ -65,7 +65,6 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
65
65
  transpiled_circuit: TranspiledCircuitData | None = pydantic.Field(default=None)
66
66
  creation_time: str = pydantic.Field(default_factory=_get_formatted_utc_current_time)
67
67
  synthesis_duration: SynthesisStepDurations | None = pydantic.Field(default=None)
68
- debug_info: list[FunctionDebugInfoInterface] | None = pydantic.Field(default=None)
69
68
  compressed_debug_info: bytes | None = pydantic.Field(default=None)
70
69
  program_id: str = pydantic.Field(default_factory=get_uuid_as_str)
71
70
  execution_primitives_input: PrimitivesInput | None = pydantic.Field(default=None)
@@ -169,9 +168,6 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
169
168
  )
170
169
 
171
170
  def get_debug_info(self) -> list[FunctionDebugInfoInterface] | None:
172
- # Support legacy uncompressed debug info
173
- if self.debug_info is not None:
174
- return self.debug_info
175
171
  if self.compressed_debug_info is None:
176
172
  return None
177
173
  decompressed_debug_info_dict_list = decompress(self.compressed_debug_info)
@@ -64,6 +64,9 @@ DEFAULT_BASIS_GATES: BasisGates = SINGLE_QUBIT_GATES | BASIC_TWO_QUBIT_GATES
64
64
  ALL_GATES: BasisGates = (
65
65
  SINGLE_QUBIT_GATES | TWO_QUBIT_GATES | THREE_QUBIT_GATES | NON_UNITARY_GATES
66
66
  )
67
+ ALL_NON_3_QBIT_GATES: BasisGates = (
68
+ SINGLE_QUBIT_GATES | TWO_QUBIT_GATES | NON_UNITARY_GATES
69
+ )
67
70
 
68
71
  ROUTING_TWO_QUBIT_BASIS_GATES: BasisGates = frozenset(
69
72
  ("cx", "ecr", "rzx", "ryy", "rxx", "rzz", "cy", "cz", "cp", "swap")
@@ -20,16 +20,7 @@ class Pauli(IntEnum):
20
20
  Z = 3
21
21
 
22
22
 
23
- class QSVMFeatureMapEntanglement(IntEnum):
24
- FULL = 0
25
- LINEAR = 1
26
- CIRCULAR = 2
27
- SCA = 3
28
- PAIRWISE = 4
29
-
30
-
31
23
  __all__ = [
32
24
  "Optimizer",
33
25
  "Pauli",
34
- "QSVMFeatureMapEntanglement",
35
26
  ]
@@ -32,7 +32,6 @@ class Provider(StrEnum):
32
32
  OQC = "OQC"
33
33
  INTEL = "Intel"
34
34
  AQT = "AQT"
35
- IQCC = "IQCC"
36
35
  CINECA = "CINECA"
37
36
 
38
37
  @property
@@ -1 +1 @@
1
- INTERFACE_VERSION = "13"
1
+ INTERFACE_VERSION = "14"
@@ -14,3 +14,7 @@ class Block(QuantumOperation):
14
14
  statements: "StatementBlock"
15
15
 
16
16
  label: str | None = pydantic.Field(default=None)
17
+
18
+ @property
19
+ def blocks(self) -> dict[str, "StatementBlock"]:
20
+ return {"statements": self.statements}
@@ -31,6 +31,10 @@ class ClassicalIf(QuantumOperation):
31
31
  def expressions(self) -> list[Expression]:
32
32
  return [self.condition]
33
33
 
34
+ @property
35
+ def blocks(self) -> dict[str, "StatementBlock"]:
36
+ return {"then": self.then, "else_": self.else_}
37
+
34
38
  @property
35
39
  def wiring_inputs(self) -> Mapping[str, HandleBinding]:
36
40
  return functools.reduce(
@@ -47,3 +47,10 @@ class Control(QuantumExpressionOperation):
47
47
 
48
48
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
49
49
  return reset_lists(self, ["body", "else_block"])
50
+
51
+ @property
52
+ def blocks(self) -> dict[str, "StatementBlock"]:
53
+ blocks = {"body": self.body}
54
+ if self.else_block is not None:
55
+ blocks["else_block"] = self.else_block
56
+ return blocks
@@ -14,3 +14,7 @@ class Invert(QuantumOperation):
14
14
 
15
15
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
16
16
  return reset_lists(self, ["body"])
17
+
18
+ @property
19
+ def blocks(self) -> dict[str, "StatementBlock"]:
20
+ return {"body": self.body}
@@ -1,5 +1,12 @@
1
+ from collections.abc import Collection
2
+
3
+ from pydantic import BaseModel
4
+
1
5
  from classiq.interface.debug_info.debug_info import DebugInfoCollection
2
- from classiq.interface.generator.visitor import Transformer, Visitor
6
+ from classiq.interface.generator.visitor import RetType, Transformer, Visitor
7
+ from classiq.interface.model.model import Model
8
+ from classiq.interface.model.native_function_definition import NativeFunctionDefinition
9
+ from classiq.interface.model.quantum_statement import QuantumStatement
3
10
 
4
11
 
5
12
  class ModelVisitor(Visitor):
@@ -7,8 +14,40 @@ class ModelVisitor(Visitor):
7
14
  return
8
15
 
9
16
 
17
+ class ModelStatementsVisitor(ModelVisitor):
18
+ def visit_BaseModel(self, node: BaseModel) -> RetType | None:
19
+ if isinstance(node, Model):
20
+ return self.visit(node.functions)
21
+ if isinstance(node, NativeFunctionDefinition):
22
+ return self.visit(node.body)
23
+ if isinstance(node, QuantumStatement):
24
+ for block in node.blocks.values():
25
+ self.visit(block)
26
+ return None
27
+ return super().visit_BaseModel(node)
28
+
29
+
10
30
  class ModelTransformer(Transformer):
11
31
  def visit_DebugInfoCollection(
12
32
  self, debug_info: DebugInfoCollection
13
33
  ) -> DebugInfoCollection:
14
34
  return debug_info
35
+
36
+
37
+ class ModelStatementsTransformer(ModelTransformer):
38
+ def visit_BaseModel(
39
+ self, node: BaseModel, fields_to_skip: Collection[str] | None = None
40
+ ) -> RetType:
41
+ if isinstance(node, Model):
42
+ new_functions = self.visit(node.functions)
43
+ return node.model_copy(update=dict(functions=new_functions))
44
+ if isinstance(node, NativeFunctionDefinition):
45
+ new_body = self.visit(node.body)
46
+ return node.model_copy(update=dict(body=new_body))
47
+ if isinstance(node, QuantumStatement):
48
+ new_blocks = {
49
+ block_name: self.visit(block)
50
+ for block_name, block in node.blocks.items()
51
+ }
52
+ return node.model_copy(update=new_blocks)
53
+ return super().visit_BaseModel(node, fields_to_skip)
@@ -20,3 +20,7 @@ class Power(QuantumOperation):
20
20
  @property
21
21
  def expressions(self) -> list[Expression]:
22
22
  return [self.power]
23
+
24
+ @property
25
+ def blocks(self) -> dict[str, "StatementBlock"]:
26
+ return {"body": self.body}
@@ -1,6 +1,6 @@
1
1
  from collections.abc import Callable, Iterable, Mapping, Sequence
2
2
  from dataclasses import dataclass
3
- from typing import Any
3
+ from typing import TYPE_CHECKING, Any
4
4
  from uuid import UUID, uuid4
5
5
 
6
6
  import pydantic
@@ -17,6 +17,9 @@ from classiq.interface.model.handle_binding import (
17
17
  HandleBinding,
18
18
  )
19
19
 
20
+ if TYPE_CHECKING:
21
+ from classiq.interface.model.statement_block import StatementBlock
22
+
20
23
 
21
24
  class QuantumStatement(ASTNode):
22
25
  kind: str
@@ -45,6 +48,10 @@ class QuantumStatement(ASTNode):
45
48
  def expressions(self) -> list[Expression]:
46
49
  return []
47
50
 
51
+ @property
52
+ def blocks(self) -> dict[str, "StatementBlock"]:
53
+ return {}
54
+
48
55
 
49
56
  @dataclass
50
57
  class HandleMetadata:
@@ -21,3 +21,7 @@ class Repeat(QuantumOperation):
21
21
  @property
22
22
  def expressions(self) -> list[Expression]:
23
23
  return [self.count]
24
+
25
+ @property
26
+ def blocks(self) -> dict[str, "StatementBlock"]:
27
+ return {"body": self.body}
@@ -9,3 +9,7 @@ if TYPE_CHECKING:
9
9
  class SkipControl(QuantumOperation):
10
10
  kind: Literal["SkipControl"]
11
11
  body: "StatementBlock"
12
+
13
+ @property
14
+ def blocks(self) -> dict[str, "StatementBlock"]:
15
+ return {"body": self.body}
@@ -16,6 +16,10 @@ class WithinApply(QuantumOperation):
16
16
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
17
17
  return reset_lists(self, ["compute", "action"])
18
18
 
19
+ @property
20
+ def blocks(self) -> dict[str, "StatementBlock"]:
21
+ return {"compute": self.compute, "action": self.action}
22
+
19
23
 
20
24
  class Compute(QuantumOperation):
21
25
  kind: Literal["Compute"]
@@ -4,7 +4,6 @@ EXECUTION_SESSIONS_PREFIX = EXECUTION_PREFIX + "/sessions"
4
4
  CONVERSION_PREFIX = "/conversion"
5
5
  PROVIDERS_PREFIX = "/providers"
6
6
 
7
- IQCC_PREFIX = PROVIDERS_PREFIX + "/iqcc"
8
7
  USER_BUDGETS_PREFIX = "/user_budgets"
9
8
 
10
9
 
@@ -70,17 +69,6 @@ QASM_TO_QMOD_FULL_PATH = CONVERSION_PREFIX + QASM_TO_QMOD_SUFFIX
70
69
 
71
70
  STATIC_SEMANTICS_VALIDATION_PATH = "/validate_static_semantics"
72
71
 
73
- IQCC_INIT_AUTH_SUFFIX = "/init_auth"
74
- IQCC_INIT_AUTH_FULL_PATH = IQCC_PREFIX + IQCC_INIT_AUTH_SUFFIX
75
- IQCC_PROBE_AUTH_SUFFIX = "/probe_auth"
76
- IQCC_PROBE_AUTH_FULL_PATH = IQCC_PREFIX + IQCC_PROBE_AUTH_SUFFIX
77
- IQCC_LIST_AUTH_SCOPES_SUFFIX = "/auth_scopes"
78
- IQCC_LIST_AUTH_SCOPES_FULL_PATH = IQCC_PREFIX + IQCC_LIST_AUTH_SCOPES_SUFFIX
79
- IQCC_LIST_AUTH_METHODS_SUFFIX = "/auth_methods"
80
- IQCC_LIST_AUTH_METHODS_FULL_PATH = IQCC_PREFIX + IQCC_LIST_AUTH_METHODS_SUFFIX
81
- IQCC_LIST_AUTH_TARGETS_SUFFIX = "/auth_targets"
82
- IQCC_LIST_AUTH_TARGETS_FULL_PATH = IQCC_PREFIX + IQCC_LIST_AUTH_TARGETS_SUFFIX
83
-
84
72
  USER_BUDGETS_SUFFIX = "/all"
85
73
  USER_BUDGETS_FULL_PATH = USER_BUDGETS_PREFIX + USER_BUDGETS_SUFFIX
86
74
  USER_BUDGET_SET_LIMIT_SUFFIX = "/set_limit"
@@ -172,20 +172,18 @@ class _InterpreterExpandable(QFunc):
172
172
  scope_func_decls: dict[str, QuantumFunctionDeclaration] = {}
173
173
  for name, evaluated in self._interpreter._builder.current_scope.items():
174
174
  value = evaluated.value
175
+ if (
176
+ isinstance(value, list)
177
+ and len(value) > 0
178
+ and isinstance(value[0], FunctionClosure)
179
+ ):
180
+ value = value[0]
175
181
  if isinstance(value, FunctionClosure):
176
182
  scope_func_decls[name] = QuantumFunctionDeclaration(
177
183
  name=name,
178
184
  positional_arg_declarations=value.positional_arg_declarations,
179
185
  )
180
186
  continue
181
- op_param = self._interpreter._builder.current_function.parameters_dict.get(
182
- name
183
- )
184
- if isinstance(op_param, QuantumOperandDeclaration):
185
- scope_func_decls[name] = QuantumFunctionDeclaration(
186
- name=name,
187
- positional_arg_declarations=op_param.positional_arg_declarations,
188
- )
189
187
  return (
190
188
  nameables_to_dict(self._interpreter._get_function_declarations())
191
189
  | scope_func_decls
@@ -324,7 +324,7 @@ class BaseInterpreter:
324
324
  def emit_statement(self, statement: QuantumStatement) -> None:
325
325
  source_ref = statement.source_ref
326
326
  error_context = (
327
- self._error_manager.node_context(statement)
327
+ self._error_manager.source_ref_context(statement.source_ref)
328
328
  if source_ref is not None
329
329
  else nullcontext()
330
330
  )
@@ -41,7 +41,8 @@ class FrontendGenerativeInterpreter(GenerativeInterpreter):
41
41
  if module is None or not module.__name__.startswith("classiq."):
42
42
  file_name = os.path.split(frame.filename)[-1]
43
43
  if (
44
- frame.positions is not None
44
+ hasattr(frame, "positions")
45
+ and frame.positions is not None
45
46
  and frame.positions.lineno is not None
46
47
  and frame.positions.col_offset is not None
47
48
  and frame.positions.end_lineno is not None
@@ -19,7 +19,7 @@ from classiq.interface.model.classical_parameter_declaration import (
19
19
  AnonClassicalParameterDeclaration,
20
20
  )
21
21
  from classiq.interface.model.handle_binding import FieldHandleBinding, HandleBinding
22
- from classiq.interface.model.model_visitor import ModelVisitor
22
+ from classiq.interface.model.model_visitor import ModelStatementsVisitor
23
23
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
24
24
  from classiq.interface.model.quantum_function_call import ArgValue, QuantumFunctionCall
25
25
  from classiq.interface.model.quantum_function_declaration import (
@@ -84,7 +84,7 @@ def _get_param_expressions(param: AnonPositionalArg) -> list[Expression]:
84
84
  return param.quantum_type.expressions
85
85
 
86
86
 
87
- class SymbolicParamInference(ModelVisitor):
87
+ class SymbolicParamInference(ModelStatementsVisitor):
88
88
  def __init__(
89
89
  self,
90
90
  functions: list[NativeFunctionDefinition],
@@ -166,7 +166,7 @@ class SymbolicParamInference(ModelVisitor):
166
166
  else:
167
167
  for expr in _get_expressions(arg):
168
168
  self._process_nested_compile_time_expression(expr.expr)
169
- self.generic_visit(call)
169
+ self.visit(call.positional_args)
170
170
 
171
171
  def _get_params(self, call: QuantumFunctionCall) -> Sequence[AnonPositionalArg]:
172
172
  name = call.func_name
@@ -18,9 +18,10 @@ from classiq.interface.generator.visitor import NodeType
18
18
  from classiq.interface.model.allocate import Allocate
19
19
  from classiq.interface.model.bind_operation import BindOperation
20
20
  from classiq.interface.model.block import Block
21
+ from classiq.interface.model.classical_if import ClassicalIf
21
22
  from classiq.interface.model.control import Control
22
23
  from classiq.interface.model.invert import Invert
23
- from classiq.interface.model.model_visitor import ModelVisitor
24
+ from classiq.interface.model.model_visitor import ModelStatementsVisitor
24
25
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
25
26
  from classiq.interface.model.power import Power
26
27
  from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
@@ -48,7 +49,7 @@ class _BoundVars(NamedTuple):
48
49
  )
49
50
 
50
51
 
51
- class UncomputationSignatureInference(ModelVisitor):
52
+ class UncomputationSignatureInference(ModelStatementsVisitor):
52
53
  """
53
54
  Infers the uncomputation signature of a function (permutation/non-permutation for
54
55
  the function, and const/non-const for each parameter).
@@ -139,7 +140,13 @@ class UncomputationSignatureInference(ModelVisitor):
139
140
  self._bound_vars_list.append(bound_vars)
140
141
 
141
142
  def visit_ArithmeticOperation(self, arith: ArithmeticOperation) -> None:
142
- self._mark_as_non_const(arith.result_var.name, True)
143
+ if arith.classical_assignment:
144
+ if arith.var_handles:
145
+ self._mark_as_non_permutation()
146
+ for handle in arith.var_handles:
147
+ self._mark_as_non_const(handle.name, False)
148
+ else:
149
+ self._mark_as_non_const(arith.result_var.name, True)
143
150
 
144
151
  def visit_AmplitudeLoadingOperation(
145
152
  self, amp_load: AmplitudeLoadingOperation
@@ -172,6 +179,10 @@ class UncomputationSignatureInference(ModelVisitor):
172
179
  def visit_SkipControl(self, block: SkipControl) -> None:
173
180
  self.visit(block.body)
174
181
 
182
+ def visit_ClassicalIf(self, classical_if: ClassicalIf) -> None:
183
+ self.visit(classical_if.then)
184
+ self.visit(classical_if.else_)
185
+
175
186
  def _mark_as_non_permutation(self) -> None:
176
187
  self._is_permutation = False
177
188
  self._non_permutation_reasons.append(self._source_ref)
@@ -3,6 +3,7 @@ from .amplitude_amplification import (
3
3
  exact_amplitude_amplification,
4
4
  )
5
5
  from .amplitude_estimation import *
6
+ from .amplitude_loading import assign_amplitude_table
6
7
  from .discrete_sine_cosine_transform import *
7
8
  from .discrete_sine_cosine_transform import _qct_d_operator, _qct_pi_operator
8
9
  from .grover import *
@@ -11,7 +12,6 @@ from .hea import *
11
12
  from .lcu import *
12
13
  from .linear_pauli_rotation import *
13
14
  from .linear_pauli_rotation import _single_pauli
14
- from .lookup_table import span_lookup_table
15
15
  from .modular_exponentiation import *
16
16
  from .modular_exponentiation import _check_msb
17
17
  from .qaoa_penalty import *
@@ -92,6 +92,7 @@ __all__ = [
92
92
  "amplitude_amplification",
93
93
  "amplitude_estimation",
94
94
  "apply_to_all",
95
+ "assign_amplitude_table",
95
96
  "c_modular_multiply",
96
97
  "cc_modular_add",
97
98
  "encode_in_angle",
@@ -126,6 +127,7 @@ __all__ = [
126
127
  "prepare_ghz_state",
127
128
  "prepare_int",
128
129
  "prepare_linear_amplitudes",
130
+ "prepare_select",
129
131
  "prepare_sparse_amplitudes",
130
132
  "prepare_uniform_interval_state",
131
133
  "prepare_uniform_trimmed_state",
@@ -151,7 +153,6 @@ __all__ = [
151
153
  "qsvt_lcu_step",
152
154
  "qsvt_step",
153
155
  "reflect_about_zero",
154
- "span_lookup_table",
155
156
  "suzuki_trotter",
156
157
  "swap_test",
157
158
  "switch",
@@ -0,0 +1,85 @@
1
+ from typing import cast
2
+
3
+ import numpy as np
4
+ from sympy import fwht
5
+
6
+ from classiq.interface.exceptions import ClassiqValueError
7
+
8
+ from classiq.qmod.builtins.functions import CX, RY
9
+ from classiq.qmod.builtins.operations import (
10
+ bind,
11
+ skip_control,
12
+ )
13
+ from classiq.qmod.qfunc import qfunc
14
+ from classiq.qmod.qmod_variable import QArray, QBit, QNum
15
+
16
+
17
+ def _get_graycode(size: int, i: int) -> int:
18
+ if i == 2**size:
19
+ return _get_graycode(size, 0)
20
+ return i ^ (i >> 1)
21
+
22
+
23
+ def _get_graycode_angles_wh(size: int, angles: list[float]) -> list[float]:
24
+ transformed_angles = fwht(np.array(angles) / 2**size)
25
+ return [transformed_angles[_get_graycode(size, j)] for j in range(2**size)]
26
+
27
+
28
+ def _get_graycode_ctrls(size: int) -> list[int]:
29
+ return [
30
+ (_get_graycode(size, i) ^ _get_graycode(size, i + 1)).bit_length() - 1
31
+ for i in range(2**size)
32
+ ]
33
+
34
+
35
+ @qfunc
36
+ def assign_amplitude_table(
37
+ amplitudes: list[float], index: QNum, indicator: QBit
38
+ ) -> None:
39
+ """
40
+ [Qmod Classiq-library function]
41
+
42
+ Load a specified list of real amplitudes into a quantum variable using an extra indicator qubit:
43
+ \\( |i\\rangle|0\\rangle \\rightarrow a(i),\\ |i\\rangle|1\\rangle + \\sqrt{1 - a(i)^2},\\ |x\\rangle|0\\rangle \\).
44
+ Here, \\(a(i)\\) is the i-th amplitude, determined by the QNum when the index is in state \\(i\\).
45
+ A list extracted from a given classical function \\(f(x)\\), with indexing according to a given QNum, can be obtained via the utility SDK function `lookup_table`.
46
+ This function expects the indicator qubit to be initialized to \\(|0\\rangle\\).
47
+
48
+ Args:
49
+ amplitudes: Real values for the amplitudes
50
+ index: The quantum variable used for amplitude indexing
51
+ indicator: The quantum indicator qubit
52
+
53
+ Example:
54
+ ```python
55
+ from classiq import *
56
+
57
+
58
+ @qfunc
59
+ def main(x: Output[QNum[5, UNSIGNED, 5]], ind: Output[QBit]) -> None:
60
+ allocate(x)
61
+ hadamard_transform(x)
62
+ allocate(ind)
63
+
64
+ assign_amplitude_table(lookup_table(lambda x: x**2, x), x, ind)
65
+ ```
66
+ """
67
+ if len(amplitudes) != 2**index.size:
68
+ raise ClassiqValueError(
69
+ f"The number of amplitudes must be 2**index.size={2 ** index.size}, got "
70
+ f"{len(amplitudes)}"
71
+ )
72
+
73
+ angles_to_load = cast(list[float], 2 * np.arcsin(amplitudes))
74
+ size = cast(int, index.size)
75
+ transformed_angles = _get_graycode_angles_wh(size, angles_to_load)
76
+ controllers = _get_graycode_ctrls(size)
77
+
78
+ qba: QArray = QArray()
79
+ bind(index, qba)
80
+ for k in range(2**size):
81
+ RY(transformed_angles[k], indicator)
82
+ skip_control(
83
+ lambda k=k: CX(qba[controllers[k]], indicator) # type:ignore[misc]
84
+ )
85
+ bind(qba, index)