classiq 0.75.0__py3-none-any.whl → 0.76.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 (83) hide show
  1. classiq/_internals/api_wrapper.py +36 -0
  2. classiq/analyzer/show_interactive_hack.py +58 -2
  3. classiq/applications/chemistry/chemistry_model_constructor.py +8 -1
  4. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -0
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -4
  6. classiq/applications/qnn/gradients/quantum_gradient.py +3 -5
  7. classiq/applications/qnn/gradients/simple_quantum_gradient.py +2 -2
  8. classiq/applications/qnn/qlayer.py +14 -19
  9. classiq/applications/qnn/types.py +1 -4
  10. classiq/execution/__init__.py +3 -0
  11. classiq/execution/execution_session.py +3 -16
  12. classiq/execution/qnn.py +2 -2
  13. classiq/execution/user_budgets.py +38 -0
  14. classiq/executor.py +7 -19
  15. classiq/interface/_version.py +1 -1
  16. classiq/interface/debug_info/debug_info.py +16 -2
  17. classiq/interface/executor/user_budget.py +56 -0
  18. classiq/interface/generator/application_apis/finance_declarations.py +3 -0
  19. classiq/interface/generator/expressions/atomic_expression_functions.py +3 -0
  20. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +30 -124
  21. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +45 -21
  22. classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
  23. classiq/interface/generator/expressions/proxies/classical/utils.py +12 -11
  24. classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +9 -2
  25. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +4 -1
  26. classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
  27. classiq/interface/generator/functions/classical_type.py +6 -1
  28. classiq/interface/generator/functions/type_name.py +7 -2
  29. classiq/interface/generator/functions/type_qualifier.py +15 -0
  30. classiq/interface/generator/model/preferences/preferences.py +7 -0
  31. classiq/interface/generator/quantum_program.py +5 -19
  32. classiq/interface/helpers/backward_compatibility.py +9 -0
  33. classiq/interface/helpers/datastructures.py +6 -0
  34. classiq/interface/model/port_declaration.py +1 -2
  35. classiq/interface/model/quantum_lambda_function.py +2 -1
  36. classiq/interface/server/routes.py +6 -0
  37. classiq/model_expansions/atomic_expression_functions_defs.py +62 -19
  38. classiq/model_expansions/capturing/captured_vars.py +2 -0
  39. classiq/model_expansions/closure.py +5 -0
  40. classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
  41. classiq/model_expansions/evaluators/parameter_types.py +26 -13
  42. classiq/model_expansions/expression_evaluator.py +1 -1
  43. classiq/model_expansions/generative_functions.py +61 -34
  44. classiq/model_expansions/interpreters/base_interpreter.py +17 -6
  45. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +5 -0
  46. classiq/model_expansions/interpreters/generative_interpreter.py +13 -1
  47. classiq/model_expansions/quantum_operations/allocate.py +6 -1
  48. classiq/model_expansions/quantum_operations/assignment_result_processor.py +219 -20
  49. classiq/model_expansions/quantum_operations/bind.py +54 -30
  50. classiq/model_expansions/quantum_operations/block_evaluator.py +42 -0
  51. classiq/model_expansions/quantum_operations/call_emitter.py +14 -7
  52. classiq/model_expansions/quantum_operations/composite_emitter.py +1 -1
  53. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +23 -9
  54. classiq/model_expansions/quantum_operations/emitter.py +20 -3
  55. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
  56. classiq/model_expansions/scope.py +10 -7
  57. classiq/model_expansions/sympy_conversion/arithmetics.py +18 -0
  58. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +2 -0
  59. classiq/model_expansions/sympy_conversion/sympy_to_python.py +10 -1
  60. classiq/model_expansions/transformers/model_renamer.py +45 -7
  61. classiq/model_expansions/utils/handles_collector.py +1 -1
  62. classiq/model_expansions/visitors/variable_references.py +45 -9
  63. classiq/qmod/builtins/functions/allocation.py +2 -2
  64. classiq/qmod/builtins/functions/arithmetic.py +14 -12
  65. classiq/qmod/builtins/functions/standard_gates.py +23 -23
  66. classiq/qmod/declaration_inferrer.py +19 -7
  67. classiq/qmod/generative.py +9 -1
  68. classiq/qmod/native/expression_to_qmod.py +4 -0
  69. classiq/qmod/native/pretty_printer.py +8 -3
  70. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  71. classiq/qmod/python_classical_type.py +4 -5
  72. classiq/qmod/qmod_constant.py +15 -7
  73. classiq/qmod/qmod_variable.py +7 -1
  74. classiq/qmod/quantum_function.py +19 -6
  75. classiq/qmod/semantics/lambdas.py +6 -2
  76. classiq/qmod/semantics/validation/main_validation.py +17 -4
  77. classiq/qmod/symbolic.py +8 -19
  78. classiq/qmod/symbolic_expr.py +26 -0
  79. classiq/synthesis.py +17 -31
  80. classiq/visualization.py +35 -0
  81. {classiq-0.75.0.dist-info → classiq-0.76.0.dist-info}/METADATA +1 -1
  82. {classiq-0.75.0.dist-info → classiq-0.76.0.dist-info}/RECORD +83 -79
  83. {classiq-0.75.0.dist-info → classiq-0.76.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,56 @@
1
+ import datetime
2
+ from collections import defaultdict
3
+ from typing import Optional
4
+
5
+ import pydantic
6
+ from pydantic import ConfigDict, Field
7
+
8
+ from classiq.interface.helpers.versioned_model import VersionedModel
9
+
10
+
11
+ class UserBudget(VersionedModel):
12
+ provider: str
13
+ currency_code: str
14
+ organization: Optional[str] = Field(default=None)
15
+ available_budget: float
16
+ used_budget: float
17
+ last_allocation_date: datetime.datetime
18
+
19
+ model_config = ConfigDict(extra="ignore")
20
+
21
+
22
+ class UserBudgets(VersionedModel):
23
+ budgets: list[UserBudget] = pydantic.Field(default=[])
24
+
25
+ def print_budgets(self) -> None:
26
+ def format_header() -> str:
27
+ return f"| {'Provider':<20} | {'Available Budget':<18} | {'Used Budget':<18} | {'Currency':<8} |"
28
+
29
+ def format_row(
30
+ provider: str, available: float, used: float, currency: str
31
+ ) -> str:
32
+ return f"| {provider:<20} | {available:<18.0f} | {used:<18.0f} | {currency:<8} |"
33
+
34
+ table_data: dict = defaultdict(
35
+ lambda: {"used": 0.0, "available": 0.0, "currency": "USD"}
36
+ )
37
+
38
+ for budget in self.budgets:
39
+ provider = budget.provider
40
+ table_data[provider]["available"] += budget.available_budget
41
+ table_data[provider]["used"] += budget.used_budget
42
+ table_data[provider]["currency"] = budget.currency_code
43
+
44
+ line = "=" * 77
45
+ print(line) # noqa: T201
46
+ print(format_header()) # noqa: T201
47
+ print(line) # noqa: T201
48
+
49
+ for provider, values in table_data.items():
50
+ print( # noqa: T201
51
+ format_row(
52
+ provider, values["available"], values["used"], values["currency"]
53
+ )
54
+ )
55
+
56
+ print(line) # noqa: T201
@@ -10,6 +10,7 @@ from classiq.interface.generator.functions.port_declaration import (
10
10
  PortDeclarationDirection,
11
11
  )
12
12
  from classiq.interface.generator.functions.type_name import Struct
13
+ from classiq.interface.generator.functions.type_qualifier import TypeQualifier
13
14
  from classiq.interface.model.classical_parameter_declaration import (
14
15
  ClassicalParameterDeclaration,
15
16
  )
@@ -55,11 +56,13 @@ def _generate_finance_function(
55
56
  )
56
57
  ),
57
58
  direction=PortDeclarationDirection.Inout,
59
+ type_qualifier=TypeQualifier.Quantum,
58
60
  ),
59
61
  PortDeclaration(
60
62
  name=OBJECTIVE_PORT_NAME,
61
63
  quantum_type=QuantumBit(),
62
64
  direction=PortDeclarationDirection.Inout,
65
+ type_qualifier=TypeQualifier.Quantum,
63
66
  ),
64
67
  ],
65
68
  )
@@ -15,6 +15,7 @@ SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS = {
15
15
  "molecule_problem_to_hamiltonian",
16
16
  "fock_hamiltonian_problem_to_hamiltonian",
17
17
  "molecule_ground_state_solution_post_process",
18
+ "mod_inverse",
18
19
  }
19
20
 
20
21
  SUPPORTED_CLASSIQ_SYMPY_WRAPPERS = {
@@ -23,6 +24,8 @@ SUPPORTED_CLASSIQ_SYMPY_WRAPPERS = {
23
24
  "BitwiseNot",
24
25
  "BitwiseOr",
25
26
  "LogicalXor",
27
+ "RShift",
28
+ "LShift",
26
29
  }
27
30
 
28
31
  SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS = {
@@ -1,135 +1,41 @@
1
+ import inspect
1
2
  from typing import Any
2
3
 
4
+ import sympy
3
5
 
4
- class AnyClassicalValue:
5
- def __init__(self, expr: str) -> None:
6
- self._expr = expr
6
+ _SYMPY_MEMBERS = [name for name, _ in inspect.getmembers(sympy.Symbol)] + ["precedence"]
7
7
 
8
- def __str__(self) -> str:
9
- return self._expr
10
8
 
11
- def __repr__(self) -> str:
12
- return str(self)
9
+ def subscript_to_str(index: Any) -> str:
10
+ if not isinstance(index, slice):
11
+ return str(index)
12
+ expr = ""
13
+ if index.start is not None:
14
+ expr += str(index.start)
15
+ expr += ":"
16
+ if index.stop is not None:
17
+ expr += str(index.stop)
18
+ if index.step is not None:
19
+ expr += f":{index.step}"
20
+ return expr
13
21
 
14
- def __getitem__(self, item: Any) -> "AnyClassicalValue":
15
- if isinstance(item, slice):
16
- subscript = ""
17
- if item.start is not None:
18
- subscript += str(item.start)
19
- subscript += ":"
20
- if item.stop is not None:
21
- subscript += str(item.stop)
22
- if item.step is not None:
23
- subscript += f":{item.stop}"
24
- item = subscript
25
- return AnyClassicalValue(f"{self}[{item}]")
26
-
27
- @staticmethod
28
- def _binary_op(lhs: Any, rhs: Any, op: str) -> "AnyClassicalValue":
29
- return AnyClassicalValue(f"{lhs} {op} {rhs}")
30
-
31
- @staticmethod
32
- def _unary_op(arg: Any, op: str) -> "AnyClassicalValue":
33
- return AnyClassicalValue(f"{op}({arg})")
34
-
35
- def __add__(self, other: Any) -> "AnyClassicalValue":
36
- return AnyClassicalValue._binary_op(self, other, "+")
37
-
38
- def __sub__(self, other: Any) -> "AnyClassicalValue":
39
- return AnyClassicalValue._binary_op(self, other, "-")
40
-
41
- def __mul__(self, other: Any) -> "AnyClassicalValue":
42
- return AnyClassicalValue._binary_op(self, other, "*")
43
-
44
- def __truediv__(self, other: Any) -> "AnyClassicalValue":
45
- return AnyClassicalValue._binary_op(self, other, "/")
46
-
47
- def __floordiv__(self, other: Any) -> "AnyClassicalValue":
48
- return AnyClassicalValue._binary_op(self, other, "//")
49
-
50
- def __mod__(self, other: Any) -> "AnyClassicalValue":
51
- return AnyClassicalValue._binary_op(self, other, "%")
52
-
53
- def __pow__(self, other: Any) -> "AnyClassicalValue":
54
- return AnyClassicalValue._binary_op(self, other, "**")
55
-
56
- def __lshift__(self, other: Any) -> "AnyClassicalValue":
57
- return AnyClassicalValue._binary_op(self, other, "<<")
58
-
59
- def __rshift__(self, other: Any) -> "AnyClassicalValue":
60
- return AnyClassicalValue._binary_op(self, other, ">>")
61
-
62
- def __and__(self, other: Any) -> "AnyClassicalValue":
63
- return AnyClassicalValue._binary_op(self, other, "&")
64
-
65
- def __xor__(self, other: Any) -> "AnyClassicalValue":
66
- return AnyClassicalValue._binary_op(self, other, "^")
67
-
68
- def __or__(self, other: Any) -> "AnyClassicalValue":
69
- return AnyClassicalValue._binary_op(self, other, "|")
70
-
71
- def __radd__(self, other: Any) -> "AnyClassicalValue":
72
- return AnyClassicalValue._binary_op(other, self, "+")
73
-
74
- def __rsub__(self, other: Any) -> "AnyClassicalValue":
75
- return AnyClassicalValue._binary_op(other, self, "-")
76
22
 
77
- def __rmul__(self, other: Any) -> "AnyClassicalValue":
78
- return AnyClassicalValue._binary_op(other, self, "*")
23
+ class AnyClassicalValue(sympy.Symbol):
79
24
 
80
- def __rtruediv__(self, other: Any) -> "AnyClassicalValue":
81
- return AnyClassicalValue._binary_op(other, self, "/")
25
+ is_commutative = None
26
+ is_infinite = None
27
+ is_finite = None
28
+ is_extended_real = None
82
29
 
83
- def __rfloordiv__(self, other: Any) -> "AnyClassicalValue":
84
- return AnyClassicalValue._binary_op(other, self, "//")
85
-
86
- def __rmod__(self, other: Any) -> "AnyClassicalValue":
87
- return AnyClassicalValue._binary_op(other, self, "%")
88
-
89
- def __rpow__(self, other: Any) -> "AnyClassicalValue":
90
- return AnyClassicalValue._binary_op(other, self, "**")
91
-
92
- def __rlshift__(self, other: Any) -> "AnyClassicalValue":
93
- return AnyClassicalValue._binary_op(other, self, "<<")
94
-
95
- def __rrshift__(self, other: Any) -> "AnyClassicalValue":
96
- return AnyClassicalValue._binary_op(other, self, ">>")
97
-
98
- def __rand__(self, other: Any) -> "AnyClassicalValue":
99
- return AnyClassicalValue._binary_op(other, self, "&")
100
-
101
- def __rxor__(self, other: Any) -> "AnyClassicalValue":
102
- return AnyClassicalValue._binary_op(other, self, "^")
103
-
104
- def __ror__(self, other: Any) -> "AnyClassicalValue":
105
- return AnyClassicalValue._binary_op(other, self, "|")
106
-
107
- def __lt__(self, other: Any) -> "AnyClassicalValue":
108
- return AnyClassicalValue._binary_op(self, other, "<")
109
-
110
- def __le__(self, other: Any) -> "AnyClassicalValue":
111
- return AnyClassicalValue._binary_op(self, other, "<=")
112
-
113
- def __eq__(self, other: Any) -> "AnyClassicalValue": # type:ignore[override]
114
- return AnyClassicalValue._binary_op(self, other, "==")
115
-
116
- def __ne__(self, other: Any) -> "AnyClassicalValue": # type: ignore[override]
117
- return AnyClassicalValue._binary_op(self, other, "!=")
118
-
119
- def __gt__(self, other: Any) -> "AnyClassicalValue":
120
- return AnyClassicalValue._binary_op(self, other, ">")
121
-
122
- def __ge__(self, other: Any) -> "AnyClassicalValue":
123
- return AnyClassicalValue._binary_op(self, other, ">=")
124
-
125
- def __neg__(self) -> "AnyClassicalValue":
126
- return AnyClassicalValue._unary_op(self, "-")
127
-
128
- def __pos__(self) -> "AnyClassicalValue":
129
- return AnyClassicalValue._unary_op(self, "+")
30
+ def __getitem__(self, item: Any) -> "AnyClassicalValue":
31
+ if isinstance(item, slice):
32
+ return AnyClassicalValue(f"{self}[{subscript_to_str(item)}]")
33
+ return AnyClassicalValue(f"do_subscript({self}, {item})")
130
34
 
131
- def __abs__(self) -> "AnyClassicalValue":
132
- return AnyClassicalValue._unary_op(self, "abs")
35
+ def __getattribute__(self, attr: str) -> Any:
36
+ if attr.startswith("_") or attr in _SYMPY_MEMBERS:
37
+ return super().__getattribute__(attr)
38
+ return AnyClassicalValue(f"get_field({self}, '{attr}')")
133
39
 
134
- def __invert__(self) -> "AnyClassicalValue":
135
- return AnyClassicalValue._unary_op(self, "~")
40
+ def __len__(self) -> "AnyClassicalValue":
41
+ return self.len
@@ -1,11 +1,16 @@
1
1
  from collections.abc import Mapping
2
- from typing import TYPE_CHECKING, Union
2
+ from typing import TYPE_CHECKING, Any, Union
3
3
 
4
+ import sympy
4
5
  from sympy import Integer
6
+ from typing_extensions import TypeGuard
5
7
 
6
8
  from classiq.interface.exceptions import ClassiqIndexError
7
9
  from classiq.interface.generator.expressions.expression import Expression
8
10
  from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
11
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
12
+ AnyClassicalValue,
13
+ )
9
14
  from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
10
15
  ClassicalProxy,
11
16
  )
