classiq 0.89.0__py3-none-any.whl → 0.90.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 (86) hide show
  1. classiq/__init__.py +1 -0
  2. classiq/_internals/api_wrapper.py +16 -32
  3. classiq/analyzer/show_interactive_hack.py +26 -1
  4. classiq/applications/chemistry/chemistry_model_constructor.py +14 -2
  5. classiq/applications/combinatorial_helpers/pyomo_utils.py +9 -6
  6. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -2
  7. classiq/applications/combinatorial_optimization/combinatorial_problem.py +16 -8
  8. classiq/evaluators/classical_expression.py +63 -41
  9. classiq/evaluators/control.py +31 -52
  10. classiq/evaluators/expression_evaluator.py +8 -4
  11. classiq/evaluators/parameter_types.py +200 -104
  12. classiq/evaluators/qmod_annotated_expression.py +3 -1
  13. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +12 -37
  14. classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +8 -17
  15. classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +1 -1
  16. classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +7 -1
  17. classiq/evaluators/qmod_node_evaluators/name_evaluation.py +0 -1
  18. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +9 -1
  19. classiq/evaluators/qmod_node_evaluators/utils.py +33 -0
  20. classiq/evaluators/qmod_type_inference/classical_type_inference.py +4 -7
  21. classiq/interface/_version.py +1 -1
  22. classiq/interface/analyzer/analysis_params.py +1 -25
  23. classiq/interface/analyzer/result.py +4 -0
  24. classiq/interface/chemistry/ground_state_problem.py +16 -2
  25. classiq/interface/executor/optimizer_preferences.py +0 -112
  26. classiq/interface/generator/application_apis/chemistry_declarations.py +3 -1
  27. classiq/interface/generator/arith/arithmetic_expression_validator.py +2 -7
  28. classiq/interface/generator/expressions/evaluated_expression.py +3 -13
  29. classiq/interface/generator/expressions/expression_types.py +8 -22
  30. classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +2 -2
  31. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +1 -2
  32. classiq/interface/generator/functions/concrete_types.py +1 -1
  33. classiq/interface/generator/generated_circuit_data.py +4 -0
  34. classiq/interface/generator/preferences/qasm_to_qmod_params.py +14 -0
  35. classiq/interface/helpers/model_normalizer.py +0 -6
  36. classiq/interface/ide/visual_model.py +1 -0
  37. classiq/interface/model/handle_binding.py +1 -1
  38. classiq/interface/model/port_declaration.py +2 -1
  39. classiq/interface/model/quantum_expressions/arithmetic_operation.py +16 -12
  40. classiq/interface/model/quantum_type.py +1 -1
  41. classiq/interface/server/routes.py +2 -3
  42. classiq/model_expansions/atomic_expression_functions_defs.py +4 -22
  43. classiq/model_expansions/capturing/captured_vars.py +7 -3
  44. classiq/model_expansions/closure.py +8 -0
  45. classiq/model_expansions/interpreters/base_interpreter.py +84 -22
  46. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +1 -1
  47. classiq/model_expansions/interpreters/generative_interpreter.py +7 -5
  48. classiq/model_expansions/quantum_operations/allocate.py +92 -21
  49. classiq/model_expansions/quantum_operations/assignment_result_processor.py +28 -27
  50. classiq/model_expansions/quantum_operations/call_emitter.py +32 -26
  51. classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -2
  52. classiq/model_expansions/quantum_operations/emitter.py +39 -69
  53. classiq/model_expansions/quantum_operations/expression_evaluator.py +13 -2
  54. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -5
  55. classiq/model_expansions/quantum_operations/variable_decleration.py +16 -11
  56. classiq/model_expansions/scope.py +36 -29
  57. classiq/model_expansions/scope_initialization.py +3 -6
  58. classiq/model_expansions/sympy_conversion/sympy_to_python.py +6 -2
  59. classiq/model_expansions/transformers/model_renamer.py +35 -64
  60. classiq/model_expansions/transformers/type_modifier_inference.py +6 -6
  61. classiq/model_expansions/visitors/boolean_expression_transformers.py +7 -31
  62. classiq/model_expansions/visitors/symbolic_param_inference.py +9 -3
  63. classiq/open_library/functions/state_preparation.py +3 -3
  64. classiq/qmod/builtins/functions/allocation.py +8 -8
  65. classiq/qmod/builtins/functions/arithmetic.py +1 -1
  66. classiq/qmod/builtins/functions/chemistry.py +64 -0
  67. classiq/qmod/builtins/functions/exponentiation.py +7 -13
  68. classiq/qmod/builtins/functions/qsvm.py +1 -1
  69. classiq/qmod/builtins/operations.py +38 -10
  70. classiq/qmod/generative.py +2 -4
  71. classiq/qmod/native/pretty_printer.py +1 -1
  72. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  73. classiq/qmod/qmod_constant.py +1 -1
  74. classiq/qmod/qmod_parameter.py +2 -2
  75. classiq/qmod/qmod_variable.py +15 -15
  76. classiq/qmod/quantum_expandable.py +1 -1
  77. classiq/synthesis.py +37 -1
  78. classiq/visualization.py +1 -1
  79. {classiq-0.89.0.dist-info → classiq-0.90.0.dist-info}/METADATA +1 -1
  80. {classiq-0.89.0.dist-info → classiq-0.90.0.dist-info}/RECORD +81 -85
  81. classiq/evaluators/arg_type_match.py +0 -168
  82. classiq/evaluators/classical_type_inference.py +0 -121
  83. classiq/interface/combinatorial_optimization/optimization_problem.py +0 -17
  84. classiq/interface/combinatorial_optimization/result.py +0 -9
  85. classiq/model_expansions/transformers/ast_renamer.py +0 -26
  86. {classiq-0.89.0.dist-info → classiq-0.90.0.dist-info}/WHEEL +0 -0
