use std::fmt::{self, Debug, Display};
use itertools::Itertools;
use crate::{Hugr, HugrView, IncomingPort, Node, ops::Value};
pub fn display_list<T>(ts: impl IntoIterator<Item = T>, f: &mut fmt::Formatter) -> fmt::Result
where
T: Display,
{
display_list_with_separator(ts, f, ", ")
}
pub fn display_list_with_separator<T>(
ts: impl IntoIterator<Item = T>,
f: &mut fmt::Formatter,
sep: &str,
) -> fmt::Result
where
T: Display,
{
let mut first = true;
for t in ts {
if !first {
f.write_str(sep)?;
}
t.fmt(f)?;
if first {
first = false;
}
}
Ok(())
}
#[inline]
#[track_caller]
pub fn collect_array<const N: usize, T: Debug>(arr: impl IntoIterator<Item = T>) -> [T; N] {
match try_collect_array(arr) {
Ok(v) => v,
Err(v) => panic!("Expected {N} elements, got {v:?}"),
}
}
#[inline]
#[track_caller]
pub fn try_collect_array<const N: usize, T>(
arr: impl IntoIterator<Item = T>,
) -> Result<[T; N], Vec<T>> {
arr.into_iter().collect_vec().try_into()
}
#[allow(dead_code)]
pub(crate) fn is_default<T: Default + PartialEq>(t: &T) -> bool {
*t == Default::default()
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum Never {}
#[cfg(test)]
pub(crate) mod test_quantum_extension {
use std::sync::{Arc, LazyLock};
use crate::ops::{OpName, OpNameRef};
use crate::std_extensions::arithmetic::float_ops;
use crate::std_extensions::logic;
use crate::types::FuncValueType;
use crate::{
Extension,
extension::{
ExtensionId, ExtensionRegistry, PRELUDE,
prelude::{bool_t, qb_t},
},
ops::ExtensionOp,
std_extensions::arithmetic::float_types,
type_row,
types::{PolyFuncTypeRV, Signature},
};
fn one_qb_func() -> PolyFuncTypeRV {
FuncValueType::new_endo([qb_t()]).into()
}
fn two_qb_func() -> PolyFuncTypeRV {
FuncValueType::new_endo([qb_t(), qb_t()]).into()
}
pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("test.quantum");
fn extension() -> Arc<Extension> {
Extension::new_test_arc(EXTENSION_ID, |extension, extension_ref| {
extension
.add_op(
OpName::new_inline("H"),
"Hadamard".into(),
one_qb_func(),
extension_ref,
)
.unwrap();
extension
.add_op(
OpName::new_inline("RzF64"),
"Rotation specified by float".into(),
Signature::new(vec![qb_t(), float_types::float64_type()], vec![qb_t()]),
extension_ref,
)
.unwrap();
extension
.add_op(
OpName::new_inline("CX"),
"CX".into(),
two_qb_func(),
extension_ref,
)
.unwrap();
extension
.add_op(
OpName::new_inline("Measure"),
"Measure a qubit, returning the qubit and the measurement result.".into(),
Signature::new(vec![qb_t()], vec![qb_t(), bool_t()]),
extension_ref,
)
.unwrap();
extension
.add_op(
OpName::new_inline("QAlloc"),
"Allocate a new qubit.".into(),
Signature::new(type_row![], vec![qb_t()]),
extension_ref,
)
.unwrap();
extension
.add_op(
OpName::new_inline("QDiscard"),
"Discard a qubit.".into(),
Signature::new(vec![qb_t()], type_row![]),
extension_ref,
)
.unwrap();
})
}
pub static EXTENSION: LazyLock<Arc<Extension>> = LazyLock::new(extension);
pub static REG: LazyLock<ExtensionRegistry> = LazyLock::new(|| {
ExtensionRegistry::new([
EXTENSION.clone(),
PRELUDE.clone(),
float_types::EXTENSION.clone(),
float_ops::EXTENSION.clone(),
logic::EXTENSION.clone(),
])
});
fn get_gate(gate_name: &OpNameRef) -> ExtensionOp {
EXTENSION.instantiate_extension_op(gate_name, []).unwrap()
}
pub(crate) fn h_gate() -> ExtensionOp {
get_gate("H")
}
pub(crate) fn cx_gate() -> ExtensionOp {
get_gate("CX")
}
pub(crate) fn measure() -> ExtensionOp {
get_gate("Measure")
}
pub(crate) fn rz_f64() -> ExtensionOp {
get_gate("RzF64")
}
pub(crate) fn q_alloc() -> ExtensionOp {
get_gate("QAlloc")
}
pub(crate) fn q_discard() -> ExtensionOp {
get_gate("QDiscard")
}
}
fn sort_by_in_port(consts: &[(IncomingPort, Value)]) -> Vec<&(IncomingPort, Value)> {
let mut v: Vec<_> = consts.iter().collect();
v.sort_by_key(|(i, _)| i);
v
}
#[must_use]
pub fn sorted_consts(consts: &[(IncomingPort, Value)]) -> Vec<&Value> {
sort_by_in_port(consts)
.into_iter()
.map(|(_, c)| c)
.collect()
}
pub fn depth(h: &Hugr, n: Node) -> u32 {
match h.get_parent(n) {
Some(p) => 1 + depth(h, p),
None => 0,
}
}
#[allow(dead_code)]
#[cfg(test)]
pub(crate) mod test {
#[allow(unused_imports)]
use crate::HugrView;
use crate::{
Hugr,
ops::{OpType, Value},
};
pub(crate) fn assert_fully_folded(h: &Hugr, expected_value: &Value) {
assert_fully_folded_with(h, |v| v == expected_value);
}
pub(crate) fn assert_fully_folded_with(h: &Hugr, check_value: impl Fn(&Value) -> bool) {
let mut node_count = 0;
for node in h.children(h.entrypoint()) {
let op = h.get_optype(node);
match op {
OpType::Input(_) | OpType::Output(_) | OpType::LoadConstant(_) => node_count += 1,
OpType::Const(c) if check_value(c.value()) => node_count += 1,
_ => panic!("unexpected op: {}\n{}", op, h.mermaid_string()),
}
}
assert_eq!(node_count, 4);
}
}