classiq 0.33.0__py3-none-any.whl → 0.35.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 (59) hide show
  1. classiq/_internals/api_wrapper.py +61 -23
  2. classiq/_internals/client.py +4 -1
  3. classiq/_internals/jobs.py +9 -2
  4. classiq/applications_model_constructors/grover_model_constructor.py +1 -1
  5. classiq/execution/__init__.py +9 -2
  6. classiq/execution/jobs.py +84 -11
  7. classiq/executor.py +3 -10
  8. classiq/interface/_version.py +1 -1
  9. classiq/interface/backend/backend_preferences.py +17 -0
  10. classiq/interface/backend/pydantic_backend.py +8 -0
  11. classiq/interface/backend/quantum_backend_providers.py +15 -1
  12. classiq/interface/chemistry/ground_state_problem.py +1 -1
  13. classiq/interface/chemistry/operator.py +198 -0
  14. classiq/interface/execution/jobs.py +28 -0
  15. classiq/interface/executor/execution_request.py +2 -12
  16. classiq/interface/generator/arith/arithmetic_expression_validator.py +1 -0
  17. classiq/interface/generator/arith/arithmetic_param_getters.py +12 -0
  18. classiq/interface/generator/arith/binary_ops.py +34 -0
  19. classiq/interface/generator/expressions/expression.py +3 -0
  20. classiq/interface/generator/expressions/qmod_sized_proxy.py +12 -2
  21. classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
  22. classiq/interface/generator/function_params.py +4 -0
  23. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/atomic_quantum_functions.py +2 -2
  24. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +42 -105
  25. classiq/interface/generator/generated_circuit.py +8 -44
  26. classiq/interface/generator/generated_circuit_data.py +2 -11
  27. classiq/interface/generator/model/preferences/preferences.py +4 -2
  28. classiq/interface/generator/quantum_function_call.py +1 -1
  29. classiq/interface/hardware.py +2 -0
  30. classiq/interface/ide/show.py +1 -14
  31. classiq/interface/model/bind_operation.py +10 -0
  32. classiq/interface/model/handle_binding.py +18 -0
  33. classiq/interface/model/model.py +12 -3
  34. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +2 -1
  35. classiq/interface/model/quantum_expressions/arithmetic_operation.py +3 -1
  36. classiq/interface/model/quantum_expressions/quantum_expression.py +9 -6
  37. classiq/interface/model/quantum_function_call.py +9 -339
  38. classiq/interface/model/quantum_statement.py +3 -2
  39. classiq/interface/server/routes.py +8 -6
  40. classiq/model/function_handler.pyi +86 -85
  41. classiq/qmod/__init__.py +2 -2
  42. classiq/qmod/builtins/__init__.py +8 -0
  43. classiq/{interface/model/clients/qmod/qmod_builtins.py → qmod/builtins/functions.py} +46 -165
  44. classiq/qmod/builtins/operations.py +19 -0
  45. classiq/qmod/builtins/structs.py +128 -0
  46. classiq/qmod/declaration_inferrer.py +34 -17
  47. classiq/qmod/model_state_container.py +6 -3
  48. classiq/qmod/qmod_parameter.py +24 -8
  49. classiq/qmod/qmod_variable.py +4 -4
  50. classiq/qmod/quantum_callable.py +2 -2
  51. classiq/qmod/quantum_expandable.py +7 -3
  52. classiq/qmod/quantum_function.py +9 -9
  53. {classiq-0.33.0.dist-info → classiq-0.35.0.dist-info}/METADATA +1 -1
  54. {classiq-0.33.0.dist-info → classiq-0.35.0.dist-info}/RECORD +56 -55
  55. classiq/interface/model/clients/qmod/__init__.py +0 -0
  56. classiq/interface/model/semantics.py +0 -15
  57. classiq/qmod/qmod_builtins.py +0 -4
  58. /classiq/interface/{model/clients → execution}/__init__.py +0 -0
  59. {classiq-0.33.0.dist-info → classiq-0.35.0.dist-info}/WHEEL +0 -0
@@ -224,6 +224,204 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
224
224
  frozen = True
225
225
 
226
226
 
