amazon-braket / amazon-braket-sdk-python

A Python SDK for interacting with quantum devices on Amazon Braket
https://aws.amazon.com/braket/
Apache License 2.0
295 stars 116 forks source link

`Circuit.from_ir` not supporting observables on physical qubits #854

Open yitchen-tim opened 7 months ago

yitchen-tim commented 7 months ago

Describe the bug When the openqasm IR includes #pragma braket result expectation z($0) @ z($1), Circuit.from_ir throws error line 1:28 no viable alternative at input 'z($0'.

This error only happens to physical qubits. When using observables on virtual qubits, there is no error.

To reproduce

qasm_program = """
OPENQASM 3.0;
#pragma braket verbatim
box{
gpi2(6.283185307179586) $1;
gpi2(6.283185307179586) $0;
ms(4.397592653589793, 0.0) $0, $1;
}
#pragma braket result expectation z($0) @ z($1)
"""

circuit = Circuit.from_ir(qasm_program)
print(circuit)

Expected behavior Returns a Braket circuit that represents the QASM program above.

Screenshots or logs

line 1:28 no viable alternative at input 'z($0'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[62], line 12
      1 qasm_program = """
      2 OPENQASM 3.0;
      3 #pragma braket verbatim
   (...)
      9 #pragma braket result expectation z($0) @ z($1)
     10 """
---> 12 circuit = Circuit.from_ir(qasm_program)
     13 print(circuit)

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/circuits/circuit.py:1175, in Circuit.from_ir(source, inputs)
   1172     source = source.source
   1173 from braket.circuits.braket_program_context import BraketProgramContext
-> 1175 return Interpreter(BraketProgramContext()).build_circuit(
   1176     source=source,
   1177     inputs=inputs,
   1178     is_file=False,
   1179 )

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/interpreter.py:130, in Interpreter.build_circuit(self, source, inputs, is_file)
    126 def build_circuit(
    127     self, source: str, inputs: Optional[dict[str, io_type]] = None, is_file: bool = False
    128 ) -> Circuit:
    129     """Interpret an OpenQASM program and build a Circuit IR."""
--> 130     return self.run(source, inputs, is_file).circuit

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/interpreter.py:145, in Interpreter.run(self, source, inputs, is_file)
    143 program = parse(source)
    144 self._uses_advanced_language_features = False
--> 145 self.visit(program)
    146 if self._uses_advanced_language_features:
    147     self.logger.warning(
    148         "This program uses OpenQASM language features that may "
    149         "not be supported on QPUs or on-demand simulators."
    150     )

File ~/miniconda/lib/python3.10/functools.py:926, in singledispatchmethod.__get__.<locals>._method(*args, **kwargs)
    924 def _method(*args, **kwargs):
    925     method = self.dispatcher.dispatch(args[0].__class__)
--> 926     return method.__get__(obj, cls)(*args, **kwargs)

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/interpreter.py:172, in Interpreter._(self, node)
    170 @visit.register
    171 def _(self, node: Program) -> None:
--> 172     self.visit(node.statements)

File ~/miniconda/lib/python3.10/functools.py:926, in singledispatchmethod.__get__.<locals>._method(*args, **kwargs)
    924 def _method(*args, **kwargs):
    925     method = self.dispatcher.dispatch(args[0].__class__)
--> 926     return method.__get__(obj, cls)(*args, **kwargs)

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/interpreter.py:168, in Interpreter._(self, node_list)
    165 @visit.register
    166 def _(self, node_list: list) -> list[QASMNode]:
    167     """Generic visit function for a list of AST nodes"""
--> 168     return [n for n in [self.visit(node) for node in node_list] if n is not None]

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/interpreter.py:168, in <listcomp>(.0)
    165 @visit.register
    166 def _(self, node_list: list) -> list[QASMNode]:
    167     """Generic visit function for a list of AST nodes"""
--> 168     return [n for n in [self.visit(node) for node in node_list] if n is not None]

File ~/miniconda/lib/python3.10/functools.py:926, in singledispatchmethod.__get__.<locals>._method(*args, **kwargs)
    924 def _method(*args, **kwargs):
    925     method = self.dispatcher.dispatch(args[0].__class__)
--> 926     return method.__get__(obj, cls)(*args, **kwargs)

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/interpreter.py:543, in Interpreter._(self, node)
    541 @visit.register
    542 def _(self, node: Pragma) -> None:
--> 543     parsed = self.context.parse_pragma(node.command)
    544     if node.command.startswith("braket result"):
    545         if not parsed:

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/program_context.py:444, in AbstractProgramContext.parse_pragma(self, pragma_body)
    437 def parse_pragma(self, pragma_body: str):
    438     """
    439     Parse pragma
    440 
    441     Args:
    442         pragma_body (str): The body of the pragma statement.
    443     """