@@ -22,9 +27,20 @@ if TYPE_CHECKING:
22
27
  )
23
28
 
24
29
 
30
+ def _is_int(val: Any) -> TypeGuard[Union[int, sympy.Basic]]:
31
+ if isinstance(val, AnyClassicalValue):
32
+ return False
33
+ if isinstance(val, sympy.Basic):
34
+ return val.is_Number
35
+ return isinstance(val, int)
36
+
37
+
25
38
  class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
26
39
  def __init__(
27
- self, handle: HandleBinding, element_type: "ConcreteClassicalType", length: int
40
+ self,
41
+ handle: HandleBinding,
42
+ element_type: "ConcreteClassicalType",
43
+ length: "ExpressionValue",
28
44
  ) -> None:
29
45
  super().__init__(handle)
30
46
  self._element_type = element_type
@@ -39,39 +55,47 @@ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
39
55
  return "Array"
40
56
 
41
57
  @property
42
- def length(self) -> int:
58
+ def length(self) -> "ExpressionValue":
43
59
  return self._length
44
60
 
45
- def __getitem__(self, key: Union[slice, int, Integer]) -> ClassicalProxy:
61
+ def __getitem__(
62
+ self, key: Union[slice, int, Integer, ClassicalProxy]
63
+ ) -> ClassicalProxy:
46
64
  return (
47
65
  self._get_slice(key) if isinstance(key, slice) else self._get_subscript(key)
48
66
  )
