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
@@ -1,11 +1,12 @@
1
+ import datetime
1
2
  from datetime import date
2
- from typing import Any, Dict, List, Optional
3
+ from typing import Any, Dict, List, Optional, Union
3
4
 
4
5
  import pydantic
5
- from pydantic import validator
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+ from pydantic_core.core_schema import ValidationInfo
6
8
 
7
9
  from classiq.interface.enum_utils import StrEnum
8
- from classiq.interface.exceptions import ClassiqValueError
9
10
 
10
11
 
11
12
  class Granularity(StrEnum):
@@ -19,30 +20,44 @@ class CostScope(StrEnum):
19
20
  organization = "organization"
20
21
 
21
22
 
22
- class ExecutionCostForTimePeriod(pydantic.BaseModel):
23
- start: date = pydantic.Field(
24
- description="The beginning of the time period for tasks usage and cost ("
25
- "inclusive).",
23
+ class ExecutionCostForTimePeriod(BaseModel):
24
+ start: date = Field(
25
+ description="The beginning of the time period for tasks usage and cost (inclusive)."
26
26
  )
27
- end: date = pydantic.Field(
28
- description="The end of the time period for tasks usage and cost (exclusive).",
27
+ end: date = Field(
28
+ description="The end of the time period for tasks usage and cost (exclusive)."
29
29
  )
30
- granularity: Granularity = pydantic.Field(
30
+ granularity: Granularity = Field(
31
31
  description="Either MONTHLY or DAILY, or HOURLY.", default=Granularity.daily
32
32
  )
33
- cost_scope: CostScope = pydantic.Field(
33
+ cost_scope: CostScope = Field(
34
34
  description="Either user or organization", default=CostScope.user
35
35
  )
36
36
 
37
- class Config:
38
- json_encoders = {date: lambda v: v.strftime("%Y-%m-%d")}
39
-
40
- @validator("end")
41
- def date_order(cls, v: date, values: Dict[str, Any], **kwargs: Any) -> date:
42
- if "start" in values and v <= values["start"]:
43
- raise ClassiqValueError('"end" date should be after "start" date')
37
+ @pydantic.field_validator("start", mode="before")
38
+ @classmethod
39
+ def validate_start_date(cls, start_date: Union[datetime.datetime, date]) -> date:
40
+ if isinstance(start_date, datetime.datetime):
41
+ return start_date.date()
42
+ return start_date
43
+
44
+ @pydantic.field_validator("end", mode="before")
45
+ @classmethod
46
+ def validate_date_and_date_order(
47
+ cls, v: Union[date, datetime.datetime], info: ValidationInfo
48
+ ) -> date:
49
+ if isinstance(v, datetime.datetime):
50
+ v = v.date()
51
+ if "start" in info.data and v <= info.data["start"]:
52
+ raise ValueError('"end" date should be after "start" date')
44
53
  return v
45
54
 
55
+ def dict(self, **kwargs: Any) -> Dict[str, Any]:
56
+ data = super().model_dump(**kwargs)
57
+ data["start"] = self.start.strftime("%Y-%m-%d")
58
+ data["end"] = self.end.strftime("%Y-%m-%d")
59
+ return data
60
+
46
61
 
47
62
  """The following models describe the aws response model and based on this schema:
48
63
  https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ce.html#CostExplorer.Client.get_cost_and_usage"""
@@ -65,8 +80,10 @@ class Total(pydantic.BaseModel):
65
80
  class ExecutedTaskForPeriodItem(pydantic.BaseModel):
66
81
  TimePeriod: TimePeriod
67
82
  Total: Total
68
- Groups: Optional[List]
69
- Estimated: Optional[bool]
83
+ Groups: Optional[List] = None
84
+ Estimated: Optional[bool] = None
85
+
86
+ model_config = ConfigDict(extra="forbid")
70
87
 
71
88
 
72
89
  class ExecutionCostForTimePeriodResponse(pydantic.BaseModel):
@@ -59,8 +59,7 @@ class ExecutionPreferences(pydantic.BaseModel):
59
59
  title="Transpilation Option",
60
60
  )
61
61
  job_name: Optional[str] = pydantic.Field(
62
- min_length=1,
63
- description="The job name",
62
+ min_length=1, description="The job name", default=None
64
63
  )
65
64
 
66
65
 
@@ -57,9 +57,9 @@ class ExecutionJobDetails(VersionedModel):
57
57
  status: JobStatus
58
58
 
59
59
  num_shots: Optional[int]
60
- program_id: Optional[str]
60
+ program_id: Optional[str] = Field(default=None)
61
61
 
62
- error: Optional[str]
62
+ error: Optional[str] = Field(default=None)
63
63
 
64
64
 
65
65
  class ExecutionJobsQueryResults(VersionedModel):
@@ -1,6 +1,6 @@
1
1
  from typing import Any, List, Literal, Union
2
2
 
3
- from pydantic import BaseModel, Field
3
+ from pydantic import BaseModel, ConfigDict, Field
4
4
  from typing_extensions import Annotated, TypeAlias
5
5
 
6
6
  from classiq.interface.enum_utils import StrEnum
@@ -85,7 +85,7 @@ class TaggedIQAEResult(BaseModel):
85
85
  class TaggedUnstructured(BaseModel):
86
86
  value_type: Literal[SavedResultValueType.Unstructured]
87
87
  name: str
88
- value: Any
88
+ value: Any = None
89
89
 
90
90
 
91
91
  SavedResult = Annotated[
@@ -109,3 +109,5 @@ ResultsCollection: TypeAlias = List[SavedResult]
109
109
 
110
110
  class ExecuteGeneratedCircuitResults(VersionedModel):
111
111
  results: ResultsCollection
112
+
113
+ model_config = ConfigDict(extra="forbid")
@@ -14,6 +14,6 @@ class IQAEIterationData(BaseModel):
14
14
 
15
15
  class IQAEResult(VersionedModel, QmodPyObject):
16
16
  estimation: float
17
- confidence_interval: List[float] = Field(min_items=2, max_items=2)
17
+ confidence_interval: List[float] = Field(min_length=2, max_length=2)
18
18
  iterations_data: List[IQAEIterationData]
19
19
  warnings: List[str]
@@ -1,7 +1,8 @@
1
- from typing import Any, Dict, List, Optional
1
+ from typing import List, Optional
2
2
 
3
3
  import pydantic
4
4
  from pydantic import BaseModel
5
+ from pydantic_core.core_schema import ValidationInfo
5
6
 
6
7
  from classiq.interface.enum_utils import StrEnum
7
8
  from classiq.interface.exceptions import ClassiqValueError
@@ -53,11 +54,12 @@ class OptimizerPreferences(BaseModel):
53
54
  description="If True, the optimizer will not compute the variance of the ansatz.",
54
55
  )
55
56
 
56
- @pydantic.validator("tolerance", pre=True, always=True)
57
+ @pydantic.field_validator("tolerance", mode="before")
58
+ @classmethod
57
59
  def check_tolerance(
58
- cls, tolerance: Optional[pydantic.PositiveFloat], values: Dict[str, Any]
60
+ cls, tolerance: Optional[pydantic.PositiveFloat], info: ValidationInfo
59
61
  ) -> Optional[pydantic.PositiveFloat]:
60
- optimizer_type = values.get("type")
62
+ optimizer_type = info.data.get("type")
61
63
  if tolerance is not None and optimizer_type == OptimizerType.SPSA:
62
64
  raise ClassiqValueError("No tolerance param for SPSA optimizer")
63
65
 
@@ -66,11 +68,12 @@ class OptimizerPreferences(BaseModel):
66
68
 
67
69
  return tolerance
68
70
 
69
- @pydantic.validator("step_size", pre=True, always=True)
71
+ @pydantic.field_validator("step_size", mode="before")
72
+ @classmethod
70
73
  def check_step_size(
71
- cls, step_size: Optional[pydantic.PositiveFloat], values: Dict[str, Any]
74
+ cls, step_size: Optional[pydantic.PositiveFloat], info: ValidationInfo
72
75
  ) -> Optional[pydantic.PositiveFloat]:
73
- optimizer_type = values.get("name")
76
+ optimizer_type = info.data.get("name")
74
77
  if step_size is not None and optimizer_type not in (
75
78
  OptimizerType.L_BFGS_B,
76
79
  OptimizerType.ADAM,
@@ -109,11 +112,12 @@ class CombinatorialOptimizer(OptimizerPreferences):
109
112
  description="Whether to check if all the solutions satisfy the constraints",
110
113
  )
111
114
 
112
- @pydantic.validator("alpha_cvar", pre=True, always=True)
115
+ @pydantic.field_validator("alpha_cvar", mode="before")
116
+ @classmethod
113
117
  def check_alpha_cvar(
114
- cls, alpha_cvar: Optional[PydanticAlphaParamCVAR], values: Dict[str, Any]
118
+ cls, alpha_cvar: Optional[PydanticAlphaParamCVAR], info: ValidationInfo
115
119
  ) -> Optional[PydanticAlphaParamCVAR]:
116
- cost_type = values.get("cost_type")
120
+ cost_type = info.data.get("cost_type")
117
121
  if alpha_cvar is not None and cost_type != CostType.CVAR:
118
122
  raise ClassiqValueError("Use CVAR params only for CostType.CVAR.")
119
123
 
@@ -4,7 +4,8 @@ from pathlib import Path
4
4
  from typing import Any, Dict, Optional, Tuple, Union
5
5
 
6
6
  import pydantic
7
- from pydantic import BaseModel
7
+ from pydantic import BaseModel, ConfigDict
8
+ from pydantic_core.core_schema import ValidationInfo
8
9
 
9
10
  from classiq.interface.backend.ionq.ionq_quantum_program import IonqQuantumCircuit
10
11
  from classiq.interface.backend.pydantic_backend import PydanticArgumentNameType
@@ -31,17 +32,18 @@ class QuantumBaseCode(BaseModel):
31
32
  ..., description="The textual representation of the program"
32
33
  )
33
34
 
34
- @pydantic.validator("code")
35
+ @pydantic.field_validator("code")
36
+ @classmethod
35
37
  def load_quantum_program(
36
- cls, code: Union[CodeType, IonqQuantumCircuit], values: Dict[str, Any]
38
+ cls, code: Union[CodeType, IonqQuantumCircuit], values: ValidationInfo
37
39
  ) -> CodeType:
38
- syntax = values.get("syntax")
40
+ syntax = values.data.get("syntax")
39
41
  if isinstance(code, IonqQuantumCircuit):
40
42
  if syntax != QuantumInstructionSet.IONQ:
41
43
  raise ClassiqValueError(
42
44
  f"Invalid code type {type(code)} for syntax: {syntax}"
43
45
  )
44
- return code.json()
46
+ return code.model_dump_json()
45
47
 
46
48
  return code
47
49
 
@@ -56,41 +58,44 @@ class QuantumCode(QuantumBaseCode):
56
58
  description="The map of outputs to their qubits in the circuit.",
57
59
  )
58
60
  registers_initialization: Optional[RegistersInitialization] = pydantic.Field(
59
- default_factory=None,
61
+ default=None,
60
62
  description="Initial conditions for the different registers in the circuit.",
61
63
  )
62
64
  synthesis_execution_data: Optional[ExecutionData] = pydantic.Field(default=None)
63
65
  synthesis_execution_arguments: Arguments = pydantic.Field(default_factory=dict)
66
+ model_config = ConfigDict(validate_assignment=True)
64
67
 
65
- class Config:
66
- validate_assignment = True
67
-
68
- @pydantic.validator("arguments")
68
+ @pydantic.field_validator("arguments", mode="before")
69
+ @classmethod
69
70
  def validate_arguments(
70
- cls, arguments: MultipleArguments, values: Dict[str, Any]
71
+ cls, arguments: MultipleArguments, info: ValidationInfo
71
72
  ) -> MultipleArguments:
72
- if arguments and values.get("syntax") not in (
73
+ if arguments and info.data.get("syntax") not in (
73
74
  QuantumInstructionSet.QSHARP,
74
75
  QuantumInstructionSet.QASM,
75
76
  ):
76
77
  raise ClassiqValueError("Only QASM or Q# programs support arguments")
77
78
 
78
- if values.get("syntax") == QuantumInstructionSet.QSHARP and len(arguments) > 1:
79
+ if (
80
+ info.data.get("syntax") == QuantumInstructionSet.QSHARP
81
+ and len(arguments) > 1
82
+ ):
79
83
  raise ClassiqValueError(
80
84
  f"Q# programs supports only one group of arguments. {len(arguments)} given"
81
85
  )
82
86
 
83
87
  return arguments
84
88
 
85
- @pydantic.validator("synthesis_execution_data")
89
+ @pydantic.field_validator("synthesis_execution_data")
90
+ @classmethod
86
91
  def validate_synthesis_execution_data(
87
92
  cls,
88
93
  synthesis_execution_data: Optional[ExecutionData],
89
- values: Dict[str, Any],
94
+ values: ValidationInfo,
90
95
  ) -> Optional[ExecutionData]:
91
96
  if (
92
97
  synthesis_execution_data is not None
93
- and values.get("syntax") is not QuantumInstructionSet.QASM
98
+ and values.data.get("syntax") is not QuantumInstructionSet.QASM
94
99
  ):
95
100
  raise ClassiqValueError("Only QASM supports the requested configuration")
96
101
 
@@ -1,6 +1,7 @@
1
- from typing import Any, Dict, List
1
+ from typing import List
2
2
 
3
3
  import pydantic
4
+ from typing_extensions import Self
4
5
 
5
6
  from classiq.interface.exceptions import ClassiqStateInitializationError
6
7
  from classiq.interface.generator.arith import number_utils
@@ -15,19 +16,18 @@ class RegisterInitialization(pydantic.BaseModel):
15
16
  qubits: List[int]
16
17
  initial_condition: pydantic.NonNegativeInt
17
18
 
18
- @pydantic.validator("initial_condition", pre=True)
19
+ @pydantic.field_validator("initial_condition", mode="before")
20
+ @classmethod
19
21
  def _validate_initial_condition(cls, value: int) -> int:
20
22
  if not isinstance(value, int) or value < 0:
21
23
  raise ClassiqStateInitializationError(_NON_INTEGER_INITIALIZATION_ERROR_MSG)
22
24
  return value
23
25
 
24
- @pydantic.root_validator()
25
- def _validate_register_initialization(
26
- cls, values: Dict[str, Any]
27
- ) -> Dict[str, Any]:
28
- qubits: List[int] = values.get("qubits", list())
29
- initial_condition: int = values.get("initial_condition", 0)
30
- name: str = values.get("name", "")
26
+ @pydantic.model_validator(mode="after")
27
+ def _validate_register_initialization(self) -> Self:
28
+ qubits: List[int] = self.qubits or []
29
+ initial_condition: int = self.initial_condition or 0
30
+ name: str = self.name or ""
31
31
 
32
32
  initial_condition_length = number_utils.size(initial_condition)
33
33
  register_length = len(qubits)
@@ -35,4 +35,4 @@ class RegisterInitialization(pydantic.BaseModel):
35
35
  raise ClassiqStateInitializationError(
36
36
  f"Register {name} has {register_length} qubits, which is not enough to represent the number {initial_condition}."
37
37
  )
38
- return values
38
+ return self
@@ -16,7 +16,7 @@ from typing import (
16
16
 
17
17
  import pydantic
18
18
  from pydantic import BaseModel
19
- from typing_extensions import TypeAlias
19
+ from typing_extensions import Self, TypeAlias
20
20
 
21
21
  from classiq.interface.exceptions import ClassiqError
22
22
  from classiq.interface.executor.quantum_code import OutputQubitsMap, Qubits
@@ -24,6 +24,7 @@ from classiq.interface.generator.arith import number_utils
24
24
  from classiq.interface.generator.complex_type import Complex
25
25
  from classiq.interface.generator.functions.classical_type import QmodPyObject
26
26
  from classiq.interface.helpers.custom_pydantic_types import PydanticNonNegIntTuple
27
+ from classiq.interface.helpers.dotdict import get_recursive_dotdict
27
28
  from classiq.interface.helpers.versioned_model import VersionedModel
28
29
 
29
30
  _ILLEGAL_QUBIT_ERROR_MSG: str = "Illegal qubit index requested"
@@ -37,16 +38,26 @@ MeasuredShots: TypeAlias = pydantic.NonNegativeInt
37
38
  ParsedState: TypeAlias = Mapping[Name, RegisterValue]
38
39
  ParsedStates: TypeAlias = Mapping[State, ParsedState]
39
40
  Counts: TypeAlias = Dict[State, MeasuredShots]
40
- StateVector: TypeAlias = Optional[Dict[str, Any]]
41
+ StateVector: TypeAlias = Optional[Dict[str, Complex]]
42
+
43
+ if TYPE_CHECKING:
44
+ DotAccessParsedState = Mapping[Name, Any]
45
+ else:
46
+ DotAccessParsedState = ParsedState
41
47
 
42
48
 
43
49
  class SampledState(BaseModel):
44
- state: ParsedState
50
+ state: DotAccessParsedState
45
51
  shots: MeasuredShots
46
52
 
47
53
  def __repr__(self) -> str:
48
54
  return f"{self.state}: {self.shots}"
49
55
 
56
+ @pydantic.field_validator("state", mode="after")
57
+ @classmethod
58
+ def _convert_state_to_dotdict(cls, state: ParsedState) -> DotAccessParsedState:
59
+ return {name: get_recursive_dotdict(value) for name, value in state.items()}
60
+
50
61
 
51
62
  ParsedCounts: TypeAlias = List[SampledState]
52
63
 
@@ -60,7 +71,7 @@ class SimulatedState(BaseModel):
60
71
  return self.state[item]
61
72
 
62
73
 
63
- SimulatedState.update_forward_refs(RegisterValue=RegisterValue)
74
+ SimulatedState.model_rebuild()
64
75
  ParsedStateVector: TypeAlias = List[SimulatedState]
65
76
 
66
77
 
@@ -170,7 +181,7 @@ class ExecutionDetails(BaseModel, QmodPyObject):
170
181
  default=None,
171
182
  description="The state vector when executed on a simulator, with LSB right qubit order",
172
183
  )
173
- parsed_state_vector_states: ParsedStates = pydantic.Field(
184
+ parsed_state_vector_states: Optional[ParsedStates] = pydantic.Field(
174
185
  default=None,
175
186
  description="A mapping between the raw states of the state vector (bitstrings) to their parsed states (registers' values)",
176
187
  )
@@ -182,22 +193,23 @@ class ExecutionDetails(BaseModel, QmodPyObject):
182
193
  default=None, description="The total number of shots the circuit was executed"
183
194
  )
184
195
 
185
- @pydantic.validator("counts", pre=True)
196
+ @pydantic.field_validator("counts", mode="after")
197
+ @classmethod
186
198
  def _clean_spaces_from_counts_keys(cls, v: Counts) -> Counts:
187
199
  if not v or " " not in list(v.keys())[0]:
188
200
  return v
189
201
  return {state.replace(" ", ""): v[state] for state in v}
190
202
 
191
- @pydantic.validator("num_shots", always=True)
192
- def _validate_num_shots(
193
- cls, num_shots: Optional[int], values: Dict[str, Any]
194
- ) -> Optional[int]:
195
- if num_shots is not None:
196
- return num_shots
197
- counts = values.get("counts")
198
- if not counts:
199
- return None
200
- return sum(shots for _, shots in counts.items())
203
+ @pydantic.model_validator(mode="after")
204
+ def _validate_num_shots(self) -> Self:
205
+ if isinstance(self, ExecutionDetails):
206
+ if self.num_shots is not None:
207
+ return self
208
+ counts = self.counts
209
+ if not counts:
210
+ return self
211
+ self.num_shots = sum(shots for _, shots in counts.items())
212
+ return self
201
213
 
202
214
  @property
203
215
  def parsed_counts(self) -> ParsedCounts:
@@ -205,6 +217,8 @@ class ExecutionDetails(BaseModel, QmodPyObject):
205
217
 
206
218
  @property
207
219
  def parsed_state_vector(self) -> Optional[ParsedStateVector]:
220
+ if TYPE_CHECKING:
221
+ assert self.parsed_state_vector_states is not None
208
222
  return prepare_parsed_state_vector(
209
223
  self.state_vector, self.parsed_state_vector_states
210
224
  )
@@ -297,7 +311,7 @@ class MultipleExecutionDetails(VersionedModel):
297
311
  return self.details[index]
298
312
 
299
313
 
300
- class EstimationMetadata(BaseModel, extra=pydantic.Extra.allow):
314
+ class EstimationMetadata(BaseModel, extra="allow"):
301
315
  shots: Optional[pydantic.NonNegativeInt] = None
302
316
  remapped_qubits: bool = False
303
317
  input_qubit_map: Optional[List[PydanticNonNegIntTuple]] = None
@@ -19,7 +19,7 @@ class SolverResult(BaseModel):
19
19
  energy: float
20
20
  # TODO: add time units (like seconds)
21
21
  time: Optional[float] = None
22
- solution: Optional[Solution]
22
+ solution: Optional[Solution] = None
23
23
 
24
24
 
25
25
  class SolutionData(BaseModel):
@@ -1,6 +1,8 @@
1
1
  from typing import Any, Dict, Optional, Union
2
2
 
3
3
  import pydantic
4
+ from pydantic import ConfigDict
5
+ from pydantic_core.core_schema import ValidationInfo
4
6
 
5
7
  from classiq.interface.exceptions import ClassiqValueError
6
8
  from classiq.interface.generator.expressions.enums.finance_functions import (
@@ -20,9 +22,7 @@ class FunctionCondition(pydantic.BaseModel):
20
22
  default=False,
21
23
  description="When true, function is set when input is larger to threshold and otherwise 0. Default is False.",
22
24
  )
23
-
24
- class Config:
25
- frozen = True
25
+ model_config = ConfigDict(frozen=True)
26
26
 
27
27
 
28
28
  class FinanceFunctionInput(pydantic.BaseModel):
@@ -50,24 +50,33 @@ class FinanceFunctionInput(pydantic.BaseModel):
50
50
  description="The required probability on the tail of the distribution (1 - percentile)",
51
51
  )
52
52
 
53
- @pydantic.validator("f", pre=True)
54
- def _convert_f_if_str(cls, f: Any) -> "FinanceFunctionType":
55
- # Keep this for backwards-compatible string support
56
- if f in get_finance_function_dict():
57
- return get_finance_function_dict()[f]
58
- return f
53
+ @pydantic.model_validator(mode="before")
54
+ @classmethod
55
+ def _convert_f_if_str(cls, values: Any, info: ValidationInfo) -> Dict[str, Any]:
56
+ if isinstance(values, dict):
57
+ f = values.get("f")
58
+ elif isinstance(values, FinanceFunctionInput):
59
+ f = values.f
60
+ values = values.model_dump()
61
+ else:
62
+ f = info.data.get("f")
63
+ if isinstance(f, str) and f in get_finance_function_dict():
64
+ values["f"] = get_finance_function_dict()[f]
65
+ return values
59
66
 
60
- @pydantic.validator("use_chebyshev_polynomial_approximation")
67
+ @pydantic.field_validator("use_chebyshev_polynomial_approximation", mode="before")
68
+ @classmethod
61
69
  def _validate_polynomial_flag(
62
- cls, use_chebyshev_flag: bool, values: Dict[str, Any]
70
+ cls, use_chebyshev_flag: bool, info: ValidationInfo
63
71
  ) -> bool:
64
- if use_chebyshev_flag ^ (values.get("polynomial_degree") is None):
72
+ if use_chebyshev_flag ^ (info.data.get("polynomial_degree") is None):
65
73
  return use_chebyshev_flag
66
74
  raise ClassiqValueError(
67
75
  "Degree must be positive and use_chebyshev_polynomial_approximation set to True"
68
76
  )
69
77
 
70
- @pydantic.validator("f")
78
+ @pydantic.field_validator("f", mode="before")
79
+ @classmethod
71
80
  def _validate_finance_function(
72
81
  cls, f: Union[int, str, "FinanceFunctionType"]
73
82
  ) -> FinanceFunctionType:
@@ -77,17 +86,17 @@ class FinanceFunctionInput(pydantic.BaseModel):
77
86
  return FinanceFunctionType(f)
78
87
  return get_finance_function_dict()[f]
79
88
 
80
- @pydantic.validator("tail_probability", always=True)
89
+ @pydantic.field_validator("tail_probability", mode="before")
90
+ @classmethod
81
91
  def _validate_tail_probability_assignment_for_shortfall(
82
92
  cls,
83
93
  tail_probability: Optional[PydanticNonZeroProbabilityFloat],
84
- values: Dict[str, Any],
94
+ info: ValidationInfo,
85
95
  ) -> Optional[PydanticNonZeroProbabilityFloat]:
86
- if values.get("f") == FinanceFunctionType.SHORTFALL and not tail_probability:
96
+ if info.data.get("f") == FinanceFunctionType.SHORTFALL and not tail_probability:
87
97
  raise ClassiqValueError(
88
98
  "Tail probability must be set for expected shortfall"
89
99
  )
90
100
  return tail_probability
91
101
 
92
- class Config:
93
- frozen = True
102
+ model_config = ConfigDict(frozen=True)
@@ -2,6 +2,7 @@ from typing import Literal, Tuple
2
2
 
3
3
  import numpy as np
4
4
  import pydantic
5
+ from pydantic import ConfigDict
5
6
 
6
7
  from classiq.interface.finance.model_input import FinanceModelInput
7
8
 
@@ -36,5 +37,4 @@ class LogNormalModelInput(FinanceModelInput):
36
37
  def num_output_qubits(self) -> int:
37
38
  return self.num_qubits
38
39
 
39
- class Config:
40
- frozen = True
40
+ model_config = ConfigDict(frozen=True)
@@ -1,6 +1,8 @@
1
1
  import abc
2
2
  from typing import Tuple
3
3
 
4
+ from pydantic import ConfigDict
5
+
4
6
  from classiq.interface.helpers.hashable_pydantic_base_model import (
5
7
  HashablePydanticBaseModel,
6
8
  )
@@ -13,8 +15,7 @@ class FinanceModelInput(HashablePydanticBaseModel):
13
15
  def num_output_qubits(self) -> int:
14
16
  return 0
15
17
 
16
- class Config:
17
- frozen = True
18
+ model_config = ConfigDict(frozen=True)
18
19
 
19
20
  @property
20
21
  @abc.abstractmethod
@@ -1,8 +1,9 @@
1
1
  import re
2
- from typing import Any, Dict, get_args
2
+ from typing import get_args
3
3
 
4
4
  import pydantic
5
5
  import sympy
6
+ from typing_extensions import Self
6
7
 
7
8
  from classiq.interface.enum_utils import StrEnum
8
9
  from classiq.interface.exceptions import ClassiqValueError
@@ -50,7 +51,8 @@ class AmplitudeLoading(FunctionParams):
50
51
  description="Implementation options.",
51
52
  )
52
53
 
53
- @pydantic.validator("expression", pre=True)
54
+ @pydantic.field_validator("expression", mode="before")
55
+ @classmethod
54
56
  def validate_coefficient(cls, expression: str) -> str:
55
57
  if isinstance(expression, str):
56
58
  # We validate the given value is legal and does not contain code that will be executed in our BE.
@@ -65,9 +67,9 @@ class AmplitudeLoading(FunctionParams):
65
67
  return str(expression)
66
68
  return expression
67
69
 
68
- @pydantic.root_validator()
69
- def check_all_variable_are_defined(cls, values: Dict[str, Any]) -> Dict[str, Any]:
70
- expression = values.get("expression", "")
70
+ @pydantic.model_validator(mode="after")
71
+ def check_all_variable_are_defined(self) -> Self:
72
+ expression = self.expression or ""
71
73
  literals = set(re.findall(SUPPORTED_VAR_NAMES_REG, expression))
72
74
 
73
75
  not_allowed = literals.intersection(FORBIDDEN_LITERALS) - BOOLEAN_LITERALS
@@ -79,7 +81,7 @@ class AmplitudeLoading(FunctionParams):
79
81
 
80
82
  if len(variables) != 1:
81
83
  raise ClassiqValueError(f"{variables} must contain exactly single variable")
