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
@@ -0,0 +1,22 @@
1
+ from sympy import Symbol
2
+
3
+ from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
4
+ from classiq.interface.model.quantum_type import QuantumNumeric
5
+
6
+
7
+ class QmodQNumProxy(Symbol, QmodSizedProxy):
8
+ def __new__(cls, name, **assumptions):
9
+ return super().__new__(cls, name, **assumptions)
10
+
11
+ def __init__(self, name: str, quantum_type: QuantumNumeric) -> None:
12
+ super().__init__(quantum_type.size_in_bits)
13
+ self._fraction_digits = quantum_type.fraction_digits
14
+ self._is_signed = quantum_type.is_signed
15
+
16
+ @property
17
+ def fraction_digits(self) -> int:
18
+ return self._fraction_digits
19
+
20
+ @property
21
+ def is_signed(self) -> bool:
22
+ return self._is_signed
@@ -1,16 +1,6 @@
1
- from sympy import Symbol
2
-
3
-
4
- class QmodSizedProxy(Symbol):
5
- def __new__(cls, name, **assumptions):
6
- return super().__new__(cls, name, **assumptions)
7
-
8
- def __init__(self, name: str, size: int) -> None:
1
+ class QmodSizedProxy:
2
+ def __init__(self, size: int) -> None:
9
3
  self._size = size
10
4
 
11
- @property
12
- def size(self) -> int:
13
- return self.args[0]
14
-
15
5
  def __len__(self) -> int:
16
6
  return self._size