49
67
 
50
68
  def _get_slice(self, slice_: slice) -> ClassicalProxy:
51
- start = int(slice_.start)
52
- stop = int(slice_.stop)
53
- if start >= stop:
54
- raise ClassiqIndexError("Array slice has non-positive length")
55
- if start < 0 or stop > self._length:
56
- raise ClassiqIndexError("Array slice is out of bounds")
69
+ start_ = slice_.start
70
+ stop_ = slice_.stop
71
+ if _is_int(start_) and _is_int(stop_):
72
+ start = int(start_)
73
+ stop = int(stop_)
74
+ if start >= stop:
75
+ raise ClassiqIndexError("Array slice has non-positive length")
76
+ if start < 0 or (isinstance(self._length, int) and stop > self._length):
77
+ raise ClassiqIndexError("Array slice is out of bounds")
57
78
  return ClassicalArrayProxy(
58
79
  SlicedHandleBinding(
59
80
  base_handle=self.handle,
60
- start=Expression(expr=str(start)),
61
- end=Expression(expr=str(stop)),
81
+ start=Expression(expr=str(start_)),
82
+ end=Expression(expr=str(stop_)),
62
83
  ),
63
84
  self._element_type,
64
- stop - start,
85
+ stop_ - start_,
65
86
  )
66
87
 
