• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LearnLib / learnlib / 19556545386

21 Nov 2025 01:10AM UTC coverage: 94.471%. First build
19556545386

Pull #155

github

web-flow
Merge d1e6e0721 into 7c236fec9
Pull Request #155: Bump Java Version to 17/25

59 of 83 new or added lines in 25 files covered. (71.08%)

12611 of 13349 relevant lines covered (94.47%)

1.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.86
/examples/src/main/java/de/learnlib/example/Example3.java
1
/* Copyright (C) 2013-2025 TU Dortmund University
2
 * This file is part of LearnLib <https://learnlib.de>.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package de.learnlib.example;
17

18
import java.util.ArrayList;
19
import java.util.Collection;
20
import java.util.List;
21
import java.util.function.Supplier;
22

23
import de.learnlib.algorithm.LearningAlgorithm.MealyLearner;
24
import de.learnlib.algorithm.lstar.mealy.ExtensibleLStarMealyBuilder;
25
import de.learnlib.example.Example2.BoundedStringQueue;
26
import de.learnlib.filter.reuse.ReuseCapableOracle;
27
import de.learnlib.filter.reuse.ReuseOracle;
28
import de.learnlib.filter.reuse.ReuseOracleBuilder;
29
import de.learnlib.filter.reuse.tree.SystemStateHandler;
30
import de.learnlib.oracle.MembershipOracle.MealyMembershipOracle;
31
import de.learnlib.query.Query;
32
import net.automatalib.alphabet.Alphabet;
33
import net.automatalib.alphabet.impl.GrowingMapAlphabet;
34
import net.automatalib.automaton.transducer.MealyMachine;
35
import net.automatalib.util.automaton.Automata;
36
import net.automatalib.word.Word;
37
import net.automatalib.word.WordBuilder;
38
import org.checkerframework.checker.nullness.qual.Nullable;
39

40
/**
41
 * This example shows how to use the reuse filter on the {@link BoundedStringQueue} of {@link Example2}.
42
 * <p>
43
 * Please note that there is no equivalence oracle used in this example so the resulting mealy machines are only first
44
 * "guesses".
45
 */
46
@SuppressWarnings("PMD.SystemPrintln")
47
public class Example3 {
48

49
    private static final String OFFER_1 = "offer_1";
50
    private static final String OFFER_2 = "offer_2";
51
    private static final String POLL = "poll";
52
    private final Alphabet<String> sigma;
53
    private final List<Word<String>> initialSuffixes;
54

55
    public Example3() {
1✔
56
        sigma = new GrowingMapAlphabet<>();
1✔
57
        sigma.add(OFFER_1);
1✔
58
        sigma.add(OFFER_2);
1✔
59
        sigma.add(POLL);
1✔
60

61
        initialSuffixes = new ArrayList<>();
1✔
62
        for (String symbol : sigma) {
1✔
63
            initialSuffixes.add(Word.fromLetter(symbol));
1✔
64
        }
1✔
65
    }
1✔
66

67
    public static void main(String[] args) {
68
        Example3 example = new Example3();
1✔
69
        System.out.println("--");
1✔
70
        System.out.println("Run experiment 1 (ReuseOracle):");
1✔
71
        MealyMachine<?, String, ?, @Nullable String> result1 = example.runExperiment1();
1✔
72
        System.out.println("--");
1✔
73
        System.out.println("Run experiment 2:");
1✔
74
        MealyMachine<?, String, ?, @Nullable String> result2 = example.runExperiment2();
1✔
75
        System.out.println("--");
1✔
76
        System.out.println("Model 1: " + result1.size() + " states");
1✔
77
        System.out.println("Model 2: " + result2.size() + " states");
1✔
78

79
        Word<String> sepWord;
80
        sepWord = Automata.findSeparatingWord(result1, result2, example.sigma);
1✔
81

82
        System.out.println("Difference (separating word)? " + sepWord);
1✔
83

84
        /*
85
         * Background knowledge: L^* creates an observation table with
86
         * |S|=3,|SA|=7,|E|=3 so 30 MQs should be sufficient. The reuse filter
87
         * should sink system states from S*E to SA*E so the upper part is the
88
         * number of saved resets (=9).
89
         *
90
         * If the numbers don't match:
91
         * https://github.com/LearnLib/learnlib/issues/5 (9 queries will be
92
         * pumped by the reuse filter)
93
         */
94
    }
1✔
95

96
    /*
97
     * A "normal" scenario without reuse filter technique.
98
     */
99
    public MealyMachine<?, String, ?, @Nullable String> runExperiment1() {
100
        // For each membership query a new instance of BoundedStringQueue will
101
        // be created (normal learning scenario without filters)
102
        FullMembershipQueryOracle oracle = new FullMembershipQueryOracle();
1✔
103

104
        // construct L* instance (almost classic Mealy version)
105
        // almost: we use words (Word<String>) in cells of the table
106
        // instead of single outputs.
107
        MealyLearner<String, @Nullable String> lstar;
108
        lstar = new ExtensibleLStarMealyBuilder<String, @Nullable String>().withAlphabet(sigma)
1✔
109
                                                                           .withInitialSuffixes(initialSuffixes)
1✔
110
                                                                           .withOracle(oracle)
1✔
111
                                                                           .create();
1✔
112

113
        lstar.startLearning();
1✔
114
        MealyMachine<?, String, ?, @Nullable String> result;
115
        result = lstar.getHypothesisModel();
1✔
116

117
        System.out.println("Resets:  " + oracle.resets);
1✔
118
        System.out.println("Symbols: " + oracle.symbols);
1✔
119

120
        return result;
1✔
121
    }
122

123
    /*
124
     * Scenario with reuse filter technique.
125
     */
126
    public MealyMachine<?, String, ?, @Nullable String> runExperiment2() {
127
        MySystemStateHandler ssh = new MySystemStateHandler();
1✔
128

129
        // This time we use the reuse filter to avoid some resets and
130
        // save execution of symbols
131
        Supplier<ReuseCapableOracle<BoundedStringQueue, String, @Nullable String>> supplier = ReuseCapableImpl::new;
1✔
132
        ReuseOracle<BoundedStringQueue, String, @Nullable String> reuseOracle =
1✔
133
                new ReuseOracleBuilder<>(sigma, supplier).withSystemStateHandler(ssh).build();
1✔
134

135
        // construct L* instance (almost classic Mealy version)
136
        // almost: we use words (Word<String>) in cells of the table
137
        // instead of single outputs.
138

139
        MealyLearner<String, @Nullable String> lstar;
140
        lstar = new ExtensibleLStarMealyBuilder<String, @Nullable String>().withAlphabet(sigma)
1✔
141
                                                                           .withInitialSuffixes(initialSuffixes)
1✔
142
                                                                           .withOracle(reuseOracle)
1✔
143
                                                                           .create();
1✔
144

145
        lstar.startLearning();
1✔
146

147
        // get learned model
148
        MealyMachine<?, String, ?, @Nullable String> result = lstar.getHypothesisModel();
1✔
149

150
        // now invalidate all system states and count the number of disposed
151
        // queues (equals number of resets)
152
        reuseOracle.getReuseTree().disposeSystemStates();
1✔
153
        ReuseCapableImpl reuseCapableOracle = (ReuseCapableImpl) reuseOracle.getReuseCapableOracle();
1✔
154
        System.out.println("Resets:   " + reuseCapableOracle.fullQueries);
1✔
155
        System.out.println("Reused:   " + reuseCapableOracle.reused);
1✔
156
        System.out.println("Symbols:  " + reuseCapableOracle.symbols);
1✔
157
        // disposed = resets
158
        System.out.println("Disposed: " + ssh.disposed);
1✔
159

160
        return result;
1✔
161
    }
162

163
    private @Nullable String exec(BoundedStringQueue s, String input) {
164
        return switch (input) {
1✔
165
            case OFFER_1, OFFER_2 -> {
166
                s.offer(input);
1✔
167
                yield "void";
1✔
168
            }
169
            case POLL -> s.poll();
1✔
NEW
170
            default -> throw new IllegalArgumentException("unknown input symbol");
×
171
        };
172
    }
173

174
    /**
175
     * For running the example this class could also be removed/ignored. It is only here for documentation purposes.
176
     */
177
    static class MySystemStateHandler implements SystemStateHandler<BoundedStringQueue> {
1✔
178

179
        private int disposed;
180

181
        @Override
182
        public void dispose(BoundedStringQueue state) {
183
            /*
184
             * When learning e.g. examples that involve databases, here would be
185
             * a good point to remove database entities. In this example we just
186
             * count the number of disposed (garbage collection inside the reuse
187
             * tree) objects.
188
             */
189
            disposed++;
1✔
190
        }
1✔
191
    }
192

193
    /**
194
     * An oracle that also does the reset by creating a new instance of the {@link BoundedStringQueue}.
195
     */
196
    class FullMembershipQueryOracle implements MealyMembershipOracle<String, @Nullable String> {
1✔
197

198
        private int resets;
199
        private int symbols;
200

201
        @Override
202
        public void processQueries(Collection<? extends Query<String, Word<@Nullable String>>> queries) {
203
            for (Query<String, Word<@Nullable String>> query : queries) {
1✔
204
                resets++;
1✔
205
                symbols += query.getInput().size();
1✔
206

207
                BoundedStringQueue s = new BoundedStringQueue();
1✔
208

209
                WordBuilder<@Nullable String> output = new WordBuilder<>();
1✔
210
                for (String input : query.getInput()) {
1✔
211
                    output.add(exec(s, input));
1✔
212
                }
1✔
213

214
                query.answer(output.toWord().suffix(query.getSuffix().size()));
1✔
215
            }
1✔
216
        }
1✔
217
    }
218

219
    /**
220
     * An implementation of the {@link ReuseCapableOracle} needed for the {@link ReuseOracle}. It only does reset by
221
     * means of creating a new {@link BoundedStringQueue} instance in {@link ReuseCapableOracle#processQuery(Word)}.
222
     */
223
    class ReuseCapableImpl implements ReuseCapableOracle<BoundedStringQueue, String, @Nullable String> {
1✔
224

225
        private int reused;
226
        private int fullQueries;
227
        private int symbols;
228

229
        @Override
230
        public QueryResult<BoundedStringQueue, @Nullable String> continueQuery(Word<String> trace,
231
                                                                               BoundedStringQueue s) {
232

233
            reused++;
1✔
234
            symbols += trace.size();
1✔
235

236
            WordBuilder<@Nullable String> output = new WordBuilder<>();
1✔
237

238
            for (String input : trace) {
1✔
239
                output.add(exec(s, input));
1✔
240
            }
1✔
241

242
            QueryResult<BoundedStringQueue, @Nullable String> result;
243
            result = new QueryResult<>(output.toWord(), s);
1✔
244

245
            return result;
1✔
246
        }
247

248
        @Override
249
        public QueryResult<BoundedStringQueue, @Nullable String> processQuery(Word<String> trace) {
250
            fullQueries++;
1✔
251
            symbols += trace.size();
1✔
252

253
            // Suppose the reset would be a time-consuming operation
254
            BoundedStringQueue s = new BoundedStringQueue();
1✔
255

256
            WordBuilder<@Nullable String> output = new WordBuilder<>();
1✔
257

258
            for (String input : trace) {
1✔
259
                output.add(exec(s, input));
1✔
260
            }
1✔
261

262
            QueryResult<BoundedStringQueue, @Nullable String> result;
263
            result = new QueryResult<>(output.toWord(), s);
1✔
264

265
            return result;
1✔
266
        }
267
    }
268
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc