2121import java .util .Collections ;
2222import java .util .Deque ;
2323import java .util .HashMap ;
24- import java .util .HashSet ;
2524import java .util .List ;
2625import java .util .Map ;
27- import java .util .Set ;
2826import java .util .function .Function ;
27+ import java .util .stream .Collectors ;
2928
3029import de .learnlib .algorithm .LearningAlgorithm .MealyLearner ;
3130import de .learnlib .counterexample .LocalSuffixFinders ;
3534import net .automatalib .alphabet .Alphabet ;
3635import net .automatalib .automaton .transducer .MealyMachine ;
3736import net .automatalib .automaton .transducer .MutableMealyMachine ;
38- import net .automatalib .common .util .HashUtil ;
3937import net .automatalib .common .util .Pair ;
4038import net .automatalib .word .Word ;
4139
4240class 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 }
0 commit comments