classiq 0.42.2__py3-none-any.whl → 0.43.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 (113) hide show
  1. classiq/__init__.py +2 -6
  2. classiq/_internals/api_wrapper.py +6 -12
  3. classiq/_internals/authentication/token_manager.py +5 -2
  4. classiq/_internals/jobs.py +5 -10
  5. classiq/analyzer/rb.py +3 -3
  6. classiq/applications/chemistry/chemistry_model_constructor.py +12 -8
  7. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -2
  8. classiq/applications/finance/finance_model_constructor.py +16 -13
  9. classiq/applications/qsvm/__init__.py +1 -3
  10. classiq/applications/qsvm/qsvm_model_constructor.py +7 -6
  11. classiq/exceptions.py +9 -4
  12. classiq/execution/execution_session.py +5 -2
  13. classiq/execution/qnn.py +1 -1
  14. classiq/executor.py +0 -2
  15. classiq/interface/_version.py +1 -1
  16. classiq/interface/chemistry/operator.py +19 -5
  17. classiq/interface/executor/constants.py +1 -0
  18. classiq/interface/finance/function_input.py +16 -10
  19. classiq/interface/generator/application_apis/chemistry_declarations.py +2 -2
  20. classiq/interface/generator/application_apis/qsvm_declarations.py +4 -2
  21. classiq/interface/generator/arith/argument_utils.py +20 -3
  22. classiq/interface/generator/arith/arithmetic_expression_validator.py +3 -26
  23. classiq/interface/generator/arith/binary_ops.py +8 -14
  24. classiq/interface/generator/arith/extremum_operations.py +30 -0
  25. classiq/interface/generator/arith/number_utils.py +1 -1
  26. classiq/interface/generator/arith/unary_ops.py +1 -3
  27. classiq/interface/generator/compiler_keywords.py +1 -1
  28. classiq/interface/generator/expressions/atomic_expression_functions.py +13 -3
  29. classiq/interface/generator/expressions/enums/__init__.py +0 -20
  30. classiq/interface/generator/expressions/enums/finance_functions.py +11 -18
  31. classiq/interface/generator/expressions/non_symbolic_expr.py +119 -0
  32. classiq/interface/generator/expressions/qmod_qarray_proxy.py +52 -37
  33. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +16 -11
  34. classiq/interface/generator/expressions/qmod_sized_proxy.py +5 -5
  35. classiq/interface/generator/function_param_list_without_self_reference.py +0 -10
  36. classiq/interface/generator/function_params.py +0 -4
  37. classiq/interface/generator/functions/__init__.py +0 -20
  38. classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +2 -2
  39. classiq/interface/generator/functions/builtins/open_lib_functions.py +530 -1
  40. classiq/interface/generator/functions/classical_type.py +22 -69
  41. classiq/interface/generator/functions/port_declaration.py +0 -11
  42. classiq/interface/generator/model/__init__.py +0 -1
  43. classiq/interface/generator/model/model.py +9 -185
  44. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +3 -1
  45. classiq/interface/generator/types/builtin_enum_declarations.py +69 -0
  46. classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +2 -2
  47. classiq/interface/generator/types/enum_declaration.py +57 -0
  48. classiq/interface/jobs.py +36 -65
  49. classiq/interface/model/bind_operation.py +3 -0
  50. classiq/interface/model/classical_parameter_declaration.py +3 -0
  51. classiq/interface/model/handle_binding.py +7 -0
  52. classiq/interface/model/inplace_binary_operation.py +13 -15
  53. classiq/interface/model/model.py +8 -20
  54. classiq/interface/model/native_function_definition.py +0 -17
  55. classiq/interface/model/quantum_function_call.py +63 -182
  56. classiq/interface/model/quantum_type.py +71 -10
  57. classiq/interface/server/routes.py +0 -6
  58. classiq/qmod/__init__.py +3 -3
  59. classiq/qmod/builtins/__init__.py +10 -1
  60. classiq/qmod/builtins/classical_execution_primitives.py +4 -2
  61. classiq/qmod/builtins/enums.py +177 -0
  62. classiq/qmod/builtins/functions.py +1 -2
  63. classiq/qmod/builtins/operations.py +2 -4
  64. classiq/qmod/builtins/structs.py +16 -17
  65. classiq/qmod/declaration_inferrer.py +23 -20
  66. classiq/qmod/model_state_container.py +2 -0
  67. classiq/qmod/native/pretty_printer.py +31 -13
  68. classiq/qmod/pretty_print/pretty_printer.py +52 -27
  69. classiq/qmod/qmod_constant.py +7 -3
  70. classiq/qmod/qmod_parameter.py +2 -1
  71. classiq/qmod/qmod_struct.py +9 -33
  72. classiq/qmod/qmod_variable.py +55 -22
  73. classiq/qmod/quantum_callable.py +6 -1
  74. classiq/qmod/quantum_expandable.py +29 -11
  75. classiq/qmod/quantum_function.py +8 -4
  76. classiq/qmod/semantics/annotation.py +38 -0
  77. classiq/qmod/semantics/error_manager.py +49 -0
  78. classiq/qmod/semantics/static_semantics_visitor.py +308 -0
  79. classiq/qmod/semantics/validation/func_call_validation.py +149 -0
  80. classiq/qmod/semantics/validation/types_validation.py +21 -0
  81. classiq/qmod/symbolic.py +6 -6
  82. classiq/qmod/symbolic_expr.py +26 -11
  83. classiq/qmod/utilities.py +23 -1
  84. {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/METADATA +2 -2
  85. {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/RECORD +88 -103
  86. classiq/_internals/_qfunc_ext.py +0 -6
  87. classiq/applications/libraries/ampltitude_estimation_library.py +0 -11
  88. classiq/interface/generator/credit_risk_example/linear_gci.py +0 -122
  89. classiq/interface/generator/credit_risk_example/weighted_adder.py +0 -69
  90. classiq/interface/generator/expressions/enums/chemistry.py +0 -28
  91. classiq/interface/generator/expressions/enums/classical_enum.py +0 -20
  92. classiq/interface/generator/expressions/enums/ladder_operator.py +0 -6
  93. classiq/interface/generator/expressions/enums/optimizers.py +0 -9
  94. classiq/interface/generator/expressions/enums/pauli.py +0 -8
  95. classiq/interface/generator/expressions/enums/qsvm_feature_map_entanglement.py +0 -9
  96. classiq/interface/generator/functions/foreign_function_definition.py +0 -114
  97. classiq/interface/generator/functions/function_implementation.py +0 -107
  98. classiq/interface/generator/functions/native_function_definition.py +0 -155
  99. classiq/interface/generator/functions/quantum_function_declaration.py +0 -69
  100. classiq/interface/generator/functions/register.py +0 -44
  101. classiq/interface/generator/functions/register_mapping_data.py +0 -106
  102. classiq/interface/generator/inequality_mixer.py +0 -51
  103. classiq/interface/generator/model/classical_main_validator.py +0 -106
  104. classiq/interface/generator/range_mixer.py +0 -56
  105. classiq/interface/generator/state_propagator.py +0 -74
  106. classiq/interface/model/resolvers/function_call_resolver.py +0 -64
  107. classiq/interface/model/validations/__init__.py +0 -0
  108. classiq/interface/model/validations/handle_validation_base.py +0 -55
  109. classiq/interface/model/validations/handles_validator.py +0 -153
  110. classiq/interface/model/validations/port_to_wire_name_generator.py +0 -12
  111. /classiq/{interface/generator/credit_risk_example → qmod/semantics}/__init__.py +0 -0
  112. /classiq/{interface/model/resolvers → qmod/semantics/validation}/__init__.py +0 -0
  113. {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/WHEEL +0 -0
classiq/interface/jobs.py CHANGED
@@ -1,16 +1,18 @@
1
- from typing import Any, Dict, Generic, Literal, TypeVar, Union
1
+ from typing import Any, Dict, Generic, Optional, TypeVar, Union
2
2
 
3
3
  import pydantic
4
- from pydantic import BaseModel, Field
4
+ from pydantic import BaseModel
5
5
  from pydantic.generics import GenericModel
6
6
 
7
7
  from classiq.interface.helpers.custom_encoders import CUSTOM_ENCODERS
8
8
 
9
9
  from classiq._internals.enum_utils import StrEnum
10
+ from classiq.exceptions import ClassiqAPIError
10
11
 
11
12
  JSONObject = Dict[str, Any]
12
13
  T = TypeVar("T", bound=Union[pydantic.BaseModel, JSONObject])
13
14
  AUTH_HEADER = "Classiq-BE-Auth"
15
+ INVALID_RESPONSE_ERROR_MSG = "Invalid response from Classiq API"
14
16
 
15
17
 
16
18
  class JobID(BaseModel):
@@ -34,70 +36,39 @@ class JobStatus(StrEnum):
34
36
  """
35
37
  A job can be in either of 3 states: ongoing, completed successfully or completed
36
38
  unsuccessfully. Each job status belongs to one of the 3 states
37
- The class JobDescriptionBase represents a job description, regardless of its state
38
- JobDescriptionSuccess represents a job that was completed successfully. It contains the
39
- job result in the description field. The type of the result depends on the route, and
40
- so it's defined as a generic class
41
- JobDescriptionFailure represents a job that was completed unsuccessfully. It contains
42
- the failure details (i.e., error message) in the description field
43
- JobDescriptionNonFinal represents a job that has not terminated yet. It does not contain
44
- any additional information
45
- JobDescriptionUnion is used to define a discriminator field between the 3 states. Since
46
- JobDescriptionSuccess is generic, so is the union. This means it cannot be defined
47
- as an annotated type alias (that is, we cannot define
48
- JobDescriptionUnion = Annotated[Union[...], Field(discriminator="kind")])
39
+ For ongoing jobs, we expect both the failure_details and result to be None
40
+ For successful jobs, we expect failure_details to be None and result to be an instance of T
41
+ For unsuccessful jobs, we expect failure_details to be a string and result to be None
49
42
  """
50
43
 
51
- SuccessStatus = Literal[JobStatus.COMPLETED]
52
- FailureStatus = Union[
53
- Literal[JobStatus.FAILED],
54
- Literal[JobStatus.CANCELLED],
55
- ]
56
- NonFinalStatus = Union[
57
- Literal[JobStatus.QUEUED],
58
- Literal[JobStatus.RUNNING],
59
- Literal[JobStatus.READY],
60
- Literal[JobStatus.CANCELLING],
61
- Literal[JobStatus.UNKNOWN],
62
- ]
63
44
 
64
-
65
- class FailureDetails(BaseModel):
66
- details: str
67
-
68
-
69
- class JobDescriptionBase(GenericModel, Generic[T], json_encoders=CUSTOM_ENCODERS):
70
- kind: str
45
+ class JobDescription(GenericModel, Generic[T], json_encoders=CUSTOM_ENCODERS):
71
46
  status: JobStatus
72
- description: Union[T, FailureDetails, Dict]
73
-
74
-
75
- class JobDescriptionSuccess(JobDescriptionBase[T], Generic[T]):
76
- kind: Literal["success"] = pydantic.Field(default="success")
77
- status: SuccessStatus = Field(default=JobStatus.COMPLETED)
78
- description: T
79
-
80
-
81
- class JobDescriptionFailure(JobDescriptionBase[Any]):
82
- kind: Literal["failure"] = pydantic.Field(default="failure")
83
- status: FailureStatus
84
- description: FailureDetails
85
-
86
-
87
- class JobDescriptionNonFinal(JobDescriptionBase[Any]):
88
- kind: Literal["non_final"] = pydantic.Field(default="non_final")
89
- status: NonFinalStatus
90
- description: Dict = Field(default_factory=dict)
91
-
92
- @pydantic.validator("description")
93
- def validate_empty_description(cls, description: Dict) -> Dict:
94
- if description:
95
- raise ValueError("Non-final job description must be empty")
96
-
97
- return description
98
-
99
-
100
- class JobDescriptionUnion(GenericModel, Generic[T], json_encoders=CUSTOM_ENCODERS):
101
- __root__: Union[
102
- JobDescriptionSuccess[T], JobDescriptionFailure, JobDescriptionNonFinal
103
- ] = Field(discriminator="kind")
47
+ failure_details: Optional[str]
48
+ result: Optional[T]
49
+
50
+ @pydantic.root_validator
51
+ def validate_status_and_fields(cls, values: Dict[str, Any]) -> Dict[str, Any]:
52
+ status: Optional[JobStatus] = values.get("status")
53
+ if status is None or "result" not in values or "failure_details" not in values:
54
+ # If any of the fields doesn't exist, then previous validations failed
55
+ # result and failure_details are Optional, so we explicitly check if they
56
+ # exist in the values dictionary
57
+ return values
58
+
59
+ result = values["result"]
60
+ failure_details = values["failure_details"]
61
+
62
+ if status is JobStatus.COMPLETED:
63
+ # Completed job must return result and not have an error
64
+ if result is None or failure_details is not None:
65
+ raise ClassiqAPIError(INVALID_RESPONSE_ERROR_MSG)
66
+ elif status in (JobStatus.FAILED, JobStatus.CANCELLED):
67
+ # Failed job must return error and not have result
68
+ if result is not None or failure_details is None:
69
+ raise ClassiqAPIError(INVALID_RESPONSE_ERROR_MSG)
70
+ elif result is not None or failure_details is not None:
71
+ # Pending job must have no result and no error
72
+ raise ClassiqAPIError(INVALID_RESPONSE_ERROR_MSG)
73
+
74
+ return values
@@ -37,3 +37,6 @@ class BindOperation(QuantumOperation):
37
37
  raise ClassiqValueError(f"Cannot bind '{handle}'")
38
38
 
39
39
  return handles
40
+
41
+ def reversed(self) -> "BindOperation":
42
+ return BindOperation(in_handles=self.out_handles, out_handles=self.in_handles)
@@ -18,3 +18,6 @@ class ClassicalParameterDeclaration(ASTNode):
18
18
  return values_with_discriminator(
19
19
  values, "kind", "ClassicalParameterDeclaration"
20
20
  )
21
+
22
+ def rename(self, new_name: str) -> "ClassicalParameterDeclaration":
23
+ return self.copy(update=dict(name=new_name))
@@ -1,3 +1,5 @@
1
+ from typing import Union
2
+
1
3
  from pydantic import Extra
2
4
 
3
5
  from classiq.interface.ast_node import ASTNode
@@ -45,3 +47,8 @@ class SlicedHandleBinding(HandleBinding):
45
47
 
46
48
  def is_bindable(self) -> bool:
47
49
  return False
50
+
51
+
52
+ ConcreteHandleBinding = Union[
53
+ HandleBinding, SubscriptHandleBinding, SlicedHandleBinding
54
+ ]
@@ -1,13 +1,17 @@
1
1
  from typing import Literal, Mapping
2
2
 
3
- import pydantic
4
-
3
+ from classiq.interface.generator.functions.builtins.core_library import (
4
+ INTEGER_XOR_FUNCTION,
5
+ MODULAR_ADD_FUNCTION,
6
+ )
5
7
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
6
- from classiq.interface.model.handle_binding import HandleBinding
8
+ from classiq.interface.model.handle_binding import ConcreteHandleBinding, HandleBinding
9
+ from classiq.interface.model.quantum_function_declaration import (
10
+ QuantumFunctionDeclaration,
11
+ )
7
12
  from classiq.interface.model.quantum_statement import QuantumOperation
8
13
 
9
14
  from classiq._internals.enum_utils import StrEnum
10
- from classiq.exceptions import ClassiqValueError
11
15
 
12
16
 
13
17
  class BinaryOperation(StrEnum):
@@ -15,26 +19,20 @@ class BinaryOperation(StrEnum):
15
19
  Xor = "inplace_xor"
16
20
 
17
21
  @property
18
- def internal_function(self) -> str:
22
+ def internal_function_declaration(self) -> QuantumFunctionDeclaration:
19
23
  return {
20
- BinaryOperation.Addition: "modular_add",
21
- BinaryOperation.Xor: "integer_xor",
24
+ BinaryOperation.Addition: MODULAR_ADD_FUNCTION,
25
+ BinaryOperation.Xor: INTEGER_XOR_FUNCTION,
22
26
  }[self]
23
27
 
24
28
 
25
29
  class InplaceBinaryOperation(QuantumOperation):
26
30
  kind: Literal["InplaceBinaryOperation"]
27
31
 
28
- target: HandleBinding
29
- value: HandleBinding
32
+ target: ConcreteHandleBinding
33
+ value: ConcreteHandleBinding
30
34
  operation: BinaryOperation
31
35
 
32
36
  @property
33
37
  def wiring_inouts(self) -> Mapping[str, HandleBinding]:
34
38
  return nameables_to_dict([self.target, self.value])
35
-
36
- @pydantic.validator("target", "value")
37
- def validate_handle(cls, handle: HandleBinding) -> HandleBinding:
38
- if not handle.is_bindable():
39
- raise ClassiqValueError(f"Cannot bind '{handle!r}'")
40
- return handle
@@ -1,5 +1,5 @@
1
1
  from collections import Counter
2
- from typing import Any, Dict, List, Literal, NewType, Optional, Set
2
+ from typing import Dict, List, Literal, NewType, Set
3
3
 
4
4
  import pydantic
5
5
 
@@ -12,6 +12,7 @@ from classiq.interface.generator.functions.port_declaration import (
12
12
  from classiq.interface.generator.model.constraints import Constraints
13
13
  from classiq.interface.generator.model.preferences.preferences import Preferences
14
14
  from classiq.interface.generator.quantum_function_call import SUFFIX_RANDOMIZER
15
+ from classiq.interface.generator.types.enum_declaration import EnumDeclaration
15
16
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
16
17
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
17
18
  from classiq.interface.helpers.versioned_model import VersionedModel
@@ -19,9 +20,6 @@ from classiq.interface.model.native_function_definition import NativeFunctionDef
19
20
  from classiq.interface.model.quantum_function_declaration import (
20
21
  QuantumFunctionDeclaration,
21
22
  )
22
- from classiq.interface.model.resolvers.function_call_resolver import (
23
- resolve_function_calls,
24
- )
25
23
  from classiq.interface.model.statement_block import StatementBlock
26
24
 
27
25
  from classiq.exceptions import ClassiqValueError
@@ -65,9 +63,14 @@ class Model(VersionedModel, ASTNode):
65
63
  description="The user-defined custom type library.",
66
64
  )
67
65
 
66
+ enums: List[EnumDeclaration] = pydantic.Field(
67
+ default_factory=list,
68
+ description="user-defined enums",
69
+ )
70
+
68
71
  types: List[StructDeclaration] = pydantic.Field(
69
72
  default_factory=list,
70
- description="The user-defined custom function library.",
73
+ description="user-defined structs",
71
74
  )
72
75
 
73
76
  classical_execution_code: str = pydantic.Field(
@@ -118,21 +121,6 @@ class Model(VersionedModel, ASTNode):
118
121
  functions.append(_create_empty_main_function())
119
122
  return functions
120
123
 
121
- @pydantic.root_validator()
122
- def validate_static_correctness(cls, values: Dict[str, Any]) -> Dict[str, Any]:
123
- functions: Optional[List[QuantumFunctionDeclaration]] = values.get("functions")
124
- if functions is None:
125
- return values
126
-
127
- resolve_function_calls(
128
- values,
129
- nameables_to_dict(functions),
130
- )
131
- for func_def in functions:
132
- if isinstance(func_def, NativeFunctionDefinition):
133
- func_def.validate_body()
134
- return values
135
-
136
124
  @pydantic.validator("types")
137
125
  def types_validator(cls, types: List[StructDeclaration]) -> List[StructDeclaration]:
138
126
  user_defined_types: Set[str] = set()
@@ -4,12 +4,6 @@ from classiq.interface.model.quantum_function_declaration import (
4
4
  QuantumFunctionDeclaration,
5
5
  )
6
6
  from classiq.interface.model.statement_block import StatementBlock
7
- from classiq.interface.model.validations.handles_validator import HandleValidator
8
- from classiq.interface.model.variable_declaration_statement import (
9
- VariableDeclarationStatement,
10
- )
11
-
12
- from classiq.exceptions import ClassiqValueError
13
7
 
14
8
 
15
9
  class NativeFunctionDefinition(QuantumFunctionDeclaration):
@@ -23,14 +17,3 @@ class NativeFunctionDefinition(QuantumFunctionDeclaration):
23
17
  body: StatementBlock = pydantic.Field(
24
18
  default_factory=list, description="List of function calls to perform."
25
19
  )
26
-
27
- def validate_body(self) -> None:
28
- handle_validator = HandleValidator(self.port_declarations)
29
-
30
- for statement in self.body:
31
- if isinstance(statement, VariableDeclarationStatement):
32
- handle_validator.handle_variable_declaration(statement)
33
- else:
34
- handle_validator.handle_operation(statement)
35
-
36
- handle_validator.report_errored_handles(ClassiqValueError)
@@ -1,6 +1,5 @@
1
1
  import itertools
2
- import re
3
- from typing import Any, Dict, List, Literal, Mapping, Optional, Set, Type, Union
2
+ from typing import Any, Dict, List, Literal, Mapping, Optional, Type, Union
4
3
 
5
4
  import pydantic
6
5
 
@@ -19,11 +18,8 @@ from classiq.interface.model.handle_binding import (
19
18
  )
20
19
  from classiq.interface.model.quantum_function_declaration import (
21
20
  QuantumFunctionDeclaration,
22
- QuantumOperandDeclaration,
23
21
  )
24
22
  from classiq.interface.model.quantum_lambda_function import (
25
- QuantumCallable,
26
- QuantumLambdaFunction,
27
23
  QuantumOperand,
28
24
  )
29
25
  from classiq.interface.model.quantum_statement import QuantumOperation
@@ -117,22 +113,22 @@ class QuantumFunctionCall(QuantumOperation):
117
113
  function: Union[str, OperandIdentifier] = pydantic.Field(
118
114
  description="The function that is called"
119
115
  )
120
- params: Dict[str, Expression] = pydantic.Field(default_factory=dict)
121
- inputs: Dict[str, HandleBinding] = pydantic.Field(
116
+ designated_params: Dict[str, Expression] = pydantic.Field(default_factory=dict)
117
+ designated_inputs: Dict[str, HandleBinding] = pydantic.Field(
122
118
  default_factory=dict,
123
119
  description="A mapping from the input name to the wire it connects to",
124
120
  )
125
- inouts: Dict[
121
+ designated_inouts: Dict[
126
122
  str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
127
123
  ] = pydantic.Field(
128
124
  default_factory=dict,
129
125
  description="A mapping from in/out name to the wires that connect to it",
130
126
  )
131
- outputs: Dict[str, HandleBinding] = pydantic.Field(
127
+ designated_outputs: Dict[str, HandleBinding] = pydantic.Field(
132
128
  default_factory=dict,
133
129
  description="A mapping from the output name to the wire it connects to",
134
130
  )
135
- operands: Dict[str, QuantumOperand] = pydantic.Field(
131
+ designated_operands: Dict[str, QuantumOperand] = pydantic.Field(
136
132
  description="Function calls passed to the operator",
137
133
  default_factory=dict,
138
134
  )
@@ -181,15 +177,15 @@ class QuantumFunctionCall(QuantumOperation):
181
177
  def get_positional_args(self) -> List[ArgValue]:
182
178
  result: List[ArgValue] = self.positional_args
183
179
  if not result:
184
- result = list(self.params.values())
185
- result.extend(self.operands.values())
186
- result.extend(self.inputs.values())
187
- result.extend(self.inouts.values())
188
- result.extend(self.outputs.values())
180
+ result = list(self.designated_params.values())
181
+ result.extend(self.designated_operands.values())
182
+ result.extend(self.designated_inputs.values())
183
+ result.extend(self.designated_inouts.values())
184
+ result.extend(self.designated_outputs.values())
189
185
  return result
190
186
 
191
187
  @property
192
- def pos_param_args(self) -> Dict[str, Expression]:
188
+ def positional_params(self) -> Dict[str, Expression]:
193
189
  return dict(
194
190
  zip(
195
191
  self.func_decl.param_decls.keys(),
@@ -202,7 +198,11 @@ class QuantumFunctionCall(QuantumOperation):
202
198
  )
203
199
 
204
200
  @property
205
- def pos_operand_args(self) -> Dict[str, "QuantumOperand"]:
201
+ def params(self) -> Dict[str, Expression]:
202
+ return self.positional_params or self.designated_params
203
+
204
+ @property
205
+ def positional_operands(self) -> Dict[str, "QuantumOperand"]:
206
206
  return dict(
207
207
  zip(
208
208
  self.func_decl.operand_declarations.keys(),
@@ -214,6 +214,10 @@ class QuantumFunctionCall(QuantumOperation):
214
214
  )
215
215
  )
216
216
 
217
+ @property
218
+ def operands(self) -> Dict[str, "QuantumOperand"]:
219
+ return self.positional_operands or self.designated_operands
220
+
217
221
  @property
218
222
  def pos_port_args(self) -> Dict[str, HandleBinding]:
219
223
  return dict(
@@ -227,181 +231,58 @@ class QuantumFunctionCall(QuantumOperation):
227
231
  )
228
232
  )
229
233
 
230
- def _update_pos_port_params(self) -> None:
231
- for name, port_decl in self.func_decl.port_declarations.items():
232
- if port_decl.direction == PortDeclarationDirection.Input:
233
- self.inputs[name] = self.pos_port_args[name]
234
- elif port_decl.direction == PortDeclarationDirection.Output:
235
- self.outputs[name] = self.pos_port_args[name]
236
- else:
237
- self.inouts[name] = self.pos_port_args[name]
238
-
239
- def _reduce_positional_args_to_keywords(self) -> None:
240
- self.params.update(self.pos_param_args)
241
- self.operands.update(self.pos_operand_args)
242
- self._update_pos_port_params()
243
-
244
- def resolve_function_decl(
245
- self,
246
- function_dict: Mapping[str, QuantumFunctionDeclaration],
247
- check_operands: bool,
248
- ) -> None:
234
+ def _get_pos_port_args_by_direction(
235
+ self, direction: PortDeclarationDirection
236
+ ) -> Dict[str, HandleBinding]:
237
+ # This is a hack for handles to wires reduction tests,
238
+ # that initialize function definitions or calls not in the scope of a model,
239
+ # so there is no function resolution annotation.
249
240
  if self._func_decl is None:
250
- func_decl = function_dict.get(self.func_name)
251
- if func_decl is None:
252
- raise ClassiqValueError(
253
- f"Error resolving function {self.func_name}, the function is not found in included library."
254
- )
255
- self.set_func_decl(func_decl)
256
-
257
- if self.positional_args:
258
- self._reduce_positional_args_to_keywords()
259
-
260
- _check_params_against_declaration(
261
- set(self.params.keys()),
262
- set(self.func_decl.param_decls.keys()),
263
- self.func_decl.name,
241
+ return dict()
242
+ return {
243
+ port_decl.name: port
244
+ for port_decl, port in zip(
245
+ self.func_decl.port_declarations.values(),
246
+ (
247
+ param
248
+ for param in self.positional_args
249
+ if isinstance(param, HandleBinding)
250
+ ),
251
+ )
252
+ if direction == port_decl.direction
253
+ }
254
+
255
+ @property
256
+ def inputs(self) -> Dict[str, HandleBinding]:
257
+ return (
258
+ self._get_pos_port_args_by_direction(PortDeclarationDirection.Input)
259
+ or self.designated_inputs
264
260
  )
265
- _check_ports_against_declaration(self, self.func_decl)
266
- _check_params_against_declaration(
267
- set(self.operands.keys()),
268
- set(self.func_decl.operand_declarations.keys()),
269
- self.func_name,
261
+
262
+ @property
263
+ def inouts(
264
+ self,
265
+ ) -> Dict[str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]]:
266
+ return (
267
+ self._get_pos_port_args_by_direction(PortDeclarationDirection.Inout)
268
+ or self.designated_inouts
270
269
  )
271
- if check_operands:
272
- _check_operands_against_declaration(self, self.func_decl, function_dict)
273
270
 
274
- for name, op in self.operands.items():
275
- op_decl = self.func_decl.operand_declarations[name]
276
- for qlambda in get_lambda_defs(op):
277
- if isinstance(qlambda, QuantumLambdaFunction):
278
- qlambda.set_op_decl(op_decl)
271
+ @property
272
+ def outputs(self) -> Dict[str, HandleBinding]:
273
+ return (
274
+ self._get_pos_port_args_by_direction(PortDeclarationDirection.Output)
275
+ or self.designated_outputs
276
+ )
279
277
 
280
278
  @pydantic.root_validator()
281
279
  def validate_handles(cls, values: Dict[str, Any]) -> Dict[str, Any]:
282
- inputs = values.get("inputs", dict())
283
- outputs = values.get("outputs", dict())
284
- inouts = values.get("inouts", dict())
280
+ inputs = values.get("designated_inputs", dict())
281
+ outputs = values.get("designated_outputs", dict())
282
+ inouts = values.get("designated_inouts", dict())
285
283
 
286
284
  _validate_no_duplicated_ports(inputs, outputs, inouts)
287
285
  _validate_no_duplicated_handles(inputs, outputs, inouts)
288
286
  _validate_no_mixing_sliced_and_whole_handles(inouts)
289
287
 
290
288
  return values
291
-
292
-
293
- def get_lambda_defs(operand: QuantumOperand) -> List[QuantumCallable]:
294
- if isinstance(operand, list):
295
- return operand
296
- return [operand]
297
-
298
-
299
- def _check_ports_against_declaration(
300
- call: QuantumFunctionCall, decl: QuantumFunctionDeclaration
301
- ) -> None:
302
- call_input_names = set(call.inputs.keys())
303
-
304
- _check_params_against_declaration(
305
- call_input_names,
306
- decl.ports_by_declaration_direction(PortDeclarationDirection.Input),
307
- call.func_name,
308
- )
309
-
310
- call_output_names = set(call.outputs.keys())
311
-
312
- _check_params_against_declaration(
313
- call_output_names,
314
- decl.ports_by_declaration_direction(PortDeclarationDirection.Output),
315
- call.func_name,
316
- )
317
-
318
- inout_params = set(call.inouts.keys())
319
-
320
- _check_params_against_declaration(
321
- inout_params,
322
- decl.ports_by_declaration_direction(PortDeclarationDirection.Inout),
323
- call.func_name,
324
- )
325
-
326
-
327
- def _check_operand_against_declaration(
328
- call: QuantumFunctionCall,
329
- operand_decl: QuantumOperandDeclaration,
330
- operand_argument: QuantumOperand,
331
- function_dict: Mapping[str, QuantumFunctionDeclaration],
332
- in_list: bool = False,
333
- ) -> None:
334
- if isinstance(operand_argument, list):
335
- if in_list:
336
- raise ClassiqValueError(
337
- f"{str(operand_argument)!r} argument to {call.func_decl.name!r} is not "
338
- f"a valid operand. Nested operand lists are not permitted"
339
- )
340
- for arg in operand_argument:
341
- _check_operand_against_declaration(
342
- call, operand_decl, arg, function_dict, in_list=True
343
- )
344
- return
345
- operand_arg_decl: QuantumFunctionDeclaration
346
- if isinstance(operand_argument, str):
347
- if operand_argument not in function_dict:
348
- raise ClassiqValueError(
349
- f"{operand_argument!r} argument to {call.func_decl.name!r} is not a "
350
- f"registered function"
351
- )
352
- operand_arg_decl = function_dict[operand_argument]
353
- elif isinstance(operand_argument, QuantumLambdaFunction):
354
- if operand_argument.func_decl is None:
355
- return
356
- operand_arg_decl = operand_argument.func_decl
357
- else:
358
- raise ClassiqValueError(
359
- f"{str(operand_argument)!r} argument to {call.func_decl.name!r} is not a "
360
- f"valid operand"
361
- )
362
- num_arg_parameters = len(operand_arg_decl.get_positional_arg_decls())
363
- num_decl_parameters = len(operand_decl.get_positional_arg_decls())
364
- if num_arg_parameters != num_decl_parameters:
365
- raise ClassiqValueError(
366
- f"Signature of argument {operand_argument!r} to {call.func_decl.name!r} "
367
- f"does not match the signature of parameter {operand_decl.name!r}. "
368
- f"{operand_decl.name!r} accepts {num_decl_parameters} parameters but "
369
- f"{operand_argument!r} accepts {num_arg_parameters} parameters"
370
- )
371
-
372
-
373
- def _check_operands_against_declaration(
374
- call: QuantumFunctionCall,
375
- decl: QuantumFunctionDeclaration,
376
- function_dict: Mapping[str, QuantumFunctionDeclaration],
377
- ) -> None:
378
- for operand_parameter, operand_argument in call.operands.items():
379
- _check_operand_against_declaration(
380
- call,
381
- decl.operand_declarations[operand_parameter],
382
- operand_argument,
383
- function_dict,
384
- )
385
-
386
-
387
- def _check_params_against_declaration(
388
- call_params: Set[str],
389
- param_decls: Set[str],
390
- callee_name: str,
391
- ) -> None:
392
- unknown_params = call_params - param_decls
393
- if any(re.match(r"arg\d+", param) for param in unknown_params):
394
- error_msg = (
395
- f"Unsupported passing of named function {callee_name!r} as an operand."
396
- "\nSuggestion: replace the named function with lambda function."
397
- )
398
- else:
399
- error_msg = f"Unknown parameters {unknown_params} in call to {callee_name!r}."
400
- if unknown_params:
401
- raise ClassiqValueError(error_msg)
402
-
403
- missing_params = param_decls - call_params
404
- if missing_params:
405
- raise ClassiqValueError(
406
- f"Missing parameters {missing_params} in call to {callee_name!r}."
407
- )