classiq 0.62.0__py3-none-any.whl → 0.63.1__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 (75) hide show
  1. classiq/__init__.py +3 -0
  2. classiq/_internals/api_wrapper.py +6 -26
  3. classiq/_internals/client.py +1 -9
  4. classiq/applications/chemistry/chemistry_model_constructor.py +1 -1
  5. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +26 -8
  6. classiq/applications/combinatorial_helpers/optimization_model.py +5 -1
  7. classiq/applications/combinatorial_helpers/pyomo_utils.py +106 -27
  8. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
  9. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -2
  10. classiq/applications/grover/grover_model_constructor.py +1 -1
  11. classiq/applications/libraries/qmci_library.py +2 -1
  12. classiq/execution/execution_session.py +66 -96
  13. classiq/execution/jobs.py +3 -9
  14. classiq/interface/_version.py +1 -1
  15. classiq/interface/backend/backend_preferences.py +8 -5
  16. classiq/interface/backend/pydantic_backend.py +1 -1
  17. classiq/interface/chemistry/operator.py +0 -204
  18. classiq/interface/execution/primitives.py +1 -0
  19. classiq/interface/generator/compiler_keywords.py +4 -0
  20. classiq/interface/generator/functions/type_name.py +6 -0
  21. classiq/interface/generator/generated_circuit_data.py +22 -7
  22. classiq/interface/generator/model/model.py +3 -0
  23. classiq/interface/generator/model/preferences/preferences.py +13 -0
  24. classiq/interface/generator/quantum_function_call.py +4 -2
  25. classiq/interface/model/handle_binding.py +50 -5
  26. classiq/interface/model/quantum_type.py +16 -0
  27. classiq/interface/server/routes.py +1 -3
  28. classiq/model_expansions/capturing/captured_vars.py +102 -19
  29. classiq/model_expansions/closure.py +19 -56
  30. classiq/model_expansions/function_builder.py +13 -8
  31. classiq/model_expansions/generative_functions.py +15 -1
  32. classiq/model_expansions/interpreter.py +94 -32
  33. classiq/model_expansions/model_tables.py +4 -0
  34. classiq/model_expansions/quantum_operations/call_emitter.py +61 -2
  35. classiq/model_expansions/quantum_operations/classicalif.py +1 -1
  36. classiq/model_expansions/quantum_operations/control.py +3 -10
  37. classiq/model_expansions/quantum_operations/emitter.py +1 -1
  38. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +1 -2
  39. classiq/model_expansions/quantum_operations/repeat.py +4 -3
  40. classiq/model_expansions/scope.py +7 -1
  41. classiq/model_expansions/scope_initialization.py +34 -25
  42. classiq/model_expansions/transformers/var_splitter.py +57 -7
  43. classiq/open_library/__init__.py +4 -0
  44. classiq/open_library/functions/__init__.py +130 -0
  45. classiq/{qmod/builtins → open_library}/functions/amplitude_estimation.py +2 -2
  46. classiq/{qmod/builtins → open_library}/functions/discrete_sine_cosine_transform.py +6 -4
  47. classiq/{qmod/builtins → open_library}/functions/grover.py +2 -2
  48. classiq/{qmod/builtins → open_library}/functions/linear_pauli_rotation.py +1 -1
  49. classiq/{qmod/builtins → open_library}/functions/modular_exponentiation.py +2 -2
  50. classiq/{qmod/builtins → open_library}/functions/qpe.py +2 -2
  51. classiq/{qmod/builtins → open_library}/functions/state_preparation.py +6 -149
  52. classiq/{qmod/builtins → open_library}/functions/swap_test.py +1 -1
  53. classiq/open_library/functions/utility_functions.py +81 -0
  54. classiq/{qmod/builtins → open_library}/functions/variational.py +1 -1
  55. classiq/qmod/builtins/functions/__init__.py +4 -130
  56. classiq/qmod/builtins/functions/allocation.py +150 -0
  57. classiq/qmod/builtins/functions/arithmetic.py +0 -34
  58. classiq/qmod/builtins/functions/operators.py +0 -6
  59. classiq/qmod/create_model_function.py +8 -162
  60. classiq/qmod/generative.py +0 -16
  61. classiq/qmod/model_state_container.py +7 -0
  62. classiq/qmod/native/pretty_printer.py +10 -11
  63. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  64. classiq/qmod/qfunc.py +11 -12
  65. classiq/qmod/qmod_variable.py +1 -3
  66. classiq/qmod/quantum_expandable.py +21 -0
  67. classiq/qmod/quantum_function.py +65 -3
  68. {classiq-0.62.0.dist-info → classiq-0.63.1.dist-info}/METADATA +1 -1
  69. {classiq-0.62.0.dist-info → classiq-0.63.1.dist-info}/RECORD +74 -71
  70. classiq/qmod/builtins/functions/utility_functions.py +0 -43
  71. /classiq/{qmod/builtins → open_library}/functions/hea.py +0 -0
  72. /classiq/{qmod/builtins → open_library}/functions/qaoa_penalty.py +0 -0
  73. /classiq/{qmod/builtins → open_library}/functions/qft_functions.py +0 -0
  74. /classiq/{qmod/builtins → open_library}/functions/qsvt.py +0 -0
  75. {classiq-0.62.0.dist-info → classiq-0.63.1.dist-info}/WHEEL +0 -0
