classiq 0.51.1__py3-none-any.whl → 0.53.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 (158) hide show
  1. classiq/_internals/api_wrapper.py +47 -15
  2. classiq/_internals/authentication/auth0.py +20 -4
  3. classiq/_internals/authentication/password_manager.py +16 -4
  4. classiq/_internals/client.py +3 -3
  5. classiq/_internals/host_checker.py +5 -3
  6. classiq/_internals/jobs.py +3 -3
  7. classiq/analyzer/analyzer_utilities.py +1 -1
  8. classiq/applications/chemistry/ground_state_problem.py +1 -1
  9. classiq/applications/combinatorial_helpers/pyomo_utils.py +3 -1
  10. classiq/applications/qnn/gradients/quantum_gradient.py +1 -1
  11. classiq/applications/qnn/qlayer.py +25 -6
  12. classiq/execution/__init__.py +5 -0
  13. classiq/execution/execution_session.py +43 -2
  14. classiq/execution/iqcc.py +63 -0
  15. classiq/execution/jobs.py +2 -2
  16. classiq/execution/qaoa.py +84 -0
  17. classiq/executor.py +3 -3
  18. classiq/interface/_version.py +1 -1
  19. classiq/interface/analyzer/analysis_params.py +19 -9
  20. classiq/interface/analyzer/cytoscape_graph.py +10 -3
  21. classiq/interface/analyzer/result.py +6 -5
  22. classiq/interface/applications/qsvm.py +13 -12
  23. classiq/interface/backend/backend_preferences.py +78 -105
  24. classiq/interface/backend/ionq/ionq_quantum_program.py +12 -19
  25. classiq/interface/backend/pydantic_backend.py +24 -12
  26. classiq/interface/backend/quantum_backend_providers.py +2 -0
  27. classiq/interface/chemistry/fermionic_operator.py +7 -7
  28. classiq/interface/chemistry/ground_state_problem.py +23 -18
  29. classiq/interface/chemistry/molecule.py +10 -5
  30. classiq/interface/chemistry/operator.py +71 -44
  31. classiq/interface/combinatorial_optimization/mht_qaoa_input.py +2 -1
  32. classiq/interface/debug_info/debug_info.py +3 -4
  33. classiq/interface/exceptions.py +3 -1
  34. classiq/interface/execution/iqcc.py +19 -0
  35. classiq/interface/execution/jobs.py +10 -10
  36. classiq/interface/executor/aws_execution_cost.py +37 -20
  37. classiq/interface/executor/execution_preferences.py +1 -2
  38. classiq/interface/executor/execution_request.py +2 -2
  39. classiq/interface/executor/execution_result.py +4 -2
  40. classiq/interface/executor/iqae_result.py +1 -1
  41. classiq/interface/executor/optimizer_preferences.py +14 -10
  42. classiq/interface/executor/quantum_code.py +21 -16
  43. classiq/interface/executor/register_initialization.py +10 -10
  44. classiq/interface/executor/result.py +31 -17
  45. classiq/interface/executor/vqe_result.py +1 -1
  46. classiq/interface/finance/function_input.py +27 -18
  47. classiq/interface/finance/log_normal_model_input.py +2 -2
  48. classiq/interface/finance/model_input.py +3 -2
  49. classiq/interface/generator/amplitude_loading.py +8 -6
  50. classiq/interface/generator/arith/argument_utils.py +24 -0
  51. classiq/interface/generator/arith/arithmetic.py +5 -3
  52. classiq/interface/generator/arith/arithmetic_expression_abc.py +36 -14
  53. classiq/interface/generator/arith/arithmetic_operations.py +6 -3
  54. classiq/interface/generator/arith/binary_ops.py +88 -63
  55. classiq/interface/generator/arith/extremum_operations.py +22 -13
  56. classiq/interface/generator/arith/logical_ops.py +6 -4
  57. classiq/interface/generator/arith/number_utils.py +3 -3
  58. classiq/interface/generator/arith/register_user_input.py +32 -17
  59. classiq/interface/generator/arith/unary_ops.py +5 -4
  60. classiq/interface/generator/chemistry_function_params.py +2 -1
  61. classiq/interface/generator/circuit_code/circuit_code.py +2 -1
  62. classiq/interface/generator/commuting_pauli_exponentiation.py +6 -5
  63. classiq/interface/generator/complex_type.py +14 -18
  64. classiq/interface/generator/control_state.py +32 -26
  65. classiq/interface/generator/expressions/expression.py +6 -5
  66. classiq/interface/generator/function_params.py +22 -39
  67. classiq/interface/generator/functions/classical_function_declaration.py +1 -1
  68. classiq/interface/generator/functions/classical_type.py +32 -23
  69. classiq/interface/generator/functions/concrete_types.py +8 -7
  70. classiq/interface/generator/functions/function_declaration.py +4 -5
  71. classiq/interface/generator/functions/type_name.py +5 -4
  72. classiq/interface/generator/generated_circuit_data.py +9 -6
  73. classiq/interface/generator/grover_diffuser.py +26 -18
  74. classiq/interface/generator/grover_operator.py +32 -22
  75. classiq/interface/generator/hamiltonian_evolution/exponentiation.py +3 -4
  76. classiq/interface/generator/hamiltonian_evolution/qdrift.py +4 -4
  77. classiq/interface/generator/hamiltonian_evolution/suzuki_trotter.py +8 -7
  78. classiq/interface/generator/hardware/hardware_data.py +27 -26
  79. classiq/interface/generator/hardware_efficient_ansatz.py +11 -6
  80. classiq/interface/generator/hartree_fock.py +2 -1
  81. classiq/interface/generator/identity.py +7 -2
  82. classiq/interface/generator/linear_pauli_rotations.py +27 -14
  83. classiq/interface/generator/mcu.py +15 -12
  84. classiq/interface/generator/mcx.py +18 -10
  85. classiq/interface/generator/model/constraints.py +4 -2
  86. classiq/interface/generator/model/model.py +2 -1
  87. classiq/interface/generator/model/preferences/preferences.py +30 -32
  88. classiq/interface/generator/oracles/custom_oracle.py +13 -10
  89. classiq/interface/generator/piecewise_linear_amplitude_loading.py +37 -21
  90. classiq/interface/generator/qpe.py +38 -26
  91. classiq/interface/generator/qsvm.py +4 -4
  92. classiq/interface/generator/quantum_function_call.py +57 -44
  93. classiq/interface/generator/quantum_program.py +8 -6
  94. classiq/interface/generator/range_types.py +10 -11
  95. classiq/interface/generator/standard_gates/controlled_standard_gates.py +9 -5
  96. classiq/interface/generator/standard_gates/standard_angle_metaclass.py +2 -6
  97. classiq/interface/generator/standard_gates/u_gate.py +7 -10
  98. classiq/interface/generator/state_preparation/computational_basis_state_preparation.py +2 -1
  99. classiq/interface/generator/state_preparation/distributions.py +12 -12
  100. classiq/interface/generator/state_preparation/state_preparation.py +22 -16
  101. classiq/interface/generator/types/enum_declaration.py +2 -1
  102. classiq/interface/generator/ucc.py +2 -1
  103. classiq/interface/generator/unitary_gate.py +2 -1
  104. classiq/interface/generator/user_defined_function_params.py +3 -0
  105. classiq/interface/generator/visitor.py +1 -1
  106. classiq/interface/hardware.py +18 -3
  107. classiq/interface/helpers/custom_pydantic_types.py +38 -47
  108. classiq/interface/helpers/dotdict.py +18 -0
  109. classiq/interface/helpers/pydantic_model_helpers.py +3 -2
  110. classiq/interface/helpers/versioned_model.py +1 -4
  111. classiq/interface/ide/ide_data.py +5 -5
  112. classiq/interface/ide/visual_model.py +18 -5
  113. classiq/interface/interface_version.py +1 -1
  114. classiq/interface/jobs.py +12 -22
  115. classiq/interface/model/bind_operation.py +2 -1
  116. classiq/interface/model/classical_parameter_declaration.py +10 -4
  117. classiq/interface/model/handle_binding.py +20 -24
  118. classiq/interface/model/inplace_binary_operation.py +16 -9
  119. classiq/interface/model/model.py +21 -11
  120. classiq/interface/model/native_function_definition.py +10 -0
  121. classiq/interface/model/port_declaration.py +10 -7
  122. classiq/interface/model/quantum_expressions/arithmetic_operation.py +6 -4
  123. classiq/interface/model/quantum_function_declaration.py +22 -11
  124. classiq/interface/model/quantum_statement.py +6 -7
  125. classiq/interface/model/quantum_type.py +22 -19
  126. classiq/interface/model/statement_block.py +9 -9
  127. classiq/interface/server/global_versions.py +4 -5
  128. classiq/interface/server/routes.py +8 -0
  129. classiq/model_expansions/evaluators/parameter_types.py +3 -3
  130. classiq/model_expansions/expression_renamer.py +1 -1
  131. classiq/model_expansions/quantum_operations/control.py +11 -12
  132. classiq/model_expansions/quantum_operations/emitter.py +22 -0
  133. classiq/model_expansions/quantum_operations/expression_operation.py +2 -20
  134. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +42 -12
  135. classiq/model_expansions/quantum_operations/invert.py +1 -1
  136. classiq/model_expansions/quantum_operations/phase.py +4 -5
  137. classiq/model_expansions/quantum_operations/power.py +1 -1
  138. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +50 -9
  139. classiq/model_expansions/quantum_operations/variable_decleration.py +2 -2
  140. classiq/model_expansions/quantum_operations/within_apply.py +1 -1
  141. classiq/qmod/__init__.py +2 -0
  142. classiq/qmod/builtins/__init__.py +1 -3
  143. classiq/qmod/builtins/functions/__init__.py +9 -0
  144. classiq/qmod/builtins/functions/arithmetic.py +10 -0
  145. classiq/qmod/builtins/functions/standard_gates.py +14 -14
  146. classiq/qmod/builtins/functions/variational.py +37 -0
  147. classiq/qmod/create_model_function.py +16 -6
  148. classiq/qmod/qmod_parameter.py +3 -1
  149. classiq/qmod/quantum_expandable.py +43 -10
  150. classiq/qmod/quantum_function.py +24 -2
  151. classiq/qmod/semantics/static_semantics_visitor.py +3 -1
  152. classiq/qmod/synthesize_separately.py +16 -0
  153. classiq/qmod/type_attribute_remover.py +1 -1
  154. classiq/qmod/write_qmod.py +2 -4
  155. classiq/synthesis.py +11 -13
  156. {classiq-0.51.1.dist-info → classiq-0.53.0.dist-info}/METADATA +3 -2
  157. {classiq-0.51.1.dist-info → classiq-0.53.0.dist-info}/RECORD +158 -152
  158. {classiq-0.51.1.dist-info → classiq-0.53.0.dist-info}/WHEEL +0 -0