82
- return values
84
+ return self
83
85
 
84
86
  def _create_ios(self) -> None:
85
87
  self._inputs = {
@@ -1,6 +1,9 @@
1
1
  from typing import Tuple, Union
2
2
 
3
3
  from classiq.interface.generator.arith import number_utils
4
+ from classiq.interface.generator.arith.number_utils import (
5
+ get_int_representation_and_fraction_places,
6
+ )
4
7
  from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
5
8
 
6
9
  RegisterOrConst = Union[RegisterArithmeticInfo, float]
@@ -73,3 +76,24 @@ def as_arithmetic_info(
73
76
  fraction_places=number_utils.fraction_places(arg),
74
77
  bounds=(arg, arg) if with_bounds else None,
75
78
  )
79
+
80
+
81
+ def unsigned_integer_interpretation(
82
+ value: float, register: RegisterArithmeticInfo
83
+ ) -> int:
84
+ int_val, fraction_digits = get_int_representation_and_fraction_places(value)
85
+
86
+ # align fraction digits
87
+ fraction_digits_diff = register.fraction_places - fraction_digits
88
+ if fraction_digits_diff < 0:
89
+ int_val >>= -fraction_digits_diff
90
+ else:
91
+ int_val <<= fraction_digits_diff
92
+
93
+ # extend sign bit
94
+ if int(value) < 0:
95
+ bin_val = number_utils.binary_string(int_val)
96
+ bin_val += "1" * (register.size - len(bin_val))
97
+ int_val = number_utils.binary_to_int(bin_val[::-1])
98
+
99
+ return int_val