classiq 0.40.0__py3-none-any.whl → 0.41.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 (81) hide show
  1. classiq/__init__.py +4 -2
  2. classiq/_internals/api_wrapper.py +3 -21
  3. classiq/applications/chemistry/chemistry_model_constructor.py +86 -100
  4. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +6 -24
  5. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +33 -45
  6. classiq/applications/combinatorial_optimization/__init__.py +2 -0
  7. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +29 -26
  8. classiq/applications/finance/finance_model_constructor.py +23 -26
  9. classiq/applications/grover/grover_model_constructor.py +37 -38
  10. classiq/applications/qsvm/qsvm.py +1 -2
  11. classiq/applications/qsvm/qsvm_model_constructor.py +15 -16
  12. classiq/execution/__init__.py +4 -0
  13. classiq/execution/execution_session.py +151 -0
  14. classiq/execution/qnn.py +80 -0
  15. classiq/executor.py +2 -109
  16. classiq/interface/_version.py +1 -1
  17. classiq/interface/analyzer/analysis_params.py +11 -0
  18. classiq/interface/applications/qsvm.py +0 -8
  19. classiq/interface/ast_node.py +12 -2
  20. classiq/interface/backend/backend_preferences.py +25 -1
  21. classiq/interface/backend/quantum_backend_providers.py +4 -4
  22. classiq/interface/executor/execution_preferences.py +4 -59
  23. classiq/interface/executor/execution_result.py +22 -1
  24. classiq/interface/generator/arith/arithmetic_expression_validator.py +0 -2
  25. classiq/interface/generator/arith/binary_ops.py +88 -25
  26. classiq/interface/generator/arith/unary_ops.py +28 -19
  27. classiq/interface/generator/expressions/atomic_expression_functions.py +6 -2
  28. classiq/interface/generator/expressions/enums/__init__.py +10 -0
  29. classiq/interface/generator/expressions/enums/classical_enum.py +5 -1
  30. classiq/interface/generator/expressions/expression.py +9 -2
  31. classiq/interface/generator/expressions/qmod_qarray_proxy.py +7 -0
  32. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +0 -1
  33. classiq/interface/generator/expressions/sympy_supported_expressions.py +10 -1
  34. classiq/interface/generator/functions/builtins/internal_operators.py +7 -62
  35. classiq/interface/generator/functions/builtins/open_lib_functions.py +810 -2
  36. classiq/interface/generator/functions/classical_type.py +1 -3
  37. classiq/interface/generator/synthesis_metadata/synthesis_duration.py +0 -4
  38. classiq/interface/model/bind_operation.py +3 -1
  39. classiq/interface/model/call_synthesis_data.py +2 -13
  40. classiq/interface/model/classical_if.py +3 -1
  41. classiq/interface/model/classical_parameter_declaration.py +13 -0
  42. classiq/interface/model/control.py +3 -101
  43. classiq/interface/model/inplace_binary_operation.py +3 -1
  44. classiq/interface/model/invert.py +3 -1
  45. classiq/interface/model/port_declaration.py +8 -1
  46. classiq/interface/model/power.py +3 -1
  47. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +4 -2
  48. classiq/interface/model/quantum_expressions/arithmetic_operation.py +3 -1
  49. classiq/interface/model/quantum_expressions/quantum_expression.py +11 -1
  50. classiq/interface/model/quantum_function_call.py +4 -10
  51. classiq/interface/model/quantum_function_declaration.py +26 -4
  52. classiq/interface/model/quantum_lambda_function.py +1 -20
  53. classiq/interface/model/quantum_statement.py +9 -2
  54. classiq/interface/model/repeat.py +3 -1
  55. classiq/interface/model/statement_block.py +19 -13
  56. classiq/interface/model/validations/handles_validator.py +8 -2
  57. classiq/interface/model/variable_declaration_statement.py +3 -1
  58. classiq/interface/model/within_apply_operation.py +3 -1
  59. classiq/interface/server/routes.py +0 -5
  60. classiq/qmod/__init__.py +1 -2
  61. classiq/qmod/builtins/classical_execution_primitives.py +22 -2
  62. classiq/qmod/builtins/functions.py +51 -1
  63. classiq/qmod/builtins/operations.py +6 -36
  64. classiq/qmod/declaration_inferrer.py +8 -15
  65. classiq/qmod/native/__init__.py +9 -0
  66. classiq/qmod/native/expression_to_qmod.py +12 -9
  67. classiq/qmod/native/pretty_printer.py +4 -4
  68. classiq/qmod/pretty_print/__init__.py +9 -0
  69. classiq/qmod/pretty_print/expression_to_python.py +221 -0
  70. classiq/qmod/pretty_print/pretty_printer.py +421 -0
  71. classiq/qmod/qmod_parameter.py +7 -4
  72. classiq/qmod/quantum_callable.py +2 -1
  73. classiq/qmod/quantum_expandable.py +4 -3
  74. classiq/qmod/quantum_function.py +4 -16
  75. classiq/qmod/symbolic.py +1 -6
  76. classiq/synthesis.py +15 -16
  77. {classiq-0.40.0.dist-info → classiq-0.41.0.dist-info}/METADATA +5 -4
  78. {classiq-0.40.0.dist-info → classiq-0.41.0.dist-info}/RECORD +79 -76
  79. classiq/interface/model/common_model_types.py +0 -23
  80. classiq/interface/model/quantum_expressions/control_state.py +0 -38
  81. {classiq-0.40.0.dist-info → classiq-0.41.0.dist-info}/WHEEL +0 -0
