@@ -241,16 +241,13 @@ fn test_sat_based_reductions() {
241241 let graph = ReductionGraph :: new ( ) ;
242242
243243 // SAT -> IS
244- assert ! ( graph
245- . has_direct_reduction:: <Satisfiability , MaximumIndependentSet <SimpleGraph , i32 >>( ) ) ;
244+ assert ! ( graph. has_direct_reduction:: <Satisfiability , MaximumIndependentSet <SimpleGraph , i32 >>( ) ) ;
246245
247246 // SAT -> KColoring
248247 assert ! ( graph. has_direct_reduction:: <Satisfiability , KColoring <3 , SimpleGraph , i32 >>( ) ) ;
249248
250249 // SAT -> MinimumDominatingSet
251- assert ! (
252- graph. has_direct_reduction:: <Satisfiability , MinimumDominatingSet <SimpleGraph , i32 >>( )
253- ) ;
250+ assert ! ( graph. has_direct_reduction:: <Satisfiability , MinimumDominatingSet <SimpleGraph , i32 >>( ) ) ;
254251}
255252
256253#[ test]
@@ -832,9 +829,131 @@ fn test_concrete_variant_nodes_in_json() {
832829 } ) ;
833830 assert ! ( mis_unitdisk, "MIS/UnitDiskGraph node should exist" ) ;
834831
835- let maxcut_gridgraph = json. nodes . iter ( ) . any ( |n| {
836- n . name == "MaxCut"
837- && n . variant . get ( "graph" ) == Some ( & "GridGraph" . to_string ( ) )
838- } ) ;
832+ let maxcut_gridgraph = json
833+ . nodes
834+ . iter ( )
835+ . any ( |n| n . name == "MaxCut" && n . variant . get ( "graph" ) == Some ( & "GridGraph" . to_string ( ) ) ) ;
839836 assert ! ( maxcut_gridgraph, "MaxCut/GridGraph node should exist" ) ;
840837}
838+
839+ #[ test]
840+ fn test_natural_edge_graph_relaxation ( ) {
841+ let graph = ReductionGraph :: new ( ) ;
842+ let json = graph. to_json ( ) ;
843+
844+ // MIS/GridGraph -> MIS/SimpleGraph should exist (graph type relaxation)
845+ let has_edge = json. edges . iter ( ) . any ( |e| {
846+ e. source . name == "MaximumIndependentSet"
847+ && e. target . name == "MaximumIndependentSet"
848+ && e. source . variant . get ( "graph" ) == Some ( & "GridGraph" . to_string ( ) )
849+ && e. target . variant . get ( "graph" ) == Some ( & "SimpleGraph" . to_string ( ) )
850+ } ) ;
851+ assert ! (
852+ has_edge,
853+ "Natural edge MIS/GridGraph -> MIS/SimpleGraph should exist"
854+ ) ;
855+ }
856+
857+ #[ test]
858+ fn test_natural_edge_gridgraph_to_unitdisk ( ) {
859+ let graph = ReductionGraph :: new ( ) ;
860+ let json = graph. to_json ( ) ;
861+
862+ // MIS/GridGraph -> MIS/UnitDiskGraph should exist
863+ let has_edge = json. edges . iter ( ) . any ( |e| {
864+ e. source . name == "MaximumIndependentSet"
865+ && e. target . name == "MaximumIndependentSet"
866+ && e. source . variant . get ( "graph" ) == Some ( & "GridGraph" . to_string ( ) )
867+ && e. target . variant . get ( "graph" ) == Some ( & "UnitDiskGraph" . to_string ( ) )
868+ } ) ;
869+ assert ! (
870+ has_edge,
871+ "Natural edge MIS/GridGraph -> MIS/UnitDiskGraph should exist"
872+ ) ;
873+ }
874+
875+ #[ test]
876+ fn test_natural_edge_weight_promotion ( ) {
877+ let graph = ReductionGraph :: new ( ) ;
878+ let json = graph. to_json ( ) ;
879+
880+ // MIS{SimpleGraph, Unweighted} -> MIS{SimpleGraph, i32} should exist
881+ let has_edge = json. edges . iter ( ) . any ( |e| {
882+ e. source . name == "MaximumIndependentSet"
883+ && e. target . name == "MaximumIndependentSet"
884+ && e. source . variant . get ( "graph" ) == Some ( & "SimpleGraph" . to_string ( ) )
885+ && e. target . variant . get ( "graph" ) == Some ( & "SimpleGraph" . to_string ( ) )
886+ && e. source . variant . get ( "weight" ) == Some ( & "Unweighted" . to_string ( ) )
887+ && e. target . variant . get ( "weight" ) == Some ( & "i32" . to_string ( ) )
888+ } ) ;
889+ assert ! (
890+ has_edge,
891+ "Natural edge MIS/Unweighted -> MIS/i32 should exist"
892+ ) ;
893+ }
894+
895+ #[ test]
896+ fn test_no_natural_edge_wrong_direction ( ) {
897+ let graph = ReductionGraph :: new ( ) ;
898+ let json = graph. to_json ( ) ;
899+
900+ // MIS/SimpleGraph -> MIS/GridGraph should NOT exist (wrong direction)
901+ let has_edge = json. edges . iter ( ) . any ( |e| {
902+ e. source . name == "MaximumIndependentSet"
903+ && e. target . name == "MaximumIndependentSet"
904+ && e. source . variant . get ( "graph" ) == Some ( & "SimpleGraph" . to_string ( ) )
905+ && e. target . variant . get ( "graph" ) == Some ( & "GridGraph" . to_string ( ) )
906+ } ) ;
907+ assert ! (
908+ !has_edge,
909+ "Should NOT have MIS/SimpleGraph -> MIS/GridGraph"
910+ ) ;
911+ }
912+
913+ #[ test]
914+ fn test_no_natural_self_edge ( ) {
915+ let graph = ReductionGraph :: new ( ) ;
916+ let json = graph. to_json ( ) ;
917+
918+ // No self-edges (same variant to same variant)
919+ for edge in & json. edges {
920+ if edge. source . name == edge. target . name {
921+ assert ! (
922+ edge. source. variant != edge. target. variant,
923+ "Should not have self-edge: {} {:?}" ,
924+ edge. source. name,
925+ edge. source. variant
926+ ) ;
927+ }
928+ }
929+ }
930+
931+ #[ test]
932+ fn test_natural_edge_has_identity_overhead ( ) {
933+ let graph = ReductionGraph :: new ( ) ;
934+ let json = graph. to_json ( ) ;
935+
936+ // Find a natural edge and verify its overhead is identity (field == formula)
937+ let natural_edge = json. edges . iter ( ) . find ( |e| {
938+ e. source . name == "MaximumIndependentSet"
939+ && e. target . name == "MaximumIndependentSet"
940+ && e. source . variant . get ( "graph" ) == Some ( & "GridGraph" . to_string ( ) )
941+ && e. target . variant . get ( "graph" ) == Some ( & "SimpleGraph" . to_string ( ) )
942+ && e. source . variant . get ( "weight" ) == Some ( & "Unweighted" . to_string ( ) )
943+ && e. target . variant . get ( "weight" ) == Some ( & "Unweighted" . to_string ( ) )
944+ } ) ;
945+ assert ! ( natural_edge. is_some( ) , "Natural edge should exist" ) ;
946+ let edge = natural_edge. unwrap ( ) ;
947+ // Overhead should be identity: each field maps to itself
948+ assert ! (
949+ !edge. overhead. is_empty( ) ,
950+ "Natural edge should have identity overhead"
951+ ) ;
952+ for o in & edge. overhead {
953+ assert_eq ! (
954+ o. field, o. formula,
955+ "Natural edge overhead should be identity: {} != {}" ,
956+ o. field, o. formula
957+ ) ;
958+ }
959+ }
0 commit comments