67
- def _get_subscript(self, index_: Union[int, Integer]) -> ClassicalProxy:
68
- index = int(index_)
69
- if index < 0:
70
- raise ClassiqIndexError(
71
- "Array index is out of bounds (negative indices are not supported)"
72
- )
73
- if index >= self._length:
74
- raise ClassiqIndexError("Array index is out of bounds")
88
+ def _get_subscript(
89
+ self, index_: Union[int, Integer, ClassicalProxy]
90
+ ) -> ClassicalProxy:
91
+ if _is_int(index_):
92
+ index = int(index_)
93
+ if index < 0:
94
+ raise ClassiqIndexError(
95
+ "Array index is out of bounds (negative indices are not supported)"
96
+ )
97
+ if isinstance(self._length, int) and index >= self._length:
98
+ raise ClassiqIndexError("Array index is out of bounds")
75
99
  return self._element_type.get_classical_proxy(
76
100
  SubscriptHandleBinding(
77
101
  base_handle=self._handle, index=Expression(expr=str(index_))
@@ -25,6 +25,13 @@ class QmodStructInstance:
25
25
  def fields(self) -> Mapping[str, Any]:
26
26
  return types.MappingProxyType(self._fields)
27
27
 
28
+ def __getattr__(self, item: str) -> Any:
29
+ if item == "_fields":
30
+ return super().__getattribute__(item)
31
+ if item in self._fields:
32
+ return self._fields[item]
33
+ return super().__getattribute__(item)
34
+
28
35
  def __str__(self) -> str:
29
36
  return repr(self)
30
37
 
@@ -13,6 +13,7 @@ from classiq.interface.generator.expressions.proxies.classical.classical_struct_
13
13
  )
14
14
  from classiq.interface.generator.functions.classical_type import (
15
15
  ClassicalArray,
16
+ ClassicalList,
16
17
  ClassicalType,
17
18
  )
18
19
  from classiq.interface.generator.functions.type_name import Struct
@@ -20,15 +21,15 @@ from classiq.interface.generator.functions.type_name import Struct
20
21
 
21
22
  def get_proxy_type(proxy: ClassicalProxy) -> ClassicalType:
22
23
  if isinstance(proxy, ClassicalScalarProxy):
23
- classical_type = proxy._classical_type
24
- elif isinstance(proxy, ClassicalArrayProxy):
25
- classical_type = ClassicalArray(
26
- element_type=proxy._element_type, size=proxy.length
27
- )
28
- elif isinstance(proxy, ClassicalStructProxy):
24
+ return proxy._classical_type
25
+ if isinstance(proxy, ClassicalArrayProxy):
26
+ if not isinstance(proxy.length, int):
27
+ return ClassicalList(element_type=proxy._element_type)
28
+ return ClassicalArray(element_type=proxy._element_type, size=proxy.length)
29
+ if isinstance(proxy, ClassicalStructProxy):
29
30
  classical_type = Struct(name=proxy._decl.name)
30
- else:
31
- raise ClassiqInternalExpansionError(
32
- f"Unrecognized classical proxy {type(proxy).__name__}"
33
- )
34
- return classical_type
31
+ classical_type.set_classical_struct_decl(proxy._decl)
32
+ return classical_type
33
+ raise ClassiqInternalExpansionError(
34
+ f"Unrecognized classical proxy {type(proxy).__name__}"
35
+ )
@@ -1,7 +1,8 @@
1
1
  from collections.abc import Mapping
2
- from typing import Any
2
+ from typing import Any, Optional
3
3
 
4
4
  from sympy import Symbol
5
+ from typing_extensions import Self
5
6
 
6
7
  from classiq.interface.exceptions import ClassiqValueError
7
8
  from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
@@ -12,11 +13,17 @@ from classiq.interface.model.handle_binding import HandleBinding
12
13
 
13
14
  class QmodQScalarProxy(Symbol, QmodSizedProxy):
14
15
  def __new__(cls, handle: HandleBinding, **assumptions: bool) -> "QmodQScalarProxy":
15
- return super().__new__(cls, str(handle), **assumptions)
16
+ return super().__new__(cls, handle.qmod_expr, **assumptions)
16
17
 
17
18
  def __init__(self, handle: HandleBinding, size: int) -> None:
18
19
  super().__init__(handle, size)
19
20
 
21
+ def __copy__(self) -> Self:
22
+ return self
23
+
24
+ def __deepcopy__(self, memo: Optional[dict]) -> Self:
25
+ return self
26
+
20
27
 
21
28
  class QmodQBitProxy(QmodQScalarProxy):
22
29
  def __init__(self, handle: HandleBinding) -> None:
@@ -15,7 +15,10 @@ class QmodSizedProxy:
15
15
  return self._size
16
16
 
17
17
  def __str__(self) -> str:
18
- return str(self.handle)
18
+ return self.handle.qmod_expr
19
+
20
+ def __repr__(self) -> str:
21
+ return str(self)
19
22
 
20
23
  @property
21
24
  def type_name(self) -> str:
@@ -33,6 +33,7 @@ MATHEMATICAL_FUNCTIONS: list[str] = [
33
33
  "Max",
34
34
  "Min",
35
35
  "mod_inverse",
36
+ "Mod",
36
37
  ]
37
38
  SPECIAL_FUNCTIONS: list[str] = [
38
39
  "erf",
@@ -6,6 +6,9 @@ from typing_extensions import Self
6
6
 
7
7
  from classiq.interface.ast_node import HashableASTNode
8
8
  from classiq.interface.generator.expressions.expression import Expression
9
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
10
+ AnyClassicalValue,
11
+ )
9
12
  from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
10
13
  ClassicalArrayProxy,
11
14
  )
