Skip to content

Commit

Permalink
Adapt crates/qasm3 to work with recent versions of openqasm3_parser
Browse files Browse the repository at this point in the history
This commit adds no new features or capabilities to the importer. But it brings
the importer up to date with the latest version of openqasm3_parser as a preliminary
step in improving the importer.

The biggest change in openqasm3_parser is in handling stdgates.inc.  Upon encountering
`include "stdgates.inc" openqasm3_parser reads no file, but rather adds symbols to the
symbol table for gates in the standard library.

The function `convert_asg` in the importer calls oq3_semantics::symbols::SymbolTable.gates
which returns a `Vec` of information about each "declared" gate. The information is the
gate's name and signature and SymbolID, which is sufficient to do everything the importer
could do before this commit.

Encountering `Stmt::GateDefinition` now is a no-op rather than an error.  (This was
previously called `Stmt::GateDeclaration`, but importantly it is really a definition.)

How the standard library, and gates in general, are handled will continue to evolve.

A behavioral difference between this commit and its parent: Before if the importer
encountered a gate call before the corresponding definition an error would be raised. With
the current commit, the importer behaves as if all gate definitions were moved to the top
of the OQ3 program. However, the error will still be found by the parser so that the
importer never will begin processing statements.

The importer depends on a particular branch of a copy of openqasm3_parser. When
the commit is ready to merge, we will release a version of openqasm3_parser and
depend instead on that release.

See openqasm/openqasm#517
  • Loading branch information
jlapeyre committed Mar 26, 2024
1 parent 35a8e53 commit fbca320
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 63 deletions.
54 changes: 35 additions & 19 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/qasm3/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ extension-module = ["pyo3/extension-module"]
pyo3.workspace = true
indexmap.workspace = true
hashbrown.workspace = true
oq3_semantics = "0.0.7"
oq3_semantics = { git = "https://github.com/jlapeyre/openqasm3_parser", branch = "update-for-qiskit-importer"}
89 changes: 46 additions & 43 deletions crates/qasm3/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,42 +226,37 @@ impl BuilderState {
self.qc.append(py, instruction).map(|_| ())
}

fn define_gate(
&mut self,
_py: Python,
ast_symbols: &SymbolTable,
decl: &asg::GateDeclaration,
) -> PyResult<()> {
let name_id = decl
.name()
.as_ref()
.map_err(|err| QASM3ImporterError::new_err(format!("internal error: {:?}", err)))?;
let name_symbol = &ast_symbols[name_id];
let pygate = self.pygates.get(name_symbol.name()).ok_or_else(|| {
QASM3ImporterError::new_err(format!(
"can't handle non-built-in gate: '{}'",
name_symbol.name()
))
})?;
let defined_num_params = decl.params().as_ref().map_or(0, Vec::len);
let defined_num_qubits = decl.qubits().len();
if pygate.num_params() != defined_num_params {
return Err(QASM3ImporterError::new_err(format!(
"given constructor for '{}' expects {} parameters, but is defined as taking {}",
pygate.name(),
pygate.num_params(),
defined_num_params,
)));
}
if pygate.num_qubits() != defined_num_qubits {
return Err(QASM3ImporterError::new_err(format!(
"given constructor for '{}' expects {} qubits, but is defined as taking {}",
pygate.name(),
pygate.num_qubits(),
defined_num_qubits,
)));
// Map gates in the symbol table to Qiskit gates in the standard library.
// Encountering any gates not in the standard library results in raising an exception.
// Gates mapped via CustomGates will not raise an exception.
fn map_gate_ids(&mut self, _py: Python, ast_symbols: &SymbolTable) -> PyResult<()> {
for (name, name_id, defined_num_params, defined_num_qubits) in ast_symbols.gates().iter() {
let name = name.to_string();
if name == "U" {
// The sole built in gate. `gphase` is treated specially.
continue;
}
let pygate = self.pygates.get(&name).ok_or_else(|| {
QASM3ImporterError::new_err(format!("can't handle non-built-in gate: '{}'", name))
})?;
if pygate.num_params() != *defined_num_params {
return Err(QASM3ImporterError::new_err(format!(
"given constructor for '{}' expects {} parameters, but is defined as taking {}",
pygate.name(),
pygate.num_params(),
defined_num_params,
)));
}
if pygate.num_qubits() != *defined_num_qubits {
return Err(QASM3ImporterError::new_err(format!(
"given constructor for '{}' expects {} qubits, but is defined as taking {}",
pygate.name(),
pygate.num_qubits(),
defined_num_qubits,
)));
}
self.symbols.gates.insert(name_id.clone(), pygate.clone());
}
self.symbols.gates.insert(name_id.clone(), pygate.clone());
Ok(())
}

Expand Down Expand Up @@ -377,37 +372,45 @@ pub fn convert_asg(
pygates: gate_constructors,
module,
};

let _ = state.map_gate_ids(py, ast_symbols);

for statement in program.stmts().iter() {
match statement {
asg::Stmt::GateCall(call) => state.call_gate(py, ast_symbols, call)?,
asg::Stmt::DeclareClassical(decl) => state.declare_classical(py, ast_symbols, decl)?,
asg::Stmt::DeclareQuantum(decl) => state.declare_quantum(py, ast_symbols, decl)?,
asg::Stmt::GateDeclaration(decl) => state.define_gate(py, ast_symbols, decl)?,
// We ignore gate definitions because the only information we can currently use
// from them is extracted with `SymbolTable::gates` via `map_gate_ids`.
asg::Stmt::GateDefinition(_) => (),
asg::Stmt::Barrier(barrier) => state.apply_barrier(py, ast_symbols, barrier)?,
asg::Stmt::Assignment(assignment) => state.assign(py, ast_symbols, assignment)?,
asg::Stmt::Alias
asg::Stmt::Alias(_)
| asg::Stmt::AnnotatedStmt(_)
| asg::Stmt::Block(_)
| asg::Stmt::Box
| asg::Stmt::Break
| asg::Stmt::Cal
| asg::Stmt::Continue
| asg::Stmt::Def
| asg::Stmt::DeclareHardwareQubit(_)
| asg::Stmt::DefCal
| asg::Stmt::Delay
| asg::Stmt::DefStmt(_)
| asg::Stmt::Delay(_)
| asg::Stmt::End
| asg::Stmt::ExprStmt(_)
| asg::Stmt::Extern
| asg::Stmt::For
| asg::Stmt::ForStmt(_)
| asg::Stmt::GPhaseCall(_)
| asg::Stmt::IODeclaration
| asg::Stmt::If(_)
| asg::Stmt::Include(_)
| asg::Stmt::InputDeclaration(_)
| asg::Stmt::ModifiedGPhaseCall(_)
| asg::Stmt::NullStmt
| asg::Stmt::OldStyleDeclaration
| asg::Stmt::OutputDeclaration(_)
| asg::Stmt::Pragma(_)
| asg::Stmt::Reset
| asg::Stmt::Return
| asg::Stmt::Reset(_)
| asg::Stmt::SwitchCaseStmt(_)
| asg::Stmt::While(_) => {
return Err(QASM3ImporterError::new_err(format!(
"this statement is not yet handled during OpenQASM 3 import: {:?}",
Expand Down

0 comments on commit fbca320

Please sign in to comment.