@@ -2,8 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING, List, Optional
4
4
 
5
- import pydantic
6
- from pydantic import BaseModel
5
+ from pydantic import BaseModel, ConfigDict, Field
6
+ from typing_extensions import Annotated
7
7
 
8
8
  # This file is based on autogenerated code from: https://static.ionq.co/schemas/circuit-v0.json using
9
9
  # https://pydantic-docs.helpmanual.io/datamodel_code_generator/
@@ -12,10 +12,13 @@ from pydantic import BaseModel
12
12
  if TYPE_CHECKING:
13
13
  PydanticGateName = str
14
14
  else:
15
- PydanticGateName = pydantic.constr(
16
- regex=r"^\w+$",
17
- min_length=1,
18
- )
15
+ PydanticGateName = Annotated[
16
+ str,
17
+ Field(
18
+ pattern=r"^\w+$",
19
+ min_length=1,
20
+ ),
21
+ ]
19
22
 
20
23
 
21
24
  class Gate(BaseModel):
@@ -24,23 +27,13 @@ class Gate(BaseModel):
24
27
  control: Optional[int] = None
25
28
  targets: Optional[List[int]] = None
26
29
  controls: Optional[List[int]] = None
27
-
28
- # Ionq changes format sometimes.
29
- # One example is that `IonqQauntumCircuit` got a field name "gateset" with the value "qis"
30
- # Another is that `Gate` got a field named "rotation"
31
- class Config:
32
- extra = pydantic.Extra.allow
30
+ model_config = ConfigDict(extra="allow")
33
31
 