@@ -94,7 +97,9 @@ class ClassicalList(ClassicalType):
94
97
  return values_with_discriminator(values, "kind", "list")
95
98
 
96
99
  def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
97
- raise NotImplementedError
100
+ return ClassicalArrayProxy(
101
+ handle, self.element_type, AnyClassicalValue(f"get_field({handle}, 'len')")
102
+ )
98
103
 
99
104
  @property
100
105
  def expressions(self) -> list[Expression]:
@@ -9,6 +9,9 @@ from classiq.interface.generator.expressions.expression import Expression
9
9
  from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
10
10
  ClassicalProxy,
11
11
  )
12
+ from classiq.interface.generator.expressions.proxies.classical.classical_scalar_proxy import (
13
+ ClassicalScalarProxy,
14
+ )
12
15
  from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
13
16
  ClassicalStructProxy,
14
17
  )
@@ -109,8 +112,10 @@ class TypeName(ClassicalType, QuantumType):
109
112
  self._classical_struct_decl = decl
110
113
 
111
114
  def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
112
- if self._classical_struct_decl is None:
113
- raise ClassiqExpansionError(f"Type {self.name!r} is undefined")
115
+ if self.is_enum:
116
+ return ClassicalScalarProxy(handle, self)
117
+ if TYPE_CHECKING:
118
+ assert self._classical_struct_decl is not None
114
119
  return ClassicalStructProxy(handle, self._classical_struct_decl)