classiq/__init__.py CHANGED
@@ -46,6 +46,8 @@ from classiq.executor import (
46
46
  execute_async,
47
47
  set_quantum_program_execution_preferences,
48
48
  )
49
+ from classiq.open_library import * # noqa: F403
50
+ from classiq.open_library import __all__ as _open_library_all
49
51
  from classiq.qmod import * # noqa: F403
50
52
  from classiq.qmod import __all__ as _qmod_all
51
53
  from classiq.synthesis import (
@@ -114,6 +116,7 @@ __all__ = (
114
116
  + _sub_modules
115
117
  + _application_constructors_all
116
118
  + _qmod_all
119
+ + _open_library_all
117
120
  )
118
121
 
119
122
 
@@ -33,10 +33,6 @@ from classiq._internals.client import client
33
33
  from classiq._internals.jobs import JobPoller
34
34
 
35
35
  ResultType = TypeVar("ResultType", bound=pydantic.BaseModel)
36
- CLASSIQ_ACCEPT_HEADER = "X-Classiq-Accept-Version"
37
-
38
- _ACCEPT_HEADER = "X-Classiq-Accept-Version"
39
- _CONTENT_TYPE_HEADER = "X-Classiq-Content-Type-Version"
40
36
 
41
37
 
42
38
  class HTTPMethod(StrEnum):
@@ -143,16 +139,10 @@ class ApiWrapper:
143
139
  execution_input: dict,
144
140
  http_client: Optional[httpx.AsyncClient] = None,
145
141
  ) -> execution_request.ExecutionJobDetails:
146
- headers = {
147
- _ACCEPT_HEADER: "v1",
148
- _CONTENT_TYPE_HEADER: execution_input["version"],
149
- }
150
142
  data = await cls._call_task(
151
143
  http_method=HTTPMethod.POST,
152
- headers=headers,
153
- url=routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH,
144
+ url=routes.EXECUTION_JOBS_FULL_PATH,
154
145
  body=execution_input,
155
- use_versioned_url=False,
156
146
  http_client=http_client,
157
147
  )
158
148
  return execution_request.ExecutionJobDetails.model_validate(data)
@@ -163,12 +153,9 @@ class ApiWrapper:
163
153
  job_id: JobID,
164
154
  http_client: Optional[httpx.AsyncClient] = None,
165
155
  ) -> execution_request.ExecutionJobDetails:
166
- headers = {_ACCEPT_HEADER: "v1"}
167
156
  data = await cls._call_task(
168
157
  http_method=HTTPMethod.GET,
169
- headers=headers,
170
- url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}",
171
- use_versioned_url=False,
158
+ url=f"{routes.EXECUTION_JOBS_FULL_PATH}/{job_id.job_id}",
172
159
  http_client=http_client,