34
32
 
35
33
  class IonqQuantumCircuit(BaseModel):
36
34
  qubits: int
37
35
  circuit: List[Gate]
38
-
39
- # Ionq changes format sometimes.
40
- # One example is that `IonqQuantumCircuit` got a field name "gateset" with the value "qis"
41
- # Another is that `Gate` got a field named "rotation"
42
- class Config:
43
- extra = pydantic.Extra.allow
36
+ model_config = ConfigDict(extra="allow")
44
37
 
45
38
  @classmethod
46
39
  def from_string(cls, code: str) -> IonqQuantumCircuit:
@@ -48,4 +41,4 @@ class IonqQuantumCircuit(BaseModel):
48
41
  commentless_code = "\n".join(
49
42
  line for line in code_lines if not line.startswith("//")
50
43
  )
51
- return cls.parse_raw(commentless_code)
44
+ return cls.model_validate_json(commentless_code)
@@ -1,6 +1,7 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
- import pydantic
3
+ from pydantic import Field, StrictStr, constr
4
+ from typing_extensions import Annotated
4
5
 
5
6
  AZURE_QUANTUM_RESOURCE_ID_REGEX = r"^/subscriptions/([a-fA-F0-9-]*)/resourceGroups/([^\s/]*)/providers/Microsoft\.Quantum/Workspaces/([^\s/]*)$"
6
7
 
@@ -25,20 +26,31 @@ if TYPE_CHECKING:
25
26
  else:
26
27
  # TODO Simplify regular expressions in this file
27
28
 
28
- PydanticAwsRoleArn = pydantic.constr(
29
- strip_whitespace=True,
30
- )
29
+ PydanticAwsRoleArn = Annotated[
30
+ StrictStr,
31
+ constr(
32
+ strip_whitespace=True,
33
+ ),
34
+ ]
31
35
 
32
- PydanticS3BucketKey = pydantic.constr(strip_whitespace=True, min_length=1)
36
+ PydanticS3BucketKey = Annotated[
37
+ StrictStr, constr(strip_whitespace=True, min_length=1)
38
+ ]
33
39
 
34
- PydanticAzureResourceIDType = pydantic.constr(regex=AZURE_QUANTUM_RESOURCE_ID_REGEX)
40
+ PydanticAzureResourceIDType = Annotated[
41
+ str, Field(pattern=AZURE_QUANTUM_RESOURCE_ID_REGEX)
42
+ ]
35
43
 
36
- PydanticIonQApiKeyType = pydantic.constr(
37
- regex=f"[A-Za-z0-9]{{{_IONQ_API_KEY_LENGTH}}}"
38
- )
44
+ PydanticIonQApiKeyType = Annotated[
45
+ str, Field(pattern=f"[A-Za-z0-9]{{{_IONQ_API_KEY_LENGTH}}}")
46
+ ]
39
47
 