115
120
 
116
121
  @property
@@ -1,7 +1,22 @@
1
1
  from classiq.interface.enum_utils import StrEnum
2
+ from classiq.interface.exceptions import ClassiqInternalExpansionError
2
3
 
3
4
 
4
5
  class TypeQualifier(StrEnum):
5
6
  Const = "const"
6
7
  QFree = "qfree"
7
8
  Quantum = "quantum"
9
+ Inferred = "inferred"
10
+
11
+ @staticmethod
12
+ def and_(first: "TypeQualifier", second: "TypeQualifier") -> "TypeQualifier":
13
+ if second is TypeQualifier.Inferred:
14
+ raise ClassiqInternalExpansionError
15
+ if first is TypeQualifier.Quantum or second is TypeQualifier.Quantum:
16
+ return TypeQualifier.Quantum
17
+ elif first is TypeQualifier.QFree or second is TypeQualifier.QFree:
18
+ return TypeQualifier.QFree
19
+ else:
20
+ if first is not TypeQualifier.Const and second is not TypeQualifier.Const:
21
+ raise ClassiqInternalExpansionError("Unexpected type qualifiers")
22
+ return TypeQualifier.Const
@@ -102,6 +102,13 @@ class Preferences(pydantic.BaseModel, extra="forbid"):
102
102
  debug_mode (bool): If `True`, debug information is added to the
103
103
  synthesized result, potentially slowing down the synthesis. Useful for
104
104
  executing interactive algorithms. Defaults to `True`.
105
+ optimization_level (OptimizationLevel) : The optimization level used during synthesis (0-3);
106
+ determines the trade-off between synthesis speed and the quality of the results. Defaults to 3.
107
+ OptimizationLevel Options:
108
+ - NONE = 0
109
+ - LIGHT = 1
110
+ - MEDIUM = 2
111
+ - HIGH = 3
105
112
  output_format (List[QuantumFormat]): Lists the output format(s)