classiq/__init__.py CHANGED
@@ -52,6 +52,7 @@ from classiq.qmod import * # noqa: F403
52
52
  from classiq.qmod import __all__ as _qmod_all
53
53
  from classiq.quantum_program import ExecutionParams, assign_parameters, transpile
54
54
  from classiq.synthesis import (
55
+ qasm_to_qmod,
55
56
  quantum_program_from_qasm,
56
57
  quantum_program_from_qasm_async,
57
58
  set_constraints,
@@ -9,7 +9,7 @@ import classiq.interface.executor.execution_result
9
9
  import classiq.interface.pyomo_extension
10
10
  from classiq.interface.analyzer import analysis_params, result as analysis_result
11
11
  from classiq.interface.analyzer.analysis_params import AnalysisRBParams
12
- from classiq.interface.analyzer.result import GraphStatus
12
+ from classiq.interface.analyzer.result import GraphStatus, QmodCode
13
13
  from classiq.interface.chemistry import ground_state_problem, operator
14
14
  from classiq.interface.enum_utils import StrEnum
15
15
  from classiq.interface.exceptions import ClassiqAPIError, ClassiqValueError
@@ -23,13 +23,14 @@ from classiq.interface.execution.iqcc import (
23
23
  IQCCProbeAuthResponse,
24
24
  )
25
25
  from classiq.interface.execution.primitives import PrimitivesInput
26
- from classiq.interface.executor import execution_request, quantum_program_params
26
+ from classiq.interface.executor import execution_request
27
27
  from classiq.interface.executor.quantum_program_params import (
28
28
  ParameterAssignmentsParams,
29
29
  TranspilationParams,
30
30
  )
31
31
  from classiq.interface.executor.user_budget import UserBudget
32
32
  from classiq.interface.generator import quantum_program as generator_result
33
+ from classiq.interface.generator.preferences.qasm_to_qmod_params import QasmToQmodParams
33
34
  from classiq.interface.hardware import HardwareInformation, Provider
34
35
  from classiq.interface.ide.visual_model import ProgramVisualModel
35
36
  from classiq.interface.jobs import JobDescription, JobID, JSONObject
@@ -169,6 +170,19 @@ class ApiWrapper:
169
170
  http_client,
170
171
  )