40
- PydanticAliceBobApiKeyType = pydantic.constr(min_length=1, strip_whitespace=True)
48
+ PydanticAliceBobApiKeyType = Annotated[
49
+ StrictStr, constr(min_length=1, strip_whitespace=True)
50
+ ]
41
51
 
42
- PydanticArgumentNameType = pydantic.constr(regex="[_a-zA-Z][_a-zA-Z0-9]*")
52
+ PydanticArgumentNameType = Annotated[str, Field(pattern="[_a-zA-Z][_a-zA-Z0-9]*")]
43
53
 
44
- PydanticExecutionParameter = pydantic.constr(regex=EXECUTION_PARAMETER_PATTERN)
54
+ PydanticExecutionParameter = Annotated[
55
+ str, Field(pattern=EXECUTION_PARAMETER_PATTERN)
56
+ ]
@@ -20,6 +20,7 @@ class ProviderVendor(StrEnum):
20
20
  OQC = "OQC"
21
21
  INTEL = "Intel"
22
22
  AQT = "AQT"
23
+ IQCC = "IQCC"
23
24
 
24
25
 
25
26
  class ProviderTypeVendor:
@@ -33,6 +34,7 @@ class ProviderTypeVendor:
33
34
  OQC = Literal[ProviderVendor.OQC]
34
35
  INTEL = Literal[ProviderVendor.INTEL]
35
36
  AQT = Literal[ProviderVendor.AQT]
37
+ IQCC = Literal[ProviderVendor.IQCC]
36
38
 
37
39
 
38
40
  class ClassiqSimulatorBackendNames(StrEnum):
@@ -5,6 +5,7 @@ from typing import Set, Tuple, Union
5
5
 
6
6
  import numpy as np
7
7
  import pydantic
8
+ from pydantic import ConfigDict
8
9
 
9
10
  from classiq.interface.exceptions import ClassiqValueError
10
11
  from classiq.interface.helpers.hashable_pydantic_base_model import (
@@ -75,7 +76,8 @@ class FermionicOperator(HashablePydanticBaseModel):
75
76
 
76
77
  return (op_symbol, op_index)
77
78
 
78
- @pydantic.validator("op_list")
79
+ @pydantic.field_validator("op_list")
80
+ @classmethod
79
81
  def _validate_op_list(cls, op_list: list) -> list:
80
82
  return list(map(cls._validate_single_op, op_list))
81
83
 
@@ -99,8 +101,7 @@ class FermionicOperator(HashablePydanticBaseModel):
99
101
  "FermionicOperator can be summed together only with type FermionicOperator or SummedFermionicOperator"
100
102
  )
101
103
 
102
- class Config:
103
- frozen = True
104
+ model_config = ConfigDict(frozen=True)
104
105
 
105
106
  @staticmethod
106
107
  def _to_ladder_op(char: str) -> str:
@@ -135,9 +136,7 @@ class SummedFermionicOperator(HashablePydanticBaseModel):
135
136
  op_list: list = pydantic.Field(
136
137
  description="A list of tuples each containing a FermionicOperator and a coefficient.",
137
138
  )
138
-
139
- class Config:
140
- frozen = True
139
+ model_config = ConfigDict(frozen=True)
141
140
 
142
141
  @staticmethod
143
142
  def _validate_single_op(op: tuple) -> FermionicOperatorTuple:
@@ -166,7 +165,8 @@ class SummedFermionicOperator(HashablePydanticBaseModel):
166
165
 
167
166
  return op # type: ignore[return-value] # mypy thinks that it is `Tuple[Any, ...]`, though the asserts here tell otherwise..
168
167
 
169
- @pydantic.validator("op_list")
168
+ @pydantic.field_validator("op_list")
169
+ @classmethod
170
170
  def _validate_op_list(cls, op_list: list) -> list:
171
171
  return list(map(cls._validate_single_op, op_list))
172
172
 
@@ -1,7 +1,8 @@
1
- from typing import Any, Dict, List, Literal, Optional, Tuple, Union
1
+ from typing import List, Literal, Optional, Tuple, Union, cast
2
2
 
3
3
  import pydantic
4
- from pydantic import Field
4
+ from pydantic import ConfigDict, Field
5
+ from pydantic_core.core_schema import ValidationInfo
5
6
  from typing_extensions import Annotated
6
7
 
7
8
  from classiq.interface.chemistry.fermionic_operator import SummedFermionicOperator
@@ -52,18 +53,19 @@ class GroundStateProblem(HashablePydanticBaseModel):
52
53
  )
53
54
  num_qubits: Optional[int] = pydantic.Field(default=None)
54
55
 
55
- @pydantic.validator("z2_symmetries")
56
- def _validate_z2_symmetries(
57
- cls, z2_symmetries: bool, values: Dict[str, Any]
58
- ) -> bool:
59
- if z2_symmetries and values.get("mapping") == FermionMapping.FAST_BRAVYI_KITAEV:
56
+ @pydantic.field_validator("z2_symmetries")
57
+ @classmethod
58
+ def _validate_z2_symmetries(cls, z2_symmetries: bool, info: ValidationInfo) -> bool:
59
+ if (
60
+ z2_symmetries
61
+ and info.data.get("mapping") == FermionMapping.FAST_BRAVYI_KITAEV
62
+ ):
60
63
  raise ClassiqValueError(
61
64
  "z2 symmetries reduction can not be used for fast_bravyi_kitaev mapping"
62
65
  )