106
113
  for the quantum circuit. Defaults to `[QuantumFormat.QASM]`.
107
114
  `QuantumFormat` Options:
@@ -1,11 +1,9 @@
1
1
  import uuid
2
2
  from datetime import datetime, timezone
3
3
  from pathlib import Path
4
- from typing import Any, Optional, Union
4
+ from typing import Optional, Union
5
5
 
6
6
  import pydantic
7
- from pydantic import model_validator
8
- from pydantic_core.core_schema import ValidationInfo
9
7
  from typing_extensions import TypeAlias
10
8
 
11
9
  from classiq.interface.compression_utils import decompress
@@ -39,8 +37,6 @@ from classiq.interface.ide.visual_model import CircuitMetrics
39
37
  RegisterName: TypeAlias = str
40
38
  InitialConditions: TypeAlias = dict[RegisterName, int]
41
39
 
42
- OMIT_DEBUG_INFO_FLAG = "omit_debug_info"
43
-
44
40
 
45
41
  class TranspiledCircuitData(CircuitCodeInterface):
46
42
  depth: int
@@ -77,18 +73,8 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
77
73
  program_id: str = pydantic.Field(default_factory=get_uuid_as_str)
78
74
  execution_primitives_input: Optional[PrimitivesInput] = pydantic.Field(default=None)
79
75
 
80
- @model_validator(mode="before")
81
- @classmethod
82
- def remove_debug_info(
83
- cls, data: dict[str, Any], info: ValidationInfo
84
- ) -> dict[str, Any]:
85
- if (
86
- isinstance(data, dict)
87
- and info.context is not None
88
- and info.context.get(OMIT_DEBUG_INFO_FLAG, False)
89
- ):
90
- data.pop("debug_info", None)
91
- return data
76
+ def __str__(self) -> str:
77
+ return self.model_dump_json(indent=2)
92
78
 
93
79
  def _hardware_agnostic_program_code(self) -> CodeAndSyntax:
94
80
  circuit_code = self.program_circuit.get_code_by_priority()
@@ -177,7 +163,7 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
177
163
  file.write(self.model_dump_json(indent=4))
178
164
 
179
165
  @classmethod
180
- def from_qprog(cls, qprog: str) -> "QuantumProgram":
166
+ def from_qprog(cls, qprog: "QuantumProgram") -> "QuantumProgram":
181
167
  """
182
168
  Creates a `QuantumProgram` instance from a raw quantum program string.
183
169
 
@@ -187,7 +173,7 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
187
173
  Returns:
188
174
  QuantumProgram: The `QuantumProgram` instance.
189
175
  """
190
- return cls.model_validate_json(qprog)
176
+ return qprog
191
177
 
192
178
  @property
193
179
  def _can_use_transpiled_code(self) -> bool:
@@ -0,0 +1,9 @@
1
+ import sys
2
+ from typing import Any
3
+
4
+ if sys.version_info[0:2] >= (3, 10):
5
+ zip_strict = zip
6
+ else:
7
+
8
+ def zip_strict(*iterables: Any, strict: bool = False) -> zip:
9
+ return zip(*iterables)
@@ -17,6 +17,12 @@ class LenList(list):
17
17
  def len(self) -> int:
18
18
  return len(self)
19
19
 
20
+ def __getitem__(self, item: Any) -> Any:
21
+ res = super().__getitem__(item)
22
+ if isinstance(item, slice):
23
+ res = type(self)(res)
24
+ return res
25
+
20
26
 
21
27
  def get_sdk_compatible_python_object(obj: Any) -> Any:
22
28
  if isinstance(obj, list):
@@ -16,8 +16,7 @@ from classiq.interface.model.parameter import Parameter
16
16
  class AnonPortDeclaration(Parameter):
17
17
  quantum_type: ConcreteQuantumType
18
18
  direction: PortDeclarationDirection
19
- # TODO remove default after BWC-breaking version
20
- type_qualifier: TypeQualifier = pydantic.Field(default=TypeQualifier.Quantum)
19
+ type_qualifier: TypeQualifier
21
20
  kind: Literal["PortDeclaration"]
22
21
 
23
22
  @pydantic.model_validator(mode="before")