@@ -7,11 +7,11 @@ use hugr::builder::{
77 Container , Dataflow , DataflowHugr , DataflowSubContainer , FunctionBuilder , HugrBuilder ,
88 ModuleBuilder , SubContainer ,
99} ;
10- use hugr:: extension:: prelude:: { bool_t, qb_t} ;
10+ use hugr:: extension:: prelude:: { bool_t, option_type , qb_t, UnwrapBuilder } ;
1111
1212use hugr:: hugr:: hugrmut:: HugrMut ;
1313use hugr:: ops:: handle:: FuncID ;
14- use hugr:: ops:: { OpParent , Value } ;
14+ use hugr:: ops:: { OpParent , OpType , Value } ;
1515use hugr:: std_extensions:: arithmetic:: float_ops:: FloatOps ;
1616use hugr:: types:: Signature ;
1717use hugr:: HugrView ;
@@ -27,8 +27,9 @@ use crate::extension::bool::BoolOp;
2727use crate :: extension:: rotation:: { rotation_type, ConstRotation , RotationOp } ;
2828use crate :: extension:: sympy:: SympyOpDef ;
2929use crate :: extension:: TKET1_EXTENSION_ID ;
30+ use crate :: serialize:: pytket:: extension:: OpaqueTk1Op ;
3031use crate :: serialize:: pytket:: {
31- DecodeInsertionTarget , DecodeOptions , EncodeOptions , EncodedCircuit ,
32+ DecodeInsertionTarget , DecodeOptions , EncodeOptions , EncodedCircuit , PytketEncodeOpError ,
3233} ;
3334use crate :: TketOp ;
3435
@@ -277,6 +278,51 @@ fn circ_parameterized() -> Circuit {
277278 hugr. into ( )
278279}
279280
281+ /// A circuit with a nested unsupported operation.
282+ ///
283+ /// Tries to allocate a qubit, and panics if it fails.
284+ /// This creates an unsupported conditional inside the region.
285+ #[ fixture]
286+ fn circ_flat_opaque ( ) -> Circuit {
287+ let input_t = vec ! [ qb_t( ) , qb_t( ) ] ;
288+ let output_t = vec ! [ qb_t( ) , qb_t( ) ] ;
289+ let mut h = FunctionBuilder :: new ( "flat_opaque" , Signature :: new ( input_t, output_t) ) . unwrap ( ) ;
290+
291+ let [ q1, q2] = h. input_wires_arr ( ) ;
292+
293+ // An unsupported tk1-only operation.
294+ let mut tk1op = tket_json_rs:: circuit_json:: Operation :: default ( ) ;
295+ tk1op. op_type = tket_json_rs:: optype:: OpType :: CH ;
296+ tk1op. n_qb = Some ( 2 ) ;
297+ let op: OpType = OpaqueTk1Op :: new_from_op ( & tk1op, 2 , 0 )
298+ . as_extension_op ( )
299+ . into ( ) ;
300+ let [ q1, q2] = h. add_dataflow_op ( op, [ q1, q2] ) . unwrap ( ) . outputs_arr ( ) ;
301+
302+ let hugr = h. finish_hugr_with_outputs ( [ q1, q2] ) . unwrap ( ) ;
303+ hugr. into ( )
304+ }
305+
306+ /// A circuit with a nested unsupported operation.
307+ ///
308+ /// Tries to allocate a qubit, and panics if it fails.
309+ /// This creates an unsupported conditional inside the region.
310+ #[ fixture]
311+ fn circ_nested_opaque ( ) -> Circuit {
312+ let input_t = vec ! [ ] ;
313+ let output_t = vec ! [ qb_t( ) ] ;
314+ let mut h = FunctionBuilder :: new ( "nested_opaque" , Signature :: new ( input_t, output_t) ) . unwrap ( ) ;
315+
316+ let [ maybe_q] = h
317+ . add_dataflow_op ( TketOp :: TryQAlloc , [ ] )
318+ . unwrap ( )
319+ . outputs_arr ( ) ;
320+ let [ q] = h. build_unwrap_sum ( 1 , option_type ( qb_t ( ) ) , maybe_q) . unwrap ( ) ;
321+
322+ let hugr = h. finish_hugr_with_outputs ( [ q] ) . unwrap ( ) ;
323+ hugr. into ( )
324+ }
325+
280326/// A circuit with a recursive function call.
281327#[ fixture]
282328fn circ_recursive ( ) -> Circuit {
@@ -606,6 +652,7 @@ fn json_file_roundtrip(#[case] circ: impl AsRef<std::path::Path>) {
606652#[ case:: preset_qubits( circ_preset_qubits( ) , 1 ) ]
607653#[ case:: preset_parameterized( circ_parameterized( ) , 1 ) ]
608654#[ case:: nested_dfgs( circ_nested_dfgs( ) , 1 ) ]
655+ #[ case:: flat_opaque( circ_flat_opaque( ) , 1 ) ]
609656fn circuit_standalone_roundtrip ( #[ case] circ : Circuit , #[ case] num_circuits : usize ) {
610657 let circ_signature = circ. circuit_signature ( ) . into_owned ( ) ;
611658
@@ -639,12 +686,35 @@ fn circuit_standalone_roundtrip(#[case] circ: Circuit, #[case] num_circuits: usi
639686 compare_serial_circs ( ser, & reser) ;
640687}
641688
689+ /// Test that more complex unsupported subgraphs (nested structure, non-local edges) are rejected when encoding a standalone circuit.
690+ #[ rstest]
691+ //#[case::nested_opaque(circ_nested_opaque())] TODO: Raises a different error
692+ #[ case:: global_defs( circ_global_defs( ) ) ]
693+ #[ case:: recursive( circ_recursive( ) ) ]
694+ fn test_complex_unsupported_subgraphs ( #[ case] circ : Circuit ) {
695+ use cool_asserts:: assert_matches;
696+
697+ use crate :: serialize:: pytket:: PytketEncodeError ;
698+
699+ println ! ( "{}" , circ. mermaid_string( ) ) ;
700+
701+ let try_encoded = EncodedCircuit :: new_standalone ( & circ, EncodeOptions :: new ( ) ) ;
702+ assert_matches ! (
703+ try_encoded,
704+ Err ( PytketEncodeError :: OpEncoding (
705+ PytketEncodeOpError :: UnsupportedStandaloneSubgraph { .. }
706+ ) )
707+ ) ;
708+ }
709+
642710/// Test the serialisation roundtrip from a tket circuit into an EncodedCircuit and back.
643711#[ rstest]
644712#[ case:: meas_ancilla( circ_measure_ancilla( ) , 1 ) ]
645713#[ case:: preset_qubits( circ_preset_qubits( ) , 1 ) ]
646714#[ case:: preset_parameterized( circ_parameterized( ) , 1 ) ]
647715#[ case:: nested_dfgs( circ_nested_dfgs( ) , 1 ) ]
716+ #[ case:: flat_opaque( circ_flat_opaque( ) , 1 ) ]
717+ //#[case::nested_opaque(circ_nested_opaque(), 1)] TODO: Raises a different error
648718#[ case:: global_defs( circ_global_defs( ) , 1 ) ]
649719#[ case:: recursive( circ_recursive( ) , 1 ) ]
650720// TODO: fix edge case: non-local edge from an unsupported node inside a nested CircBox
0 commit comments