classiq 0.65.4__py3-none-any.whl → 0.66.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 (62) hide show
  1. classiq/_internals/api_wrapper.py +43 -0
  2. classiq/applications/qnn/qlayer.py +65 -3
  3. classiq/execution/execution_session.py +0 -2
  4. classiq/execution/iqcc.py +66 -1
  5. classiq/interface/_version.py +1 -1
  6. classiq/interface/ast_node.py +15 -1
  7. classiq/interface/backend/backend_preferences.py +0 -14
  8. classiq/interface/debug_info/debug_info.py +2 -0
  9. classiq/interface/execution/iqcc.py +25 -0
  10. classiq/interface/generator/expressions/qmod_qarray_proxy.py +1 -13
  11. classiq/interface/generator/visitor.py +7 -4
  12. classiq/interface/model/classical_if.py +4 -0
  13. classiq/interface/model/control.py +4 -0
  14. classiq/interface/model/invert.py +4 -0
  15. classiq/interface/model/model.py +3 -1
  16. classiq/interface/model/model_visitor.py +14 -0
  17. classiq/interface/model/power.py +4 -0
  18. classiq/interface/model/quantum_statement.py +3 -3
  19. classiq/interface/model/repeat.py +4 -0
  20. classiq/interface/model/within_apply_operation.py +4 -0
  21. classiq/interface/server/routes.py +6 -0
  22. classiq/model_expansions/closure.py +0 -11
  23. classiq/model_expansions/evaluators/quantum_type_utils.py +6 -6
  24. classiq/model_expansions/expression_evaluator.py +10 -1
  25. classiq/model_expansions/interpreters/base_interpreter.py +28 -18
  26. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +58 -1
  27. classiq/model_expansions/interpreters/generative_interpreter.py +7 -13
  28. classiq/model_expansions/quantum_operations/allocate.py +69 -0
  29. classiq/model_expansions/quantum_operations/call_emitter.py +7 -6
  30. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +4 -4
  31. classiq/model_expansions/quantum_operations/emitter.py +2 -15
  32. classiq/model_expansions/quantum_operations/quantum_function_call.py +22 -0
  33. classiq/model_expansions/quantum_operations/shallow_emitter.py +21 -35
  34. classiq/model_expansions/scope_initialization.py +49 -34
  35. classiq/model_expansions/transformers/model_renamer.py +98 -0
  36. classiq/model_expansions/transformers/var_splitter.py +7 -82
  37. classiq/open_library/functions/__init__.py +8 -0
  38. classiq/open_library/functions/amplitude_amplification.py +92 -0
  39. classiq/open_library/functions/grover.py +5 -5
  40. classiq/qmod/builtins/__init__.py +1 -1
  41. classiq/qmod/builtins/functions/__init__.py +0 -2
  42. classiq/qmod/builtins/functions/allocation.py +1 -26
  43. classiq/qmod/builtins/operations.py +12 -6
  44. classiq/qmod/generative.py +6 -4
  45. classiq/qmod/native/pretty_printer.py +3 -2
  46. classiq/qmod/pretty_print/pretty_printer.py +3 -1
  47. classiq/qmod/qmod_variable.py +6 -1
  48. classiq/qmod/semantics/annotation/call_annotation.py +30 -2
  49. classiq/qmod/semantics/annotation/qstruct_annotator.py +2 -2
  50. classiq/qmod/semantics/error_manager.py +20 -6
  51. classiq/qmod/semantics/static_semantics_visitor.py +3 -40
  52. classiq/qmod/semantics/validation/constants_validation.py +2 -3
  53. classiq/qmod/semantics/validation/function_name_collisions_validation.py +6 -9
  54. classiq/qmod/semantics/validation/main_validation.py +2 -3
  55. classiq/qmod/semantics/validation/model_validation.py +25 -0
  56. classiq/qmod/semantics/validation/signature_validation.py +24 -0
  57. classiq/qmod/semantics/validation/types_validation.py +45 -46
  58. classiq/qmod/utilities.py +12 -0
  59. {classiq-0.65.4.dist-info → classiq-0.66.1.dist-info}/METADATA +1 -1
  60. {classiq-0.65.4.dist-info → classiq-0.66.1.dist-info}/RECORD +61 -56
  61. classiq/model_expansions/expression_renamer.py +0 -76
  62. {classiq-0.65.4.dist-info → classiq-0.66.1.dist-info}/WHEEL +0 -0