171
172
 
173
+ @classmethod
174
+ async def call_qasm_to_qmod_task(
175
+ cls,
176
+ params: QasmToQmodParams,
177
+ http_client: Optional[httpx.AsyncClient] = None,
178
+ ) -> QmodCode:
179
+ return await cls._call_job_and_poll(
180
+ routes.QASM_TO_QMOD_FULL_PATH,
181
+ params,
182
+ QmodCode,
183
+ http_client,
184
+ )
185
+
172
186
  @classmethod
173
187
  async def call_get_visual_model(
174
188
  cls,
@@ -239,36 +253,6 @@ class ApiWrapper:
239
253
  exclude={"debug_info"},
240
254
  )
241
255
 
242
- @classmethod
243
- async def transpile_quantum_program(
244
- cls,
245
- params: quantum_program_params.TranspilationParams,
246
- http_client: Optional[httpx.AsyncClient] = None,
247
- ) -> generator_result.QuantumProgram:
248
- data = await cls._call_task_pydantic(
249
- http_method=HTTPMethod.POST,
250
- url=routes.TRANSPILATION_FULL_PATH,
251
- model=params,
252
- http_client=http_client,
253
- exclude={"debug_info"},
254
- )
255
- return generator_result.QuantumProgram.model_validate(data)
256
-
257
- @classmethod
258
- async def assign_parameters_quantum_program(
259
- cls,
260
- params: quantum_program_params.ParameterAssignmentsParams,
261
- http_client: Optional[httpx.AsyncClient] = None,
262
- ) -> generator_result.QuantumProgram:
263
- data = await cls._call_task_pydantic(
264
- http_method=HTTPMethod.POST,
265
- url=routes.ASSIGN_PARAMETERS_FULL_PATH,
266
- model=params,
267
- http_client=http_client,
268
- exclude={"debug_info"},
269
- )
270
- return generator_result.QuantumProgram.model_validate(data)
271
-
272
256
  @classmethod
