Skip to content

Commit 72524f6

Browse files
reverted some refactorings and extended comments
1 parent 9908faf commit 72524f6

File tree

7 files changed

+73
-64
lines changed

7 files changed

+73
-64
lines changed

algorithms/active/sparse/src/main/java/de/learnlib/algorithm/sparse/CoreRow.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,28 @@
2222

2323
import net.automatalib.word.Word;
2424

25+
/**
26+
* Each core row represents some hypothesis state and stores its outputs for all table suffixes.
27+
*/
2528
class CoreRow<S, I, O> extends Row<S, I, O> {
2629

2730
/**
2831
* Hypothesis state associated with this row.
2932
*/
3033
final S state;
34+
3135
/**
32-
* Index in core row list.
36+
* Index of this row in the core row list.
3337
*/
3438
final int idx;
39+
3540
/**
36-
* Maps suffixes to the outputs contained in this row.
41+
* Maps suffixes to their outputs.
3742
*/
3843
final Map<Word<I>, Word<O>> sufToOut;
44+
3945
/**
40-
* Also store identifiers of suffix-output pairs for fast compatibility checking.
46+
* Identifiers of all suffix-output pairs in this row, used for fast compatibility checking.
4147
*/
4248
final Set<Integer> cellIds;
4349

@@ -46,7 +52,7 @@ class CoreRow<S, I, O> extends Row<S, I, O> {
4652
this.state = state;
4753
this.idx = idx;
4854
sufToOut = new HashMap<>();
49-
cellIds = new HashSet<>();
55+
cellIds = new HashSet<>(); // use HashSet to enable fast containment checks
5056
}
5157

5258
void addSuffix(Word<I> suf, Word<O> out, int cell) {

algorithms/active/sparse/src/main/java/de/learnlib/algorithm/sparse/FringeRow.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
import net.automatalib.word.Word;
1919

2020
/**
21-
* Each fringe row represents a hypothesis transition.
21+
* Each fringe row represents some hypothesis transition
22+
* outside the spanning tree defined by the core prefixes.
2223
*
2324
* @param <S>
2425
* state type
@@ -33,17 +34,21 @@ class FringeRow<S, I, O> extends Row<S, I, O> {
3334
* Source state.
3435
*/
3536
final S srcState;
37+
3638
/**
3739
* Input symbol.
3840
*/
3941
final I transIn;
42+
4043
/**
4144
* Output symbol (determined dynamically).
4245
*/
4346
O transOut;
47+
4448
/**
45-
* For compression, fringe rows do not store observations directly. Instead, they point to some leaf in a tree
46-
* encoding their classification history. This trick avoids redundantly storing identical observations.
49+
* For compression, fringe rows do not store observations directly.
50+
* Instead, they point to some leaf in a tree encoding their classification history.
51+
* This trick avoids redundantly storing identical observations.
4752
*/
4853
Leaf<S, I, O> leaf;
4954

algorithms/active/sparse/src/main/java/de/learnlib/algorithm/sparse/GenericSparseLearner.java

Lines changed: 37 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@
2121
import java.util.Collections;
2222
import java.util.Deque;
2323
import java.util.HashMap;
24-
import java.util.HashSet;
2524
import java.util.List;
2625
import java.util.Map;
27-
import java.util.Set;
2826
import java.util.function.Function;
27+
import java.util.stream.Collectors;
2928

3029
import de.learnlib.algorithm.LearningAlgorithm.MealyLearner;
3130
import de.learnlib.counterexample.LocalSuffixFinders;
@@ -35,57 +34,66 @@
3534
import net.automatalib.alphabet.Alphabet;
3635
import net.automatalib.automaton.transducer.MealyMachine;
3736
import net.automatalib.automaton.transducer.MutableMealyMachine;
38-
import net.automatalib.common.util.HashUtil;
3937
import net.automatalib.common.util.Pair;
4038
import net.automatalib.word.Word;
4139

4240
class GenericSparseLearner<S, I, O> implements MealyLearner<I, O> {
4341

4442
private final Alphabet<I> alphabet;
4543
private final MealyMembershipOracle<I, O> oracle;
44+
4645
/**
4746
* Suffixes.
4847
*/
4948
private final Deque<Word<I>> sufs;
49+
5050
/**
5151
* Core rows.
5252
*/
5353
private final List<CoreRow<S, I, O>> cRows;
54+
5455
/**
5556
* Fringe rows.
5657
*/
5758
private final Deque<FringeRow<S, I, O>> fRows;
59+
5860
/**
59-
* Fringe prefix to row.
61+
* Maps fringe prefixes to rows.
6062
*/
6163
private final Map<Word<I>, FringeRow<S, I, O>> prefToFringe;
64+
6265
/**
63-
* List of unique cells.
66+
* List of unique suffix-output cells.
6467
*/
6568
private final List<Pair<Word<I>, Word<O>>> cells;
69+
6670
/**
67-
* Maps each unique cell to its list index.
71+
* Maps each suffix-output cell to its list index.
6872
*/
6973
private final Map<Pair<Word<I>, Word<O>>, Integer> cellToIdx;
74+
7075
/**
7176
* Hypothesis.
7277
*/
7378
private final MutableMealyMachine<S, I, ?, O> hyp;
79+
7480
/**
7581
* Maps each state to its core row prefix.
7682
*/
7783
private final Map<S, Word<I>> stateToPrefix;
84+
7885
/**
79-
* Access sequences.
86+
* Computes access sequences.
8087
*/
8188
private final Function<Word<I>, Word<I>> accSeq;
8289

8390
/**
84-
* For fast suffix ranking, we track for each suffix how the core rows are partitioned by it.
91+
* For fast suffix ranking, this map stores the core row partitions created by each suffix.
8592
*/
8693
private final Map<Word<I>, List<BitSet>> sufToVecs;
94+
8795
/**
88-
* See {@link #sufToVecs}.
96+
* Helper map for efficiently constructing the suffix partition map (see {@link #sufToVecs}).
8997
*/
9098
private final Map<Word<I>, Map<Word<O>, Integer>> sufToOutToIdx;
9199

@@ -122,7 +130,8 @@ public void startLearning() {
122130
stateToPrefix.put(init, c.prefix);
123131
extendFringe(c, init, new Leaf<>(c, 1, sufs.size(), Collections.emptyList()));
124132
fRows.forEach(f -> query(f, Word.epsilon())); // query transition outputs
125-
// initially, transition outputs must be queried manually for later transitions, they derive from suffix queries
133+
// initially, transition outputs must be queried manually,
134+
// for later transitions, they derive from suffix queries
126135
updateHypothesis();
127136
}
128137

@@ -188,7 +197,8 @@ private Word<I> pickSuffix(BitSet remRows) {
188197

189198
assert sumOccur == remRows.cardinality();
190199
if (maxOccur < bestRank) {
191-
// among equally ranked suffixes, pick youngest (mind that suffixes are stored/iterated LIFO)
200+
// among equally ranked suffixes, pick youngest
201+
// (mind that suffixes are stored/iterated LIFO)
192202
bestSuf = s;
193203
bestRank = maxOccur;
194204
if (bestRank == 1) { // optimization: no better suffix is possible
@@ -219,15 +229,11 @@ private void followNode(FringeRow<S, I, O> f, Node<S, I, O> n) {
219229
}
220230

221231
final BitSet remRows = new BitSet();
222-
for (int i = sep.remRows.nextSetBit(0); i >= 0; i = sep.remRows.nextSetBit(i + 1)) {
223-
if (cRows.get(i).cellIds.contains(cellIdx)) {
224-
remRows.set(i);
225-
}
226-
}
227-
final List<Integer> cellIds = new ArrayList<>(sep.cellsIds.size() + 1);
228-
cellIds.addAll(sep.cellsIds);
232+
sep.remRows.stream().filter(i -> cRows.get(i).cellIds.contains(cellIdx)).forEach(remRows::set);
233+
final List<Integer> cellIds = new ArrayList<>(sep.cellsIds); // important: copy elements!
229234
cellIds.add(cellIdx);
230-
if (remRows.isEmpty()) { // no compatible core prefix
235+
if (remRows.isEmpty()) {
236+
// no compatible core prefix
231237
f.leaf = null;
232238
moveToCore(f, cellIds);
233239
} else if (remRows.cardinality() == 1) {
@@ -252,7 +258,8 @@ private Word<O> query(Row<S, I, O> r, Word<I> suf) {
252258
}
253259

254260
/**
255-
* Adds suffix-output pair to index if not yet contained and returns a unique identifier representing the pair.
261+
* Adds suffix-output pair to index if not yet contained,
262+
* and returns a unique identifier representing the pair.
256263
*/
257264
private int getUniqueCellIdx(Word<I> suf, Word<O> out) {
258265
assert suf.length() == out.length();
@@ -270,7 +277,7 @@ private int getUniqueCellIdx(Word<I> suf, Word<O> out) {
270277
* Returns index of new core row.
271278
*/
272279
private int moveToCore(FringeRow<S, I, O> f, List<Integer> cellIds) {
273-
boolean removed = fRows.remove(f);
280+
final boolean removed = fRows.remove(f);
274281
assert removed;
275282
final S state = hyp.addState();
276283
final CoreRow<S, I, O> c = new CoreRow<>(f.prefix, state, cRows.size());
@@ -290,25 +297,14 @@ private int moveToCore(FringeRow<S, I, O> f, List<Integer> cellIds) {
290297
}
291298

292299
/**
293-
* Takes fringe row and its observations, queries the missing entries, and returns a list containing the
294-
* observations for all suffixes.
300+
* Takes fringe row and its observations, queries the missing entries,
301+
* and returns a list containing the observations for all suffixes.
295302
*/
296303
private List<Integer> completeRowObservations(FringeRow<S, I, O> f, List<Integer> cellIds) {
297-
final Set<Word<I>> sufsPresent = new HashSet<>(HashUtil.capacity(cellIds.size()));
298-
for (Integer id : cellIds) {
299-
sufsPresent.add(this.cells.get(id).getFirst());
300-
}
301-
final List<Word<I>> sufsMissing = new ArrayList<>(sufs.size());
302-
for (Word<I> s : sufs) {
303-
if (!sufsPresent.contains(s)) {
304-
sufsMissing.add(s);
305-
}
306-
}
307-
final List<Integer> cellIdsFull = new ArrayList<>(cellIds.size() + sufsMissing.size());
308-
cellIdsFull.addAll(cellIds);
309-
for (Word<I> s : sufsMissing) {
310-
cellIdsFull.add(getUniqueCellIdx(s, query(f, s)));
311-
}
304+
final List<Word<I>> sufsPresent = cellIds.stream().map(c -> this.cells.get(c).getFirst()).collect(Collectors.toList());
305+
final List<Word<I>> sufsMissing = sufs.stream().filter(s -> !sufsPresent.contains(s)).collect(Collectors.toList());
306+
final List<Integer> cellIdsFull = new ArrayList<>(cellIds); // important: copy elements!
307+
sufsMissing.forEach(s -> cellIdsFull.add(getUniqueCellIdx(s, query(f, s))));
312308
return cellIdsFull;
313309
}
314310

@@ -340,11 +336,9 @@ private void identifyNewState(DefaultQuery<I, Word<O>> q) {
340336
private void addSuffixToTable(Word<I> suf) {
341337
assert !sufs.contains(suf);
342338
sufs.push(suf);
343-
/*
344-
* This might be an extension of an existing suffix -> storing/iterating suffixes in LIFO order exploits caching
345-
* when filling core rows. Similarly, since core rows are prefix-closed, cache hit rate for adding suffixes is
346-
* maximized by iterating core rows in LIFO order
347-
*/
339+
340+
// since core rows are prefix-closed,
341+
// cache hit rate is maximized by LIFO iteration
348342
for (int i = cRows.size() - 1; i >= 0; i--) {
349343
addSuffixToCoreRow(cRows.get(i), suf);
350344
}

algorithms/active/sparse/src/main/java/de/learnlib/algorithm/sparse/Leaf.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ class Leaf<S, I, O> extends Node<S, I, O> {
2828
private int lastNumSufs;
2929

3030
/**
31-
* Split leafs always remember how many core rows and suffixes the table contained at their last visit. This
32-
* information is used as a logical timestamp to check whether the separator is still guaranteed to be optimal or if
33-
* it needs to be recomputed.
31+
* Split leafs always remember how many core rows and suffixes the table contained
32+
* at their last visit. This information is used as a logical timestamp to check
33+
* if the separator is still guaranteed to be optimal or if it needs to be recomputed.
3434
*/
3535
@Nullable Separator<S, I, O> sep;
3636

@@ -72,10 +72,9 @@ void update(List<CoreRow<S, I, O>> cRows, int numSufs) {
7272
sep = null;
7373
}
7474

75-
/*
76-
* Since suffixes and core rows grow monotonically, the separator only needs to be recomputed whenever new
77-
* compatible core prefixes emerge or when the suffix set grows.
78-
*/
75+
// Since suffixes and core rows grow monotonically,
76+
// the separator only needs to be recomputed whenever
77+
// new compatible core prefixes emerge or the suffix set grows.
7978

8079
for (int i = lastNumCRows; i < cRows.size(); i++) {
8180
final CoreRow<S, I, O> c = cRows.get(i);

algorithms/active/sparse/src/main/java/de/learnlib/algorithm/sparse/Node.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,14 @@
2121
class Node<S, I, O> { // type parameters required for safe casting
2222

2323
/**
24-
* Cell identifiers of the fringe rows at this node.
24+
* Suffix-output cell identifiers of the fringe rows that share this node.
2525
*/
2626
final List<Integer> cellsIds;
27+
28+
/**
29+
* Bit vector indicating the core rows that remain compatible
30+
* with the observations associated with this node.
31+
*/
2732
final BitSet remRows;
2833

2934
protected Node(List<Integer> cellsIds) {

algorithms/active/sparse/src/main/java/de/learnlib/algorithm/sparse/SparseLearner.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
import net.automatalib.word.Word;
2525

2626
/**
27-
* Optimized implementation of the Ls learning algorithm, as described in the paper <a
28-
* href="https://doi.org/10.1007/978-3-032-05792-1_10">Learning Mealy Machines with Sparse Observation Tables</a> by
29-
* Wolffhardt Schwabe, Paul Kogel, and Sabine Glesner.
27+
* Optimized implementation of the L<sup>s</sup> learning algorithm, as described in the paper
28+
* <a href="https://doi.org/10.1007/978-3-032-05792-1_10">Learning Mealy Machines with Sparse Observation Tables</a>
29+
* by Wolffhardt Schwabe, Paul Kogel, and Sabine Glesner.
3030
*/
3131
public class SparseLearner<I, O> extends GenericSparseLearner<Integer, I, O> {
3232

algorithms/active/sparse/src/main/java/module-info.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
*/
1616

1717
/**
18-
* This module provides the implementation of the Sparse OT learning algorithm as described in the paper <a
19-
* href="https://doi.org/10.1007/978-3-032-05792-1_10">Learning Mealy Machines with Sparse Observation Tables</a> by
20-
* Wolffhardt Schwabe, Paul Kogel, and Sabine Glesner.
18+
* This module provides the implementation of the Sparse OT learning algorithm as described in the paper
19+
* <a href="https://doi.org/10.1007/978-3-032-05792-1_10">Learning Mealy Machines with Sparse Observation Tables</a>
20+
* by Wolffhardt Schwabe, Paul Kogel, and Sabine Glesner.
2121
* <p>
2222
* This module is provided by the following Maven dependency:
2323
* <pre>

0 commit comments

Comments
 (0)