Skip to content

Commit

Permalink
refactor: Drop dead code from circuits, Hugr updates (#30)
Browse files Browse the repository at this point in the history
* refactor: Drop dead code from circuits, Hugr updates

* Circuit commands

* Cleanup tests

* Move Command to a submodule

* Use hugr's Region trait, some lifetime workarounds
  • Loading branch information
aborgna-q authored Jul 11, 2023
1 parent cc5db29 commit b9a7750
Show file tree
Hide file tree
Showing 10 changed files with 347 additions and 1,108 deletions.
17 changes: 7 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,21 @@ serde_json = "1.0"
downcast-rs = "1.2.0"
portgraph = "0.7.0"
priority-queue = "1.3.0"
quantinuum-hugr = { git = "https://github.com/CQCL-DEV/hugr", tag = "v0.0.0-alpha.1" }
quantinuum-hugr = { git = "https://github.com/CQCL-DEV/hugr", tag = "v0.0.0-alpha.3" }
smol_str = "0.2.0"
typetag = "0.2.8"

itertools = "0.11.0"
petgraph = { version = "0.6.3", default-features = false }

[features]
pyo3 = [
"dep:pyo3",
"tket-json-rs/pyo3",
"tket-json-rs/tket2ops",
"portgraph/pyo3",
]
pyo3 = ["dep:pyo3", "tket-json-rs/pyo3", "tket-json-rs/tket2ops", "portgraph/pyo3", "quantinuum-hugr/pyo3"]
tkcxx = ["dep:tket-rs", "dep:num-complex"]

[dev-dependencies]
rstest = "0.17.0"
rstest = "0.18.1"
criterion = { version = "0.5.1", features = ["html_reports"] }

webbrowser = "0.8.10"
urlencoding = "2.1.2"

#[[bench]]
#name = "tket2_bench"
Expand Down
2 changes: 1 addition & 1 deletion pyrs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ portgraph = { version = "0.7.0", features = ["pyo3"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tket-json-rs = { git = "https://github.com/CQCL/tket-json-rs", features = ["pyo3"] }
quantinuum-hugr = { version = "0.1.0", git="https://github.com/CQCL-DEV/hugr", tag = "v0.0.0-alpha.1" }
quantinuum-hugr = { version = "0.1.0", git="https://github.com/CQCL-DEV/hugr", tag = "v0.0.0-alpha.3" }
110 changes: 106 additions & 4 deletions src/circuit.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,114 @@
#[allow(clippy::module_inception)]
pub mod circuit;
//pub mod dag;
//pub mod operation;
//! Quantum circuit representation and operations.

pub mod command;

//#[cfg(feature = "pyo3")]
//pub mod py_circuit;

//#[cfg(feature = "tkcxx")]
//pub mod unitarybox;

// TODO: Move TKET1's custom op definition to tket-rs (or hugr?)
//mod tk1ops;

use self::command::{Command, CommandIterator, Unit};

use hugr::ops::OpTrait;

pub use hugr::hugr::region::Region;
pub use hugr::ops::OpType;
pub use hugr::types::{ClassicType, EdgeKind, LinearType, Signature, SimpleType, TypeRow};
pub use hugr::{Node, Port, Wire};
use petgraph::visit::{GraphBase, IntoNeighborsDirected, IntoNodeIdentifiers};

/// An object behaving like a quantum circuit.
//
// TODO: More methods:
// - other_{in,out}puts (for non-linear i/o + const inputs)?
// - Vertical slice iterator
// - Gate count map
// - Depth
pub trait Circuit<'circ> {
/// An iterator over the commands in the circuit.
type Commands: Iterator<Item = Command<'circ>>;

/// An iterator over the commands applied to an unit.
type UnitCommands: Iterator<Item = Command<'circ>>;

/// Return the name of the circuit
fn name(&self) -> Option<&str>;

/// Get the linear inputs of the circuit and their types.
fn units(&self) -> Vec<(Unit, SimpleType)>;

/// Returns the ports corresponding to qubits inputs to the circuit.
#[inline]
fn qubits(&self) -> Vec<Unit> {
self.units()
.iter()
.filter(|(_, typ)| typ == &LinearType::Qubit.into())
.map(|(unit, _)| *unit)
.collect()
}

/// Given a linear port in a node, returns the corresponding port on the other side of the node (if any).
fn follow_linear_port(&self, node: Node, port: Port) -> Option<Port>;

/// Returns all the commands in the circuit, in some topological order.
fn commands<'a: 'circ>(&'a self) -> Self::Commands;

/// Returns all the commands applied to the given unit, in order.
fn unit_commands<'a: 'circ>(&'a self) -> Self::UnitCommands;
}

impl<'circ, T> Circuit<'circ> for T
where
T: 'circ + Region<'circ>,
for<'a> &'a T: GraphBase<NodeId = Node> + IntoNeighborsDirected + IntoNodeIdentifiers,
{
type Commands = CommandIterator<'circ, T>;
type UnitCommands = std::iter::Empty<Command<'circ>>;

#[inline]
fn name(&self) -> Option<&str> {
let meta = self.get_metadata(self.root()).as_object()?;
meta.get("name")?.as_str()
}

#[inline]
fn units(&self) -> Vec<(Unit, SimpleType)> {
let root = self.root();
let optype = self.get_optype(root);
optype
.signature()
.input_df_types()
.iter()
.filter(|typ| typ.is_linear())
.enumerate()
.map(|(i, typ)| (i.into(), typ.clone()))
.collect()
}

fn follow_linear_port(&self, node: Node, port: Port) -> Option<Port> {
let optype = self.get_optype(node);
if !optype.port_kind(port)?.is_linear() {
return None;
}
// TODO: We assume the linear data uses the same port offsets on both sides of the node.
// In the future we may want to have a more general mechanism to handle this.
let other_port = Port::new(port.direction().reverse(), port.index());
debug_assert_eq!(optype.port_kind(other_port), optype.port_kind(port));
Some(other_port)
}

fn commands<'a: 'circ>(&'a self) -> Self::Commands {
// Traverse the circuit in topological order.
CommandIterator::new(self)
}

fn unit_commands<'a: 'circ>(&'a self) -> Self::UnitCommands {
// TODO Can we associate linear i/o with the corresponding unit without
// doing the full toposort?
todo!()
}
}
Loading

0 comments on commit b9a7750

Please sign in to comment.