63
66
  return z2_symmetries
64
67
 
65
- class Config:
66
- frozen = True
68
+ model_config = ConfigDict(frozen=True)
67
69
 
68
70
 
69
71
  class MoleculeProblem(GroundStateProblem):
@@ -87,20 +89,23 @@ class HamiltonianProblem(GroundStateProblem):
87
89
  description="Tuple containing the numbers of alpha particles and beta particles"
88
90
  )
89
91
 
90
- @pydantic.validator("num_particles")
91
- def _validate_num_particles(cls, num_particles: List[int]) -> List[int]:
92
- assert isinstance(num_particles, list)
92
+ @pydantic.field_validator("num_particles", mode="before")
93
+ @classmethod
94
+ def _validate_num_particles(
95
+ cls,
96
+ num_particles: Union[
97
+ List[Union[int, float]], Tuple[Union[int, float], Union[int, float]]
98
+ ],
99
+ ) -> List[int]:
100
+ assert isinstance(num_particles, (list, tuple))
93
101
  assert len(num_particles) == 2
94
102
 
95
- # This probably will never happen, since pydantic automatically converts
96
- # floats to ints
97
- assert isinstance(num_particles[0], int)
98
- assert num_particles[0] >= 1
103
+ num_particles = [int(x) for x in num_particles]
99
104
 
100
- assert isinstance(num_particles[1], int)
105
+ assert num_particles[0] >= 1
101
106
  assert num_particles[1] >= 1
102
107
 
103
- return num_particles
108
+ return cast(List[int], num_particles)
104
109
 
105
110
 
106
111
  CHEMISTRY_PROBLEMS = (MoleculeProblem, HamiltonianProblem)
@@ -1,6 +1,7 @@
1
1
  from typing import List, Literal, Union
2
2
 
3
3
  import pydantic
4
+ from pydantic import ConfigDict
4
5
 
5
6
  from classiq.interface.chemistry.elements import ELEMENTS
6
7
  from classiq.interface.exceptions import ClassiqValueError
@@ -20,7 +21,7 @@ class Atom(HashablePydanticBaseModel):
20
21
  class Molecule(HashablePydanticBaseModel):
21
22
  atoms: List[Atom] = pydantic.Field(
22
23
  description="A list of atoms each containing the atoms symbol and its (x,y,z) location",
23
- min_items=1,
24
+ min_length=1,
24
25
  )
25
26
  spin: pydantic.NonNegativeInt = pydantic.Field(
26
27
  default=1, description="spin of the molecule"
@@ -33,12 +34,17 @@ class Molecule(HashablePydanticBaseModel):
33
34
  def atoms_type(self) -> List[AtomType]:
34
35
  return [(atom.symbol, [atom.x, atom.y, atom.z]) for atom in self.atoms]
35
36
 
36
- @pydantic.validator("atoms", each_item=True, pre=True)
37
- def _validate_atoms(cls, atom: Union[AtomType, Atom]) -> Atom:
37
+ @classmethod
38
+ def _validate_atom(cls, atom: Union[AtomType, Atom]) -> Atom:
38
39
  if isinstance(atom, (list, tuple)):
39
40
  return cls._validate_old_atoms_type(atom)
40
41
  return atom
41
42
 
43
+ @pydantic.field_validator("atoms", mode="before")
44
+ @classmethod
45
+ def _validate_atoms(cls, atoms: List[Union[AtomType, Atom]]) -> List[Atom]:
46
+ return [cls._validate_atom(atom) for atom in atoms]
47
+
42
48
  @staticmethod
43
49
  def _validate_old_atoms_type(atom: AtomType) -> Atom:
44
50
  if len(atom) != 2:
@@ -62,5 +68,4 @@ class Molecule(HashablePydanticBaseModel):
62
68
 
63
69
  return Atom(symbol=symbol, x=coordinate[0], y=coordinate[1], z=coordinate[2])
64
70
 
65
- class Config:
66
- frozen = True
71
+ model_config = ConfigDict(frozen=True)
@@ -5,7 +5,6 @@ from typing import (
5
5
  Dict,
6
6
  List,
7
7
  Optional,
8
- Tuple,
9
8
  Union,
10
9
  cast,
11
10
  )
@@ -14,6 +13,7 @@ import numpy as np
14
13
  import pydantic
15
14
  import sympy
16
15
  from more_itertools import all_equal
16
+ from pydantic import ConfigDict
17
17
 
18
18
  from classiq.interface.exceptions import ClassiqValueError
19
19
  from classiq.interface.generator.function_params import validate_expression_str
@@ -42,8 +42,8 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
42
42
  pauli_list: PydanticPauliList = pydantic.Field(
43
43
  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)].",
44
44
  )
45
- is_hermitian: bool = pydantic.Field(default=False)
46
45
  has_complex_coefficients: bool = pydantic.Field(default=True)