@@ -34,35 +34,101 @@ QFT = QuantumFunctionDeclaration.parse_raw(
34
34
  }"""
35
35
  )
36
36
 
37
- STANDARD_QPE = QuantumFunctionDeclaration.parse_raw(
37
+ QPE_FLEXIBLE = QuantumFunctionDeclaration.parse_raw(
38
38
  """{
39
- "name": "standard_qpe",
40
- "param_decls": {
41
- "precision": {
42
- "kind": "int"
39
+ "name": "qpe_flexible",
40
+ "param_decls": {},
41
+ "port_declarations": {
42
+ "phase": {
43
+ "name": "phase",
44
+ "quantum_type": {
45
+ "size": null,
46
+ "kind": "qnum"
47
+ },
48
+ "direction": "inout"
43
49
  }
44
50
  },
51
+ "operand_declarations": {
52
+ "unitary_with_power": {
53
+ "name": "unitary_with_power",
54
+ "positional_arg_declarations": [
55
+ {
56
+ "name": "arg0",
57
+ "classical_type": {
58
+ "kind": "int"
59
+ }
60
+ }
61
+ ],
62
+ "is_list": false
63
+ }
64
+ },
65
+ "positional_arg_declarations": [
66
+ {
67
+ "name": "unitary_with_power",
68
+ "positional_arg_declarations": [
69
+ {
70
+ "name": "arg0",
71
+ "classical_type": {
72
+ "kind": "int"
73
+ }
74
+ }
75
+ ],
76
+ "is_list": false
77
+ },
78
+ {
79
+ "name": "phase",
80
+ "quantum_type": {
81
+ "size": null,
82
+ "kind": "qnum"
83
+ },
84
+ "direction": "inout"
85
+ }
86
+ ]
87
+ }"""
88
+ )
89
+
90
+ QPE = QuantumFunctionDeclaration.parse_raw(
91
+ """{
92
+ "name": "qpe",
93
+ "param_decls": {},
45
94
  "port_declarations": {
46
95
  "phase": {
47
96
  "name": "phase",
48
- "size": {
49
- "expr": "precision"
97
+ "quantum_type": {
98
+ "size": null,
99
+ "kind": "qnum"
50
100
  },
51
101
  "direction": "inout"
52
102
  }
53
103
  },
54
104
  "operand_declarations": {
55
105
  "unitary": {
56
- "name": "unitary"
106
+ "name": "unitary",
107
+ "positional_arg_declarations": [],
108
+ "is_list": false
57
109
  }
58
110
  },
59
- "positional_arg_declarations": []
111
+ "positional_arg_declarations": [
112
+ {
113
+ "name": "unitary",
114
+ "positional_arg_declarations": [],
115
+ "is_list": false
116
+ },
117
+ {
118
+ "name": "phase",
119
+ "quantum_type": {
120
+ "size": null,
121
+ "kind": "qnum"
122
+ },
123
+ "direction": "inout"
124
+ }
125
+ ]
60
126
  }"""
61
127
  )
62
128
 
63
- QPE = QuantumFunctionDeclaration.parse_raw(
129
+ STANDARD_QPE = QuantumFunctionDeclaration.parse_raw(
64
130
  """{
65
- "name": "qpe",
131
+ "name": "standard_qpe",
66
132
  "param_decls": {
67
133
  "precision": {
68
134
  "kind": "int"
@@ -74,7 +140,7 @@ QPE = QuantumFunctionDeclaration.parse_raw(
74
140
  "size": {
75
141
  "expr": "precision"
76
142
  },
77
- "direction": "output"
143
+ "direction": "inout"
78
144
  }
79
145
  },
80
146
  "operand_declarations": {
@@ -450,7 +516,7 @@ PREPARE_INT = QuantumFunctionDeclaration.parse_raw(
450
516
  "out": {
451
517
  "name": "out",
452
518
  "quantum_type": {
453
- "kind": "qint"
519
+ "kind": "qnum"
454
520
  },
455
521
  "direction": "output"
456
522
  }
@@ -711,11 +777,70 @@ FULL_HEA = QuantumFunctionDeclaration.parse_raw(
711
777
  }"""
712
778
  )
713
779
 
780
+ SWAP_TEST = QuantumFunctionDeclaration.parse_raw(
781
+ """{
782
+ "name": "swap_test",
783
+ "param_decls": {},
784
+ "port_declarations": {
785
+ "state1": {
786
+ "name": "state1",
787
+ "quantum_type": {
788
+ "length": null,
789
+ "kind": "qvec"
790
+ },
791
+ "direction": "inout"
792
+ },
793
+ "state2": {
794
+ "name": "state2",
795
+ "quantum_type": {
796
+ "length": null,
797
+ "kind": "qvec"
798
+ },
799
+ "direction": "inout"
800
+ },
801
+ "test": {
802
+ "name": "test",
803
+ "quantum_type": {
804
+ "kind": "qbit"
805
+ },
806
+ "direction": "output"
807
+ }
808
+ },
809
+ "operand_declarations": {},
810
+ "positional_arg_declarations": [
811
+ {
812
+ "name": "state1",
813
+ "quantum_type": {
814
+ "length": null,
815
+ "kind": "qvec"
816
+ },
817
+ "direction": "inout"
818
+ },
819
+ {
820
+ "name": "state2",
821
+ "quantum_type": {
822
+ "length": null,
823
+ "kind": "qvec"
824
+ },
825
+ "direction": "inout"
826
+ },
827
+ {
828
+ "name": "test",
829
+ "quantum_type": {
830
+ "kind": "qbit"
831
+ },
832
+ "direction": "output"
833
+ }
834
+ ]
835
+ }"""
836
+ )
837
+
714
838
  __all__ = [
715
839
  "QFT_STEP",
716
840
  "QFT",
717
- "STANDARD_QPE",
841
+ "QPE_FLEXIBLE",
718
842
  "QPE",
843
+ "STANDARD_QPE",
719
844
  "SINGLE_PAULI",
720
845
  "LINEAR_PAULI_ROTATIONS",
721
846
  "AMPLITUDE_ESTIMATION",
@@ -733,4 +858,5 @@ __all__ = [
733
858
  "QAOA_INIT",
734
859
  "QAOA_PENALTY",
735
860
  "FULL_HEA",
861
+ "SWAP_TEST",
736
862
  ]
@@ -9,7 +9,6 @@ from classiq.interface.model.quantum_function_declaration import (
9
9
  QuantumFunctionDeclaration,
10
10
  QuantumOperandDeclaration,
11
11
  )
12
- from classiq.interface.model.quantum_type import QuantumInteger
13
12
 
14
13
  REPEAT_OPERATOR = QuantumFunctionDeclaration(
15
14
  name="repeat",
@@ -22,6 +21,7 @@ REPEAT_OPERATOR = QuantumFunctionDeclaration(
22
21
  )
23
22
 
24
23
  OPERAND_FIELD_NAME = "operand"
24
+ CTRL_FIELD_NAME = "ctrl"
25
25
 
26
26
 
27
27
  INVERT_OPERATOR = QuantumFunctionDeclaration(
@@ -35,8 +35,8 @@ INVERT_OPERATOR = QuantumFunctionDeclaration(
35
35
  CONTROL_OPERATOR = QuantumFunctionDeclaration(
36
36
  name="control",
37
37
  port_declarations={
38
- "ctrl": PortDeclaration(
39
- name="ctrl",
38
+ CTRL_FIELD_NAME: PortDeclaration(
39
+ name=CTRL_FIELD_NAME,
40
40
  direction=PortDeclarationDirection.Inout,
41
41
  )
42
42
  },
@@ -118,22 +118,6 @@ POWER_OPERATOR = QuantumFunctionDeclaration(
118
118
  },
119
119
  )
120
120
 
121
-
122
- CONTROL_WITH_VALUE_OPERATOR = QuantumFunctionDeclaration(
123
- name="control_with_value",
124
- param_decls={"ctrl_val": Integer()},
125
- port_declarations={
126
- "ctrl": PortDeclaration(
127
- name="ctrl",
128
- quantum_type=QuantumInteger(),
129
- direction=PortDeclarationDirection.Inout,
130
- )
131
- },
132
- operand_declarations={
133
- OPERAND_FIELD_NAME: QuantumOperandDeclaration(name=OPERAND_FIELD_NAME)
134
- },
135
- )
136
-
137
121
  APPLY = QuantumFunctionDeclaration(
138
122
  name="apply",
139
123
  operand_declarations={
@@ -174,7 +158,6 @@ BUILTIN_QUANTUM_OPERATOR_LIST = [
174
158
  SPLIT_OPERATOR,
175
159
  PERMUTE_OPERATOR,
176
160
  POWER_OPERATOR,
177
- CONTROL_WITH_VALUE_OPERATOR,
178
161
  APPLY,
179
162
  COMPUTE,
180
163
  UNCOMPUTE,
@@ -89,9 +89,9 @@ class SynthesisNativeFunctionDefinition(SynthesisQuantumFunctionDeclaration):
89
89
 
90
90
  @pydantic.root_validator
91
91
  def validate_ports(cls, values: Dict[str, Any]) -> Dict[str, Any]:
92
- port_declarations: Optional[
93
- Dict[IOName, SynthesisPortDeclaration]
94
- ] = values.get("port_declarations")
92
+ port_declarations: Optional[Dict[IOName, SynthesisPortDeclaration]] = (
93
+ values.get("port_declarations")
94
+ )
95
95
  if port_declarations is None:
96
96
  return values
97
97
  cls._validate_direction_ports(
@@ -40,9 +40,9 @@ class Constraints(BaseModel, extra=Extra.forbid):
40
40
  )
41
41
  max_depth: Optional[pydantic.PositiveInt] = None
42
42
 
43
- max_gate_count: Dict[
44
- TranspilerBasisGates, pydantic.NonNegativeInt
45
- ] = pydantic.Field(default_factory=lambda: defaultdict(int))
43
+ max_gate_count: Dict[TranspilerBasisGates, pydantic.NonNegativeInt] = (
44
+ pydantic.Field(default_factory=lambda: defaultdict(int))
45
+ )
46
46
 
47
47
  optimization_parameter: OptimizationParameterType = pydantic.Field(
48
48
  default=OptimizationParameter.NO_OPTIMIZATION,
@@ -78,15 +78,17 @@ class Preferences(pydantic.BaseModel, extra=pydantic.Extra.forbid):
78
78
  default=None
79
79
  )
80
80
  machine_precision: PydanticMachinePrecision = MAXIMAL_MACHINE_PRECISION
81
- backend_service_provider: Optional[
82
- Union[Provider, ProviderVendor, str]
83
- ] = pydantic.Field(
84
- default=None, description="Provider company or cloud for the requested backend."
81
+
82
+ backend_service_provider: Optional[Union[Provider, ProviderVendor, str]] = (
83
+ pydantic.Field(
84
+ default=None,
85
+ description="Provider company or cloud for the requested backend.",
86
+ )
85
87
  )
86
- backend_name: Optional[
87
- Union[PydanticBackendName, AllBackendsNameByVendor]
88
- ] = pydantic.Field(
89
- default=None, description="Name of the requested backend or target."
88
+ backend_name: Optional[Union[PydanticBackendName, AllBackendsNameByVendor]] = (
89
+ pydantic.Field(
90
+ default=None, description="Name of the requested backend or target."
91
+ )
90
92
  )
91
93
  custom_hardware_settings: CustomHardwareSettings = pydantic.Field(
92
94
  default_factory=CustomHardwareSettings,
@@ -7,9 +7,9 @@ from classiq.interface.helpers.custom_pydantic_types import PydanticProbabilityF
7
7
 
8
8
 
9
9
  class NoiseProperties(BaseModel):
10
- measurement_bit_flip_probability: Optional[
11
- PydanticProbabilityFloat
12
- ] = pydantic.Field(
13
- default=None,
14
- description="Probability of measuring the wrong value for each qubit.",
10
+ measurement_bit_flip_probability: Optional[PydanticProbabilityFloat] = (
11
+ pydantic.Field(
12
+ default=None,
13
+ description="Probability of measuring the wrong value for each qubit.",
14
+ )
15
15
  )
@@ -84,11 +84,11 @@ class PhaseEstimation(FunctionParams):
84
84
  description="The unitary function parameters.",
85
85
  default_factory=CustomFunction,
86
86
  )
87
- exponentiation_specification: Optional[
88
- ExponentiationSpecification
89
- ] = pydantic.Field(
90
- default=None,
91
- description="The specifications for phase estimation of exponentiation functions.",
87
+ exponentiation_specification: Optional[ExponentiationSpecification] = (
88
+ pydantic.Field(
89
+ default=None,
90
+ description="The specifications for phase estimation of exponentiation functions.",
91
+ )
92
92
  )
93
93
 
94
94
  _output_name: IOName = pydantic.PrivateAttr(
@@ -537,9 +537,11 @@ class SynthesisQuantumFunctionCall(BaseModel):
537
537
  valid_matches: List[Match] = []
538
538
  invalid_expressions: List[str] = []
539
539
  for expression, expression_match in zip(expressions, expression_matches):
540
- invalid_expressions.append(
541
- expression
542
- ) if expression_match is None else valid_matches.append(expression_match)
540
+ (
541
+ invalid_expressions.append(expression)
542
+ if expression_match is None
543
+ else valid_matches.append(expression_match)
544
+ )
543
545
 
544
546
  invalid_slicings: List[str] = []
545
547
  invalid_names: List[str] = []
@@ -81,8 +81,7 @@ class Visitor:
81
81
  class Transformer(Visitor):
82
82
  if TYPE_CHECKING:
83
83
 
84
- def visit(self, node: NodeType) -> Any:
85
- ...
84
+ def visit(self, node: NodeType) -> Any: ...
86
85
 
87
86
  def visit_list(self, node: List[NodeType]) -> List[RetType]:
88
87
  return [self.visit(elem) for elem in node]
@@ -13,7 +13,7 @@ class Provider(StrEnum):
13
13
  IONQ = "IonQ"
14
14
  CLASSIQ = "Classiq"
15
15
  GOOGLE = "Google"
16
- ALICE_AND_BOB = "Alice and Bob"
16
+ ALICE_AND_BOB = "Alice & Bob"
17
17
  OQC = "OQC"
18
18
 
19
19
  @property
@@ -1,9 +1,7 @@
1
- from typing import Any, Dict, List, Optional
1
+ from typing import List
2
2
 
3
3
  import pydantic
4
4
 
5
- from classiq.interface.model.local_variable_declaration import LocalVariableDeclaration
6
- from classiq.interface.model.port_declaration import PortDeclaration
7
5
  from classiq.interface.model.quantum_function_call import ConcreteQuantumStatement
8
6
  from classiq.interface.model.quantum_function_declaration import (
9
7
  QuantumFunctionDeclaration,
@@ -28,12 +26,8 @@ class NativeFunctionDefinition(QuantumFunctionDeclaration):
28
26
  default_factory=list, description="List of function calls to perform."
29
27
  )
30
28
 
31
- local_handles: List[LocalVariableDeclaration] = pydantic.Field(
32
- default_factory=list, description="List of local handles."
33
- )
34
-
35
29
  def validate_body(self) -> None:
36
- handle_validator = HandleValidator(self.port_declarations, self.local_handles)
30
+ handle_validator = HandleValidator(self.port_declarations)
37
31
 
38
32
  for statement in self.body:
39
33
  if isinstance(statement, VariableDeclarationStatement):
@@ -42,19 +36,3 @@ class NativeFunctionDefinition(QuantumFunctionDeclaration):
42
36
  handle_validator.handle_call(statement)
43
37
 
44
38
  handle_validator.report_errored_handles(ClassiqValueError)
45
-
46
- @pydantic.validator("local_handles")
47
- def validate_local_handles(
48
- cls, local_handles: List[LocalVariableDeclaration], values: Dict[str, Any]
49
- ) -> List[LocalVariableDeclaration]:
50
- ports: Optional[Dict[str, PortDeclaration]] = values.get("port_declarations")
51
- if ports is None:
52
- return local_handles
53
-
54
- intersection = {handle.name for handle in local_handles} & ports.keys()
55
- if intersection:
56
- raise ClassiqValueError(
57
- f"The names {intersection} are both local handles and ports"
58
- )
59
-
60
- return local_handles
@@ -12,7 +12,7 @@ from classiq.interface.model.handle_binding import (
12
12
  SubscriptHandleBinding,
13
13
  )
14
14
  from classiq.interface.model.quantum_expressions.quantum_expression import (
15
- QuantumExpressionOperation,
15
+ QuantumAssignmentOperation,
16
16
  )
17
17
  from classiq.interface.model.quantum_type import QuantumBit, QuantumNumeric, QuantumType
18
18
 
@@ -27,7 +27,7 @@ VAR_DOMAIN_ILLEGAL = (
27
27
  )
28
28
 
29
29
 
30
- class AmplitudeLoadingOperation(QuantumExpressionOperation):
30
+ class AmplitudeLoadingOperation(QuantumAssignmentOperation):
31
31
  _result_type: QuantumType = pydantic.PrivateAttr(default_factory=QuantumBit)
32
32
 
33
33
  @property
@@ -12,12 +12,12 @@ from classiq.interface.model.handle_binding import (
12
12
  SubscriptHandleBinding,
13
13
  )
14
14
  from classiq.interface.model.quantum_expressions.quantum_expression import (
15
- QuantumExpressionOperation,
15
+ QuantumAssignmentOperation,
16
16
  )
17
17
  from classiq.interface.model.quantum_type import QuantumType
18
18
 
19
19
 
20
- class ArithmeticOperation(QuantumExpressionOperation):
20
+ class ArithmeticOperation(QuantumAssignmentOperation):
21
21
  inplace_result: bool = pydantic.Field(
22
22
  description="Determines whether the result variable is initialized",
23
23
  )
@@ -0,0 +1,38 @@
1
+ from classiq.exceptions import ClassiqValueError
2
+
3
+
4
+ def min_unsigned_bit_length(number: int) -> int:
5
+ if number < 0:
6
+ raise ClassiqValueError(
7
+ f"Quantum register is not signed but control value " # noqa: B907
8
+ f"'{number}' is negative"
9
+ )
10
+ return 1 if number == 0 else number.bit_length()
11
+
12
+
13
+ def min_signed_bit_length(number: int) -> int:
14
+ pos_val = abs(number)
15
+ is_whole = pos_val & (pos_val - 1) == 0
16
+ if number <= 0 and is_whole:
17
+ return min_unsigned_bit_length(pos_val)
18
+ return min_unsigned_bit_length(pos_val) + 1
19
+
20
+
21
+ def min_bit_length(number: int, is_signed: bool) -> int:
22
+ return (
23
+ min_signed_bit_length(number) if is_signed else min_unsigned_bit_length(number)
24
+ )
25
+
26
+
27
+ def to_twos_complement(value: int, bits: int, is_signed: bool) -> str:
28
+ required_bits = min_bit_length(value, is_signed)
29
+ if bits < required_bits:
30
+ raise ClassiqValueError(
31
+ f"Cannot express '{value}' using {bits} bits: " # noqa: B907
32
+ f"at least {required_bits} bits are required"
33
+ )
34
+ if value >= 0:
35
+ return bin(value)[2:].zfill(bits)[::-1]
36
+ mask = (1 << bits) - 1
37
+ value = (abs(value) ^ mask) + 1
38
+ return bin(value)[:1:-1].rjust(bits, "1")
@@ -35,18 +35,12 @@ class VarRefCollector(ast.NodeVisitor):
35
35
 
36
36
  class QuantumExpressionOperation(QuantumOperation):
37
37
  expression: Expression = pydantic.Field()
38
- result_var: HandleBinding = pydantic.Field(
39
- description="The variable storing the expression result"
40
- )
41
38
  _var_handles: List[HandleBinding] = pydantic.PrivateAttr(
42
39
  default_factory=list,
43
40
  )
44
41
  _var_types: Dict[str, QuantumType] = pydantic.PrivateAttr(
45
42
  default_factory=dict,
46
43
  )
47
- _result_type: Optional[QuantumType] = pydantic.PrivateAttr(
48
- default=None,
49
- )
50
44
 
51
45
  @property
52
46
  def var_handles(self) -> List[HandleBinding]:
@@ -75,15 +69,24 @@ class QuantumExpressionOperation(QuantumOperation):
75
69
  ]:
76
70
  return nameables_to_dict(self.var_handles)
77
71
 
78
- @property
79
- def wiring_outputs(self) -> Mapping[str, HandleBinding]:
80
- return {self.result_name(): self.result_var}
72
+
73
+ class QuantumAssignmentOperation(QuantumExpressionOperation):
74
+ result_var: HandleBinding = pydantic.Field(
75
+ description="The variable storing the expression result"
76
+ )
77
+ _result_type: Optional[QuantumType] = pydantic.PrivateAttr(
78
+ default=None,
79
+ )
81
80
 
82
81
  @property
83
82
  def result_type(self) -> QuantumType:
84
83
  assert self._result_type is not None
85
84
  return self._result_type
86
85
 
86
+ @property
87
+ def wiring_outputs(self) -> Mapping[str, HandleBinding]:
88
+ return {self.result_name(): self.result_var}
89
+
87
90
  @classmethod
88
91
  @abc.abstractmethod
89
92
  def result_name(cls) -> str:
@@ -38,6 +38,7 @@ from classiq.interface.model.quantum_function_declaration import (
38
38
  QuantumFunctionDeclaration,
39
39
  QuantumOperandDeclaration,
40
40
  )
41
+ from classiq.interface.model.quantum_if_operation import QuantumIfOperation
41
42
  from classiq.interface.model.quantum_statement import QuantumOperation
42
43
  from classiq.interface.model.validation_handle import get_unique_handle_names
43
44
  from classiq.interface.model.variable_declaration_statement import (
@@ -356,6 +357,7 @@ ConcreteQuantumStatement = Union[
356
357
  BindOperation,
357
358
  NumericReinterpretationOperation,
358
359
  InplaceBinaryOperation,
360
+ QuantumIfOperation,
359
361
  ]
360
362
 
361
363
 
@@ -405,6 +407,7 @@ QuantumCallable = Union[str, QuantumLambdaFunction]
405
407
  QuantumOperand = Union[QuantumCallable, List[QuantumCallable], LambdaListComprehension]
406
408
 
407
409
  QuantumFunctionCall.update_forward_refs()
410
+ QuantumIfOperation.update_forward_refs(QuantumOperand=QuantumOperand)
408
411
 
409
412
 
410
413
  def get_lambda_defs(operand: QuantumOperand) -> List[QuantumCallable]:
@@ -83,9 +83,9 @@ class QuantumFunctionDeclaration(FunctionDeclaration):
83
83
  default_factory=list
84
84
  )
85
85
 
86
- BUILTIN_FUNCTION_DECLARATIONS: ClassVar[
87
- Dict[str, "QuantumFunctionDeclaration"]
88
- ] = {}
86
+ BUILTIN_FUNCTION_DECLARATIONS: ClassVar[Dict[str, "QuantumFunctionDeclaration"]] = (
87
+ {}
88
+ )
89
89
 
90
90
  @property
91
91
  def input_set(self) -> Set[str]: