55import java .util .HashSet ;
66import java .util .List ;
77import java .util .Map ;
8+ import java .util .Map .Entry ;
89import java .util .Set ;
910
1011import android .util .SparseIntArray ;
@@ -31,6 +32,9 @@ public interface Holder {
3132 void putBlock (NetBlock block , IntMatrixCol matrix );
3233
3334 void clearBlocks (String why );
35+
36+ /** Forget one block, keeping the others. */
37+ void removeBlock (NetBlock block , String why );
3438 }
3539
3640 private NetBlocks () {
@@ -47,29 +51,36 @@ private NetBlocks() {
4751 */
4852 public static void transitionsFused (Holder net , int count , List <Integer > dropped ,
4953 Map <Integer , Integer > survivorOf ) {
50- if (! net . hasAnyBlock () || dropped . isEmpty ( )) {
54+ if (dropped . isEmpty () || ! tracksArcs ( net )) {
5155 return ;
5256 }
5357 IntMatrixCol next = fuse (net .getBlock (NetBlock .TMULT ), count , dropped , survivorOf );
5458 if (next == null ) {
55- net .clearBlocks ("a fusion of " + dropped .size () + " duplicate transitions it could not follow" );
59+ net .removeBlock (NetBlock .TMULT ,
60+ "a fusion of " + dropped .size () + " duplicate transitions it could not follow" );
5661 } else {
5762 net .putBlock (NetBlock .TMULT , next );
5863 }
5964 }
6065
6166 /**
6267 * Transitions were removed and no surviving transition stands for them, so
63- * the arcs they contributed to the graph a record counts are gone with
64- * them: the record cannot be maintained. Keeping their guards and weights
65- * instead (the ghost contributors of HSC_PLAN.md section 11) is what would
66- * let it survive.
68+ * the arcs they contributed are gone with them and only the arc block goes:
69+ * a state or token record is untouched by a transition disappearing.
70+ * Keeping their guards and weights instead (the ghost contributors of
71+ * HSC_PLAN.md section 11) is what would let the arc block survive too .
6772 */
6873 public static void transitionsDropped (Holder net , int howMany , String why ) {
69- if (! net . hasAnyBlock () || howMany == 0 ) {
74+ if (howMany == 0 || ! tracksArcs ( net ) ) {
7075 return ;
7176 }
72- net .clearBlocks (howMany + " transitions removed (" + why + ") whose arcs it cannot account for" );
77+ net .removeBlock (NetBlock .TMULT ,
78+ howMany + " transitions removed (" + why + ") whose arcs it cannot account for" );
79+ }
80+
81+ /** Is an arc count being tracked? Only then do arc-destroying rules matter. */
82+ private static boolean tracksArcs (Holder net ) {
83+ return net .hasAnyBlock () && net .getBlock (NetBlock .TMULT ) != null ;
7384 }
7485
7586 /**
@@ -82,7 +93,7 @@ public static void transitionsDropped(Holder net, int howMany, String why) {
8293 * there to be counted.
8394 */
8495 public static boolean mayDropNoEffect (Holder net ) {
85- if (!net . hasAnyBlock ( )) {
96+ if (!tracksArcs ( net )) {
8697 return true ;
8798 }
8899 System .out .println ("Keeping the transitions with no effect: a counting record needs their arcs." );
@@ -99,7 +110,7 @@ public static boolean mayDropNoEffect(Holder net) {
99110 * paths and buys little.
100111 */
101112 public static boolean mayComposeRedundant (Holder net ) {
102- if (!net . hasAnyBlock ( )) {
113+ if (!tracksArcs ( net )) {
103114 return true ;
104115 }
105116 System .out .println ("Skipping the redundant composition rule: a counting record needs the arcs it removes." );
@@ -173,6 +184,68 @@ public static void deadTransitionsDropped(Holder net, int count, Collection<Inte
173184 net .putBlock (NetBlock .TMULT , next );
174185 }
175186
187+ /**
188+ * Free components were fused: each surviving place now stands for the sum
189+ * of what its component's places stood for, and the places merged into it
190+ * are gone. The arc count cannot follow — the component's internal moves
191+ * are not the moves of the fused net — so {@link NetBlock#TMULT} goes
192+ * while {@link NetBlock#PCOEF} takes the new coefficients.
193+ *
194+ * @param placeCount the place count before the removals
195+ * @param mergedInto for each removed place, the place that absorbed it
196+ */
197+ public static void freeComponentsFused (Holder net , int placeCount , Map <Integer , Integer > mergedInto ) {
198+ if (!net .hasAnyBlock () || mergedInto .isEmpty ()) {
199+ return ;
200+ }
201+ // the component's internal moves become self-loops here, so the arcs of
202+ // this net are no longer the arcs of the one asked about; the token and
203+ // state records stay valid, and the self-loops become free to remove
204+ net .removeBlock (NetBlock .TMULT ,
205+ "free components were fused, and the moves inside them are not the moves of this net" );
206+ long [] coeff = new long [placeCount ];
207+ Arrays .fill (coeff , 1L );
208+ IntMatrixCol pcoef = net .getBlock (NetBlock .PCOEF );
209+ if (pcoef != null && pcoef .getColumnCount () > 0 ) {
210+ SparseIntArray col = pcoef .getColumn (0 );
211+ for (int i = 0 , ie = col .size (); i < ie ; i ++) {
212+ coeff [col .keyAt (i )] = 1L + col .valueAt (i );
213+ }
214+ }
215+ Set <Integer > gone = new HashSet <>(mergedInto .keySet ());
216+ for (Entry <Integer , Integer > e : mergedInto .entrySet ()) {
217+ // the absorbing place may itself be absorbed further up the chain
218+ int root = e .getValue ();
219+ while (gone .contains (root )) {
220+ Integer next = mergedInto .get (root );
221+ if (next == null || next .intValue () == root ) {
222+ net .clearBlocks ("a fused free component has no surviving place" );
223+ return ;
224+ }
225+ root = next .intValue ();
226+ }
227+ coeff [root ] += coeff [e .getKey ()];
228+ if (coeff [root ] > Integer .MAX_VALUE ) {
229+ net .clearBlocks ("a place coefficient outgrew an int" );
230+ return ;
231+ }
232+ }
233+ IntMatrixCol next = new IntMatrixCol (placeCount - gone .size (), 0 );
234+ SparseIntArray kept = new SparseIntArray ();
235+ int index = 0 ;
236+ for (int p = 0 ; p < placeCount ; p ++) {
237+ if (gone .contains (p )) {
238+ continue ;
239+ }
240+ if (coeff [p ] != 1L ) {
241+ kept .append (index , (int ) (coeff [p ] - 1L ));
242+ }
243+ index ++;
244+ }
245+ next .appendColumn (kept );
246+ net .putBlock (NetBlock .PCOEF , next );
247+ }
248+
176249 /** TMULT after a fusion, or null when the fusion cannot be followed. */
177250 private static IntMatrixCol fuse (IntMatrixCol tmult , int count , List <Integer > dropped ,
178251 Map <Integer , Integer > survivorOf ) {
0 commit comments