173
160
  )
174
161
  return execution_request.ExecutionJobDetails.model_validate(data)
@@ -177,14 +164,11 @@ class ApiWrapper:
177
164
  async def call_get_execution_job_result(
178
165
  cls,
179
166
  job_id: JobID,
180
- version: str,
181
167
  http_client: Optional[httpx.AsyncClient] = None,
182
168
  ) -> classiq.interface.executor.execution_result.ExecuteGeneratedCircuitResults:
183
169
  data = await cls._call_task(
184
170
  http_method=HTTPMethod.GET,
185
- url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}/result",
186
- use_versioned_url=False,
187
- headers={CLASSIQ_ACCEPT_HEADER: version},
171
+ url=f"{routes.EXECUTION_JOBS_FULL_PATH}/{job_id.job_id}/result",
188
172
  http_client=http_client,
189
173
  )
190
174
  return classiq.interface.executor.execution_result.ExecuteGeneratedCircuitResults.model_validate(
@@ -200,11 +184,10 @@ class ApiWrapper:
200
184
  ) -> ExecutionJobDetailsV1:
201
185
  data = await cls._call_task(
202
186
  http_method=HTTPMethod.PATCH,
203
- url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}",
187
+ url=f"{routes.EXECUTION_JOBS_FULL_PATH}/{job_id.job_id}",
204
188
  params={
205
189
  "name": name,
206
190
  },
207
- use_versioned_url=False,
208
191
  http_client=http_client,
209
192
  )
210
193
  return ExecutionJobDetailsV1.model_validate(data)
@@ -217,8 +200,7 @@ class ApiWrapper:
217
200
  ) -> None:
218
201
  await cls._call_task(
219
202
  http_method=HTTPMethod.PUT,
220
- url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}/cancel",
221
- use_versioned_url=False,
203
+ url=f"{routes.EXECUTION_JOBS_FULL_PATH}/{job_id.job_id}/cancel",
222
204
  allow_none=True,
223
205
  http_client=http_client,
224
206
  )
@@ -232,12 +214,11 @@ class ApiWrapper:
232
214
  ) -> ExecutionJobsQueryResultsV1:
233
215
  data = await cls._call_task(
234
216
  http_method=HTTPMethod.GET,
235
- url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}",
217
+ url=f"{routes.EXECUTION_JOBS_FULL_PATH}",
236
218
  params={
237
219
  "offset": offset,
238
220
  "limit": limit,
239
221
  },
240
- use_versioned_url=False,
241
222
  http_client=http_client,
242
223
  )
243
224
  return ExecutionJobsQueryResultsV1.model_validate(data)
@@ -395,7 +376,6 @@ class ApiWrapper:
395
376
  ) -> operator.PauliOperator:
396
377
  poller = JobPoller(
397
378
  base_url=routes.GENERATE_HAMILTONIAN_FULL_PATH,
398
- use_versioned_url=False,
399
379
  )
