classiq 0.86.0__py3-none-any.whl → 0.87.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 (96) hide show
  1. classiq/__init__.py +2 -0
  2. classiq/applications/chemistry/hartree_fock.py +5 -1
  3. classiq/applications/chemistry/op_utils.py +2 -2
  4. classiq/applications/combinatorial_helpers/encoding_mapping.py +11 -15
  5. classiq/applications/combinatorial_helpers/encoding_utils.py +6 -6
  6. classiq/applications/combinatorial_helpers/memory.py +4 -4
  7. classiq/applications/combinatorial_helpers/optimization_model.py +5 -5
  8. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +6 -10
  9. classiq/applications/combinatorial_helpers/pyomo_utils.py +27 -26
  10. classiq/applications/combinatorial_helpers/sympy_utils.py +2 -2
  11. classiq/applications/combinatorial_helpers/transformations/encoding.py +4 -6
  12. classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +4 -4
  13. classiq/applications/combinatorial_helpers/transformations/ising_converter.py +2 -2
  14. classiq/applications/combinatorial_helpers/transformations/penalty_support.py +3 -3
  15. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -0
  16. classiq/applications/hamiltonian/pauli_decomposition.py +33 -1
  17. classiq/evaluators/argument_types.py +15 -6
  18. classiq/evaluators/parameter_types.py +43 -39
  19. classiq/evaluators/qmod_annotated_expression.py +88 -11
  20. classiq/evaluators/qmod_expression_visitors/out_of_place_node_transformer.py +19 -0
  21. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +54 -11
  22. classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +40 -25
  23. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +29 -59
  24. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +12 -5
  25. classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +175 -28
  26. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +21 -14
  27. classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +9 -5
  28. classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +20 -1
  29. classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +97 -0
  30. classiq/evaluators/qmod_node_evaluators/name_evaluation.py +11 -26
  31. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +56 -0
  32. classiq/evaluators/qmod_node_evaluators/piecewise_evaluation.py +40 -0
  33. classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +2 -3
  34. classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +48 -21
  35. classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +53 -9
  36. classiq/evaluators/qmod_node_evaluators/utils.py +27 -5
  37. classiq/evaluators/qmod_type_inference/classical_type_inference.py +188 -0
  38. classiq/evaluators/qmod_type_inference/quantum_type_inference.py +292 -0
  39. classiq/evaluators/quantum_type_utils.py +0 -131
  40. classiq/execution/execution_session.py +1 -1
  41. classiq/execution/qnn.py +4 -1
  42. classiq/execution/user_budgets.py +1 -1
  43. classiq/interface/_version.py +1 -1
  44. classiq/interface/backend/backend_preferences.py +10 -30
  45. classiq/interface/backend/quantum_backend_providers.py +63 -52
  46. classiq/interface/generator/arith/binary_ops.py +107 -115
  47. classiq/interface/generator/arith/extremum_operations.py +33 -45
  48. classiq/interface/generator/arith/number_utils.py +4 -1
  49. classiq/interface/generator/circuit_code/types_and_constants.py +0 -9
  50. classiq/interface/generator/compiler_keywords.py +2 -0
  51. classiq/interface/generator/function_param_list.py +133 -5
  52. classiq/interface/generator/functions/classical_type.py +59 -2
  53. classiq/interface/generator/functions/qmod_python_interface.py +15 -0
  54. classiq/interface/generator/functions/type_name.py +6 -0
  55. classiq/interface/generator/model/preferences/preferences.py +1 -17
  56. classiq/interface/generator/quantum_program.py +1 -13
  57. classiq/interface/helpers/model_normalizer.py +2 -2
  58. classiq/interface/helpers/text_utils.py +7 -2
  59. classiq/interface/interface_version.py +1 -1
  60. classiq/interface/model/classical_if.py +40 -0
  61. classiq/interface/model/handle_binding.py +28 -16
  62. classiq/interface/model/quantum_type.py +61 -2
  63. classiq/interface/pretty_print/expression_to_qmod.py +24 -11
  64. classiq/interface/pyomo_extension/__init__.py +0 -4
  65. classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +2 -2
  66. classiq/model_expansions/arithmetic.py +43 -1
  67. classiq/model_expansions/arithmetic_compute_result_attrs.py +255 -0
  68. classiq/model_expansions/capturing/captured_vars.py +2 -5
  69. classiq/model_expansions/quantum_operations/allocate.py +22 -15
  70. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -3
  71. classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -71
  72. classiq/model_expansions/quantum_operations/bind.py +15 -7
  73. classiq/model_expansions/quantum_operations/call_emitter.py +2 -10
  74. classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -0
  75. classiq/open_library/functions/__init__.py +3 -0
  76. classiq/open_library/functions/lcu.py +117 -0
  77. classiq/qmod/builtins/enums.py +2 -2
  78. classiq/qmod/builtins/structs.py +33 -18
  79. classiq/qmod/pretty_print/expression_to_python.py +7 -9
  80. {classiq-0.86.0.dist-info → classiq-0.87.0.dist-info}/METADATA +3 -3
  81. {classiq-0.86.0.dist-info → classiq-0.87.0.dist-info}/RECORD +83 -89
  82. classiq/interface/generator/amplitude_estimation.py +0 -34
  83. classiq/interface/generator/function_param_list_without_self_reference.py +0 -160
  84. classiq/interface/generator/grover_diffuser.py +0 -93
  85. classiq/interface/generator/grover_operator.py +0 -106
  86. classiq/interface/generator/oracles/__init__.py +0 -3
  87. classiq/interface/generator/oracles/arithmetic_oracle.py +0 -82
  88. classiq/interface/generator/oracles/custom_oracle.py +0 -65
  89. classiq/interface/generator/oracles/oracle_abc.py +0 -76
  90. classiq/interface/generator/oracles/oracle_function_param_list.py +0 -6
  91. classiq/interface/generator/piecewise_linear_amplitude_loading.py +0 -165
  92. classiq/interface/generator/qpe.py +0 -169
  93. classiq/interface/grover/grover_modelling_params.py +0 -13
  94. classiq/model_expansions/transformers/var_splitter.py +0 -224
  95. /classiq/{interface/grover → evaluators/qmod_type_inference}/__init__.py +0 -0
  96. {classiq-0.86.0.dist-info → classiq-0.87.0.dist-info}/WHEEL +0 -0