273
257
  async def call_execute_execution_input(
274
258
  cls,
@@ -1,3 +1,4 @@
1
+ import json
1
2
  import os
2
3
  import subprocess
3
4
  import webbrowser
@@ -19,6 +20,7 @@ VisualizationRenderer = Callable[[DataID, QuantumProgram], Awaitable[None]]
19
20
 
20
21
  # In Classiq Studio (openvscode env) we use this command to open files
21
22
  VSCODE_COMMAND = "code"
23
+ MODEL_SIZE_THRESHOLD = 0.5 * 1024 * 1024 # 0.5MiB
22
24
 
23
25
 
24
26
  def is_classiq_studio() -> bool:
@@ -54,15 +56,37 @@ async def editor_renderer(data_id: DataID, circuit: QuantumProgram) -> None:
54
56
  subprocess.run([VSCODE_COMMAND, file.name])
55
57
 
56
58
 
59
+ def is_large_file_content(value: str) -> bool:
60
+ if len(value) > MODEL_SIZE_THRESHOLD:
61
+ # Skip encoding when it already exceeds the threshold
62
+ return True
63
+ return len(value.encode("utf-8", errors="ignore")) > MODEL_SIZE_THRESHOLD
64
+
65
+
57
66
  async def notebook_renderer(data_id: DataID, circuit: QuantumProgram) -> None:
58
67
  from IPython.display import display # type: ignore[import]
59
68
 
60
69
  visual_model = await visualize_async(data_id)
61
70
  app_url = get_app_url(data_id, circuit)
71
+
72
+ # In case the visual model is large, pass it as transient data,
73
+ # so that it won't be saved into the notebook as cell output (performance concern).
74
+ #
75
+ # For the data argument, provide only "program_id", so the renderer could still
76
+ # retrieve visualization from the API (via extension host).
77
+ # This will happen on further notebook reload - when the transient data is already
78
+ # lost, but the cell hasn't yet been executed.
79
+ if is_large_file_content(visual_model):
80
+ data_payload = json.dumps({"program_id": data_id.id})
81
+ transient_payload = {"visual_model": visual_model}
82
+ else:
83
+ data_payload = visual_model
84
+ transient_payload = None
85
+
62
86
  display(
63
87
  {
64
88
  # Attempt to handle by notebook renderer from Classiq vscode extension
65
- "application/vnd.classiq+qviz": visual_model,
89
+ "application/vnd.classiq+qviz": data_payload,
66
90
  # Fallback to IDE link display when no extension available.
67
91
  # Shouldn't normally happen.
68
92
  # Otherwise, is_classiq_studio detection is not correct.
@@ -72,6 +96,7 @@ async def notebook_renderer(data_id: DataID, circuit: QuantumProgram) -> None:
72
96
  metadata={
73
97
  "url": app_url,
74
98
  },
99
+ transient=transient_payload,
75
100
  )
76
101
 
77
102
 
@@ -1,3 +1,4 @@
1
+ import warnings
1
2
  from collections.abc import Mapping
2
3
  from typing import Optional, cast
3
4
 
@@ -11,7 +12,7 @@ from classiq.interface.chemistry.ground_state_problem import (
11
12
  MoleculeProblem,
12
13
  )
13
14
  from classiq.interface.chemistry.molecule import Atom
14
- from classiq.interface.exceptions import ClassiqError
15
+ from classiq.interface.exceptions import ClassiqDeprecationWarning, ClassiqError
15
16
  from classiq.interface.generator.expressions.expression import Expression
16
17
  from classiq.interface.generator.function_params import IOName
17
18
  from classiq.interface.generator.functions.classical_type import (
@@ -454,7 +455,7 @@ def _get_chemistry_quantum_main(
454
455
  body.append(
455
456
  Allocate(
456
457
  size=Expression(
457
- expr=f"get_field(get_field({_get_problem_to_hamiltonian_name(chemistry_problem)}({_convert_library_problem_to_qmod_problem(chemistry_problem)})[0], 'pauli'), 'len')"
458
+ expr=f"{_get_problem_to_hamiltonian_name(chemistry_problem)}({_convert_library_problem_to_qmod_problem(chemistry_problem)})[0].pauli.len"
458
459
  ),
459
460
  target=HandleBinding(name="qbv"),
460
461
  ),
@@ -502,6 +503,17 @@ def construct_chemistry_model(
502
503
  ansatz_parameters: AnsatzParameters,
503
504
  execution_parameters: ChemistryExecutionParameters,
504
505
  ) -> SerializedModel:
506
+ warnings.warn(
507
+ (
508
+ "The function `construct_chemistry_model` is deprecated and will no "
509
+ "longer be supported starting on 2025-09-18 at the earliest. "
510
+ "For more information on Classiq's chemistry application, see "
511
+ "https://docs.classiq.io/latest/explore/applications/chemistry/classiq_chemistry_application/classiq_chemistry_application/."
512
+ ),
513
+ category=ClassiqDeprecationWarning,
514
+ stacklevel=2,
515
+ )
516
+
505
517
  chemistry_functions = [
506
518
  _get_chemistry_quantum_main(
507
519
  chemistry_problem,
@@ -240,12 +240,15 @@ def pyomo2qmod(
240
240
  bounds_set = True
241
241
 
242
242
  constraint_exprs: list[sympy.Expr] = []
243
-
244
- constraint_exprs.extend(
245
- Pyomo2SympyVisitor(symbols_map).walk_expression(constraint_dict[key].expr)
246
- for constraint_dict in pyo_model.component_objects(Constraint)
247
- for key in constraint_dict
248
- )
243
+ for constraint_dict in pyo_model.component_objects(Constraint):
244
+ for key in constraint_dict:
245
+ constraint_expr = Pyomo2SympyVisitor(symbols_map).walk_expression(
246
+ constraint_dict[key].expr
247
+ )
248
+ if constraint_expr is False:
249
+ ClassiqValueError(f"Constraint {constraint_dict[key]} is infeasible")
250
+ if constraint_expr is not True:
251
+ constraint_exprs.append(constraint_expr)
249
252
 
250
253
  pyo_objective: ScalarObjective = next(pyo_model.component_objects(Objective))
251
254
  objective_type_str = "Max" if pyo_objective.sense == maximize else "Min"
@@ -101,13 +101,13 @@ def construct_combi_opt_py_model(
101
101
  ],
102
102
  body=[
103
103
  Allocate(
104
- size=Expression(expr="get_field(target, 'len')"),
104
+ size=Expression(expr="target.len"),
105
105
  target=HandleBinding(name="target"),
106
106
  ),
107
107
  QuantumFunctionCall(
108
108
  function="qaoa_penalty",
109
109
  positional_args=[
110
- Expression(expr="get_field(target, 'len')"),
110
+ Expression(expr="target.len"),
111
111
  Expression(expr="params_list"),
112
112
  Expression(expr="hamiltonian"),
113
113
  HandleBinding(name="target"),
@@ -96,24 +96,32 @@ class CombinatorialProblem:
96
96
  execution_preferences: Optional[ExecutionPreferences] = None,
97
97
  maxiter: int = 20,
98
98
  quantile: float = 1.0,
99
+ initial_params: Optional[list[float]] = None,
99
100
  ) -> list[float]:
100
101
  if self.qprog_ is None:
101
102
  self.get_qprog()
102
103
  _es = ExecutionSession(
103
104
  self.qprog_, execution_preferences # type:ignore[arg-type]
104
105
  )
105
- initial_params = (
106
- np.concatenate(
107
- (
108
- np.linspace(1 / self.num_layers_, 1, self.num_layers_),
109
- np.linspace(1, 1 / self.num_layers_, self.num_layers_),
106
+ if initial_params is not None:
107
+ assert (
108
+ len(initial_params) == 2 * self.num_layers_
109
+ ), "inital_params should be twice the size of the number of layers"
110
+ initial_params = np.array(initial_params) # type:ignore[assignment]
111
+ else:
112
+ initial_params = (
113
+ np.concatenate(
114
+ (
115
+ np.linspace(1 / self.num_layers_, 1, self.num_layers_),
116
+ np.linspace(1, 1 / self.num_layers_, self.num_layers_),
117
+ )
110
118
  )
119
+ * math.pi
111
120
  )
112
- * math.pi
113
- )
121
+
114
122
  result = _es.minimize(
115
123
  lambda v: self.cost_func(v), # type:ignore[arg-type]
116
- {"params": initial_params.tolist()},
124
+ {"params": initial_params.tolist()}, # type:ignore[union-attr]
117
125
  maxiter,
118
126
  quantile,
119
127
  )
@@ -1,53 +1,75 @@
1
- from typing import get_args
1
+ import ast
2
+ from enum import IntEnum
3
+ from typing import Any
4
+
5
+ import sympy
2
6
 
3
- from classiq.interface.generator.expressions.evaluated_expression import (
4
- EvaluatedExpression,
5
- )
6
7
  from classiq.interface.generator.expressions.expression import Expression
7
- from classiq.interface.generator.expressions.expression_types import ExpressionValue
8
- from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
9
- AnyClassicalValue,
8
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
9
+ ClassicalProxy,
10
+ )
11
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
12
+ QmodStructInstance,
10
13
  )
11
- from classiq.interface.model.handle_binding import HandleBinding
14
+ from classiq.interface.generator.expressions.proxies.classical.utils import (
15
+ get_proxy_type,
16
+ )
17
+ from classiq.interface.generator.functions.classical_type import ClassicalArray, Integer
12
18
 
13
- from classiq.evaluators.expression_evaluator import evaluate
19
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
20
+ from classiq.evaluators.qmod_expression_visitors.qmod_expression_bwc import (
21
+ QmodExpressionBwc,
22
+ )
23
+ from classiq.evaluators.qmod_expression_visitors.qmod_expression_evaluator import (
24
+ evaluate_qmod_expression,
25
+ )
26
+ from classiq.model_expansions.closure import FunctionClosure
14
27
  from classiq.model_expansions.scope import (
15
- ClassicalSymbol,
28
+ ClassicalVariable,
16
29
  Evaluated,
17
- QuantumSymbol,
30
+ QuantumVariable,
18
31
  Scope,
19
32
  )
33
+ from classiq.qmod.model_state_container import QMODULE
20
34
 
21
35
 
22
- def evaluate_classical_expression(expr: Expression, scope: Scope) -> Evaluated:
23
- all_symbols = scope.items()
24
- locals_dict = (
25
- {
26
- name: EvaluatedExpression(value=evaluated.value)
27
- for name, evaluated in all_symbols
28
- if isinstance(evaluated.value, get_args(ExpressionValue))
29
- }
30
- | {
31
- name: EvaluatedExpression(
32
- value=(
33
- evaluated.value.quantum_type.get_proxy(HandleBinding(name=name))
34
- if evaluated.value.quantum_type.is_evaluated
35
- else AnyClassicalValue(name)
36
- )
37
- )
38
- for name, evaluated in all_symbols
39
- if isinstance(evaluated.value, QuantumSymbol)
40
- }
41
- | {
42
- name: EvaluatedExpression(
43
- value=evaluated.value.classical_type.get_classical_proxy(
44
- HandleBinding(name=name)
45
- )
36
+ def process_scope_val(val: Any) -> Any:
37
+ if isinstance(val, list):
38
+ if len(val) > 0 and isinstance(val[0], FunctionClosure):
39
+ return ClassicalArray(
40
+ element_type=Integer(), length=Expression(expr=str(len(val)))
46
41
  )
47
- for name, evaluated in all_symbols
48
- if isinstance(evaluated.value, ClassicalSymbol)
49
- }
50
- )
42
+ return val
43
+ if isinstance(val, (int, float, bool, IntEnum, QmodStructInstance)):
44
+ return val
45
+ if isinstance(val, ClassicalProxy):
46
+ return get_proxy_type(val)
47
+ if isinstance(val, ClassicalVariable):
48
+ return val.classical_type
49
+ if isinstance(val, QuantumVariable):
50
+ return val.quantum_type
51
+ if isinstance(val, QmodAnnotatedExpression):
52
+ return val.get_type(val.root)
53
+ if isinstance(val, sympy.Basic):
54
+ return val
55
+ return None
51
56
 
52
- ret = evaluate(expr, locals_dict)
53
- return Evaluated(value=ret.value)
57
+
58
+ def evaluate_classical_expression(expr: Expression, scope: Scope) -> Evaluated:
59
+ if expr.is_evaluated():
60
+ return Evaluated(value=expr.value.value)
61
+ expr_ast = ast.parse(expr.expr)
62
+ expr_ast = QmodExpressionBwc().visit(expr_ast)
63
+ expr_val = evaluate_qmod_expression(
64
+ ast.unparse(expr_ast),
65
+ classical_struct_declarations=list(QMODULE.type_decls.values()),
66
+ enum_declarations=list(QMODULE.enum_decls.values()),
67
+ scope={
68
+ name: processed_val
69
+ for name, val in scope.items()
70
+ if (processed_val := process_scope_val(val.value)) is not None
71
+ },
72
+ )
73
+ if expr_val.has_value(expr_val.root):
74
+ expr_val = expr_val.get_value(expr_val.root)
75
+ return Evaluated(value=expr_val)
@@ -1,61 +1,55 @@
1
- from typing import Any
2
-
3
- from sympy import Equality
4
- from sympy.core.numbers import Number
1
+ import ast
2
+ from typing import cast
5
3
 
6
4
  from classiq.interface.exceptions import ClassiqExpansionError
7
5
  from classiq.interface.generator.arith.argument_utils import (
8
6
  unsigned_integer_interpretation,
9
7
  )
10
8
  from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
11
- from classiq.interface.generator.expressions.proxies.quantum.qmod_qscalar_proxy import (
12
- QmodQNumProxy,
13
- QmodQScalarProxy,
14
- QmodSizedProxy,
15
- )
16
-
17
- CONTROL_INOUT_NAME = "ctrl"
9
+ from classiq.interface.model.quantum_type import QuantumScalar
18
10
 
11
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
12
+ from classiq.evaluators.qmod_node_evaluators.utils import is_classical_type
13
+ from classiq.model_expansions.scope import QuantumSymbol
19
14
 
20
- def type_name(obj: Any) -> str:
21
- if isinstance(obj, QmodSizedProxy):
22
- return obj.type_name
23
- return type(obj).__name__
15
+ CONTROL_INOUT_NAME = "ctrl"
24
16
 
25
17
 
26
- def resolve_num_condition(condition: Equality) -> tuple[QmodSizedProxy, str]:
27
- ctrl, ctrl_val = condition.args
28
- if isinstance(ctrl, Number) and isinstance(ctrl_val, QmodQScalarProxy):
18
+ def resolve_num_condition(
19
+ condition: QmodAnnotatedExpression,
20
+ ) -> tuple[QuantumSymbol, str]:
21
+ expr_ast = cast(ast.Compare, condition.root)
22
+ ctrl, ctrl_val = expr_ast.left, expr_ast.comparators[0]
23
+ if is_classical_type(condition.get_type(ctrl)):
29
24
  ctrl, ctrl_val = ctrl_val, ctrl
30
- if not isinstance(ctrl, QmodSizedProxy) or not (isinstance(ctrl_val, Number)):
31
- _raise_numeric_condition_error(ctrl, ctrl_val)
32
- return ctrl, _calculate_ctrl_state(ctrl, float(ctrl_val))
25
+ ctrl_sym = QuantumSymbol(
26
+ handle=condition.get_var(ctrl), quantum_type=condition.get_quantum_type(ctrl)
27
+ )
28
+ return ctrl_sym, _calculate_ctrl_state(
29
+ ctrl_sym, float(condition.get_value(ctrl_val))
30
+ )
33
31
 
34
32
 
35
- def _calculate_ctrl_state(ctrl: QmodSizedProxy, ctrl_val: float) -> str:
36
- is_signed, fraction_places = _get_numeric_attributes(ctrl)
33
+ def _calculate_ctrl_state(ctrl: QuantumSymbol, ctrl_val: float) -> str:
34
+ ctrl_type = cast(QuantumScalar, ctrl.quantum_type)
35
+ is_signed = ctrl_type.sign_value
36
+ fraction_places = ctrl_type.fraction_digits_value
37
37
 
38
38
  reg = RegisterArithmeticInfo(
39
- size=ctrl.size, is_signed=is_signed, fraction_places=fraction_places
39
+ size=ctrl.quantum_type.size_in_bits,
40
+ is_signed=is_signed,
41
+ fraction_places=fraction_places,
40
42
  )
41
43
  uint_ctrl_val = unsigned_integer_interpretation(ctrl_val, reg)
42
44
 
43
45
  _validate_control_value_sign(ctrl, ctrl_val, is_signed)
44
46
  _validate_control_var_qubits(ctrl, uint_ctrl_val, fraction_places, ctrl_val)
45
47
 
46
- return _to_twos_complement(uint_ctrl_val, ctrl.size)
47
-
48
-
49
- def _get_numeric_attributes(ctrl: QmodSizedProxy) -> tuple[bool, int]:
50
- return (
51
- (ctrl.is_signed, ctrl.fraction_digits)
52
- if isinstance(ctrl, QmodQNumProxy)
53
- else (False, 0)
54
- )
48
+ return _to_twos_complement(uint_ctrl_val, ctrl.quantum_type.size_in_bits)
55
49
 
56
50
 
57
51
  def _validate_control_value_sign(
58
- ctrl: QmodSizedProxy, ctrl_val: float, is_signed: bool
52
+ ctrl: QuantumSymbol, ctrl_val: float, is_signed: bool
59
53
  ) -> None:
60
54
  if not is_signed and ctrl_val < 0:
61
55
  raise ClassiqExpansionError(
@@ -65,7 +59,7 @@ def _validate_control_value_sign(
65
59
 
66
60
 
67
61
  def _validate_control_var_qubits(
68
- ctrl: QmodSizedProxy,
62
+ ctrl: QuantumSymbol,
69
63
  ctrl_val: int,
70
64
  fraction_places: int,
71
65
  orig_ctrl_val: float,
@@ -74,28 +68,13 @@ def _validate_control_var_qubits(
74
68
  fraction_places_message = (
75
69
  f" with {fraction_places} fraction digits" if fraction_places else ""
76
70
  )
77
- if ctrl.size < required_qubits:
71
+ if ctrl.quantum_type.size_in_bits < required_qubits:
78
72
  raise ClassiqExpansionError(
79
- f"Variable {str(ctrl)!r} has {ctrl.size} qubits{fraction_places_message} but control value "
73
+ f"Variable {str(ctrl)!r} has {ctrl.quantum_type.size_in_bits} qubits{fraction_places_message} but control value "
80
74
  f"{str(orig_ctrl_val if fraction_places else int(orig_ctrl_val))!r} requires at least {required_qubits} qubits{fraction_places_message}"
81
75
  )
82
76
 
83
77
 
84
- def _raise_numeric_condition_error(ctrl: Any, ctrl_val: Any) -> None:
85
- message = (
86
- "Control condition must be of the form '<quantum-variable> == "
87
- "<classical-number-expression>' or vice versa. "
88
- )
89
- prefix = f"Neither {ctrl!r} (type {type_name(ctrl)}) or {ctrl_val!r} (type {type_name(ctrl_val)}) is a "
90
- if not isinstance(ctrl, QmodSizedProxy) and not isinstance(
91
- ctrl_val, QmodSizedProxy
92
- ):
93
- message += prefix + "quantum variable."
94
- elif not isinstance(ctrl, Number) and not isinstance(ctrl_val, Number):
95
- message += prefix + "classical number."
96
- raise ClassiqExpansionError(message)
97
-
98
-
99
78
  def _min_unsigned_bit_length(number: int) -> int:
100
79
  if number < 0:
101
80
  raise ClassiqExpansionError(
@@ -20,10 +20,12 @@ from classiq.interface.generator.expressions.sympy_supported_expressions import
20
20
  SYMPY_SUPPORTED_EXPRESSIONS,
21
21
  )
22
22
 
23
+ from classiq.evaluators.classical_expression import evaluate_classical_expression
23
24
  from classiq.model_expansions.atomic_expression_functions_defs import (
24
25
  ATOMIC_EXPRESSION_FUNCTIONS,
25
26
  qmod_val_to_python,
26
27
  )
28
+ from classiq.model_expansions.scope import Evaluated, Scope
27
29
  from classiq.model_expansions.sympy_conversion.expression_to_sympy import (
28
30
  translate_to_sympy,
29
31
  )
@@ -33,10 +35,12 @@ from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
33
35
  from classiq.qmod.model_state_container import QMODULE
34
36
 
35
37
 
36
- def evaluate_constants(constants: list[Constant]) -> dict[str, EvaluatedExpression]:
37
- result: dict[str, EvaluatedExpression] = {}
38
+ def evaluate_constants(constants: list[Constant]) -> Scope:
39
+ result = Scope()
38
40
  for constant in constants:
39
- result[constant.name] = evaluate(constant.value, result)
41
+ expr_val = evaluate_classical_expression(constant.value, result).value
42
+ result[constant.name] = Evaluated(value=expr_val)
43
+
40
44
  return result
41
45
 
42
46
 
@@ -69,7 +73,7 @@ def evaluate(
69
73
  if val is not None:
70
74
  return EvaluatedExpression(value=val)
71
75
 
72
- model_locals: dict[str, ExpressionValue] = {}
76
+ model_locals: dict[str, Any] = {}
73
77
  model_locals.update(ATOMIC_EXPRESSION_FUNCTIONS)
74
78
  model_locals.update(
75
79
  {