46
+ is_hermitian: bool = pydantic.Field(default=False)
47
47
 
48
48
  def show(self) -> str:
49
49
  if self.is_hermitian:
@@ -55,16 +55,19 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
55
55
  f"+({summand[1]:+.3f}) * {summand[0]}" for summand in self.pauli_list
56
56
  )
57
57
 
58
- @pydantic.validator("pauli_list", each_item=True, pre=True)
58
+ @pydantic.field_validator("pauli_list", mode="before")
59
+ @classmethod
59
60
  def _validate_pauli_monomials(
60
- cls, monomial: Tuple[PydanticPauliMonomialStr, ParameterComplexType]
61
- ) -> Tuple[PydanticPauliMonomialStr, ParameterComplexType]:
62
- _PauliMonomialLengthValidator( # type: ignore[call-arg]
63
- monomial=monomial
64
- ) # Validate the length of the monomial.
65
- coeff = cls._validate_monomial_coefficient(monomial[1])
66
- parsed_monomial = _PauliMonomialParser(string=monomial[0], coeff=coeff) # type: ignore[call-arg]
67
- return (parsed_monomial.string, parsed_monomial.coeff)
61
+ cls, pauli_list: PydanticPauliList
62
+ ) -> PydanticPauliList:
63
+ validated_pauli_list = []
64
+ for monomial in pauli_list:
65
+ # Validate the length
66
+ _PauliMonomialLengthValidator(monomial=monomial) # type: ignore[call-arg]
67
+ coeff = cls._validate_monomial_coefficient(monomial[1])
68
+ parsed_monomial = _PauliMonomialParser(string=monomial[0], coeff=coeff)
69
+ validated_pauli_list.append((parsed_monomial.string, parsed_monomial.coeff))
70
+ return validated_pauli_list
68
71
 
69
72
  @staticmethod
70
73
  def _validate_monomial_coefficient(
@@ -76,16 +79,38 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
76
79
  coeff = str(coeff)
77
80
  return coeff
78
81
 
79
- @pydantic.validator("pauli_list")
82
+ @pydantic.field_validator("pauli_list", mode="after")
83
+ @classmethod
80
84
  def _validate_pauli_list(cls, pauli_list: PydanticPauliList) -> PydanticPauliList:
81
85
  if not all_equal(len(summand[0]) for summand in pauli_list):
82
86
  raise ClassiqValueError("Pauli strings have incompatible lengths.")
83
87
  return pauli_list
84
88
 
85
- @pydantic.root_validator
89
+ @staticmethod
90
+ def check_if_hermitian(pauli_list: PydanticPauliList) -> bool:
91
+ if all(isinstance(summand[1], (float, int, complex)) for summand in pauli_list):
92
+ if all(np.isclose(summand[1].imag, 0) for summand in pauli_list): # type: ignore[union-attr]
93
+ return True
94
+
95
+ for pauli_string, coeff in pauli_list:
96
+ reverse_string = pauli_string[::-1]
97
+ reverse_found = False
98
+ for other_string, other_coeff in pauli_list:
99
+ if other_string == reverse_string and np.isclose(
100
+ coeff, other_coeff.conjugate() # type: ignore[union-attr]
101
+ ):
102
+ reverse_found = True
103
+ break
104
+ if not reverse_found:
105
+ return False
106
+ return True
107
+ return False
108
+
109
+ @pydantic.model_validator(mode="before")
110
+ @classmethod
86
111
  def _validate_hermitianity(cls, values: Dict[str, Any]) -> Dict[str, Any]:
87
112
  pauli_list = values.get("pauli_list", [])
88
- if all(isinstance(summand[1], complex) for summand in pauli_list):
113
+ if PauliOperator.check_if_hermitian(pauli_list):
89
114
  values["is_hermitian"] = all(
90
115
  np.isclose(complex(summand[1]).real, summand[1])
91
116
  for summand in pauli_list
@@ -93,7 +118,7 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
93
118
  if values.get("is_hermitian", False):
94
119
  values["has_complex_coefficients"] = False
95
120
  values["pauli_list"] = [
96
- (summand[0], complex(summand[1].real)) for summand in pauli_list
121
+ (summand[0], complex(summand[1]).real) for summand in pauli_list
97
122
  ]
98
123
  else:
99
124
  values["has_complex_coefficients"] = not all(
@@ -161,7 +186,7 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
161
186
  (self._extend_pauli_string(pauli_string, num_extra_qubits), coeff)
162
187
  for (pauli_string, coeff) in self.pauli_list
163
188
  ]
164
- return self.copy(update={"pauli_list": new_pauli_list}, deep=True)
189
+ return self.model_copy(update={"pauli_list": new_pauli_list}, deep=True)
165
190
 
166
191
  @staticmethod
167
192
  def _reorder_pauli_string(
@@ -211,7 +236,7 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
211
236
  (cls._reorder_pauli_string(pauli_string, order, new_num_qubits), coeff)
212
237
  for pauli_string, coeff in operator.pauli_list
213
238
  ]
214
- return cls(pauli_list=new_pauli_list)
239
+ return cls(pauli_list=new_pauli_list, is_hermitian=operator.is_hermitian)
215
240
 
216
241
  @classmethod
217
242
  def from_unzipped_lists(
@@ -234,8 +259,7 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
234
259
  ]
235
260
  )
236
261
 
237
- class Config:
238
- frozen = True
262
+ model_config = ConfigDict(frozen=True)
239
263
 
240
264
 
241
265
  class PauliOperatorV1(HashablePydanticBaseModel):
@@ -259,16 +283,19 @@ class PauliOperatorV1(HashablePydanticBaseModel):
259
283
  f"+({summand[1]:+.3f}) * {summand[0]}" for summand in self.pauli_list
260
284
  )
