Skip to content

Commit 4de73ff

Browse files
yanntmclaude
andcommitted
NetBlocks: invalidate per block, so a dropped transition costs the arc count alone and a fused free component leaves the state and token records intact; findFreeSCC records what each surviving place stands for (PCOEF) and runs under STATESPACE only when a record is there to absorb it
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent beab0f1 commit 4de73ff

3 files changed

Lines changed: 115 additions & 10 deletions

File tree

fr.lip6.move.gal.structural/src/fr/lip6/move/gal/structural/NetBlocks.java

Lines changed: 83 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.HashSet;
66
import java.util.List;
77
import java.util.Map;
8+
import java.util.Map.Entry;
89
import java.util.Set;
910

1011
import 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) {

fr.lip6.move.gal.structural/src/fr/lip6/move/gal/structural/SparsePetriNet.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,13 @@ public void clearBlocks() {
464464
}
465465

466466
/** Forget the blocks, saying why: for a rule that cannot maintain them. */
467+
@Override
468+
public void removeBlock(NetBlock block, String why) {
469+
if (blocks != null && blocks.remove(block) != null) {
470+
System.out.println("Counting record: " + block.blockName() + " dropped, " + why);
471+
}
472+
}
473+
467474
@Override
468475
public void clearBlocks(String why) {
469476
if (blocks != null) {

fr.lip6.move.gal.structural/src/fr/lip6/move/gal/structural/StructuralReduction.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ public void putBlock(NetBlock block, IntMatrixCol matrix) {
129129
* A consumer then leaves the value unanswered instead of reading a stale
130130
* number.
131131
*/
132+
@Override
133+
public void removeBlock(NetBlock block, String why) {
134+
if (blocks != null && blocks.remove(block) != null) {
135+
System.out.println("Counting record: " + block.blockName() + " dropped, " + why);
136+
}
137+
}
138+
132139
@Override
133140
public void clearBlocks(String why) {
134141
if (blocks != null) {
@@ -197,6 +204,15 @@ public int reduce(ReductionType rt) throws NoDeadlockExists, DeadlockFound {
197204
total += ruleReducePlaces(rt, false, false);
198205
total += ruleReduceTrans(rt);
199206
total += ruleRedundantCompositions(rt);
207+
if (hasAnyBlock()) {
208+
// a free component fuses into one place holding its total, and
209+
// what that place stands for is recorded: worth doing precisely
210+
// because someone is counting, and skipped otherwise since the
211+
// state count of the fused net is not the state count asked for
212+
if (findFreeSCC(rt)) {
213+
total++;
214+
}
215+
}
200216
total += ruleReducePlaces(rt, false, false);
201217
total += ruleReduceTrans(rt);
202218
return total;
@@ -2846,6 +2862,10 @@ public boolean findFreeSCC(ReductionType rt) {
28462862
if (rt == ReductionType.LTL) {
28472863
return false;
28482864
}
2865+
// with a counting record, what each surviving place stands for is
2866+
// recorded (NetBlocks.freeComponentsFused); without one, nothing is
2867+
// tracked and nothing is allocated
2868+
Map<Integer, Integer> mergedInto = hasAnyBlock() ? new HashMap<>() : null;
28492869
long time = System.currentTimeMillis();
28502870
// extract simple transitions to a PxP matrix
28512871
int nbP = pnames.size();
@@ -2903,10 +2923,15 @@ public boolean findFreeSCC(ReductionType rt) {
29032923
}
29042924
marks.set(kept, marks.get(kept) + marks.get(other));
29052925
tokill.add(other);
2926+
if (mergedInto != null) {
2927+
mergedInto.put(other, kept);
2928+
}
29062929
}
29072930
}
29082931

29092932
// at this stage, the other places in each SCC are now redundant, kill them
2933+
// what the surviving places now stand for, before the indices move
2934+
NetBlocks.freeComponentsFused(this, pnames.size(), mergedInto == null ? Collections.emptyMap() : mergedInto);
29102935
tokill.sort((a, b) -> -a.compareTo(b));
29112936
tflowPT = flowPT.transpose();
29122937
tflowTP = flowTP.transpose();

0 commit comments

Comments
 (0)