227
+ class PauliOperatorV1(HashablePydanticBaseModel):
228
+ """
229
+ Specification of a Pauli sum operator.
230
+ """
231
+
232
+ pauli_list: PydanticPauliList = pydantic.Field(
233
+ description="A list of tuples each containing a pauli string comprised of I,X,Y,Z characters and a complex coefficient; for example [('IZ', 0.1), ('XY', 0.2)].",
234
+ )
235
+ is_hermitian: bool = pydantic.Field(default=False)
236
+ has_complex_coefficients: bool = pydantic.Field(default=True)
237
+
238
+ def show(self) -> str:
239
+ if self.is_hermitian:
240
+ # If the operator is hermitian then the coefficients must be numeric
241
+ return "\n".join(
242
+ f"{summand[1].real:+.3f} * {summand[0]}" for summand in self.pauli_list # type: ignore[union-attr]
243
+ )
244
+ return "\n".join(
245
+ f"+({summand[1]:+.3f}) * {summand[0]}" for summand in self.pauli_list
246
+ )
247
+
248
+ @pydantic.validator("pauli_list", each_item=True, pre=True)
249
+ def _validate_pauli_monomials(
250
+ cls, monomial: Tuple[PydanticPauliMonomialStr, ParameterComplexType]
251
+ ) -> Tuple[PydanticPauliMonomialStr, ParameterComplexType]:
252
+ _PauliMonomialLengthValidator( # type: ignore[call-arg]
253
+ monomial=monomial
254
+ ) # Validate the length of the monomial.
255
+ coeff = cls._validate_monomial_coefficient(monomial[1])
256
+ parsed_monomial = _PauliMonomialParser(string=monomial[0], coeff=coeff) # type: ignore[call-arg]
257
+ return (parsed_monomial.string, parsed_monomial.coeff)
258
+
259
+ @staticmethod
260
+ def _validate_monomial_coefficient(
261
+ coeff: Union[sympy.Expr, ParameterComplexType]
262
+ ) -> ParameterComplexType:
263
+ if isinstance(coeff, str):
264
+ validate_expression_str(coeff)
265
+ elif isinstance(coeff, sympy.Expr):
266
+ coeff = str(coeff)
267
+ return coeff
268
+
269
+ @pydantic.validator("pauli_list")
270
+ def _validate_pauli_list(cls, pauli_list: PydanticPauliList) -> PydanticPauliList:
271
+ if not all_equal(len(summand[0]) for summand in pauli_list):
272
+ raise ValueError("Pauli strings have incompatible lengths.")
273
+ return pauli_list
274
+
275
+ @pydantic.root_validator
276
+ def _validate_hermitianity(cls, values: Dict[str, Any]) -> Dict[str, Any]:
277
+ pauli_list = values.get("pauli_list", [])
278
+ if all(isinstance(summand[1], complex) for summand in pauli_list):
279
+ values["is_hermitian"] = all(
280
+ np.isclose(complex(summand[1]).real, summand[1])
281
+ for summand in pauli_list
282
+ )
283
+ if values.get("is_hermitian", False):
284
+ values["has_complex_coefficients"] = False
285
+ values["pauli_list"] = [
286
+ (summand[0], complex(summand[1].real)) for summand in pauli_list
287
+ ]
288
+ else:
289
+ values["has_complex_coefficients"] = not all(
290
+ np.isclose(complex(summand[1]).real, summand[1])
291
+ for summand in pauli_list
292
+ if isinstance(summand[1], complex)
293
+ )
294
+ return values
295
+
296
+ def __mul__(self, coefficient: complex) -> "PauliOperatorV1":
297
+ multiplied_ising = [
298
+ (monomial[0], self._multiply_monomial_coefficient(monomial[1], coefficient))
299
+ for monomial in self.pauli_list
300
+ ]
301
+ return self.__class__(pauli_list=multiplied_ising)
302
+
303
+ @staticmethod
304
+ def _multiply_monomial_coefficient(
305
+ monomial_coefficient: ParameterComplexType, coefficient: complex
306
+ ) -> ParameterComplexType:
307
+ if isinstance(monomial_coefficient, ParameterType):
308
+ return str(sympy.sympify(monomial_coefficient) * coefficient)
309
+ return monomial_coefficient * coefficient
310
+
311
+ @property
312
+ def is_commutative(self) -> bool:
313
+ return all(
314
+ self._is_sub_pauli_commutative(
315
+ [summand[0][qubit_num] for summand in self.pauli_list]
316
+ )
317
+ for qubit_num in range(self.num_qubits)
318
+ )
319
+
320
+ @staticmethod
321
+ def _is_sub_pauli_commutative(qubit_pauli_string: Union[List[str], str]) -> bool:
322
+ unique_paulis = set(qubit_pauli_string) - {"I"}
323
+ return len(unique_paulis) <= 1
324
+
325
+ @property
326
+ def num_qubits(self) -> int:
327
+ return len(self.pauli_list[0][0])
328
+
329
+ def to_matrix(self) -> np.ndarray:
330
+ if not all(isinstance(summand[1], complex) for summand in self.pauli_list):
331
+ raise ClassiqValueError(
332
+ "Supporting only Hamiltonian with numeric coefficients."
333
+ )
334
+ return sum(
335
+ cast(complex, summand[1]) * to_pauli_matrix(summand[0])
336
+ for summand in self.pauli_list
337
+ ) # type: ignore[return-value]
338
+
339
+ @staticmethod
340
+ def _extend_pauli_string(
341
+ pauli_string: PydanticPauliMonomialStr, num_extra_qubits: int
342
+ ) -> PydanticPauliMonomialStr:
343
+ return "I" * num_extra_qubits + pauli_string
344
+
345
+ def extend(self, num_extra_qubits: int) -> "PauliOperatorV1":
346
+ new_pauli_list = [
347
+ (self._extend_pauli_string(pauli_string, num_extra_qubits), coeff)
348
+ for (pauli_string, coeff) in self.pauli_list
349
+ ]
350
+ return self.copy(update={"pauli_list": new_pauli_list}, deep=True)
351
+
352
+ @staticmethod
353
+ def _reorder_pauli_string(
354
+ pauli_string: PydanticPauliMonomialStr,
355
+ order: Collection[int],
356
+ new_num_qubits: int,
357
+ ) -> PydanticPauliMonomialStr:
358
+ reversed_pauli_string = pauli_string[::-1]
359
+ reversed_new_pauli_string = ["I"] * new_num_qubits
360
+
361
+ for logical_pos, actual_pos in enumerate(order):
362
+ reversed_new_pauli_string[actual_pos] = reversed_pauli_string[logical_pos]
363
+
364
+ return "".join(reversed(reversed_new_pauli_string))
365
+
366
+ @staticmethod
367
+ def _validate_reorder(
368
+ order: Collection[int],
369
+ num_qubits: int,
370
+ num_extra_qubits: int,
371
+ ) -> None:
372
+ if num_extra_qubits < 0:
373
+ raise ValueError("Number of extra qubits cannot be negative")
374
+
375
+ if len(order) != num_qubits:
376
+ raise ValueError("The qubits order doesn't match the Pauli operator")
377
+
378
+ if len(order) != len(set(order)):
379
+ raise ValueError("The qubits order is not one-to-one")
380
+
381
+ if not all(pos < num_qubits + num_extra_qubits for pos in order):
382
+ raise ValueError("The qubits order contains qubits which do no exist")
383
+
384
+ @classmethod
385
+ def reorder(
386
+ cls,
387
+ operator: "PauliOperatorV1",
388
+ order: Collection[int],
389
+ num_extra_qubits: int = 0,
390
+ ) -> "PauliOperatorV1":
391
+ cls._validate_reorder(order, operator.num_qubits, num_extra_qubits)
392
+
393
+ new_num_qubits = operator.num_qubits + num_extra_qubits
394
+ new_pauli_list = [
395
+ (cls._reorder_pauli_string(pauli_string, order, new_num_qubits), coeff)
396
+ for pauli_string, coeff in operator.pauli_list
397
+ ]
398
+ return cls(pauli_list=new_pauli_list)
399
+
400
+ @classmethod
401
+ def from_unzipped_lists(
402
+ cls,
403
+ operators: List[List[Pauli]],
404
+ coefficients: Optional[List[complex]] = None,
405
+ ) -> "PauliOperatorV1":
406
+ if coefficients is None:
407
+ coefficients = [1] * len(operators)
408
+
409
+ if len(operators) != len(coefficients):
410
+ raise ValueError(
411
+ f"The number of coefficients ({len(coefficients)}) must be equal to the number of pauli operators ({len(operators)})"
412
+ )
413
+
414
+ return cls(
415
+ pauli_list=[
416
+ (pauli_integers_to_str(op), coeff)
417
+ for op, coeff in zip(operators, coefficients)
418
+ ]
419
+ )
420
+
421
+ class Config:
422
+ frozen = True
423
+
424
+
227
425
  # This class validates the length of a monomial.