--> 444     return parse_braket_pragma(pragma_body, self.qubit_mapping)

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/parser/braket_pragmas.py:216, in parse_braket_pragma(pragma_body, qubit_table)
    214 parser = BraketPragmasParser(stream)
    215 tree = parser.braketPragma()
--> 216 visited = BraketPragmaNodeVisitor(qubit_table).visit(tree)
    217 return visited

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/antlr4/tree/Tree.py:34, in ParseTreeVisitor.visit(self, tree)
     33 def visit(self, tree):
---> 34     return tree.accept(self)

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/parser/generated/BraketPragmasParser.py:861, in BraketPragmasParser.BraketPragmaContext.accept(self, visitor)
    859 def accept(self, visitor:ParseTreeVisitor):
    860     if hasattr( visitor, "visitBraketPragma" ):
--> 861         return visitor.visitBraketPragma(self)
    862     else:
    863         return visitor.visitChildren(self)

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/parser/generated/BraketPragmasParserVisitor.py:14, in BraketPragmasParserVisitor.visitBraketPragma(self, ctx)
     13 def visitBraketPragma(self, ctx:BraketPragmasParser.BraketPragmaContext):
---> 14     return self.visitChildren(ctx)

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/antlr4/tree/Tree.py:44, in ParseTreeVisitor.visitChildren(self, node)
     41         return result
     43     c = node.getChild(i)
---> 44     childResult = c.accept(self)
     45     result = self.aggregateResult(result, childResult)
     47 return result

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/parser/generated/BraketPragmasParser.py:1226, in BraketPragmasParser.BraketResultPragmaContext.accept(self, visitor)
   1224 def accept(self, visitor:ParseTreeVisitor):
   1225     if hasattr( visitor, "visitBraketResultPragma" ):
-> 1226         return visitor.visitBraketResultPragma(self)
   1227     else:
   1228         return visitor.visitChildren(self)

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/parser/generated/BraketPragmasParserVisitor.py:39, in BraketPragmasParserVisitor.visitBraketResultPragma(self, ctx)
     38 def visitBraketResultPragma(self, ctx:BraketPragmasParser.BraketResultPragmaContext):
---> 39     return self.visitChildren(ctx)

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/antlr4/tree/Tree.py:44, in ParseTreeVisitor.visitChildren(self, node)
     41         return result
     43     c = node.getChild(i)
---> 44     childResult = c.accept(self)
     45     result = self.aggregateResult(result, childResult)
     47 return result

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/parser/generated/BraketPragmasParser.py:1290, in BraketPragmasParser.ResultTypeContext.accept(self, visitor)
   1288 def accept(self, visitor:ParseTreeVisitor):
   1289     if hasattr( visitor, "visitResultType" ):
-> 1290         return visitor.visitResultType(self)
   1291     else:
   1292         return visitor.visitChildren(self)

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/parser/generated/BraketPragmasParserVisitor.py:44, in BraketPragmasParserVisitor.visitResultType(self, ctx)
     43 def visitResultType(self, ctx:BraketPragmasParser.ResultTypeContext):
---> 44     return self.visitChildren(ctx)

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/antlr4/tree/Tree.py:44, in ParseTreeVisitor.visitChildren(self, node)
     41         return result
     43     c = node.getChild(i)
---> 44     childResult = c.accept(self)
     45     result = self.aggregateResult(result, childResult)
     47 return result

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/parser/generated/BraketPragmasParser.py:1867, in BraketPragmasParser.ObservableResultTypeContext.accept(self, visitor)
   1865 def accept(self, visitor:ParseTreeVisitor):
   1866     if hasattr( visitor, "visitObservableResultType" ):
-> 1867         return visitor.visitObservableResultType(self)
   1868     else:
   1869         return visitor.visitChildren(self)

File ~/virtual_envs/qpu-onboard/lib/python3.10/site-packages/braket/default_simulator/openqasm/parser/braket_pragmas.py:98, in BraketPragmaNodeVisitor.visitObservableResultType(self, ctx)
     92 result_type = ctx.observableResultTypeName().getText()
     93 observable_result_type_map = {
     94     "expectation": Expectation,
     95     "sample": Sample,
     96     "variance": Variance,
     97 }
---> 98 observables, targets = self.visit(ctx.observable())
     99 obs = observable_result_type_map[result_type](targets=targets, observable=observables)
    100 return obs

TypeError: cannot unpack non-iterable NoneType object

System information A description of your system. Please provide: amazon-braket-algorithm-library 1.2.0 amazon-braket-default-simulator 1.20.1 amazon-braket-pennylane-plugin 1.12.2 amazon-braket-schemas 1.19.1.post0 amazon-braket-sdk 1.65.2.dev0 Python version**: 3.10.9

speller26 commented 7 months ago

Physical qubits and verbatim boxes are not currently supported; we'll need to add the stubs to the default simulator and implement them here.

laurencap commented 6 months ago

@speller26 Should we update this issue from bug to enhancement? Edit: or, is there a way to improve the error message?