classiq/__init__.py CHANGED
@@ -37,6 +37,7 @@ from classiq.applications.combinatorial_optimization import (
37
37
  from classiq.applications.hamiltonian.pauli_decomposition import (
38
38
  hamiltonian_to_matrix,
39
39
  matrix_to_hamiltonian,
40
+ matrix_to_pauli_operator,
40
41
  )
41
42
  from classiq.execution import * # noqa: F403
42
43
  from classiq.execution import __all__ as _execution_all
@@ -109,6 +110,7 @@ __all__ = (
109
110
  "show",
110
111
  "hamiltonian_to_matrix",
111
112
  "matrix_to_hamiltonian",
113
+ "matrix_to_pauli_operator",
112
114
  "quantum_program_from_qasm",
113
115
  "quantum_program_from_qasm_async",
114
116
  ]
@@ -55,7 +55,11 @@ def _get_hf_qubit_op(
55
55
  problem: FermionHamiltonianProblem, mapper: FermionToQubitMapper
56
56
  ) -> QubitOperator:
57
57
  hf_fermion_op = get_hf_fermion_op(problem)
58
- return mapper.map(hf_fermion_op)
58
+ # In case of tapering: We need to taper the state, not the operator. This can be done by passing is_invariant=True
59
+ # (even though the operator is not necessarily invariant).
60
+ # Then, taper_off_qubits eliminates the qubits to taper off (up to a sign)
61
+ # for each term in the Hamiltonian.
62
+ return mapper.map(hf_fermion_op, is_invariant=True)
59
63
 
60
64
 
61
65
  def _apply_term_on_zero_state(
@@ -27,7 +27,7 @@ def qubit_op_to_pauli_terms(
27
27
  ) -> SparsePauliOp:
28
28
  n_qubits = _get_n_qubits(qubit_op, n_qubits)
29
29
  return SparsePauliOp(
30
- terms=[ # type:ignore[arg-type]
30
+ terms=[
31
31
  SparsePauliTerm(
32
32
  paulis=[ # type:ignore[arg-type]
33
33
  IndexedPauli(
@@ -40,7 +40,7 @@ def qubit_op_to_pauli_terms(
40
40
  )
41
41
  for term, coeff in qubit_op.terms.items()
42
42
  ],
43
- num_qubits=n_qubits, # type:ignore[arg-type]
43
+ num_qubits=n_qubits,
44
44
  )
45
45
 
46
46
 
@@ -3,7 +3,7 @@ from dataclasses import dataclass, field
3
3
  from typing import Optional, Union
4
4
 
5
5
  import pyomo.environ as pyo
6
- from pyomo.core.base import _GeneralVarData
6
+ from pyomo.core.base.var import VarData
7
7
  from pyomo.core.expr.visitor import clone_expression, identify_variables
8
8
 
9
9
  from classiq.interface.combinatorial_optimization.encoding_types import EncodingType
@@ -14,9 +14,9 @@ from classiq.applications.combinatorial_helpers import pyomo_utils
14
14
 
15
15
  @dataclass
16
16
  class VarExpressionMapping:
17
- var: _GeneralVarData
17
+ var: VarData
18
18
  expr: pyo.Expression
19
- encodings_vars: list[_GeneralVarData] = field(default_factory=list)
19
+ encodings_vars: list[VarData] = field(default_factory=list)
20
20
 
21
21
 
22
22
  class EncodingMapping:
@@ -25,11 +25,11 @@ class EncodingMapping:
25
25
  self.encoding_type = encoding_type
26
26
 
27
27
  @property
28
- def original_vars(self) -> list[_GeneralVarData]:
28
+ def original_vars(self) -> list[VarData]:
29
29
  return [pair.var for pair in self._data]
30
30
 
31
31
  @property
32
- def encodings_vars(self) -> list[_GeneralVarData]:
32
+ def encodings_vars(self) -> list[VarData]:
33
33
  return list(
34
34
  itertools.chain.from_iterable(
35
35
  var_mapping.encodings_vars for var_mapping in self._data
@@ -45,9 +45,9 @@ class EncodingMapping:
45
45
 
46
46
  def add(
47
47
  self,
48
- original_var: _GeneralVarData,
48
+ original_var: VarData,
49
49
  encoding_expr: pyo.Expression,
50
- encodings_vars: Union[list[_GeneralVarData], None] = None,
50
+ encodings_vars: Union[list[VarData], None] = None,
51
51
  ) -> None:
52
52
  if encodings_vars is None:
53
53
  encodings_vars = list(identify_variables(encoding_expr))
@@ -59,25 +59,21 @@ class EncodingMapping:
59
59
  )
60
60
  )
61
61
 
62
- def _check_unique_encoding_vars(self, variables: list[_GeneralVarData]) -> None:
62
+ def _check_unique_encoding_vars(self, variables: list[VarData]) -> None:
63
63
  assert all(
64
64
  not pyomo_utils.contains(var, self.encodings_vars) for var in variables
65
65
  )
66
66
 
67
- def get_var_expr_mapping(
68
- self, original_var: _GeneralVarData
69
- ) -> VarExpressionMapping:
67
+ def get_var_expr_mapping(self, original_var: VarData) -> VarExpressionMapping:
70
68
  for var_expr_mapping in self._data:
71
69
  if var_expr_mapping.var is original_var:
72
70
  return var_expr_mapping
73
71
  raise ClassiqCombOptError("No variable expression mapping found.")
74
72
 
75
- def get_encoding_vars(self, original_var: _GeneralVarData) -> list[_GeneralVarData]:
73
+ def get_encoding_vars(self, original_var: VarData) -> list[VarData]:
76
74
  return self.get_var_expr_mapping(original_var).encodings_vars
77
75
 
78
- def get_original_var(
79
- self, encoding_var: _GeneralVarData
80
- ) -> Optional[_GeneralVarData]:
76
+ def get_original_var(self, encoding_var: VarData) -> Optional[VarData]:
81
77
  for original_var in self.original_vars:
82
78
  if pyomo_utils.contains(encoding_var, self.get_encoding_vars(original_var)):
83
79
  return original_var
@@ -4,14 +4,14 @@ from typing import Any, Union, cast
4
4
 
5
5
  import numpy as np
6
6
  import pyomo.environ as pyo
7
- from pyomo.core.base import _GeneralVarData
8
7
  from pyomo.core.base.component import ComponentData, _ComponentBase
9
8
  from pyomo.core.base.constraint import _GeneralConstraintData
9
+ from pyomo.core.base.var import VarData
10
10
  from pyomo.core.expr.numeric_expr import (
11
11
  MonomialTermExpression,
12
12
  ProductExpression,
13
- clone_expression,
14
13
  )
14
+ from pyomo.core.expr.visitor import clone_expression
15
15
  from sympy import Expr
16
16
 
17
17
  from classiq.interface.exceptions import ClassiqCombOptNoSolutionError
@@ -26,13 +26,13 @@ def is_model_encodable(model: pyo.ConcreteModel) -> bool:
26
26
  return not all(is_var_binary(var) for var in variables)
27
27
 
28
28
 
29
- def is_var_binary(var: _GeneralVarData) -> bool:
29
+ def is_var_binary(var: VarData) -> bool:
30
30
  return var.domain == pyo.Binary or (
31
31
  var.domain in _INTEGER_TYPES and var.lb == 0 and var.ub == 1
32
32
  )
33
33
 
34
34
 
35
- def is_var_span_power_of_2(var: _GeneralVarData) -> bool:
35
+ def is_var_span_power_of_2(var: VarData) -> bool:
36
36
  var_span = get_var_span(var)
37
37
  return math.log2(var_span + 1).is_integer()
38
38
 
@@ -46,7 +46,7 @@ def is_obj_encoded(var: _ComponentBase) -> bool:
46
46
  return ENCODED_SUFFIX in var.name
47
47
 
48
48
 
49
- def get_var_span(var: _GeneralVarData) -> int:
49
+ def get_var_span(var: VarData) -> int:
50
50
  return var.ub - var.lb
51
51
 
52
52
 
@@ -54,7 +54,7 @@ def encoded_obj_name(name: str) -> str:
54
54
  return name + ENCODED_SUFFIX
55
55
 
56
56
 
57
- def get_encoded_var_index(var: _GeneralVarData) -> int:
57
+ def get_encoded_var_index(var: VarData) -> int:
58
58
  indexed_var = var.parent_component()
59
59
  index = [
60
60
  index_temp for index_temp, var_temp in indexed_var.items() if var_temp is var
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from pyomo.core.base import _GeneralVarData
3
+ from pyomo.core.base.var import VarData
4
4
 
5
5
  from classiq.interface.generator.function_params import IOName
6
6
 
@@ -19,13 +19,13 @@ class InternalQuantumReg:
19
19
  class MemoryMapping:
20
20
  def __init__(
21
21
  self,
22
- variables: list[_GeneralVarData],
22
+ variables: list[VarData],
23
23
  vars_encoding_mapping: EncodingMapping | None = None,
24
24
  ) -> None:
25
25
  self.substitution_dict: dict[int, InternalQuantumReg] = dict()
26
26
  self.qubit_allocation: dict[IOName, tuple[int, int]] = dict()
27
27
  self.vars_encoding_mapping: EncodingMapping | None = vars_encoding_mapping
28
- self.vars: list[_GeneralVarData] = variables
28
+ self.vars: list[VarData] = variables
29
29
  self._allocate_memory()
30
30
 
31
31
  def __len__(self) -> int:
@@ -65,7 +65,7 @@ class MemoryMapping:
65
65
  )
66
66
 
67
67
 
68
- def get_var_name(var: _GeneralVarData) -> str:
68
+ def get_var_name(var: VarData) -> str:
69
69
  return (
70
70
  var.name.replace("[", "")
71
71
  .replace("]", "")
@@ -6,8 +6,8 @@ from typing import Optional, Union
6
6
  import pyomo.environ as pyo
7
7
  import sympy
8
8
  from pyomo.core import ConcreteModel
9
- from pyomo.core.base import _GeneralVarData
10
9
  from pyomo.core.base.constraint import _GeneralConstraintData
10
+ from pyomo.core.base.var import VarData
11
11
  from pyomo.core.expr.sympy_tools import sympyify_expression
12
12
  from pyomo.environ import Expression
13
13
 
@@ -92,15 +92,15 @@ class OptimizationModel:
92
92
  self._initialize_objective_not_encoded(model_copy)
93
93
 
94
94
  @property
95
- def vars(self) -> list[_GeneralVarData]:
96
- return pyomo_utils.extract(self._model, _GeneralVarData)
95
+ def vars(self) -> list[VarData]:
96
+ return pyomo_utils.extract(self._model, VarData)
97
97
 
98
98
  @property
99
- def vars_not_encoded(self) -> list[_GeneralVarData]:
99
+ def vars_not_encoded(self) -> list[VarData]:
100
100
  return list(filterfalse(encoding_utils.is_obj_encoded, self.vars))
101
101
 
102
102
  @property
103
- def _ising_vars(self) -> list[_GeneralVarData]:
103
+ def _ising_vars(self) -> list[VarData]:
104
104
  if self.is_encoded:
105
105
  return [
106
106
  var
@@ -28,26 +28,22 @@ def pauli_operator_to_hamiltonian(pauli_list: PydanticPauliList) -> list[PauliTe
28
28
 
29
29
 
30
30
  def pauli_operator_to_sparse_hamiltonian(
31
- pauli_list: PydanticPauliList,
31
+ pauli_list: list[PauliTerm],
32
32
  ) -> SparsePauliOp:
33
33
  pauli_terms: list[SparsePauliTerm] = []
34
34
  for pauli_term in pauli_list:
35
- if not isinstance(pauli_term[1], complex) or pauli_term[1].imag != 0:
36
- raise ClassiqNonNumericCoefficientInPauliError(
37
- "Coefficient is not a number."
38
- )
39
35
  term = SparsePauliTerm(
40
36
  paulis=[ # type:ignore[arg-type]
41
- IndexedPauli(pauli=Pauli[p], index=i) # type:ignore[arg-type]
42
- for i, p in enumerate(pauli_term[0][::-1])
37
+ IndexedPauli(pauli=p, index=i) # type:ignore[arg-type]
38
+ for i, p in enumerate(pauli_term.pauli[::-1])
43
39
  ],
44
- coefficient=pauli_term[1].real, # type: ignore[arg-type]
40
+ coefficient=pauli_term.coefficient,
45
41
  )
46
42
  pauli_terms.append(term)
47
43
 
48
44
  return SparsePauliOp(
49
- terms=pauli_terms, # type:ignore[arg-type]
50
- num_qubits=len(pauli_list[0][0]), # type:ignore[arg-type]
45
+ terms=pauli_terms,
46
+ num_qubits=len(pauli_list[0].pauli), # type: ignore[arg-type]
51
47
  )
52
48
 
53
49
 
@@ -15,11 +15,11 @@ import pyomo.core.expr.numeric_expr as pyo_expr
15
15
  import pyomo.environ as pyo
16
16
  import sympy
17
17
  from pyomo.core import ConcreteModel, Constraint, Objective, Var, maximize
18
- from pyomo.core.base import _GeneralVarData
19
18
  from pyomo.core.base.component import ComponentData
20
19
  from pyomo.core.base.constraint import _GeneralConstraintData
21
20
  from pyomo.core.base.indexed_component import IndexedComponent
22
21
  from pyomo.core.base.objective import ScalarObjective
22
+ from pyomo.core.base.var import VarData
23
23
  from pyomo.core.expr.base import ExpressionBase
24
24
  from pyomo.core.expr.sympy_tools import (
25
25
  Pyomo2SympyVisitor,
@@ -35,7 +35,7 @@ from classiq.interface.generator.types.struct_declaration import StructDeclarati
35
35
  from classiq.qmod.qmod_variable import QArray, QBit, QNum, QStruct, QVar
36
36
  from classiq.qmod.symbolic_expr import SymbolicExpr
37
37
 
38
- ListVars = list[_GeneralVarData]
38
+ ListVars = list[VarData]
39
39
  SUPPORTED_TYPES = [
40
40
  pyo.Binary,
41
41
  pyo.Integers,
@@ -65,12 +65,12 @@ class CombinatorialOptimizationStructDeclaration(StructDeclaration):
65
65
  )
66
66
 
67
67
 
68
- def contains(var_data: _GeneralVarData, vars_data: ListVars) -> bool:
68
+ def contains(var_data: VarData, vars_data: ListVars) -> bool:
69
69
  # HACK: standard "__containts__ (in)" method doesn't work, because pyomo overrode the __eq__ method (IMO)
70
70
  return any(var_data is var_data_temp for var_data_temp in vars_data)
71
71
 
72
72
 
73
- def remove(var_data: _GeneralVarData, vars_data: ListVars) -> ListVars:
73
+ def remove(var_data: VarData, vars_data: ListVars) -> ListVars:
74
74
  # HACK: standard "list method remove" method doesn't work, because pyomo overrode the __eq__ method (IMO)
75
75
  assert contains(var_data, vars_data), "var not in list"
76
76
  vars_data = vars_data.copy()
@@ -81,7 +81,7 @@ def remove(var_data: _GeneralVarData, vars_data: ListVars) -> ListVars:
81
81
  return vars_data
82
82
 
83
83
 
84
- def index(var_data: _GeneralVarData, vars_data: ListVars) -> int:
84
+ def index(var_data: VarData, vars_data: ListVars) -> int:
85
85
  # HACK: standard "index method" doesn't work.
86
86
  assert contains(var_data, vars_data), "var not in list"
87
87
  idxs = [
@@ -94,7 +94,7 @@ T = TypeVar("T")
94
94
 
95
95
 
96
96
  def extract(model: ConcreteModel, type_: type[T]) -> list[T]:
97
- if type_ == _GeneralVarData:
97
+ if type_ == VarData:
98
98
  type_ = Var
99
99
 
100
100
  elif type_ == _GeneralConstraintData:
@@ -267,15 +267,13 @@ def pyomo2qmod(
267
267
  )
268
268
 
269
269
 
270
- def pyomo_to_qmod_qstruct(
271
- struct_name: str, vars: list[_GeneralVarData]
272
- ) -> type[QStruct]:
270
+ def pyomo_to_qmod_qstruct(struct_name: str, vars: list[VarData]) -> type[QStruct]:
273
271
  qmod_struct = type(struct_name, (QStruct,), {})
274
272
  qmod_struct.__annotations__ = _get_qstruct_fields(vars)
275
273
  return qmod_struct
276
274
 
277
275
 
278
- def _get_qstruct_fields(vars: list[_GeneralVarData]) -> dict[str, type[QVar]]:
276
+ def _get_qstruct_fields(vars: list[VarData]) -> dict[str, type[QVar]]:
279
277
  array_type_sizes = _get_array_sizes(vars)
280
278
  fields: dict[str, type[QVar]] = {}
281
279
  for var in vars:
@@ -283,7 +281,7 @@ def _get_qstruct_fields(vars: list[_GeneralVarData]) -> dict[str, type[QVar]]:
283
281
  return fields
284
282
 
285
283
 
286
- def _get_array_sizes(vars: list[_GeneralVarData]) -> dict[str, tuple[int, ...]]:
284
+ def _get_array_sizes(vars: list[VarData]) -> dict[str, tuple[int, ...]]:
287
285
  array_types: dict[str, set[tuple]] = defaultdict(set)
288
286
  for var in vars:
289
287
  if is_index_var(var):
@@ -293,11 +291,13 @@ def _get_array_sizes(vars: list[_GeneralVarData]) -> dict[str, tuple[int, ...]]:
293
291
  return {
294
292
  name: dimensions
295
293
  for name, indices in array_types.items()
296
- if (dimensions := _get_indices_dimensions(indices)) is not None
294
+ if (dimensions := _get_indices_dimensions(indices, strict=False)) is not None
297
295
  }
298
296
 
299
297
 
300
- def _get_indices_dimensions(indices: set[tuple[int, ...]]) -> Optional[tuple[int, ...]]:
298
+ def _get_indices_dimensions(
299
+ indices: set[tuple[int, ...]], *, strict: bool
300
+ ) -> Optional[tuple[int, ...]]:
301
301
  indices_list = list(indices)
302
302
  if len(indices) == 0:
303
303
  return None
@@ -313,16 +313,16 @@ def _get_indices_dimensions(indices: set[tuple[int, ...]]) -> Optional[tuple[int
313
313
  min(dimension_bounds[dim_idx][0], idx),
314
314
  max(dimension_bounds[dim_idx][1], idx),
315
315
  )
316
- if any(lb != 0 for lb, ub in dimension_bounds):
316
+ if strict and any(lb != 0 for lb, ub in dimension_bounds):
317
317
  return None
318
318
  dimensions = tuple(ub + 1 for _, ub in dimension_bounds)
319
- if reduce(mul, dimensions) != len(indices_list):
319
+ if strict and reduce(mul, dimensions) != len(indices_list):
320
320
  return None
321
321
  return dimensions
322
322
 
323
323
 
324
324
  def _add_qmod_field(
325
- var: _GeneralVarData,
325
+ var: VarData,
326
326
  array_type_sizes: dict[str, tuple[int, ...]],
327
327
  fields: dict[str, type[QVar]],
328
328
  ) -> None:
@@ -331,15 +331,16 @@ def _add_qmod_field(
331
331
  var_name = get_field_name(var)
332
332
  fields[var_name] = _get_qmod_field_type(var_name, var)
333
333
  return
334
+ if parent_name in fields:
335
+ return
334
336
  dimensions = array_type_sizes[parent_name]
335
- if index_as_tuple(var.index()) == tuple(0 for _ in range(len(dimensions))):
336
- qmod_type: type[QVar] = _get_qmod_field_type(parent_name, var)
337
- for dim in reversed(dimensions):
338
- qmod_type = QArray[qmod_type, dim] # type:ignore[valid-type]
339
- fields[parent_name] = qmod_type
337
+ qmod_type: type[QVar] = _get_qmod_field_type(parent_name, var)
338
+ for dim in reversed(dimensions):
339
+ qmod_type = QArray[qmod_type, dim] # type:ignore[valid-type]
340
+ fields[parent_name] = qmod_type
340
341
 
341
342
 
342
- def _get_qmod_field_type(var_name: str, var_data: _GeneralVarData) -> type[QVar]:
343
+ def _get_qmod_field_type(var_name: str, var_data: VarData) -> type[QVar]:
343
344
  if var_data.domain not in SUPPORTED_TYPES:
344
345
  raise ClassiqValueError(
345
346
  f"Type {str(var_data.domain)!r} of variable {var_name!r} is not supported"
@@ -396,11 +397,11 @@ def _get_item(obj: Any, multi_index: tuple[int, ...]) -> Any:
396
397
  return obj
397
398
 
398
399
 
399
- def get_field_name(var: _GeneralVarData) -> str:
400
+ def get_field_name(var: VarData) -> str:
400
401
  return var.local_name.replace("[", "_").replace("]", "").replace(",", "_")
401
402
 
402
403
 
403
- def is_index_var(var: _GeneralVarData) -> bool:
404
+ def is_index_var(var: VarData) -> bool:
404
405
  index = var.index()
405
406
  return isinstance(index, int) or (
406
407
  isinstance(index, tuple) and all(isinstance(idx, int) for idx in index)
@@ -415,7 +416,7 @@ def index_as_tuple(index: Union[int, tuple[int, ...]]) -> tuple[int, ...]:
415
416
 
416
417
  @contextmanager
417
418
  def add_var_domain_constraints(model: ConcreteModel) -> Iterator[None]:
418
- vars = extract(model, _GeneralVarData)
419
+ vars = extract(model, VarData)
419
420
  constraints = [
420
421
  constraint
421
422
  for var in vars
@@ -431,7 +432,7 @@ def add_var_domain_constraints(model: ConcreteModel) -> Iterator[None]:
431
432
  model.del_component("var_domain_constraints")
432
433
 
433
434
 
434
- def _get_var_domain_constraint(var: _GeneralVarData) -> Optional[ExpressionBase]:
435
+ def _get_var_domain_constraint(var: VarData) -> Optional[ExpressionBase]:
435
436
  bounds = var.bounds
436
437
  if (
437
438
  type(bounds) is not tuple
@@ -1,12 +1,12 @@
1
1
  from typing import Optional
2
2
 
3
3
  import pyomo.core as pyo
4
- from pyomo.core.base import _GeneralVarData
4
+ from pyomo.core.base.var import VarData
5
5
  from pyomo.core.expr.sympy_tools import Pyomo2SympyVisitor, PyomoSympyBimap
6
6
  from sympy import Expr
7
7
 
8
8
 
9
- def sympyify_vars(variables: list[_GeneralVarData]) -> PyomoSympyBimap:
9
+ def sympyify_vars(variables: list[VarData]) -> PyomoSympyBimap:
10
10
  symbols_map = PyomoSympyBimap()
11
11
  for var in variables:
12
12
  Pyomo2SympyVisitor(symbols_map).walk_expression(var)
@@ -3,7 +3,7 @@ from itertools import chain, product
3
3
  from typing import Callable, Union
4
4
 
5
5
  import pyomo.environ as pyo
6
- from pyomo.core.base import _GeneralVarData
6
+ from pyomo.core.base.var import VarData
7
7
  from pyomo.core.expr.numeric_expr import ExpressionBase
8
8
  from pyomo.core.expr.relational_expr import EqualityExpression
9
9
 
@@ -25,9 +25,7 @@ def _make_invalid_encoding_type_error(
25
25
  )
26
26
 
27
27
 
28
- def encoding_length(
29
- var: _GeneralVarData, encoding_type: Union[EncodingType, None]
30
- ) -> int:
28
+ def encoding_length(var: VarData, encoding_type: Union[EncodingType, None]) -> int:
31
29
  if encoding_type is None:
32
30
  return 1
33
31
 
@@ -99,7 +97,7 @@ class ModelEncoder:
99
97
  return vars_encoding_mapping
100
98
 
101
99
  def _get_encoding_expr(
102
- self, var_data: _GeneralVarData, encoding_vars: list[_GeneralVarData]
100
+ self, var_data: VarData, encoding_vars: list[VarData]
103
101
  ) -> pyo.Expression:
104
102
  if self.encoding_type == EncodingType.BINARY:
105
103
  var_span = encoding_utils.get_var_span(var_data)
@@ -119,7 +117,7 @@ class ModelEncoder:
119
117
  return encoding_expr
120
118
 
121
119
  def _get_binary_coeffs(
122
- self, encoding_vars: list[_GeneralVarData], var_span: int
120
+ self, encoding_vars: list[VarData], var_span: int
123
121
  ) -> list[int]:
124
122
  num_vars = len(encoding_vars)
125
123
  if self.qsolver == QSolver.QAOAMixer:
@@ -3,9 +3,9 @@ from typing import Union
3
3
 
4
4
  from pyomo.core import ConcreteModel, Var
5
5
  from pyomo.core.base.constraint import _GeneralConstraintData
6
+ from pyomo.core.base.var import VarData
6
7
  from pyomo.core.expr.relational_expr import EqualityExpression
7
8
  from pyomo.core.expr.visitor import identify_variables
8
- from pyomo.repn.standard_repn import _GeneralVarData
9
9
 
10
10
  from classiq.interface.exceptions import (
11
11
  ClassiqCombOptNoSolutionError,
@@ -81,10 +81,10 @@ def _get_fixing_constraints(model: ConcreteModel) -> list[_GeneralConstraintData
81
81
 
82
82
  def _get_var_and_value_from_fixing_constraint(
83
83
  constraint: _GeneralConstraintData,
84
- ) -> tuple[_GeneralVarData, float]:
84
+ ) -> tuple[VarData, float]:
85
85
  var = next(identify_variables(constraint.body))
86
86
 
87
- if isinstance(constraint.body, _GeneralVarData):
87
+ if isinstance(constraint.body, VarData):
88
88
  return var, constraint.upper.value
89
89
 
90
90
  symbols_map = sympyify_vars([var])
@@ -125,7 +125,7 @@ def add_fixed_variables_to_solution(
125
125
  return solution_with_fixed
126
126
 
127
127
 
128
- def _get_value_if_exists(var: _GeneralVarData) -> Union[int, _GeneralVarData]:
128
+ def _get_value_if_exists(var: VarData) -> Union[int, VarData]:
129
129
  return var.value if var.value is not None else var
130
130
 
131
131
 
@@ -1,6 +1,6 @@
1
1
  import pyomo
2
2
  import sympy as sp
3
- from pyomo.core.base import _GeneralVarData
3
+ from pyomo.core.base.var import VarData
4
4
 
5
5
  from classiq.interface.chemistry.operator import PauliOperator
6
6
  from classiq.interface.exceptions import ClassiqCombOptNotSupportedProblemError
@@ -18,7 +18,7 @@ PYOMO_PARSING_ERROR_MESAGE = "Parsing of this pyomo model is not supported."
18
18
 
19
19
  def convert_pyomo_to_hamiltonian(
20
20
  pyomo_expr: pyomo.core.Expression,
21
- ordered_pyomo_vars: list[_GeneralVarData],
21
+ ordered_pyomo_vars: list[VarData],
22
22
  qregs: list[InternalQuantumReg],
23
23
  ) -> PauliOperator:
24
24
  symbols_map = sympyify_vars(ordered_pyomo_vars)
@@ -1,7 +1,7 @@
1
1
  from pyomo.core import ConcreteModel
2
2
  from pyomo.core.base.constraint import _GeneralConstraintData
3
+ from pyomo.core.base.var import VarData
3
4
  from pyomo.core.expr.relational_expr import EqualityExpression
4
- from pyomo.repn.standard_repn import _GeneralVarData
5
5
 
6
6
  from classiq.applications.combinatorial_helpers import (
7
7
  allowed_constraints,
@@ -12,7 +12,7 @@ from classiq.applications.combinatorial_helpers.sympy_utils import sympyify_expr
12
12
 
13
13
 
14
14
  def is_model_penalty_supported(model: ConcreteModel) -> bool:
15
- variables = extract(model, _GeneralVarData)
15
+ variables = extract(model, VarData)
16
16
  is_vars_supported = all(is_var_penalty_supported(var) for var in variables)
17
17
 
18
18
  constraints = extract(model, _GeneralConstraintData)
@@ -22,7 +22,7 @@ def is_model_penalty_supported(model: ConcreteModel) -> bool:
22
22
  return is_vars_supported and is_constraints_supported
23
23
 
24
24
 
25
- def is_var_penalty_supported(var: _GeneralVarData) -> bool:
25
+ def is_var_penalty_supported(var: VarData) -> bool:
26
26
  return encoding_utils.is_var_binary(var) or encoding_utils.is_var_span_power_of_2(
27
27
  var
28
28
  )
@@ -143,6 +143,10 @@ class CombinatorialProblem:
143
143
  ]
144
144
  return pd.DataFrame.from_records(parsed_result)
145
145
 
146
+ def teardown(self) -> None:
147
+ if self._es is not None:
148
+ self._es.close()
149
+
146
150
 
147
151
  def execute_qaoa(
148
152
  problem_vars: type[QVar],
@@ -3,10 +3,14 @@ from typing import Union
3
3
  import numpy as np
4
4
  from sympy import fwht
5
5
 
6
+ from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import (
7
+ pauli_operator_to_sparse_hamiltonian,
8
+ )
6
9
  from classiq.qmod import ( # type:ignore[attr-defined]
7
10
  Pauli,
8
11
  PauliTerm,
9
12
  )
13
+ from classiq.qmod.builtins.structs import SparsePauliOp
10
14
 
11
15
  ATOL = 1e-12
12
16
  PAULI_MATRICES_DICT = {
@@ -93,6 +97,18 @@ def matrix_to_hamiltonian(
93
97
  return hamiltonian
94
98
 
95
99
 
100
+ def matrix_to_pauli_operator(
101
+ mat: np.ndarray, tol: float = ATOL, is_hermitian: bool = True
102
+ ) -> SparsePauliOp:
103
+ """
104
+ The decomposition per set is done by the Walsh-Hadamard transform,
105
+ since the transformation between {e_0,e_3} ({e_1,e_2}) to {I,Z} ({X,iY}) is the Hadamard matrix.
106
+ """
107
+ return pauli_operator_to_sparse_hamiltonian(
108
+ matrix_to_hamiltonian(mat, tol=tol, is_hermitian=is_hermitian)
109
+ )
110
+
111
+
96
112
  # convert a single puali string of length N to 2**N X 2**N matrix
97
113
  def pauli_string_to_mat(seq: list[Pauli]) -> np.ndarray:
98
114
  real_matrix = PAULI_MATRICES_DICT[seq[0]]
@@ -101,8 +117,24 @@ def pauli_string_to_mat(seq: list[Pauli]) -> np.ndarray:
101
117
  return real_matrix
102
118
 
103
119
 
120
+ def _sparse_pauli_to_list(operator: SparsePauliOp) -> list[PauliTerm]:
121
+ terms_list = []
122
+ for term in operator.terms:
123
+ pauli_list = [Pauli.I for i in range(operator.num_qubits)]
124
+ for p in term.paulis: # type:ignore[attr-defined]
125
+ pauli_list[p.index] = p.pauli
126
+ terms_list.append(
127
+ PauliTerm(coefficient=term.coefficient, pauli=list(reversed(pauli_list)))
128
+ )
129
+ return terms_list
130
+
131
+
104
132
  # return matrix from hamiltonian
105
- def hamiltonian_to_matrix(hamiltonian: list[PauliTerm]) -> np.ndarray:
133
+ def hamiltonian_to_matrix(
134
+ hamiltonian: Union[list[PauliTerm], SparsePauliOp],
135
+ ) -> np.ndarray:
136
+ if isinstance(hamiltonian, SparsePauliOp):
137
+ hamiltonian = _sparse_pauli_to_list(hamiltonian)
106
138
  matrix = np.zeros(
107
139
  [2 ** len(hamiltonian[0].pauli), 2 ** len(hamiltonian[0].pauli)],
108
140
  dtype=np.complex_,