261
285
 
262
- @pydantic.validator("pauli_list", each_item=True, pre=True)
286
+ @pydantic.field_validator("pauli_list", mode="before")
287
+ @classmethod
263
288
  def _validate_pauli_monomials(
264
- cls, monomial: Tuple[PydanticPauliMonomialStr, ParameterComplexType]
265
- ) -> Tuple[PydanticPauliMonomialStr, ParameterComplexType]:
266
- _PauliMonomialLengthValidator( # type: ignore[call-arg]
267
- monomial=monomial
268
- ) # Validate the length of the monomial.
269
- coeff = cls._validate_monomial_coefficient(monomial[1])
270
- parsed_monomial = _PauliMonomialParser(string=monomial[0], coeff=coeff) # type: ignore[call-arg]
271
- return (parsed_monomial.string, parsed_monomial.coeff)
289
+ cls, pauli_list: PydanticPauliList
290
+ ) -> PydanticPauliList:
291
+ validated_pauli_list = []
292
+ for monomial in pauli_list:
293
+ # Validate the length
294
+ _PauliMonomialLengthValidator(monomial=monomial) # type: ignore[call-arg]
295
+ coeff = cls._validate_monomial_coefficient(monomial[1])
296
+ parsed_monomial = _PauliMonomialParser(string=monomial[0], coeff=coeff)
297
+ validated_pauli_list.append((parsed_monomial.string, parsed_monomial.coeff))
298
+ return validated_pauli_list
272
299
 
273
300
  @staticmethod
