From 04a824c2201c4b24648cf1640ddb9b1a74f38aa4 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Tue, 27 Jun 2023 17:53:27 +0100 Subject: [PATCH] Various tests, and various failures --- src/hugr/validate.rs | 128 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 1 deletion(-) diff --git a/src/hugr/validate.rs b/src/hugr/validate.rs index f56ef9fdf7..4968ccffce 100644 --- a/src/hugr/validate.rs +++ b/src/hugr/validate.rs @@ -392,6 +392,15 @@ impl<'a> ValidationContext<'a> { let postorder = DfsPostOrder::new(®ion, entry_node); let nodes_visited = postorder.iter(®ion).filter(|n| *n != parent).count(); + let _module_op_count = self + .hugr + .children(parent) + .filter(|n| OpTag::ModuleOp.contains(self.hugr.get_optype(*n).tag())) + .count(); + // XXX TODO FIXME we should take _module_op_count off the child_count below, + // because e.g. Const nodes should not be reached by the DfsPostOrder in a forwards + // traversal. (Currently this would break tests because the traversal goes both forwards + // AND backwards). if nodes_visited != self.hugr.hierarchy.child_count(parent.index) { return Err(ValidationError::NotABoundedDag { node: parent, @@ -731,7 +740,7 @@ mod test { use super::*; use crate::builder::{BuildError, ModuleBuilder}; use crate::builder::{Container, Dataflow, DataflowSubContainer, HugrBuilder}; - use crate::hugr::HugrMut; + use crate::hugr::{HugrError, HugrMut}; use crate::ops::dataflow::IOTrait; use crate::ops::{self, ConstValue, LeafOp, OpType}; use crate::types::{ClassicType, LinearType, Signature}; @@ -1067,6 +1076,123 @@ mod test { ); } + #[test] + fn test_ext_edge() -> Result<(), HugrError> { + let mut h = Hugr::new(ops::DFG { + signature: Signature::new_df(type_row![B, B], type_row![B]), + }); + let input = h.add_op_with_parent(h.root(), ops::Input::new(type_row![B, B]))?; + let output = h.add_op_with_parent(h.root(), ops::Output::new(type_row![B]))?; + // Nested DFG B -> B + let sub_dfg = h.add_op_with_parent( + h.root(), + ops::DFG { + signature: Signature::new_linear(type_row![B]), + }, + )?; + // this Xor has its 2nd input unconnected + let sub_op = { + let sub_input = h.add_op_with_parent(sub_dfg, ops::Input::new(type_row![B]))?; + let sub_output = h.add_op_with_parent(sub_dfg, ops::Output::new(type_row![B]))?; + let sub_op = h.add_op_with_parent(sub_dfg, LeafOp::Xor)?; + h.connect(sub_input, 0, sub_op, 0)?; + h.connect(sub_op, 0, sub_output, 0)?; + sub_op + }; + + h.connect(input, 0, sub_dfg, 0)?; + h.connect(sub_dfg, 0, output, 0)?; + + assert_matches!(h.validate(), Err(ValidationError::UnconnectedPort { .. })); + + h.connect(input, 1, sub_op, 1)?; + assert_matches!( + h.validate(), + Err(ValidationError::InterGraphEdgeError( + InterGraphEdgeError::MissingOrderEdge { .. } + )) + ); + //Order edge. There must be an easier way to do this? + h.connect( + input, + h.get_optype(input) + .other_port_index(Direction::Outgoing) + .unwrap() + .index(), + sub_dfg, + h.get_optype(sub_dfg) + .other_port_index(Direction::Incoming) + .unwrap() + .index(), + )?; + h.validate().unwrap(); + Ok(()) + } + + #[test] + fn test_local_const() -> Result<(), HugrError> { + let mut h = Hugr::new(ops::DFG { + signature: Signature::new_df(type_row![B], type_row![B]), + }); + let input = h.add_op_with_parent(h.root(), ops::Input::new(type_row![B]))?; + let output = h.add_op_with_parent(h.root(), ops::Output::new(type_row![B]))?; + let xor = h.add_op_with_parent(h.root(), LeafOp::Xor)?; + h.connect(input, 0, xor, 0)?; + h.connect(xor, 0, output, 0)?; + assert_eq!( + h.validate(), + Err(ValidationError::UnconnectedPort { + node: xor, + port: Port::new_incoming(1), + port_kind: EdgeKind::Value(B) + }) + ); + // Second input of Xor from a constant + let cst = + h.add_op_with_parent(h.root(), ops::Const(ConstValue::Int { width: 1, value: 1 }))?; + let lcst = h.add_op_with_parent( + h.root(), + ops::LoadConstant { + datatype: ClassicType::Int(1), + }, + )?; + h.connect(cst, 0, lcst, 0)?; + h.connect(lcst, 0, xor, 1)?; + // XXX TODO FIXME This should fail, but succeeds: + h.validate().unwrap(); + // Now include the LoadConstant node in the causal cone + h.connect( + input, + h.get_optype(input) + .other_port_index(Direction::Outgoing) + .unwrap() + .index(), + lcst, + h.get_optype(lcst) + .other_port_index(Direction::Incoming) + .unwrap() + .index(), + )?; + h.validate().unwrap(); + Ok(()) + } + + #[test] + fn test_cyclic() -> Result<(), HugrError> { + let mut h = Hugr::new(ops::DFG { + signature: Signature::new_df(type_row![Q], type_row![Q]), + }); + let input = h.add_op_with_parent(h.root(), ops::Input::new(type_row![Q]))?; + let output = h.add_op_with_parent(h.root(), ops::Output::new(type_row![Q]))?; + let cx = h.add_op_with_parent(h.root(), LeafOp::CX)?; + h.connect(input, 0, cx, 0)?; + h.connect(cx, 0, output, 0)?; + h.connect(cx, 1, cx, 1)?; + // TODO FIXME We should get an error here, but this passes ATM :-( + h.validate().unwrap(); + Ok(()) + } + #[test] /// A wire with no resource requirements is wired into a node which has /// [A,B] resources required on its inputs and outputs. This could be fixed