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.
- classiq/__init__.py +1 -0
- classiq/_internals/api_wrapper.py +16 -32
- classiq/analyzer/show_interactive_hack.py +26 -1
- classiq/applications/chemistry/chemistry_model_constructor.py +14 -2
- classiq/applications/combinatorial_helpers/pyomo_utils.py +9 -6
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -2
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +16 -8
- classiq/evaluators/classical_expression.py +63 -41
- classiq/evaluators/control.py +31 -52
- classiq/evaluators/expression_evaluator.py +8 -4
- classiq/evaluators/parameter_types.py +200 -104
- classiq/evaluators/qmod_annotated_expression.py +3 -1
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +12 -37
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +8 -17
- classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +1 -1
- classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +7 -1
- classiq/evaluators/qmod_node_evaluators/name_evaluation.py +0 -1
- classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +9 -1
- classiq/evaluators/qmod_node_evaluators/utils.py +33 -0
- classiq/evaluators/qmod_type_inference/classical_type_inference.py +4 -7
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +1 -25
- classiq/interface/analyzer/result.py +4 -0
- classiq/interface/chemistry/ground_state_problem.py +16 -2
- classiq/interface/executor/optimizer_preferences.py +0 -112
- classiq/interface/generator/application_apis/chemistry_declarations.py +3 -1
- classiq/interface/generator/arith/arithmetic_expression_validator.py +2 -7
- classiq/interface/generator/expressions/evaluated_expression.py +3 -13
- classiq/interface/generator/expressions/expression_types.py +8 -22
- classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +2 -2
- classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +1 -2
- classiq/interface/generator/functions/concrete_types.py +1 -1
- classiq/interface/generator/generated_circuit_data.py +4 -0
- classiq/interface/generator/preferences/qasm_to_qmod_params.py +14 -0
- classiq/interface/helpers/model_normalizer.py +0 -6
- classiq/interface/ide/visual_model.py +1 -0
- classiq/interface/model/handle_binding.py +1 -1
- classiq/interface/model/port_declaration.py +2 -1
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +16 -12
- classiq/interface/model/quantum_type.py +1 -1
- classiq/interface/server/routes.py +2 -3
- classiq/model_expansions/atomic_expression_functions_defs.py +4 -22
- classiq/model_expansions/capturing/captured_vars.py +7 -3
- classiq/model_expansions/closure.py +8 -0
- classiq/model_expansions/interpreters/base_interpreter.py +84 -22
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +1 -1
- classiq/model_expansions/interpreters/generative_interpreter.py +7 -5
- classiq/model_expansions/quantum_operations/allocate.py +92 -21
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +28 -27
- classiq/model_expansions/quantum_operations/call_emitter.py +32 -26
- classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -2
- classiq/model_expansions/quantum_operations/emitter.py +39 -69
- classiq/model_expansions/quantum_operations/expression_evaluator.py +13 -2
- classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -5
- classiq/model_expansions/quantum_operations/variable_decleration.py +16 -11
- classiq/model_expansions/scope.py +36 -29
- classiq/model_expansions/scope_initialization.py +3 -6
- classiq/model_expansions/sympy_conversion/sympy_to_python.py +6 -2
- classiq/model_expansions/transformers/model_renamer.py +35 -64
- classiq/model_expansions/transformers/type_modifier_inference.py +6 -6
- classiq/model_expansions/visitors/boolean_expression_transformers.py +7 -31
- classiq/model_expansions/visitors/symbolic_param_inference.py +9 -3
- classiq/open_library/functions/state_preparation.py +3 -3
- classiq/qmod/builtins/functions/allocation.py +8 -8
- classiq/qmod/builtins/functions/arithmetic.py +1 -1
- classiq/qmod/builtins/functions/chemistry.py +64 -0
- classiq/qmod/builtins/functions/exponentiation.py +7 -13
- classiq/qmod/builtins/functions/qsvm.py +1 -1
- classiq/qmod/builtins/operations.py +38 -10
- classiq/qmod/generative.py +2 -4
- classiq/qmod/native/pretty_printer.py +1 -1
- classiq/qmod/pretty_print/pretty_printer.py +1 -1
- classiq/qmod/qmod_constant.py +1 -1
- classiq/qmod/qmod_parameter.py +2 -2
- classiq/qmod/qmod_variable.py +15 -15
- classiq/qmod/quantum_expandable.py +1 -1
- classiq/synthesis.py +37 -1
- classiq/visualization.py +1 -1
- {classiq-0.89.0.dist-info → classiq-0.90.0.dist-info}/METADATA +1 -1
- {classiq-0.89.0.dist-info → classiq-0.90.0.dist-info}/RECORD +81 -85
- classiq/evaluators/arg_type_match.py +0 -168
- classiq/evaluators/classical_type_inference.py +0 -121
- classiq/interface/combinatorial_optimization/optimization_problem.py +0 -17
- classiq/interface/combinatorial_optimization/result.py +0 -9
- classiq/model_expansions/transformers/ast_renamer.py +0 -26
- {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
|
|
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":
|
|
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"
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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"
|
classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py
CHANGED
|
@@ -101,13 +101,13 @@ def construct_combi_opt_py_model(
|
|
|
101
101
|
],
|
|
102
102
|
body=[
|
|
103
103
|
Allocate(
|
|
104
|
-
size=Expression(expr="
|
|
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="
|
|
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
|
-
|
|
107
|
-
(
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
8
|
-
|
|
9
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
28
|
+
ClassicalVariable,
|
|
16
29
|
Evaluated,
|
|
17
|
-
|
|
30
|
+
QuantumVariable,
|
|
18
31
|
Scope,
|
|
19
32
|
)
|
|
33
|
+
from classiq.qmod.model_state_container import QMODULE
|
|
20
34
|
|
|
21
35
|
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
48
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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)
|
classiq/evaluators/control.py
CHANGED
|
@@ -1,61 +1,55 @@
|
|
|
1
|
-
|
|
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.
|
|
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
|
-
|
|
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(
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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:
|
|
36
|
-
|
|
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.
|
|
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.
|
|
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:
|
|
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:
|
|
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.
|
|
71
|
+
if ctrl.quantum_type.size_in_bits < required_qubits:
|
|
78
72
|
raise ClassiqExpansionError(
|
|
79
|
-
f"Variable {str(ctrl)!r} has {ctrl.
|
|
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]) ->
|
|
37
|
-
result
|
|
38
|
+
def evaluate_constants(constants: list[Constant]) -> Scope:
|
|
39
|
+
result = Scope()
|
|
38
40
|
for constant in constants:
|
|
39
|
-
|
|
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,
|
|
76
|
+
model_locals: dict[str, Any] = {}
|
|
73
77
|
model_locals.update(ATOMIC_EXPRESSION_FUNCTIONS)
|
|
74
78
|
model_locals.update(
|
|
75
79
|
{
|