use crate::resource::types::ResourceId;
use derive_more::derive::{Display, Error};
use hugr::HugrView;
use hugr::ops::OpType;
use hugr::types::Type;
use itertools::{EitherOrBoth, Itertools};
#[derive(Debug, Display, Clone, PartialEq, Error)]
#[display("Unsupported operation: {_0}")]
pub struct UnsupportedOp(#[error(not(source))] OpType);
pub trait ResourceFlow<H: HugrView> {
fn map_resources(
&self,
node: H::Node,
hugr: &H,
inputs: &[Option<ResourceId>],
) -> Result<Vec<Option<ResourceId>>, UnsupportedOp>;
fn into_boxed<'a>(self) -> Box<dyn 'a + ResourceFlow<H>>
where
Self: 'a + Sized,
{
Box::new(self)
}
}
impl<H: HugrView> ResourceFlow<H> for Box<dyn '_ + ResourceFlow<H>> {
fn map_resources(
&self,
node: H::Node,
hugr: &H,
inputs: &[Option<ResourceId>],
) -> Result<Vec<Option<ResourceId>>, UnsupportedOp> {
self.as_ref().map_resources(node, hugr, inputs)
}
}
#[derive(Debug, Clone, Default)]
pub struct DefaultResourceFlow;
impl DefaultResourceFlow {
fn is_resource_preserving(
input_types: impl IntoIterator<Item = Type>,
output_types: impl IntoIterator<Item = Type>,
) -> bool {
for io_ty in input_types.into_iter().zip_longest(output_types) {
let (input_ty, output_ty) = match io_ty {
EitherOrBoth::Both(input_ty, output_ty) => (input_ty, output_ty),
EitherOrBoth::Left(ty) | EitherOrBoth::Right(ty) => {
if !ty.copyable() {
return false;
}
continue;
}
};
if !input_ty.copyable() || !output_ty.copyable() {
if input_ty != output_ty {
return false;
}
}
}
true
}
}
impl<H: HugrView> ResourceFlow<H> for DefaultResourceFlow {
fn map_resources(
&self,
node: H::Node,
hugr: &H,
inputs: &[Option<ResourceId>],
) -> Result<Vec<Option<ResourceId>>, UnsupportedOp> {
let op = hugr.get_optype(node);
debug_assert_eq!(
inputs.len(),
op.value_input_count(),
"Input resource array length must match operation input count"
);
if Self::is_resource_preserving(
hugr.in_value_types(node).map(|(_, ty)| ty),
hugr.out_value_types(node).map(|(_, ty)| ty),
) {
Ok(retain_linear_types(
inputs.to_vec(),
hugr.out_value_types(node).map(|(_, ty)| ty),
op.value_output_count(),
))
} else {
Ok(vec![None; op.value_output_count()])
}
}
}
fn retain_linear_types(
mut resources: Vec<Option<ResourceId>>,
types: impl IntoIterator<Item = Type>,
output_count: usize,
) -> Vec<Option<ResourceId>> {
resources.resize(output_count, None);
for (ty, resource) in types.into_iter().zip(resources.iter_mut()) {
if ty.copyable() {
*resource = None;
}
}
resources
}