228
426
  @pydantic.dataclasses.dataclass
229
427
  class _PauliMonomialLengthValidator:
@@ -0,0 +1,28 @@
1
+ from datetime import datetime
2
+ from typing import List, Optional
3
+
4
+ from pydantic import BaseModel, Extra
5
+
6
+ from classiq.interface.jobs import JobStatus
7
+
8
+
9
+ class ExecutionJobDetailsV1(BaseModel, extra=Extra.ignore):
10
+ id: str
11
+
12
+ name: Optional[str]
13
+ start_time: datetime
14
+ end_time: Optional[datetime]
15
+
16
+ provider: Optional[str]
17
+ backend_name: Optional[str]
18
+
19
+ status: JobStatus
20
+
21
+ num_shots: Optional[int]
22
+ program_id: Optional[str]
23
+
24
+ error: Optional[str]
25
+
26
+
27
+ class ExecutionJobsQueryResultsV1(BaseModel, extra=Extra.ignore):
28
+ results: List[ExecutionJobDetailsV1]
@@ -15,10 +15,7 @@ from classiq.interface.executor.quantum_program import (
15
15
  )
16
16
  from classiq.interface.executor.result import EstimationResult, ExecutionDetails
17
17
  from classiq.interface.executor.vqe_result import VQESolverResult
