@@ -37,8 +37,45 @@ impl<O: Clone + PartialEq, A: Clone> OpenHypergraph<O, A> {
3737 /// means that the quotient attempted to identify nodes with different
3838 /// labels; the returned finite function is the quotient witness, as for
3939 /// [`OpenHypergraph::quotient`].
40- pub fn spiderize ( mut self ) -> Result < OpenHypergraph < O , WithSpider < A > > , FiniteFunction > {
41- self . quotient ( ) ?;
40+ pub fn spiderize ( self ) -> Result < OpenHypergraph < O , WithSpider < A > > , FiniteFunction > {
41+ let nodes: Vec < NodeId > = ( 0 ..self . hypergraph . nodes . len ( ) ) . map ( NodeId ) . collect ( ) ;
42+ self . spiderize_nodes ( & nodes)
43+ }
44+
45+ /// Replace the chosen nodes' implicit wiring with explicit spider
46+ /// operations.
47+ ///
48+ /// Each selected node is replaced by the same pair of spiders used by
49+ /// [`Self::spiderize`]. Unselected nodes retain their implicit wiring.
50+ /// Consequently, unlike [`Self::spiderize`], this operation does not by
51+ /// itself guarantee that the result is acyclic or monogamous.
52+ ///
53+ /// `nodes` contains IDs from the input hypergraph. Pending identifications
54+ /// are applied first, and IDs in `nodes` are mapped through the resulting
55+ /// quotient. Selecting any representative therefore selects its complete
56+ /// equivalence class. Duplicate selections are ignored.
57+ ///
58+ /// # Panics
59+ ///
60+ /// Panics if a selected node ID is out of bounds.
61+ pub fn spiderize_nodes (
62+ mut self ,
63+ nodes : & [ NodeId ] ,
64+ ) -> Result < OpenHypergraph < O , WithSpider < A > > , FiniteFunction > {
65+ let input_node_count = self . hypergraph . nodes . len ( ) ;
66+ for node in nodes {
67+ assert ! (
68+ node. 0 < input_node_count,
69+ "node id {:?} is out of bounds" ,
70+ node
71+ ) ;
72+ }
73+
74+ let quotient = self . quotient ( ) ?;
75+ let mut selected = vec ! [ false ; quotient. target] ;
76+ for node in nodes {
77+ selected[ quotient. table [ node. 0 ] ] = true ;
78+ }
4279
4380 let OpenHypergraph {
4481 sources,
@@ -75,19 +112,27 @@ impl<O: Clone + PartialEq, A: Clone> OpenHypergraph<O, A> {
75112 . sources
76113 . into_iter ( )
77114 . map ( |node| {
78- let occurrence = result. new_node ( hypergraph. nodes [ node. 0 ] . clone ( ) ) ;
79- left_targets[ node. 0 ] . push ( occurrence) ;
80- occurrence
115+ if selected[ node. 0 ] {
116+ let occurrence = result. new_node ( hypergraph. nodes [ node. 0 ] . clone ( ) ) ;
117+ left_targets[ node. 0 ] . push ( occurrence) ;
118+ occurrence
119+ } else {
120+ central[ node. 0 ]
121+ }
81122 } )
82123 . collect ( ) ;
83124
84125 let operation_targets = adjacency
85126 . targets
86127 . into_iter ( )
87128 . map ( |node| {
88- let occurrence = result. new_node ( hypergraph. nodes [ node. 0 ] . clone ( ) ) ;
89- right_sources[ node. 0 ] . push ( occurrence) ;
90- occurrence
129+ if selected[ node. 0 ] {
130+ let occurrence = result. new_node ( hypergraph. nodes [ node. 0 ] . clone ( ) ) ;
131+ right_sources[ node. 0 ] . push ( occurrence) ;
132+ occurrence
133+ } else {
134+ central[ node. 0 ]
135+ }
91136 } )
92137 . collect ( ) ;
93138
@@ -103,27 +148,40 @@ impl<O: Clone + PartialEq, A: Clone> OpenHypergraph<O, A> {
103148 result. sources = sources
104149 . into_iter ( )
105150 . map ( |node| {
106- let occurrence = result. new_node ( hypergraph. nodes [ node. 0 ] . clone ( ) ) ;
107- left_sources[ node. 0 ] . push ( occurrence) ;
108- occurrence
151+ if selected[ node. 0 ] {
152+ let occurrence = result. new_node ( hypergraph. nodes [ node. 0 ] . clone ( ) ) ;
153+ left_sources[ node. 0 ] . push ( occurrence) ;
154+ occurrence
155+ } else {
156+ central[ node. 0 ]
157+ }
109158 } )
110159 . collect ( ) ;
111160
112161 result. targets = targets
113162 . into_iter ( )
114163 . map ( |node| {
115- let occurrence = result. new_node ( hypergraph. nodes [ node. 0 ] . clone ( ) ) ;
116- right_targets[ node. 0 ] . push ( occurrence) ;
117- occurrence
164+ if selected[ node. 0 ] {
165+ let occurrence = result. new_node ( hypergraph. nodes [ node. 0 ] . clone ( ) ) ;
166+ right_targets[ node. 0 ] . push ( occurrence) ;
167+ occurrence
168+ } else {
169+ central[ node. 0 ]
170+ }
118171 } )
119172 . collect ( ) ;
120173
121- for ( ( ( left_sources, left_targets) , right_sources) , right_targets) in left_sources
174+ for ( node , ( ( ( left_sources, left_targets) , right_sources) , right_targets) ) in left_sources
122175 . into_iter ( )
123176 . zip ( left_targets)
124177 . zip ( right_sources)
125178 . zip ( right_targets)
179+ . enumerate ( )
126180 {
181+ if !selected[ node] {
182+ continue ;
183+ }
184+
127185 result. new_edge (
128186 WithSpider :: Spider {
129187 sources : left_sources. len ( ) ,
0 commit comments