@@ -14,20 +14,23 @@ from classiq.interface.generator.functions.classical_type import (
14
14
  from classiq.interface.generator.functions.port_declaration import (
15
15
  PortDeclarationDirection,
16
16
  )
17
+ from classiq.interface.model.classical_parameter_declaration import (
18
+ ClassicalParameterDeclaration,
19
+ )
17
20
  from classiq.interface.model.handle_binding import HandleBinding
18
21
  from classiq.interface.model.model import Model, SerializedModel
19
22
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
20
23
  from classiq.interface.model.port_declaration import PortDeclaration
21
24
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
25
+ from classiq.interface.model.quantum_type import QuantumBitvector
22
26
 
23
27
  from classiq.applications.combinatorial_helpers.combinatorial_problem_utils import (
24
- _internal_pyo_model_to_hamiltonian,
25
28
  compute_qaoa_initial_point,
26
29
  convert_pyomo_to_global_presentation,
30
+ pyo_model_to_hamiltonian,
27
31
  )
28
32
  from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import (
29
- _pauli_operator_to_qmod,
30
- get_pauli_operator,
33
+ _pauli_terms_to_qmod,
31
34
  )
32
35
  from classiq.applications.combinatorial_optimization import OptimizerConfig, QAOAConfig
33
36
 
@@ -47,13 +50,10 @@ def construct_combi_opt_py_model(
47
50
  if optimizer_config.max_iteration is not None:
48
51
  max_iteration = optimizer_config.max_iteration
49
52
 
50
- hamiltonian = _internal_pyo_model_to_hamiltonian(
51
- pyo_model, qaoa_config.penalty_energy
52
- )
53
+ hamiltonian = pyo_model_to_hamiltonian(pyo_model, qaoa_config.penalty_energy)
54
+ len_hamiltonian = len(hamiltonian[0].pauli) # type: ignore[arg-type]
53
55
  qaoa_initial_point = compute_qaoa_initial_point(hamiltonian, qaoa_config.num_layers)
54
- len_hamiltonian = len(hamiltonian[0]["pauli"])
55
- pauli_oper = get_pauli_operator(hamiltonian)
56
- pauli_qmod = _pauli_operator_to_qmod(pauli_oper)
56
+ pauli_qmod = _pauli_terms_to_qmod(hamiltonian)
57
57
 
58
58
  initial_point_expression = (
59
59
  f"{optimizer_config.initial_point}"
@@ -72,34 +72,37 @@ def construct_combi_opt_py_model(
72
72
  functions=[
73
73
  NativeFunctionDefinition(
74
74
  name="main",
75
- param_decls={
76
- "params_list": ClassicalArray(
77
- element_type=Real(), size=qaoa_config.num_layers * 2
78
- )
79
- },
80
- port_declarations={
81
- "target": PortDeclaration(
75
+ positional_arg_declarations=[
76
+ ClassicalParameterDeclaration(
77
+ name="params_list",
78
+ classical_type=ClassicalArray(
79
+ element_type=Real(), size=qaoa_config.num_layers * 2
80
+ ),
81
+ ),
82
+ PortDeclaration(
82
83
  name="target",
83
- size=Expression(expr=f"{len_hamiltonian}"),
84
+ quantum_type=QuantumBitvector(
85
+ length=Expression(expr=f"{len_hamiltonian}"),
86
+ ),
84
87
  direction=PortDeclarationDirection.Output,
85
88
  ),
86
- },
89
+ ],
87
90
  body=[
88
91
  QuantumFunctionCall(
89
92
  function="allocate",
90
93
  positional_args=[
91
- Expression(expr="target.len"),
94
+ Expression(expr="get_field(target, 'len')"),
92
95
  HandleBinding(name="target"),
93
96
  ],
94
97
  ),
95
98
  QuantumFunctionCall(
96
99
  function="qaoa_penalty",
97
- params={
98
- "num_qubits": Expression(expr="target.len"),
99
- "params_list": Expression(expr="params_list"),
100
- "hamiltonian": Expression(expr="hamiltonian"),
101
- },
102
- inouts={"target": HandleBinding(name="target")},
100
+ positional_args=[
101
+ Expression(expr="get_field(target, 'len')"),
102
+ Expression(expr="params_list"),
103
+ Expression(expr="hamiltonian"),
104
+ HandleBinding(name="target"),
105
+ ],
103
106
  ),
104
107
  ],
105
108
  ),
@@ -118,7 +121,7 @@ alpha_cvar={optimizer_config.alpha_cvar}
118
121
  )
119
122
 
120
123
  save({{"vqe_result": vqe_result, "hamiltonian": hamiltonian}})
121
- """,
124
+ """.strip(),
122
125
  )
123
126
 
124
127
 
@@ -14,6 +14,7 @@ from classiq.interface.model.native_function_definition import NativeFunctionDef
14
14
  from classiq.interface.model.port_declaration import PortDeclaration
15
15
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
16
16
  from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
17
+ from classiq.interface.model.quantum_type import QuantumBitvector
17
18
  from classiq.interface.model.variable_declaration_statement import (
18
19
  VariableDeclarationStatement,
19
20
  )
@@ -66,13 +67,15 @@ def construct_finance_model(
66
67
  *QMCI_LIBRARY,
67
68
  NativeFunctionDefinition(
68
69
  name="main",
69
- port_declarations={
70
- "phase_port": PortDeclaration(
70
+ positional_arg_declarations=[
71
+ PortDeclaration(
71
72
  name="phase_port",
72
- size=Expression(expr=f"{phase_port_size}"),
73
+ quantum_type=QuantumBitvector(
74
+ length=Expression(expr=f"{phase_port_size}")
75
+ ),
73
76
  direction=PortDeclarationDirection.Output,
74
77
  ),
75
- },
78
+ ],
76
79
  body=[
77
80
  VariableDeclarationStatement(name="unitary_port"),
78
81
  QuantumFunctionCall(
@@ -93,40 +96,34 @@ def construct_finance_model(
93
96
  ),
94
97
  QuantumFunctionCall(
95
98
  function="qmci",
96
- operands={
97
- "space_transform": QuantumLambdaFunction(
99
+ positional_args=[
100
+ QuantumLambdaFunction(
98
101
  body=[
99
102
  QuantumFunctionCall(
100
103
  function=finance_function,
101
- params={
102
- "finance_model": Expression(
103
- expr=finance_model
104
- ),
105
- "finance_function": Expression(
106
- expr=finance_function_object
107
- ),
108
- },
109
- inouts={
110
- "func_port": HandleBinding(name="arg0"),
111
- "obj_port": HandleBinding(name="arg1"),
112
- },
104
+ positional_args=[
105
+ Expression(expr=finance_model),
106
+ Expression(expr=finance_function_object),
107
+ HandleBinding(name="arg0"),
108
+ HandleBinding(name="arg1"),
109
+ ],
113
110
  ),
114
111
  ],
115
112
  ),
116
- },
117
- inouts={
118
- "phase": HandleBinding(name="phase_port"),
119
- "packed_vars": HandleBinding(name="unitary_port"),
120
- },
113
+ HandleBinding(name="phase_port"),
114
+ HandleBinding(name="unitary_port"),
115
+ ],
121
116
  ),
122
117
  ],
123
118
  ),
124
119
  ],
125
- classical_execution_code=AE_CLASSICAL_LIBRARY
126
- + f"""
120
+ classical_execution_code=(
121
+ AE_CLASSICAL_LIBRARY
122
+ + f"""
127
123
  estimation = execute_amplitude_estimation({phase_port_size})
128
124
  {_OUTPUT_VARIABLE_NAME} = {post_process_function}({finance_model}, {finance_function_object}, estimation)
129
125
  save({{{_OUTPUT_VARIABLE_NAME!r}: {_OUTPUT_VARIABLE_NAME}}})
130
- """,
126
+ """
127
+ ).strip(),
131
128
  )
132
129
  return model.get_model()
@@ -1,4 +1,4 @@
1
- from typing import Dict, List, Tuple
1
+ from typing import List, Tuple
2
2
 
3
3
  from classiq.interface.generator.expressions.expression import Expression
4
4
  from classiq.interface.generator.functions.port_declaration import (
@@ -14,7 +14,7 @@ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
14
14
  )
15
15
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
16
16
  from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
17
- from classiq.interface.model.quantum_type import QuantumNumeric
17
+ from classiq.interface.model.quantum_type import QuantumBitvector, QuantumNumeric
18
18
  from classiq.interface.model.variable_declaration_statement import (
19
19
  VariableDeclarationStatement,
20
20
  )
@@ -26,16 +26,18 @@ _OUTPUT_VARIABLE_NAME = "result"
26
26
  _PREDICATE_FUNCTION_NAME = "expr_predicate"
27
27
 
28
28
 
29
- def _arithmetic_oracle_io_dict(
29
+ def _arithmetic_oracle_ios(
30
30
  definitions: List[Tuple[str, RegisterUserInput]], handle_name: str
31
- ) -> Dict[str, HandleBinding]:
31
+ ) -> List[HandleBinding]:
32
32
  cursor = 0
33
- ios: Dict[str, HandleBinding] = dict()
34
- for reg_name, reg in definitions:
35
- ios[reg_name] = SlicedHandleBinding(
36
- name=handle_name,
37
- start=Expression(expr=f"{cursor}"),
38
- end=Expression(expr=f"{cursor + reg.size}"),
33
+ ios: List[HandleBinding] = []
34
+ for _, reg in definitions:
35
+ ios.append(
36
+ SlicedHandleBinding(
37
+ name=handle_name,
38
+ start=Expression(expr=f"{cursor}"),
39
+ end=Expression(expr=f"{cursor + reg.size}"),
40
+ )
39
41
  )
40
42
  cursor += reg.size
41
43
  return ios
@@ -45,34 +47,31 @@ def _construct_arithmetic_oracle(
45
47
  predicate_function: str,
46
48
  definitions: List[Tuple[str, RegisterUserInput]],
47
49
  ) -> QuantumFunctionCall:
48
- predicate_var_binding = _arithmetic_oracle_io_dict(definitions, "arg0")
49
- predicate_var_binding["res"] = HandleBinding(name="arg1")
50
+ predicate_var_binding = _arithmetic_oracle_ios(definitions, "arg0")
51
+ predicate_var_binding.append(HandleBinding(name="arg1"))
50
52
  return QuantumFunctionCall(
51
53
  function="phase_oracle",
52
- inouts={
53
- "target": HandleBinding(name="arg0"),
54
- },
55
- operands={
56
- "predicate": QuantumLambdaFunction(
54
+ positional_args=[
55
+ QuantumLambdaFunction(
57
56
  body=[
58
57
  QuantumFunctionCall(
59
58
  function=predicate_function,
60
- inouts=predicate_var_binding,
59
+ positional_args=predicate_var_binding,
61
60
  ),
62
61
  ],
63
62
  ),
64
- },
63
+ HandleBinding(name="arg0"),
64
+ ],
65
65
  )
66
66
 
67
67
 
68
68
  def grover_main_port_declarations(
69
69
  definitions: List[Tuple[str, RegisterUserInput]],
70
70
  direction: PortDeclarationDirection,
71
- ) -> Dict[str, PortDeclaration]:
72
- return {
73
- name: PortDeclaration(
71
+ ) -> List[PortDeclaration]:
72
+ return [
73
+ PortDeclaration(
74
74
  name=name,
75
- size=Expression(expr=f"{reg.size}"),
76
75
  quantum_type=QuantumNumeric(
77
76
  size=Expression(expr=f"{reg.size}"),
78
77
  is_signed=Expression(expr=f"{reg.is_signed}"),
@@ -81,7 +80,7 @@ def grover_main_port_declarations(
81
80
  direction=direction,
82
81
  )
83
82
  for name, reg in definitions
84
- }
83
+ ]
85
84
 
86
85
 
87
86
  def construct_grover_model(
@@ -92,10 +91,12 @@ def construct_grover_model(
92
91
  predicate_port_decls = grover_main_port_declarations(
93
92
  definitions, PortDeclarationDirection.Inout
94
93
  )
95
- predicate_port_decls["res"] = PortDeclaration(
96
- name="res",
97
- size=Expression(expr="1"),
98
- direction=PortDeclarationDirection.Inout,
94
+ predicate_port_decls.append(
95
+ PortDeclaration(
96
+ name="res",
97
+ quantum_type=QuantumBitvector(length=Expression(expr="1")),
98
+ direction=PortDeclarationDirection.Inout,
99
+ )
99
100
  )
100
101
  num_qubits = sum(reg.size for _, reg in definitions)
101
102
 
@@ -103,7 +104,7 @@ def construct_grover_model(
103
104
  functions=[
104
105
  NativeFunctionDefinition(
105
106
  name=_PREDICATE_FUNCTION_NAME,
106
- port_declarations=predicate_port_decls,
107
+ positional_arg_declarations=predicate_port_decls,
107
108
  body=[
108
109
  ArithmeticOperation(
109
110
  expression=Expression(expr=expression),
@@ -114,7 +115,7 @@ def construct_grover_model(
114
115
  ),
115
116
  NativeFunctionDefinition(
116
117
  name="main",
117
- port_declarations=grover_main_port_declarations(
118
+ positional_arg_declarations=grover_main_port_declarations(
118
119
  definitions, PortDeclarationDirection.Output
119
120
  ),
120
121
  body=[
@@ -128,20 +129,18 @@ def construct_grover_model(
128
129
  ),
129
130
  QuantumFunctionCall(
130
131
  function="grover_search",
131
- params={
132
- "reps": Expression(expr=f"{num_reps}"),
133
- },
134
- inouts={"packed_vars": HandleBinding(name="packed_vars")},
135
- operands={
136
- "oracle": QuantumLambdaFunction(
132
+ positional_args=[
133
+ Expression(expr=f"{num_reps}"),
134
+ QuantumLambdaFunction(
137
135
  body=[
138
136
  _construct_arithmetic_oracle(
139
137
  _PREDICATE_FUNCTION_NAME,
140
138
  definitions,
141
139
  )
142
140
  ]
143
- )
144
- },
141
+ ),
142
+ HandleBinding(name="packed_vars"),
143
+ ],
145
144
  ),
146
145
  BindOperation(
147
146
  in_handles=[HandleBinding(name="packed_vars")],
@@ -1,10 +1,9 @@
1
1
  from typing import List
2
2
 
3
- from classiq.interface.applications.qsvm import Data, Labels, QSVMData, QSVMPreferences
3
+ from classiq.interface.applications.qsvm import Data, Labels, QSVMData
4
4
 
5
5
  __all__ = [
6
6
  "QSVMData",
7
- "QSVMPreferences",
8
7
  "Data",
9
8
  "Labels",
10
9
  ]
@@ -1,4 +1,4 @@
1
- from typing import Any, Dict, List, Tuple
1
+ from typing import Any, List, Tuple
2
2
 
3
3
  from classiq.interface.applications.qsvm import DataList, LabelsInt
4
4
  from classiq.interface.generator.expressions.enums.pauli import Pauli
@@ -24,10 +24,10 @@ _OUTPUT_VARIABLE_NAME = "qsvm_results"
24
24
 
25
25
  def _bloch_sphere_feature_map_function_params(
26
26
  bloch_feature_dimension: int,
27
- ) -> Tuple[Dict[str, Expression], str]:
28
- return {
29
- "feature_dimension": Expression(expr=f"{bloch_feature_dimension}")
30
- }, f"ceiling({bloch_feature_dimension}/2)"
27
+ ) -> Tuple[List[Expression], str]:
28
+ return [
29
+ Expression(expr=f"{bloch_feature_dimension}")
30
+ ], f"ceiling({bloch_feature_dimension}/2)"
31
31
 
32
32
 
33
33
  def _pauli_feature_map_function_params(
@@ -36,7 +36,7 @@ def _pauli_feature_map_function_params(
36
36
  alpha: int,
37
37
  reps: int,
38
38
  feature_dimension: int,
39
- ) -> Tuple[Dict[str, Expression], str]:
39
+ ) -> Tuple[List[Expression], str]:
40
40
  paulis_str = (
41
41
  "["
42
42
  + ",".join(
@@ -51,11 +51,11 @@ def _pauli_feature_map_function_params(
51
51
  f"reps={reps}, "
52
52
  f"feature_dimension={feature_dimension}"
53
53
  )
54
- return {
55
- "feature_map": Expression(
54
+ return [
55
+ Expression(
56
56
  expr=f"struct_literal(QSVMFeatureMapPauli, {pauli_feature_map_params})"
57
57
  )
58
- }, f"{feature_dimension}"
58
+ ], f"{feature_dimension}"
59
59
 
60
60
 
61
61
  def get_qsvm_qmain_body(
@@ -75,8 +75,7 @@ def get_qsvm_qmain_body(
75
75
  ),
76
76
  QuantumFunctionCall(
77
77
  function=feature_map_function_name,
78
- params=params,
79
- inouts={"qbv": HandleBinding(name="qbv")},
78
+ positional_args=[*params, HandleBinding(name="qbv")],
80
79
  ),
81
80
  ]
82
81
 
@@ -94,12 +93,12 @@ def construct_qsvm_model(
94
93
  functions=[
95
94
  NativeFunctionDefinition(
96
95
  name="main",
97
- port_declarations={
98
- "qbv": PortDeclaration(
96
+ positional_arg_declarations=[
97
+ PortDeclaration(
99
98
  name="qbv",
100
99
  direction=PortDeclarationDirection.Output,
101
- )
102
- },
100
+ ),
101
+ ],
103
102
  body=get_qsvm_qmain_body(
104
103
  feature_map_function_name=feature_map_function_name, **kwargs
105
104
  ),
@@ -114,7 +113,7 @@ def construct_qsvm_model(
114
113
  predict_data={predict_data}
115
114
  )
116
115
  save({{{_OUTPUT_VARIABLE_NAME!r}: {_OUTPUT_VARIABLE_NAME}}})
117
- """,
116
+ """.strip(),
118
117
  )
119
118
 
120
119
  return qsvm_qmod.get_model()
@@ -9,7 +9,9 @@ from ..interface.executor.execution_preferences import __all__ as _ep_all
9
9
  from ..interface.executor.iqae_result import IQAEResult
10
10
  from ..interface.executor.result import ExecutionDetails
11
11
  from ..interface.executor.vqe_result import VQESolverResult
12
+ from .execution_session import ExecutionSession
12
13
  from .jobs import ExecutionJob, get_execution_jobs, get_execution_jobs_async
14
+ from .qnn import execute_qnn
13
15
 
14
16
  __all__ = (
15
17
  _be_all
@@ -22,6 +24,8 @@ __all__ = (
22
24
  "ExecutionJob",
23
25
  "get_execution_jobs",
24
26
  "get_execution_jobs_async",
27
+ "ExecutionSession",
28
+ "execute_qnn",
25
29
  ]
26
30
  )
27
31
 
@@ -0,0 +1,151 @@
1
+ import json
2
+ from typing import Any, Callable, Dict, List, Optional, Union, cast
3
+
4
+ from classiq.interface.executor.execution_preferences import ExecutionPreferences
5
+ from classiq.interface.executor.execution_result import ResultsCollection
6
+ from classiq.interface.executor.result import (
7
+ EstimationResult,
8
+ EstimationResults,
9
+ ExecutionDetails,
10
+ MultipleExecutionDetails,
11
+ )
12
+ from classiq.interface.generator.functions.qmod_python_interface import QmodPyStruct
13
+ from classiq.interface.generator.quantum_program import QuantumProgram
14
+
15
+ from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import (
16
+ _pauli_dict_to_str,
17
+ _pauli_terms_to_qmod,
18
+ )
19
+ from classiq.exceptions import ClassiqValueError
20
+ from classiq.executor import execute
21
+ from classiq.qmod.builtins import PauliTerm
22
+ from classiq.qmod.builtins.classical_execution_primitives import ExecutionParams
23
+ from classiq.synthesis import SerializedQuantumProgram
24
+
25
+ Hamiltonian = Union[List[QmodPyStruct], List[PauliTerm]]
26
+ Program = Union[SerializedQuantumProgram, QuantumProgram]
27
+ ExecutionParameters = Union[None, ExecutionParams, List[ExecutionParams]]
28
+
29
+ SAVE_RESULT = "\nsave({'result': result})\n"
30
+
31
+
32
+ class SupportedPrimitives:
33
+ SAMPLE = "sample"
34
+ BATCH_SAMPLE = "batch_sample"
35
+ ESTIMATE = "estimate"
36
+ BATCH_ESTIMATE = "batch_estimate"
37
+
38
+
39
+ def _deserialize_program(program: Program) -> QuantumProgram:
40
+ return (
41
+ program
42
+ if isinstance(program, QuantumProgram)
43
+ else QuantumProgram.parse_obj(json.loads(program))
44
+ )
45
+
46
+
47
+ def to_hamiltonian_str(hamiltonian: Hamiltonian) -> str:
48
+ return (
49
+ _pauli_terms_to_qmod(cast(List[PauliTerm], hamiltonian))
50
+ if isinstance(hamiltonian[0], PauliTerm)
51
+ else _pauli_dict_to_str(cast(List[QmodPyStruct], hamiltonian))
52
+ )
53
+
54
+
55
+ def format_parameters(execution_params: ExecutionParameters) -> str:
56
+ return json.dumps(execution_params, default=str) if execution_params else ""
57
+
58
+
59
+ def create_estimate_execution_code(operation: str, **kwargs: Any) -> str:
60
+ hamiltonian = kwargs.get("hamiltonian", "")
61
+ parameters = kwargs.get("parameters", "")
62
+ return f"\nresult = {operation}([{hamiltonian}], {parameters})" + SAVE_RESULT
63
+
64
+
65
+ def create_sample_execution_code(operation: str, **kwargs: Any) -> str:
66
+ parameters = kwargs.get("parameters", "")
67
+ return f"\nresult = {operation}({parameters})" + SAVE_RESULT
68
+
69
+
70
+ operation_handlers: Dict[str, Callable[[str], str]] = {
71
+ "estimate": create_estimate_execution_code,
72
+ "batch_estimate": create_estimate_execution_code,
73
+ "sample": create_sample_execution_code,
74
+ "batch_sample": create_sample_execution_code,
75
+ }
76
+
77
+
78
+ def generate_code_snippet(operation: str, **kwargs: Any) -> str:
79
+ handler = operation_handlers.get(operation)
80
+ if handler:
81
+ return handler(operation, **kwargs)
82
+ raise ClassiqValueError(f"Unsupported operation type: {operation}")
83
+
84
+
85
+ class ExecutionSession:
86
+ def __init__(
87
+ self,
88
+ quantum_program: Program,
89
+ execution_preferences: Optional[ExecutionPreferences] = None,
90
+ ):
91
+ self.program: QuantumProgram = _deserialize_program(quantum_program)
92
+ self.update_execution_preferences(execution_preferences)
93
+
94
+ @property
95
+ def qprog(self) -> str:
96
+ return SerializedQuantumProgram(self.program.json(indent=2))
97
+
98
+ def update_execution_preferences(
99
+ self, execution_preferences: Optional[ExecutionPreferences]
100
+ ) -> None:
101
+ if execution_preferences is not None:
102
+ self.program.model.execution_preferences = execution_preferences
103
+
104
+ def execute_quantum_program(
105
+ self, operation: str, **kwargs: Any
106
+ ) -> ResultsCollection:
107
+ self.program.model.classical_execution_code = generate_code_snippet(
108
+ operation, **kwargs
109
+ )
110
+ return execute(self.qprog).result()
111
+
112
+ def sample(self, parameters: Optional[ExecutionParams] = None) -> ExecutionDetails:
113
+ return cast(
114
+ ExecutionDetails,
115
+ self.execute_quantum_program(
116
+ SupportedPrimitives.SAMPLE, parameters=format_parameters(parameters)
117
+ )[0].value,
118
+ )
119
+
120
+ def batch_sample(self, parameters: List[ExecutionParams]) -> List[ExecutionDetails]:
121
+ return cast(
122
+ MultipleExecutionDetails,
123
+ self.execute_quantum_program(
124
+ SupportedPrimitives.BATCH_SAMPLE,
125
+ parameters=format_parameters(parameters),
126
+ )[0].value,
127
+ ).details
128
+
129
+ def estimate(
130
+ self, hamiltonian: Hamiltonian, parameters: Optional[ExecutionParams] = None
131
+ ) -> EstimationResult:
132
+ return cast(
133
+ EstimationResult,
134
+ self.execute_quantum_program(
135
+ SupportedPrimitives.ESTIMATE,
136
+ parameters=format_parameters(parameters),
137
+ hamiltonian=to_hamiltonian_str(hamiltonian),
138
+ )[0].value,
139
+ )
140
+
141
+ def batch_estimate(
142
+ self, hamiltonian: Hamiltonian, parameters: List[ExecutionParams]
143
+ ) -> List[EstimationResult]:
144
+ return cast(
145
+ EstimationResults,
146
+ self.execute_quantum_program(
147
+ SupportedPrimitives.BATCH_ESTIMATE,
148
+ parameters=format_parameters(parameters),
149
+ hamiltonian=to_hamiltonian_str(hamiltonian),
150
+ )[0].value,
151
+ ).results