18
- from classiq.interface.generator.generated_circuit import (
19
- ExecutionCircuit,
20
- GeneratedCircuit,
21
- )
18
+ from classiq.interface.generator.generated_circuit import GeneratedCircuit
22
19
  from classiq.interface.helpers.versioned_model import VersionedModel
23
20
  from classiq.interface.jobs import JobStatus
24
21
 
@@ -29,10 +26,6 @@ class GeneratedCircuitExecution(GeneratedCircuit):
29
26
  execution_type: Literal["generated_circuit"] = "generated_circuit"
30
27
 
31
28
 
32
- class BareGeneratedCircuitExecution(ExecutionCircuit):
33
- execution_type: Literal["bare_generated_circuit"] = "bare_generated_circuit"
34
-
35
-
36
29
  class QuantumProgramExecution(QuantumProgram):
37
30
  execution_type: Literal["quantum_program"] = "quantum_program"
38
31
 
@@ -42,10 +35,7 @@ class EstimateOperatorsExecution(OperatorsEstimation):
42
35
 
43
36
 
44
37
  ExecutionPayloads = Union[
45
- GeneratedCircuitExecution,
46
- BareGeneratedCircuitExecution,
47
- QuantumProgramExecution,
48
- EstimateOperatorsExecution,
38
+ GeneratedCircuitExecution, QuantumProgramExecution, EstimateOperatorsExecution
49
39
  ]
50
40
 
51
41
 
@@ -50,6 +50,7 @@ SupportedNodesTypes = Union[
50
50
  ast.RShift,
51
51
  ast.Call,
52
52
  ast.Mult,
53
+ ast.Pow,
53
54
  ]
54
55
 
55
56
  DEFAULT_SUPPORTED_NODE_TYPES = get_args(SupportedNodesTypes)