274
301
  def _validate_monomial_coefficient(
@@ -280,32 +307,34 @@ class PauliOperatorV1(HashablePydanticBaseModel):
280
307
  coeff = str(coeff)
281
308
  return coeff
282
309
 
283
- @pydantic.validator("pauli_list")
310
+ @pydantic.field_validator("pauli_list", mode="after")
311
+ @classmethod
284
312
  def _validate_pauli_list(cls, pauli_list: PydanticPauliList) -> PydanticPauliList:
285
313
  if not all_equal(len(summand[0]) for summand in pauli_list):
286
314
  raise ClassiqValueError("Pauli strings have incompatible lengths.")
287
315
  return pauli_list
288
316
 
289
- @pydantic.root_validator
290
- def _validate_hermitianity(cls, values: Dict[str, Any]) -> Dict[str, Any]:
291
- pauli_list = values.get("pauli_list", [])
317
+ @pydantic.model_validator(mode="before")
318
+ @classmethod
319
+ def _validate_hermitianity(cls, v: Dict[str, Any]) -> Dict[str, Any]:
320
+ pauli_list = cast(PydanticPauliList, v.get("pauli_list"))
292
321
  if all(isinstance(summand[1], complex) for summand in pauli_list):
293
- values["is_hermitian"] = all(
322
+ v["is_hermitian"] = all(
294
323
  np.isclose(complex(summand[1]).real, summand[1])
295
324
  for summand in pauli_list
296
325
  )
297
- if values.get("is_hermitian", False):
298
- values["has_complex_coefficients"] = False
299
- values["pauli_list"] = [
300
- (summand[0], complex(summand[1].real)) for summand in pauli_list
326
+ if v["is_hermitian"]:
327
+ v["has_complex_coefficients"] = False
328
+ v["pauli_list"] = [
329
+ (summand[0], complex(summand[1]).real) for summand in pauli_list
301
330
  ]
302
331
  else:
303
- values["has_complex_coefficients"] = not all(
332
+ v["has_complex_coefficients"] = not all(
304
333
  np.isclose(complex(summand[1]).real, summand[1])
305
334
  for summand in pauli_list
306
335
  if isinstance(summand[1], complex)
307
336
  )
308
- return values
337
+ return v
309
338
 
310
339
  def __mul__(self, coefficient: complex) -> "PauliOperatorV1":
311
340
  multiplied_ising = [
@@ -361,7 +390,7 @@ class PauliOperatorV1(HashablePydanticBaseModel):
361
390
  (self._extend_pauli_string(pauli_string, num_extra_qubits), coeff)
362
391
  for (pauli_string, coeff) in self.pauli_list
363
392
  ]
364
- return self.copy(update={"pauli_list": new_pauli_list}, deep=True)
393
+ return self.model_copy(update={"pauli_list": new_pauli_list}, deep=True)
365
394
 
366
395
  @staticmethod
367
396
  def _reorder_pauli_string(
@@ -434,8 +463,7 @@ class PauliOperatorV1(HashablePydanticBaseModel):
434
463
  ]
435
464
  )
436
465
 
437
- class Config:
438
- frozen = True
466
+ model_config = ConfigDict(frozen=True)
439
467
 
440
468
 
441
469
  # This class validates the length of a monomial.
@@ -444,8 +472,7 @@ class _PauliMonomialLengthValidator:
444
472
  monomial: PydanticPauliMonomial
445
473
 
446
474
 
447
- @pydantic.dataclasses.dataclass
448
- class _PauliMonomialParser:
475
+ class _PauliMonomialParser(pydantic.BaseModel):
449
476
  string: PydanticPauliMonomialStr
450
477
  coeff: PydanticParameterComplexType
451
478
 
@@ -53,7 +53,8 @@ class MhtQaoaInput(BaseModel):
53
53
  def is_valid_cost(self, cost: float) -> bool:
54
54
  return True
55
55
 
56
- @pydantic.validator("plot_list")
56
+ @pydantic.field_validator("plot_list")
57
+ @classmethod
57
58
  def round_plot_list_times_and_validate(
58
59
  cls, plot_list: List[PlotData]
59
60
  ) -> List[PlotData]:
@@ -15,8 +15,7 @@ ParameterValue = Union[float, int, str, None]
15
15
  class FunctionDebugInfo(BaseModel):
16
16
  name: str
17
17
  # Parameters describe classical parameters passed to function
18
- # ParameterValue appears in type only for backwards compatibility
19
- parameters: Dict[str, str] = Field(type=Dict[str, ParameterValue])
18
+ parameters: Dict[str, str]
20
19
  level: OperationLevel
21
20
  is_allocate_or_free: bool = Field(default=False)
22
21
  is_inverse: bool = Field(default=False)
@@ -54,8 +53,8 @@ class DebugInfoCollection(BaseModel):
54
53
  # Pydantic only started supporting UUID as keys in Pydantic V2
55
54
  # See https://github.com/pydantic/pydantic/issues/2096#issuecomment-814860206
56
55
  # For now, we use strings as keys in the raw data and use UUID in the wrapper logic
57
- data: Dict[str, FunctionDebugInfo] = Field(default_factory=dict)
58
- blackbox_data: Dict[str, FunctionDebugInfoInterface] = Field(default_factory=dict)
56
+ data: Dict[str, FunctionDebugInfo] = Field(default={})
57
+ blackbox_data: Dict[str, FunctionDebugInfoInterface] = Field(default={})
59
58
 
60
59
  def __setitem__(self, key: UUID, value: FunctionDebugInfo) -> None:
61
60
  self.data[str(key)] = value
@@ -51,7 +51,9 @@ class ClassiqAnalyzerVisualizationError(ClassiqError):
51
51
 
52
52
 
53
53
  class ClassiqAPIError(ClassiqError):
54
- pass
54
+ def __init__(self, message: str, status_code: Optional[int] = None) -> None:
55
+ self.status_code = status_code
56
+ super().__init__(message)
55
57
 
56
58
 
57
59
  class ClassiqVersionError(ClassiqError):
@@ -0,0 +1,19 @@
1
+ from classiq.interface.helpers.versioned_model import VersionedModel
2
+
3
+
4
+ class IQCCInitAuthData(VersionedModel):
5
+ auth_scope_id: str
6
+ auth_method_id: str
7
+
8
+
9
+ class IQCCInitAuthResponse(VersionedModel):
10
+ auth_url: str
11
+ token_id: str
12
+
13
+
14
+ class IQCCProbeAuthData(IQCCInitAuthData):
15
+ token_id: str
16
+
17
+
18
+ class IQCCProbeAuthResponse(VersionedModel):
19
+ auth_token: str
@@ -1,28 +1,28 @@
1
1
  from datetime import datetime
2
2
  from typing import List, Optional
3
3
 
4
- from pydantic import BaseModel, Extra
4
+ from pydantic import BaseModel
5
5
 
6
6
  from classiq.interface.jobs import JobStatus
7
7
 
8
8
 
9
- class ExecutionJobDetailsV1(BaseModel, extra=Extra.ignore):
9
+ class ExecutionJobDetailsV1(BaseModel, extra="ignore"):
10
10
  id: str
11
11
 
12
- name: Optional[str]
12
+ name: Optional[str] = None
13
13
  start_time: datetime
14
- end_time: Optional[datetime]
14
+ end_time: Optional[datetime] = None
15
15
 
16
- provider: Optional[str]
17
- backend_name: Optional[str]
16
+ provider: Optional[str] = None
17
+ backend_name: Optional[str] = None
18
18
 
19
19
  status: JobStatus
20
20
 
21
- num_shots: Optional[int]
22
- program_id: Optional[str]
21
+ num_shots: Optional[int] = None
22
+ program_id: Optional[str] = None
23
23
 
24
- error: Optional[str]
24
+ error: Optional[str] = None
25
25
 
26
26
 
27
- class ExecutionJobsQueryResultsV1(BaseModel, extra=Extra.ignore):
27
+ class ExecutionJobsQueryResultsV1(BaseModel, extra="ignore"):
28
28
  results: List[ExecutionJobDetailsV1]