@@ -22,6 +22,7 @@ from typing_extensions import ParamSpec, Self, _AnnotatedAlias
22
22
 
23
23
  from classiq.interface.exceptions import ClassiqValueError
24
24
  from classiq.interface.generator.expressions.expression import Expression
25
+ from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
25
26
  from classiq.interface.generator.expressions.qmod_qarray_proxy import (
26
27
  ILLEGAL_SLICE_MSG,
27
28
  ILLEGAL_SLICING_STEP_MSG,
@@ -185,6 +186,10 @@ class QVar(Symbolic):
185
186
  return interpret_expression(str(self.size))
186
187
  return CParamScalar(f"get_field({self}, 'size')")
187
188
 
189
+ @property
190
+ def type_name(self) -> str:
191
+ return self.get_qmod_type().type_name
192
+
188
193
 
189
194
  _Q = TypeVar("_Q", bound=QVar)
190
195
  Output = Annotated[_Q, PortDeclarationDirection.Output]
@@ -414,7 +419,7 @@ class QNum(Generic[_P], QScalar):
414
419
  return _GenericAlias(cls, args)
415
420
 
416
421
 
417
- class QArray(ArrayBase[_P], QVar):
422
+ class QArray(ArrayBase[_P], QVar, NonSymbolicExpr):
418
423
  CONSTRUCTOR_DEPTH: int = 3
419
424
 
420
425
  # TODO [CAD-18620]: improve type hints
@@ -3,8 +3,17 @@ from contextlib import contextmanager
3
3
  from typing import Any
4
4
 
5
5
  from classiq.interface.exceptions import ClassiqError
6
- from classiq.interface.generator.visitor import Visitor
6
+ from classiq.interface.generator.expressions.expression import Expression
7
+ from classiq.interface.generator.functions.classical_type import Integer
8
+ from classiq.interface.generator.functions.port_declaration import (
9
+ PortDeclarationDirection,
10
+ )
11
+ from classiq.interface.model.classical_parameter_declaration import (
12
+ ClassicalParameterDeclaration,
13
+ )
14
+ from classiq.interface.model.model_visitor import ModelVisitor
7
15
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
16
+ from classiq.interface.model.port_declaration import PortDeclaration
8
17
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
9
18
  from classiq.interface.model.quantum_function_declaration import (
10
19
  AnonQuantumOperandDeclaration,
@@ -14,17 +23,36 @@ from classiq.interface.model.quantum_function_declaration import (
14
23
  from classiq.interface.model.quantum_lambda_function import (
15
24
  QuantumLambdaFunction,
16
25
  )
26
+ from classiq.interface.model.quantum_type import QuantumBitvector
17
27
 
18
28
  from classiq.qmod.builtins.functions import BUILTIN_FUNCTION_DECLARATIONS
19
29
  from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
20
30
  from classiq.qmod.semantics.error_manager import ErrorManager
21
31
  from classiq.qmod.semantics.lambdas import get_renamed_parameters
22
32
 
33
+ ALLOCATE_DECL_FOR_COMPATIBILITY = QuantumFunctionDeclaration(
34
+ name="allocate",
35
+ positional_arg_declarations=[
36
+ ClassicalParameterDeclaration(
37
+ name="num_qubits",
38
+ classical_type=Integer(),
39
+ ),
40
+ PortDeclaration(
41
+ name="out",
42
+ quantum_type=QuantumBitvector(length=Expression(expr="num_qubits")),
43
+ direction=PortDeclarationDirection.Output,
44
+ ),
45
+ ],
46
+ )
47
+
23
48
 
24
49
  def _annotate_function_call_decl(
25
50
  fc: QuantumFunctionCall,
26
51
  function_dict: Mapping[str, QuantumFunctionDeclaration],
27
52
  ) -> None:
53
+ if fc.function == "allocate": # FIXME: Remove compatibility (CAD-25935)
54
+ fc.set_func_decl(ALLOCATE_DECL_FOR_COMPATIBILITY)
55
+ return
28
56
  if fc._func_decl is None:
29
57
  func_decl = function_dict.get(fc.func_name)
30
58
  if func_decl is None:
@@ -46,7 +74,7 @@ def _annotate_function_call_decl(
46
74
  qlambda.set_op_decl(param)
47
75
 
48
76
 
49
- class _CallLambdaAnnotator(Visitor):
77
+ class _CallLambdaAnnotator(ModelVisitor):
50
78
  def __init__(
51
79
  self, quantum_functions: Mapping[str, QuantumFunctionDeclaration]
52
80
  ) -> None:
@@ -1,10 +1,10 @@
1
1
  from classiq.interface.generator.functions.type_name import TypeName
2
- from classiq.interface.generator.visitor import Visitor
2
+ from classiq.interface.model.model_visitor import ModelVisitor
3
3
 
4
4
  from classiq.qmod.model_state_container import QMODULE
5
5
 
6
6
 
7
- class QStructAnnotator(Visitor):
7
+ class QStructAnnotator(ModelVisitor):
8
8
  def __init__(self) -> None:
9
9
  self._visited: set[TypeName] = set()
10
10
 
@@ -4,7 +4,7 @@ from typing import Optional
4
4
 
5
5
  from classiq.interface.ast_node import ASTNode
6
6
  from classiq.interface.exceptions import CLASSIQ_SLACK_COMMUNITY_LINK
7
- from classiq.interface.source_reference import SourceReferencedError
7
+ from classiq.interface.source_reference import SourceReference, SourceReferencedError
8
8
 
9
9
 
10
10
  class ErrorManager:
@@ -22,6 +22,12 @@ class ErrorManager:
22
22
  self._call_stack: list[str] = []
23
23
  self._ignore_errors: bool = False
24
24
 
25
+ @property
26
+ def _current_source_ref(self) -> Optional[SourceReference]:
27
+ if self._current_nodes_stack:
28
+ return self._current_nodes_stack[-1].source_ref
29
+ return None
30
+
25
31
  @contextmanager
26
32
  def ignore_errors_context(self) -> Iterator[None]:
27
33
  previous = self._ignore_errors
@@ -35,17 +41,25 @@ class ErrorManager:
35
41
  def annotated_errors(self) -> list[str]:
36
42
  return [str(error) for error in self._errors]
37
43
 
38
- def add_error(self, error: str) -> None:
44
+ def add_error(
45
+ self,
46
+ error: str,
47
+ *,
48
+ source_ref: Optional[SourceReference] = None,
49
+ function: Optional[str] = None
50
+ ) -> None:
39
51
  if not self._ignore_errors:
40
52
  self._errors.append(
41
53
  SourceReferencedError(
42
54
  error=error.replace(CLASSIQ_SLACK_COMMUNITY_LINK, ""),
43
55
  source_ref=(
44
- self._current_nodes_stack[-1].source_ref
45
- if self._current_nodes_stack
46
- else None
56
+ source_ref
57
+ if source_ref is not None
58
+ else self._current_source_ref
59
+ ),
60
+ function=(
61
+ function if function is not None else self.current_function
47
62
  ),
48
- function=self.current_function,
49
63
  )
50
64
  )
51
65
 
@@ -13,7 +13,6 @@ from classiq.interface.generator.functions.concrete_types import ConcreteQuantum
13
13
  from classiq.interface.generator.functions.port_declaration import (
14
14
  PortDeclarationDirection,
15
15
  )
16
- from classiq.interface.generator.visitor import Visitor
17
16
  from classiq.interface.model.handle_binding import (
18
17
  FieldHandleBinding,
19
18
  HandleBinding,
@@ -22,6 +21,7 @@ from classiq.interface.model.handle_binding import (
22
21
  )
23
22
  from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
24
23
  from classiq.interface.model.model import Model
24
+ from classiq.interface.model.model_visitor import ModelVisitor
25
25
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
26
26
  from classiq.interface.model.port_declaration import PortDeclaration
27
27
  from classiq.interface.model.quantum_expressions.quantum_expression import (
@@ -50,26 +50,11 @@ from classiq.qmod.semantics.annotation.call_annotation import resolve_function_c
50
50
  from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
51
51
  from classiq.qmod.semantics.error_manager import ErrorManager
52
52
  from classiq.qmod.semantics.lambdas import get_renamed_parameters
53
- from classiq.qmod.semantics.validation.constants_validation import (
54
- check_duplicate_constants,
55
- )
56
53
  from classiq.qmod.semantics.validation.func_call_validation import (
57
54
  check_no_overlapping_quantum_args,
58
55
  validate_call_arguments,
59
56
  )
60
- from classiq.qmod.semantics.validation.function_name_collisions_validation import (
61
- _check_function_name_collisions,
62
- )
63
57
  from classiq.qmod.semantics.validation.handle_validation import resolve_handle
64
- from classiq.qmod.semantics.validation.main_validation import validate_main_function
65
- from classiq.qmod.semantics.validation.types_validation import (
66
- check_cstruct_has_fields,
67
- check_duplicate_types,
68
- check_qstruct_fields_are_defined,
69
- check_qstruct_flexibility,
70
- check_qstruct_has_fields,
71
- check_qstruct_is_not_recursive,
72
- )
73
58
 
74
59
  HANDLE_BINDING_PART_MESSAGE = {
75
60
  SubscriptHandleBinding: "array subscript",
@@ -92,7 +77,7 @@ class StaticScope:
92
77
  self.variables_to_types = variables_to_types
93
78
 
94
79
 
95
- class StaticSemanticsVisitor(Visitor):
80
+ class StaticSemanticsVisitor(ModelVisitor):
96
81
  def __init__(
97
82
  self,
98
83
  functions_dict: Mapping[str, QuantumFunctionDeclaration],
@@ -114,17 +99,6 @@ class StaticSemanticsVisitor(Visitor):
114
99
  self._scope.pop()
115
100
 
116
101
  def visit_Model(self, model: Model) -> None:
117
- check_duplicate_types([*model.enums, *model.types, *model.qstructs])
118
- check_duplicate_constants(model.constants)
119
- for qstruct in model.qstructs:
120
- check_qstruct_has_fields(qstruct)
121
- if check_qstruct_fields_are_defined(
122
- qstruct
123
- ) and check_qstruct_is_not_recursive(qstruct):
124
- check_qstruct_flexibility(qstruct)
125
- for cstruct in model.types:
126
- check_cstruct_has_fields(cstruct)
127
- validate_main_function(model.main_func)
128
102
  self.visit_BaseModel(model)
129
103
 
130
104
  def visit_NativeFunctionDefinition(
@@ -141,16 +115,6 @@ class StaticSemanticsVisitor(Visitor):
141
115
  },
142
116
  )
143
117
  with self.scoped_visit(scope), self._error_manager.call(func_def.name):
144
- parameter_declaration_names = [
145
- decl.name for decl in func_def.positional_arg_declarations
146
- ]
147
- seen_names: set[str] = set()
148
- for name in parameter_declaration_names:
149
- if name in seen_names:
150
- self._error_manager.add_error(
151
- f"duplicate parameter declaration name {name!r}"
152
- )
153
- seen_names.add(name)
154
118
  if len(func_def.body) == 0:
155
119
  return
156
120
  self.visit(func_def.body)
@@ -165,7 +129,7 @@ class StaticSemanticsVisitor(Visitor):
165
129
  and handle_state is not expected_terminal_state
166
130
  ):
167
131
  self._error_manager.add_error(
168
- f"At the end of the function, port `{port_decl.name}` is expected to be {expected_terminal_state.name.lower()} but it isn't"
132
+ f"At the end of the function, variable {port_decl.name!r} is expected to be {expected_terminal_state.name.lower()} but it isn't"
169
133
  )
170
134
 
171
135
  def visit_WithinApply(self, within_apply: WithinApply) -> None:
@@ -351,7 +315,6 @@ class StaticSemanticsVisitor(Visitor):
351
315
  def static_semantics_analysis_pass(
352
316
  model: Model, error_type: Optional[type[Exception]] = ClassiqSemanticError
353
317
  ) -> None:
354
- _check_function_name_collisions(model, error_type)
355
318
  QStructAnnotator().visit(model)
356
319
  functions = {**BUILTIN_FUNCTION_DECLARATIONS, **model.function_dict}
357
320
  resolve_function_calls(model, functions)
@@ -1,16 +1,15 @@
1
1
  from collections.abc import Sequence
2
2
 
3
+ from classiq.interface.exceptions import ClassiqExpansionError
3
4
  from classiq.interface.generator.constant import Constant
4
5
 
5
6
  from classiq.qmod.builtins import BUILTIN_CONSTANTS
6
- from classiq.qmod.semantics.error_manager import ErrorManager
7
7
 
8
8
 
9
9
  def check_duplicate_constants(constants: Sequence[Constant]) -> None:
10
10
  known_constants = {constant.name: constant for constant in BUILTIN_CONSTANTS}
11
11
  for constant in constants:
12
12
  if constant.name in known_constants:
13
- with ErrorManager().node_context(constant):
14
- ErrorManager().add_error(f"Constant {constant.name!r} already exists")
13
+ raise ClassiqExpansionError(f"Constant {constant.name!r} already exists")
15
14
  else:
16
15
  known_constants[constant.name] = constant
@@ -1,23 +1,20 @@
1
- from typing import Optional
2
-
1
+ from classiq.interface.exceptions import ClassiqExpansionError
3
2
  from classiq.interface.model.model import Model
4
3
 
5
4
  from classiq.qmod.builtins.functions import CORE_LIB_DECLS
6
5
 
7
6
 
8
- def _check_function_name_collisions(
9
- model: Model, error_type: Optional[type[Exception]]
10
- ) -> None:
11
- if error_type is None:
12
- return
7
+ def check_function_name_collisions(model: Model) -> None:
13
8
  redefined_functions = [
14
9
  function.name
15
10
  for function in CORE_LIB_DECLS
16
11
  if function.name in model.function_dict
17
12
  ]
18
13
  if len(redefined_functions) == 1:
19
- raise error_type(
14
+ raise ClassiqExpansionError(
20
15
  f"Cannot redefine built-in function {redefined_functions[0]!r}"
21
16
  )
22
17
  elif len(redefined_functions) > 1:
23
- raise error_type(f"Cannot redefine built-in functions: {redefined_functions}")
18
+ raise ClassiqExpansionError(
19
+ f"Cannot redefine built-in functions: {redefined_functions}"
20
+ )
@@ -1,3 +1,4 @@
1
+ from classiq.interface.exceptions import ClassiqExpansionError
1
2
  from classiq.interface.generator.functions.classical_type import (
2
3
  ClassicalArray,
3
4
  ClassicalList,
@@ -7,7 +8,6 @@ from classiq.interface.model.native_function_definition import NativeFunctionDef
7
8
  from classiq.interface.model.quantum_function_declaration import PositionalArg
8
9
 
9
10
  from classiq import ClassicalParameterDeclaration
10
- from classiq.qmod.semantics.error_manager import append_error
11
11
 
12
12
 
13
13
  def validate_main_function(func: NativeFunctionDefinition) -> None:
@@ -24,8 +24,7 @@ def _validate_main_classical_param_type(
24
24
  param: ConcreteClassicalType, param_name: str
25
25
  ) -> None:
26
26
  if isinstance(param, ClassicalList):
27
- append_error(
28
- param,
27
+ raise ClassiqExpansionError(
29
28
  f"Classical array parameter {param_name!r} of function 'main' must must "
30
29
  f"specify array length",
31
30
  )
@@ -0,0 +1,25 @@
1
+ from classiq.interface.model.model import Model
2
+
3
+ from classiq.qmod.semantics.validation.constants_validation import (
4
+ check_duplicate_constants,
5
+ )
6
+ from classiq.qmod.semantics.validation.function_name_collisions_validation import (
7
+ check_function_name_collisions,
8
+ )
9
+ from classiq.qmod.semantics.validation.main_validation import validate_main_function
10
+ from classiq.qmod.semantics.validation.types_validation import (
11
+ check_duplicate_types,
12
+ validate_cstruct,
13
+ validate_qstruct,
14
+ )
15
+
16
+
17
+ def validate_model(model: Model) -> None:
18
+ check_duplicate_types([*model.enums, *model.types, *model.qstructs])
19
+ check_duplicate_constants(model.constants)
20
+ for qstruct in model.qstructs:
21
+ validate_qstruct(qstruct)
22
+ for cstruct in model.types:
23
+ validate_cstruct(cstruct)
24
+ validate_main_function(model.main_func)
25
+ check_function_name_collisions(model)
@@ -0,0 +1,24 @@
1
+ from collections import Counter
2
+ from collections.abc import Sequence
3
+
4
+ from classiq.interface.exceptions import ClassiqExpansionError
5
+ from classiq.interface.model.quantum_function_declaration import (
6
+ AnonPositionalArg,
7
+ AnonQuantumOperandDeclaration,
8
+ )
9
+
10
+
11
+ def _check_duplicate_param_names(params: Sequence[AnonPositionalArg]) -> None:
12
+ param_names = [param.name for param in params if param.name is not None]
13
+ duplicates = [
14
+ param_name for param_name, count in Counter(param_names).items() if count > 1
15
+ ]
16
+ if len(duplicates) > 0:
17
+ raise ClassiqExpansionError(f"Duplicate parameter name {duplicates[0]!r}")
18
+
19
+
20
+ def validate_function_signature(params: Sequence[AnonPositionalArg]) -> None:
21
+ _check_duplicate_param_names(params)
22
+ for param in params:
23
+ if isinstance(param, AnonQuantumOperandDeclaration):
24
+ validate_function_signature(param.positional_arg_declarations)
@@ -1,6 +1,7 @@
1
1
  from collections.abc import Sequence
2
2
  from typing import Union
3
3
 
4
+ from classiq.interface.exceptions import ClassiqExpansionError
4
5
  from classiq.interface.generator.functions.type_name import TypeName
5
6
  from classiq.interface.generator.types.enum_declaration import EnumDeclaration
6
7
  from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
@@ -10,7 +11,8 @@ from classiq.interface.model.quantum_type import QuantumBitvector
10
11
  from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
11
12
  from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
12
13
  from classiq.qmod.model_state_container import QMODULE
13
- from classiq.qmod.semantics.error_manager import ErrorManager
14
+
15
+ TYPE_EXISTS_ERROR_MESSAGE = "Type {!r} already exists"
14
16
 
15
17
 
16
18
  def check_duplicate_types(
@@ -20,13 +22,12 @@ def check_duplicate_types(
20
22
  known_types |= {type_.name for type_ in BUILTIN_STRUCT_DECLARATIONS.values()}
21
23
  for type_ in types:
22
24
  if type_.name in known_types:
23
- with ErrorManager().node_context(type_):
24
- ErrorManager().add_error(f"Type {type_.name!r} already exists")
25
+ raise ClassiqExpansionError(TYPE_EXISTS_ERROR_MESSAGE.format(type_.name))
25
26
  else:
26
27
  known_types.add(type_.name)
27
28
 
28
29
 
29
- def check_qstruct_flexibility(qstruct: QStructDeclaration) -> None:
30
+ def _check_qstruct_flexibility(qstruct: QStructDeclaration) -> None:
30
31
  _check_qstruct_no_array_without_size_and_element_size(qstruct)
31
32
  _check_qstruct_at_most_one_type_without_size(qstruct)
32
33
 
@@ -42,12 +43,11 @@ def _check_qstruct_no_array_without_size_and_element_size(
42
43
  and not field_type.element_type.has_size_in_bits
43
44
  ]
44
45
  if len(offending_array_fields) > 0:
45
- with ErrorManager().node_context(qstruct):
46
- ErrorManager().add_error(
47
- f"Quantum struct {qstruct.name} contains arrays whose neither length "
48
- f"nor element size are constants. Offending fields: "
49
- f"{', '.join(offending_array_fields)}"
50
- )
46
+ raise ClassiqExpansionError(
47
+ f"Quantum struct {qstruct.name} contains arrays whose neither length "
48
+ f"nor element size are constants. Offending fields: "
49
+ f"{', '.join(offending_array_fields)}"
50
+ )
51
51
 
52
52
 
53
53
  def _check_qstruct_at_most_one_type_without_size(qstruct: QStructDeclaration) -> None:
@@ -57,46 +57,31 @@ def _check_qstruct_at_most_one_type_without_size(qstruct: QStructDeclaration) ->
57
57
  if not field_type.has_size_in_bits
58
58
  ]
59
59
  if len(fields_without_size) > 1:
60
- with ErrorManager().node_context(qstruct):
61
- ErrorManager().add_error(
62
- f"Quantum struct {qstruct.name} has more than one field whose size is "
63
- f"not constant. Offending fields: {', '.join(fields_without_size)}"
64
- )
60
+ raise ClassiqExpansionError(
61
+ f"Quantum struct {qstruct.name} has more than one field whose size is "
62
+ f"not constant. Offending fields: {', '.join(fields_without_size)}"
63
+ )
65
64
 
66
65
 
67
- def check_qstruct_fields_are_defined(qstruct: QStructDeclaration) -> bool:
68
- all_defined = True
66
+ def _check_qstruct_fields_are_defined(qstruct: QStructDeclaration) -> None:
69
67
  for field_type in qstruct.fields.values():
70
68
  if (
71
69
  isinstance(field_type, TypeName)
72
70
  and field_type.name not in QMODULE.qstruct_decls
73
71
  ):
74
- with ErrorManager().node_context(field_type):
75
- ErrorManager().add_error(
76
- f"Quantum struct {field_type.name!r} is not defined."
77
- )
78
- all_defined = False
79
- return all_defined
80
-
81
-
82
- def check_qstruct_has_fields(qstruct: QStructDeclaration) -> None:
83
- if len(qstruct.fields) == 0:
84
- with ErrorManager().node_context(qstruct):
85
- ErrorManager().add_error(
86
- f"Quantum struct {qstruct.name!r} must have at least one field."
72
+ raise ClassiqExpansionError(
73
+ f"Quantum struct {field_type.name!r} is not defined."
87
74
  )
88
75
 
89
76
 
90
- def check_cstruct_has_fields(cstruct: StructDeclaration) -> None:
91
- if len(cstruct.variables) == 0:
92
- with ErrorManager().node_context(cstruct):
93
- ErrorManager().add_error(
94
- f"Classical struct {cstruct.name!r} must have at least one field."
95
- )
77
+ def _check_qstruct_has_fields(qstruct: QStructDeclaration) -> None:
78
+ if len(qstruct.fields) == 0:
79
+ raise ClassiqExpansionError(
80
+ f"Quantum struct {qstruct.name!r} must have at least one field."
81
+ )
96
82
 
97
83
 
98
- def check_qstruct_is_not_recursive(qstruct: QStructDeclaration) -> bool:
99
- non_recursive = True
84
+ def _check_qstruct_is_not_recursive(qstruct: QStructDeclaration) -> None:
100
85
  for main_field_name, main_field in qstruct.fields.items():
101
86
  if (
102
87
  not isinstance(main_field, TypeName)
@@ -109,13 +94,10 @@ def check_qstruct_is_not_recursive(qstruct: QStructDeclaration) -> bool:
109
94
  while stack:
110
95
  qstruct = stack.pop()
111
96
  if qstruct.name in seen_qstructs:
112
- with ErrorManager().node_context(qstruct):
113
- ErrorManager().add_error(
114
- f"Declaration of field {main_field_name!r} in quantum struct "
115
- f"{qstruct.name!r} creates a recursive definition."
116
- )
117
- non_recursive = False
118
- break
97
+ raise ClassiqExpansionError(
98
+ f"Declaration of field {main_field_name!r} in quantum struct "
99
+ f"{qstruct.name!r} creates a recursive definition."
100
+ )
119
101
  seen_qstructs.add(qstruct.name)
120
102
  stack.extend(
121
103
  [
@@ -125,4 +107,21 @@ def check_qstruct_is_not_recursive(qstruct: QStructDeclaration) -> bool:
125
107
  and field.name in QMODULE.qstruct_decls
126
108
  ]
127
109
  )
128
- return non_recursive
110
+
111
+
112
+ def validate_qstruct(qstruct: QStructDeclaration) -> None:
113
+ _check_qstruct_has_fields(qstruct)
114
+ _check_qstruct_fields_are_defined(qstruct)
115
+ _check_qstruct_is_not_recursive(qstruct)
116
+ _check_qstruct_flexibility(qstruct)
117
+
118
+
119
+ def _check_cstruct_has_fields(cstruct: StructDeclaration) -> None:
120
+ if len(cstruct.variables) == 0:
121
+ raise ClassiqExpansionError(
122
+ f"Classical struct {cstruct.name!r} must have at least one field."
123
+ )
124
+
125
+
126
+ def validate_cstruct(cstruct: StructDeclaration) -> None:
127
+ _check_cstruct_has_fields(cstruct)
classiq/qmod/utilities.py CHANGED
@@ -7,6 +7,9 @@ from enum import Enum as PythonEnum
7
7
  from types import FrameType
8
8
  from typing import Any, ForwardRef, Literal, Optional, get_args, get_origin, overload
9
9
 
10
+ from classiq.interface.generator.expressions.qmod_struct_instance import (
11
+ QmodStructInstance,
12
+ )
10
13
  from classiq.interface.source_reference import SourceReference
11
14
 
12
15
  DEFAULT_DECIMAL_PRECISION = 4
@@ -89,6 +92,15 @@ def qmod_val_to_expr_str(val: Any) -> str:
89
92
  )
90
93
  return f"struct_literal({type(val).__name__}, {kwargs_str})"
91
94
 
95
+ if isinstance(val, QmodStructInstance):
96
+ kwargs_str = ", ".join(
97
+ [
98
+ f"{field_name}={qmod_val_to_expr_str(field_val)}"
99
+ for field_name, field_val in val.fields.items()
100
+ ]
101
+ )
102
+ return f"struct_literal({val.struct_declaration.name}, {kwargs_str})"
103
+
92
104
  if isinstance(val, list):
93
105
  elements_str = ", ".join([qmod_val_to_expr_str(elem) for elem in val])
94
106
  return f"[{elements_str}]"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: classiq
3
- Version: 0.65.4
3
+ Version: 0.66.1
4
4
  Summary: Classiq's Python SDK for quantum computing
5
5
  Home-page: https://classiq.io
6
6
  License: Proprietary