400
380
  result = await poller.run_pydantic(
401
381
  problem, timeout_sec=None, http_client=http_client
@@ -9,14 +9,7 @@ import sys
9
9
  import time
10
10
  from collections.abc import Awaitable
11
11
  from types import TracebackType
12
- from typing import (
13
- Any,
14
- Callable,
15
- NoReturn,
16
- Optional,
17
- TypeVar,
18
- Union,
19
- )
12
+ from typing import Any, Callable, NoReturn, Optional, TypeVar, Union
20
13
 
21
14
  import httpx
22
15
  from typing_extensions import ParamSpec
@@ -170,7 +163,6 @@ class Client:
170
163
  _UNKNOWN_VERSION = HostChecker._UNKNOWN_VERSION
171
164
  _SESSION_HEADER = "Classiq-Session"
172
165
  _WARNINGS_HEADER = "X-Classiq-Warnings"
173
- _LATEST_VERSION_API_PREFIX = "/api/v1"
174
166
  _HTTP_TIMEOUT_SECONDS = 3600 # Needs to be synced with load-balancer timeout
175
167
 
176
168
  def __init__(self, conf: config.Configuration) -> None:
@@ -53,7 +53,7 @@ from classiq.applications.chemistry.chemistry_execution_parameters import (
53
53
  )
54
54
  from classiq.interface.exceptions import ClassiqError
55
55
  from classiq.qmod.utilities import qmod_val_to_expr_str
56
- from classiq.qmod.builtins.functions.hea import full_hea
56
+ from classiq.open_library.functions.hea import full_hea
57
57
 
58
58
  _LADDER_OPERATOR_TYPE_INDICATOR_TO_QMOD_MAPPING: dict[str, str] = {
59
59
  "+": "PLUS",
@@ -7,6 +7,7 @@ import pyomo.environ as pyo
7
7
 
8
8
  from classiq.interface.combinatorial_optimization.sense import is_maximization
9
9
  from classiq.interface.combinatorial_optimization.solver_types import QSolver
10
+ from classiq.interface.exceptions import ClassiqValueError
10
11
  from classiq.interface.executor.vqe_result import VQESolverResult
11
12
  from classiq.interface.generator.functions.qmod_python_interface import QmodPyStruct
12
13
 
@@ -20,6 +21,7 @@ from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import
20
21
  pauli_operator_to_hamiltonian,
21
22
  )
22
23
  from classiq.applications.combinatorial_helpers.pyomo_utils import (
24
+ add_var_domain_constraints,
23
25
  convert_pyomo_to_global_presentation,
24
26
  evaluate_objective,
25
27
  pyomo_to_qmod_qstruct,
@@ -56,14 +58,30 @@ def pyo_model_to_hamiltonian(
56
58
  def pyo_model_to_qmod_problem(
57
59
  pyo_model: pyo.ConcreteModel, penalty_energy: float
58
60
  ) -> tuple[type[QStruct], Callable]:
59
- optimization_model = OptimizationModel(
60
- pyo_model, penalty_energy=penalty_energy, qsolver=QSolver.QAOAPenalty
61
- )
62
- qmod_struct = pyomo_to_qmod_qstruct("QAOAVars", optimization_model.vars_not_encoded)
63
- cost_func = partial(
64
- evaluate_objective, *optimization_model.objective_not_encoded_sympy
65
- )
66
- return qmod_struct, cost_func
61
+ with add_var_domain_constraints(pyo_model):
62
+ optimization_model = OptimizationModel(
63
+ pyo_model, penalty_energy=penalty_energy, qsolver=QSolver.QAOAPenalty
64
+ )
65
+ _validate_var_domains(optimization_model)
66
+ qmod_struct = pyomo_to_qmod_qstruct(
67
+ "QAOAVars", optimization_model.vars_not_encoded
68
+ )
69
+ cost_func = partial(
70
+ evaluate_objective, *optimization_model.objective_not_encoded_sympy
71
+ )
72
+ return qmod_struct, cost_func
73
+
74
+
75
+ def _validate_var_domains(model: OptimizationModel) -> None:
76
+ for var in model.vars_not_encoded:
77
+ if (
78
+ isinstance(var.bounds, tuple)
79
+ and len(var.bounds) == 2
80
+ and var.bounds[0] != 0
81
+ ):
82
+ raise ClassiqValueError(
83
+ f"Bounds of variable {var.local_name} must start at 0, got {var.bounds}"
84
+ )
67
85
 
68
86
 
69
87
  def _str_to_list_int(str_ints: str) -> list[int]:
@@ -25,6 +25,7 @@ from classiq.applications.combinatorial_helpers.encoding_mapping import Encoding
25
25
  from classiq.applications.combinatorial_helpers.memory import InternalQuantumReg
26
26
  from classiq.applications.combinatorial_helpers.pyomo_utils import (
27
27
  get_field_name,
28
+ index_as_tuple,
28
29
  is_index_var,
29
30
  )
30
31
  from classiq.applications.combinatorial_helpers.transformations import (
@@ -182,7 +183,10 @@ class OptimizationModel:
182
183
  sympy_var: (
183
184
  get_field_name(pyomo_var)
184
185
  if not is_index_var(pyomo_var)
185
- else (get_field_name(pyomo_var.parent_component()), pyomo_var.index())
186
+ else (
187
+ get_field_name(pyomo_var.parent_component()),
188
+ index_as_tuple(pyomo_var.index()),
189
+ )
186
190
  )
187
191
  for pyomo_var, sympy_var in objective_map.pyomo2sympy.items()
188
192
  }
@@ -1,7 +1,11 @@
1
1
  import math
2
2
  import re
3
3
  from collections import defaultdict
4
+ from collections.abc import Iterator
5
+ from contextlib import contextmanager
4
6
  from enum import Enum
7
+ from functools import reduce
8
+ from operator import mul
5
9
  from typing import Any, Optional, TypeVar, Union
6
10
 
7
11
  import pydantic
@@ -14,6 +18,7 @@ from pyomo.core.base.component import ComponentData
14
18
  from pyomo.core.base.constraint import _GeneralConstraintData
15
19
  from pyomo.core.base.indexed_component import IndexedComponent
16
20
  from pyomo.core.base.objective import ScalarObjective
21
+ from pyomo.core.expr.base import ExpressionBase
17
22
  from pyomo.core.expr.sympy_tools import (
18
23
  Pyomo2SympyVisitor,
19
24
  PyomoSympyBimap,
@@ -268,44 +273,67 @@ def pyomo_to_qmod_qstruct(
268
273
 
269
274
 
270
275
  def _get_qstruct_fields(vars: list[_GeneralVarData]) -> dict[str, type[QVar]]:
271
- array_types = _get_array_types(vars)
272
- array_type_sizes = _get_array_sizes(array_types)
276
+ array_type_sizes = _get_array_sizes(vars)
273
277
  fields: dict[str, type[QVar]] = {}
274
278
  for var in vars:
275
279
  _add_qmod_field(var, array_type_sizes, fields)
276
280
  return fields
277
281
 
278
282
 
279
- def _get_array_types(vars: list[_GeneralVarData]) -> dict[str, set[int]]:
280
- array_types: dict[str, set[int]] = defaultdict(set)
283
+ def _get_array_sizes(vars: list[_GeneralVarData]) -> dict[str, tuple[int, ...]]:
284
+ array_types: dict[str, set[tuple]] = defaultdict(set)
281
285
  for var in vars:
282
286
  if is_index_var(var):
283
- array_types[var.parent_component().name].add(var.index())
284
- return array_types
285
-
286
-
287
- def _get_array_sizes(array_types: dict[str, set[int]]) -> dict[str, int]:
287
+ array_types[get_field_name(var.parent_component())].add(
288
+ index_as_tuple(var.index())
289
+ )
288
290
  return {
289
- name: array_size
291
+ name: dimensions
290
292
  for name, indices in array_types.items()
291
- if indices == set(range(array_size := len(indices)))
293
+ if (dimensions := _get_indices_dimensions(indices)) is not None
292
294
  }
293
295
 
294
296
 
297
+ def _get_indices_dimensions(indices: set[tuple[int, ...]]) -> Optional[tuple[int, ...]]:
298
+ indices_list = list(indices)
299
+ if len(indices) == 0:
300
+ return None
301
+ first_idx = indices_list[0]
302
+ if len(first_idx) == 0:
303
+ return None
304
+ if any(len(idx) != len(first_idx) for idx in indices_list[1:]):
305
+ return None
306
+ dimension_bounds = [(idx, idx) for idx in first_idx]
307
+ for multi_idx in indices_list[1:]:
308
+ for dim_idx, idx in enumerate(multi_idx):
309
+ dimension_bounds[dim_idx] = (
310
+ min(dimension_bounds[dim_idx][0], idx),
311
+ max(dimension_bounds[dim_idx][1], idx),
312
+ )
313
+ if any(lb != 0 for lb, ub in dimension_bounds):
314
+ return None
315
+ dimensions = tuple(ub + 1 for _, ub in dimension_bounds)
316
+ if reduce(mul, dimensions) != len(indices_list):
317
+ return None
318
+ return dimensions
319
+
320
+
295
321
  def _add_qmod_field(
296
322
  var: _GeneralVarData,
297
- array_type_sizes: dict[str, int],
323
+ array_type_sizes: dict[str, tuple[int, ...]],
298
324
  fields: dict[str, type[QVar]],
299
325
  ) -> None:
300
- parent_name = var.parent_component().name
326
+ parent_name = get_field_name(var.parent_component())
301
327
  if parent_name not in array_type_sizes:
302
328
  var_name = get_field_name(var)
303
329
  fields[var_name] = _get_qmod_field_type(var_name, var)
304
- elif var.index() == 0:
305
- fields[parent_name] = QArray[ # type:ignore[misc]
306
- _get_qmod_field_type(parent_name, var),
307
- array_type_sizes[parent_name],
308
- ]
330
+ return
331
+ dimensions = array_type_sizes[parent_name]
332
+ if index_as_tuple(var.index()) == tuple(0 for _ in range(len(dimensions))):
333
+ qmod_type: type[QVar] = _get_qmod_field_type(parent_name, var)
334
+ for dim in reversed(dimensions):
335
+ qmod_type = QArray[qmod_type, dim] # type:ignore[valid-type]
336
+ fields[parent_name] = qmod_type
309
337
 
310
338
 
311
339
  def _get_qmod_field_type(var_name: str, var_data: _GeneralVarData) -> type[QVar]:
@@ -325,14 +353,12 @@ def _get_qmod_field_type(var_name: str, var_data: _GeneralVarData) -> type[QVar]
325
353
  raise ClassiqValueError(
326
354
  f"Non-integer bounds for variable {var_name!r} are not supported"
327
355
  )
328
- if lb > 0:
329
- ub -= lb
330
356
  qnum: Any = QNum # mypy shenanigans
331
- return qnum[math.ceil(math.log2(ub - lb)), False, 0]
357
+ return qnum[math.ceil(math.log2(ub - lb + 1)), False, 0]
332
358
 
333
359
 
334
360
  def evaluate_objective(
335
- var_mapping: dict[Any, Union[str, tuple[str, int]]],
361
+ var_mapping: dict[Any, Union[str, tuple[str, tuple[int, ...]]]],
336
362
  sympy_expr: sympy.Expr,
337
363
  struct_obj: Any,
338
364
  ) -> Any:
@@ -340,7 +366,7 @@ def evaluate_objective(
340
366
  sympy_var: (
341
367
  getattr(struct_obj, field_accessor)
342
368
  if isinstance(field_accessor, str)
343
- else getattr(struct_obj, field_accessor[0])[field_accessor[1]]
369
+ else _get_item(getattr(struct_obj, field_accessor[0]), field_accessor[1])
344
370
  )
345
371
  for sympy_var, field_accessor in var_mapping.items()
346
372
  }
@@ -357,12 +383,65 @@ def evaluate_objective(
357
383
  return SymbolicExpr(expr=expr_str, is_quantum=True)
358
384
 
359
385
 
386
+ def _get_item(obj: Any, multi_index: tuple[int, ...]) -> Any:
387
+ for idx in multi_index:
388
+ obj = obj[idx]
389
+ return obj
390
+
391
+
360
392
  def get_field_name(var: _GeneralVarData) -> str:
361
- return var.local_name.replace("[", "_").replace("]", "")
393
+ return var.local_name.replace("[", "_").replace("]", "").replace(",", "_")
362
394
 
363
395
 
364
396
  def is_index_var(var: _GeneralVarData) -> bool:
365
- return (
366
- isinstance(var.index(), int)
367
- and "[" not in var.parent_component().name # nested arrays not supported
397
+ index = var.index()
398
+ return isinstance(index, int) or (
399
+ isinstance(index, tuple) and all(isinstance(idx, int) for idx in index)
368
400
  )
401
+
402
+
403
+ def index_as_tuple(index: Union[int, tuple[int, ...]]) -> tuple[int, ...]:
404
+ if isinstance(index, int):
405
+ return (index,)
406
+ return index
407
+
408
+
409
+ @contextmanager
410
+ def add_var_domain_constraints(model: ConcreteModel) -> Iterator[None]:
411
+ vars = extract(model, _GeneralVarData)
412
+ constraints = [
413
+ constraint
414
+ for var in vars
415
+ if (constraint := _get_var_domain_constraint(var)) is not None
416
+ ]
417
+ if len(constraints) == 0:
418
+ yield
419
+ return
420
+ model.var_domain_constraints = pyo.ConstraintList()
421
+ for constraint in constraints:
422
+ model.var_domain_constraints.add(constraint)
423
+ yield
424
+ model.del_component("var_domain_constraints")
425
+
426
+
427
+ def _get_var_domain_constraint(var: _GeneralVarData) -> Optional[ExpressionBase]:
428
+ bounds = var.bounds
429
+ if (
430
+ type(bounds) is not tuple
431
+ or len(bounds) != 2
432
+ or not all(isinstance(bounds[idx], int) for idx in (0, 1))
433
+ ):
434
+ raise ClassiqValueError(
435
+ f"Missing bounds for variable {var.local_name}. Expected both lower and "
436
+ f"upper bounds, got {bounds}"
437
+ )
438
+ lb, ub = bounds
439
+ if ub < lb:
440
+ raise ClassiqValueError(
441
+ f"Illegal bounds for variable {var.local_name}. The upper bound ({ub}) is "
442
+ f"lesser than the lower bound ({lb})"
443
+ )
444
+ ub_norm = ub - lb + 1
445
+ if ub_norm & (ub_norm - 1) == 0:
446
+ return None
447
+ return var <= ub
@@ -33,7 +33,7 @@ from classiq.applications.combinatorial_helpers.pauli_helpers.pauli_utils import
33
33
  _pauli_terms_to_qmod,
34
34
  )
35
35
  from classiq.applications.combinatorial_optimization import OptimizerConfig, QAOAConfig
36
- from classiq.qmod.builtins.functions import qaoa_penalty
36
+ from classiq.open_library.functions.qaoa_penalty import qaoa_penalty
37
37
 
38
38
 
39
39
  def construct_combi_opt_py_model(
@@ -17,11 +17,13 @@ from classiq.applications.combinatorial_helpers.combinatorial_problem_utils impo
17
17
  pyo_model_to_qmod_problem,
18
18
  )
19
19
  from classiq.execution import ExecutionSession
20
+ from classiq.open_library.functions.utility_functions import (
21
+ apply_to_all,
22
+ hadamard_transform,
23
+ )
20
24
  from classiq.qmod.builtins.functions import (
21
25
  RX,
22
26
  allocate,
23
- apply_to_all,
24
- hadamard_transform,
25
27
  )
26
28
  from classiq.qmod.builtins.operations import phase, repeat
27
29
  from classiq.qmod.cparam import CReal
@@ -19,7 +19,7 @@ from classiq.interface.model.variable_declaration_statement import (
19
19
  )
20
20
 
21
21
  from classiq import RegisterUserInput
22
- from classiq.qmod.builtins.functions.grover import grover_search, phase_oracle
22
+ from classiq.open_library.functions.grover import grover_search, phase_oracle
23
23
 
24
24
  _OUTPUT_VARIABLE_NAME = "result"
25
25
 
@@ -1,4 +1,5 @@
1
- from classiq.qmod.builtins.functions import Z, amplitude_estimation
1
+ from classiq.open_library.functions.amplitude_estimation import amplitude_estimation
2
+ from classiq.qmod.builtins.functions import Z
2
3
  from classiq.qmod.qfunc import qfunc
3
4
  from classiq.qmod.qmod_variable import QArray, QBit, QNum
4
5
  from classiq.qmod.quantum_callable import QCallable