@@ -25,6 +25,7 @@ from classiq.interface.generator.arith.binary_ops import (
25
25
  Modulo,
26
26
  Multiplier,
27
27
  NotEqual,
28
+ Power,
28
29
  RegisterOrInt,
29
30
  RShift,
30
31
  Subtractor,
@@ -155,6 +156,16 @@ def multiplier_params_getter(
155
156
  return Multiplier(left_arg=left_arg, right_arg=right_arg, output_size=output_size)
156
157
 
157
158
 
159
+ def power_params_getter(
160
+ left_arg: RegisterArithmeticInfo,
161
+ right_arg: int,
162
+ output_size: Optional[int] = None,
163
+ inplace_arg: Optional[str] = None,
164
+ target: Optional[RegisterArithmeticInfo] = None,
165
+ ) -> ArithmeticOperationParams:
166
+ return Power(left_arg=left_arg, right_arg=right_arg, output_size=output_size)
167
+
168
+
158
169
  def min_params_getter(
159
170
  left_arg: RegisterOrFloat,
160
171
  right_arg: RegisterOrFloat,
@@ -367,4 +378,5 @@ params_getter_map: Dict[str, ParamsGetter] = dict(
367
378
  Mod=modulo_params_getter,
368
379
  min=min_params_getter,
369
380
  max=max_params_getter,
381
+ Pow=power_params_getter,
370
382
  )
@@ -413,6 +413,40 @@ class LessEqual(Comparator):
413
413
  output_name = "is_less_equal"
414
414
 
415
415
 
416
+ class Power(BinaryOpParams[RegisterArithmeticInfo, pydantic.PositiveInt]):
417
+ output_name = "powered"
418
+
419
+ @pydantic.validator("right_arg", pre=True)
420
+ def _validate_legal_power(cls, right_arg: Any) -> pydantic.PositiveInt:
421
+ if not float(right_arg).is_integer():
422
+ raise ValueError("Power must be an integer")
423
+ if right_arg <= 0:
424
+ raise ValueError("Power must be greater than one")
425
+ return int(right_arg)
426
+
427
+ def _get_result_bounds(self) -> Tuple[float, float]:
428
+ if (self.right_arg % 2) or min(self.left_arg.bounds) >= 0:
429
+ return (
430
+ self.left_arg.bounds[0] ** self.right_arg,
431
+ self.left_arg.bounds[1] ** self.right_arg,
432
+ )
433
+ return 0.0, max(abs(bound) for bound in self.left_arg.bounds) ** self.right_arg
434
+
435
+ def _get_result_register(self) -> RegisterArithmeticInfo:
436
+ if self.output_size:
437
+ return RegisterArithmeticInfo(size=self.output_size)
438
+
439
+ fraction_places: int = self.left_arg.fraction_places * self.right_arg
440
+ bounds = self._get_result_bounds()
441
+ size = number_utils.bounds_to_integer_part_size(*bounds) + fraction_places
442
+ return RegisterArithmeticInfo(
443
+ size=size,
444
+ is_signed=self.left_arg.is_signed and (self.right_arg % 2 == 1),
445
+ fraction_places=fraction_places,
446
+ bounds=bounds,
447
+ )
448
+
449
+
416
450
  class EffectiveUnaryOpParams(
417
451
  InplacableBinaryOpParams[RegisterArithmeticInfo, RightDataT], Generic[RightDataT]
418
452
  ):
@@ -82,3 +82,6 @@ class Expression(HashablePydanticBaseModel):
82
82
 
83
83
  class Config:
84
84
  frozen = True
85
+
86
+ def __str__(self) -> str:
87
+ return self.expr
@@ -1,6 +1,16 @@
1
- class QmodSizedProxy:
2
- def __init__(self, size: int) -> None:
1
+ from sympy import Symbol
2
+
3
+
4
+ class QmodSizedProxy(Symbol):
5
+ def __new__(cls, name, **assumptions):
6
+ return super().__new__(cls, name, **assumptions)
7
+
8
+ def __init__(self, name: str, size: int) -> None:
3
9
  self._size = size
4
10
 
11
+ @property
12
+ def size(self) -> int:
13
+ return self.args[0]
14
+
5
15
  def __len__(self) -> int:
6
16
  return self._size
@@ -18,6 +18,7 @@ from classiq.interface.generator.arith.binary_ops import (
18
18
  Modulo,
19
19
  Multiplier,
20
20
  NotEqual,
21
+ Power,
21
22
  RShift,
22
23
  Subtractor,
23
24
  )
@@ -136,6 +137,7 @@ function_param_library_without_self_reference: FunctionParamLibrary = (
136
137
  WeightedAdder,
137
138
  LinearPauliRotations,
138
139
  Multiplier,
140
+ Power,
139
141
  LinearGCI,
140
142
  HartreeFock,
141
143
  UCC,
@@ -102,6 +102,10 @@ GenerationOnlyExpressionSupportedNodeTypes = Union[
102
102
  ast.Slice,
103
103
  ast.keyword,
104
104
  ast.Attribute,
105
+ ast.BoolOp,
106
+ ast.Or,
107
+ ast.And,
108
+ ast.Not,
105
109
  ]
106
110
 
107
111
  GenerationExpressionSupportedNodeTypes = Union[
@@ -441,11 +441,11 @@ ADD_FUNCTION = QuantumFunctionDeclaration(
441
441
  port_declarations={
442
442
  "left": PortDeclaration(
443
443
  name="left",
444
- direction=PortDeclarationDirection.Input,
444
+ direction=PortDeclarationDirection.Inout,
445
445
  ),
446
446
  "right": PortDeclaration(
447
447
  name="right",
448
- direction=PortDeclarationDirection.Input,
448
+ direction=PortDeclarationDirection.Inout,
449
449
  ),
450
450
  "result": PortDeclaration(
451
451
  name="result",