Skip to content

Fixes #183#227

Closed
arunjmoorthy wants to merge 8 commits intoqBraid:mainfrom
arunjmoorthy:issue183
Closed

Fixes #183#227
arunjmoorthy wants to merge 8 commits intoqBraid:mainfrom
arunjmoorthy:issue183

Conversation

@arunjmoorthy
Copy link
Copy Markdown
Collaborator

Summary of changes

This is a draft PR. This does not solve all of the issues in issue 183. Look at the bottom of the visitor.py file. There, some of the functions are implemented to parse the defcal block, which has several restrictions. Additionally, one openpulse test example file has been added to help with iterating and testing.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Jul 6, 2025

👋 Hey there! It looks like the changelog might need an update.

Please take a moment to edit the CHANGELOG.md with:

  • A brief, one-to-two sentence summary of your changes.
  • A link back to this PR for reference.
  • (Optional) A small working example if you've added new features.

program = openqasm3.parse(program)
except (openqasm3.parser.QASM3ParsingError, openpulse.parser.OpenPulseParsingError) as err:
raise ValidationError(f"Failed to parse the OpenQASM/Openpulse string: {err}") from err
elif not isinstance(program, openqasm3.ast.Program):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we check for both the ast types here i.e. for openqasm3.ast.Program and openpulse.ast.Program?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don’t need to parse inputs separately; we can simply call openpulse.parser.parse_openpulse() inside calibration functions to handle their statement body.
Suggestion: since openpulse.parse() parses both generic QASM AST's and OpenPulse ASTs, we can replace our current call to openqasm.parse() with openpulse.parse().

visitor (QasmVisitor): The visitor to accept
"""
unrolled_stmt_list = visitor.visit_basic_block(self._statements)
# print("--------------------------------")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comments




# You'll also need these other visitor methods for your visit map:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Comment on lines +2164 to +2165
print("Calibration Statement Structure:")
print("Statement: ", statement)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Comment on lines +2184 to +2185
print("Calibration Grammar Declaration Structure:")
print("Declaration: ", declaration)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Comment on lines +2297 to +2299
print("--------------------------------")
print("Gate name: ", gate_name)
print("--------------------------------")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove, or move to logging level DEBUG

Comment on lines +2331 to +2332
print("--------------------------------")
print("Parsing physical qubits: ", qubits)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove or replace with logger

Comment on lines +2339 to +2341
print("--------------------------------")
print("Qubit: ", qubit)
print("--------------------------------")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

)

print("--------------------------------")
return physical_qubits
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Comment on lines +2374 to +2376
print("--------------------------------")
print("Validating calibration arguments: ", arguments)
print("--------------------------------")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove or replace with logger

Comment on lines +2380 to +2382
print("--------------------------------")
print("Argument: ", arg)
print("--------------------------------")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Comment on lines +2407 to +2409
print("--------------------------------")
print("Validating calibration body: ", body)
print("--------------------------------")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove or replace with logger

Comment on lines +2415 to +2417
print("--------------------------------")
print("Validating calibration body: ", body)
print("--------------------------------")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove or replace with logger

grammar_name = declaration.name

# Validate supported calibration grammars
supported_grammars = {"openpulse"} # Add more as needed
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to make this a constant and move to maps sub-package

)

try:
physical_qubit_id = int(qubit_name[1:])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check that the qubit index is >=0 here AND , if device_qubits are specified (see PR #222 for reference), within the range of the total number of the qubits present on the device for which this program is intended

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moreover, as stated above, not all args will be having a prefixed $ sign. Will need to treat those as an identifier then****

print("--------------------------------")
return physical_qubits

def _validate_calibration_arguments(self, arguments: list, gate_name: str, definition: pulse_ast.CalibrationDefinition) -> None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be refactored to the Qasm3Validator module

)
seen_qubits.add(qubit_name)

if not qubit_name.startswith('$'):
Copy link
Copy Markdown
Member

@TheGupta2012 TheGupta2012 Jul 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't assume that the qubit arguments will only be composed of physical qubits. If you see the example -

defcal rz(angle[20] theta) q { ... }
// we've defined ``rz`` on arbitrary physical qubits, so we can do:
rz(3.14) $0;
rz(3.14) $1;

the defcals blocks can also be defined for identifiers , meaning that for all physical qubits, we are to follow the given defcal body.

'DurationType': 64, 'ComplexType': 128
}.get(type_name, 32)

def _validate_openpulse_declaration_type(self, stmt, gate_name: str, definition) -> None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactor the the Qasm3Validator

span=definition.span,
)

def _basic_pulse_declaration_validation(self, stmt, gate_name: str, definition) -> None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, refactor to the Qasm3Validator module

)
self._add_var_in_scope(variable)

def _validate_openpulse_function_call(self, stmt, gate_name: str, definition) -> None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor to the Qasm3Validator module

if hasattr(stmt, 'expression') and hasattr(stmt.expression, 'name'):
func_name = stmt.expression.name.name

openpulse_functions = {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

define in the maps/pulse.py module

def _validate_openpulse_declaration_type(self, stmt, gate_name: str, definition) -> None:
"""Validate that declaration uses valid openpulse types."""
type_name = type(stmt.type).__name__
valid_types = {'WaveformType', 'FrameType', 'PortType', 'IntType', 'FloatType', 'AngleType', 'DurationType'}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

define in the maps/pulse.py module

print("Validating calibration body: ", body)
print("--------------------------------")
for stmt in body:
self._validate_calibration_statement_dispatch(stmt, gate_name, definition)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's handle exceptions here. We can extract the type name and catch the raised exceptions instead of duplicating the raising in the dispatch method

def _get_calibration_arg_size(self, arg_type) -> int:
"""Get size for calibration argument type."""
type_name = type(arg_type).__name__
return {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the dict to maps/pulse.py and directly use the .get method on it. Will ensure that we're not redefining it again and again during fn call.

pip install pyqasm
```

### Optional Dependencies
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove these README changes, I have already consolidated these extra installs under 1 head.

Copy link
Copy Markdown
Member

@TheGupta2012 TheGupta2012 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @arunjmoorthy , thank you for the great work! I have given some comments regarding the refactor of code and about adding more validations.

Also, please add unit tests for the openpulse grammar. I know that there are a LOT of cases, but we'll need to cover all of them in order to achieve our goal of full QASM coverage!

@vinayswamik do have a look at the PR, maybe you guys can collaborate on this?

@arunjmoorthy arunjmoorthy deleted the issue183 branch July 16, 2025 06:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants