Skip to content

Commit 527f580

Browse files
committed
lsharp: remove side-effects of getHypothesisModel()
closes #145
1 parent 689f8fb commit 527f580

3 files changed

Lines changed: 144 additions & 14 deletions

File tree

algorithms/active/lsharp/pom.xml

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,21 @@ limitations under the License.
7070
</dependency>
7171

7272
<!-- test -->
73+
<dependency>
74+
<groupId>de.learnlib</groupId>
75+
<artifactId>learnlib-drivers-simulator</artifactId>
76+
<scope>test</scope>
77+
</dependency>
78+
<dependency>
79+
<groupId>de.learnlib</groupId>
80+
<artifactId>learnlib-equivalence-oracles</artifactId>
81+
<scope>test</scope>
82+
</dependency>
83+
<dependency>
84+
<groupId>de.learnlib</groupId>
85+
<artifactId>learnlib-membership-oracles</artifactId>
86+
<scope>test</scope>
87+
</dependency>
7388
<dependency>
7489
<groupId>de.learnlib.testsupport</groupId>
7590
<artifactId>learnlib-learner-it-support</artifactId>
@@ -84,11 +99,28 @@ limitations under the License.
8499
<artifactId>automata-serialization-dot</artifactId>
85100
<scope>test</scope>
86101
</dependency>
102+
<dependency>
103+
<groupId>net.automatalib</groupId>
104+
<artifactId>automata-util</artifactId>
105+
<scope>test</scope>
106+
</dependency>
87107

88108
<dependency>
89109
<groupId>org.testng</groupId>
90110
<artifactId>testng</artifactId>
91111
</dependency>
92-
93112
</dependencies>
113+
114+
<build>
115+
<plugins>
116+
<plugin>
117+
<groupId>org.apache.maven.plugins</groupId>
118+
<artifactId>maven-surefire-plugin</artifactId>
119+
<configuration>
120+
<!-- append to existing argLine to nicely work together with jacoco plugin -->
121+
<argLine>@{argLine} --add-reads=de.learnlib.algorithm.lsharp=net.automatalib.util</argLine>
122+
</configuration>
123+
</plugin>
124+
</plugins>
125+
</build>
94126
</project>

algorithms/active/lsharp/src/main/java/de/learnlib/algorithm/lsharp/LSharpMealy.java

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,20 @@ public void updateFrontierAndBasis() {
229229
}
230230
}
231231

232+
public CompactMealy<I, O> buildHypothesis() {
233+
while (true) {
234+
this.makeObsTreeAdequate();
235+
CompactMealy<I, O> hyp = this.constructHypothesis();
236+
237+
DefaultQuery<I, Word<O>> ce = this.checkConsistency(hyp);
238+
if (ce != null) {
239+
this.processCex(ce, hyp);
240+
} else {
241+
return hyp;
242+
}
243+
}
244+
}
245+
232246
public CompactMealy<I, O> constructHypothesis() {
233247

234248
CompactMealy<I, O> result = new CompactMealy<>(inputAlphabet, basis.size());
@@ -319,26 +333,19 @@ public void checkFrontierConsistency() {
319333
@Override
320334
public void startLearning() {
321335
this.initObsTree(null);
336+
buildHypothesis();
322337
}
323338

324339
@Override
325340
public boolean refineHypothesis(DefaultQuery<I, Word<O>> ceQuery) {
326-
return processCex(ceQuery, getHypothesisModel());
341+
boolean result = processCex(ceQuery, constructHypothesis());
342+
buildHypothesis();
343+
return result;
327344
}
328345

329346
@Override
330-
public CompactMealy<I, O> getHypothesisModel() {
331-
while (true) {
332-
this.makeObsTreeAdequate();
333-
CompactMealy<I, O> hyp = this.constructHypothesis();
334-
335-
DefaultQuery<I, Word<O>> ce = this.checkConsistency(hyp);
336-
if (ce != null) {
337-
this.processCex(ce, hyp);
338-
} else {
339-
return hyp;
340-
}
341-
}
347+
public MealyMachine<?, I, ?, O> getHypothesisModel() {
348+
return constructHypothesis();
342349
}
343350

344351
static final class BuilderDefaults {
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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.algorithm.lsharp;
17+
18+
import java.util.Collection;
19+
import java.util.Random;
20+
21+
import de.learnlib.driver.simulator.MealySimulatorSUL;
22+
import de.learnlib.oracle.AdaptiveMembershipOracle;
23+
import de.learnlib.oracle.equivalence.MealySimulatorEQOracle;
24+
import de.learnlib.oracle.membership.SULAdaptiveOracle;
25+
import de.learnlib.query.AdaptiveQuery;
26+
import de.learnlib.query.DefaultQuery;
27+
import net.automatalib.alphabet.Alphabet;
28+
import net.automatalib.alphabet.impl.Alphabets;
29+
import net.automatalib.automaton.transducer.MealyMachine;
30+
import net.automatalib.automaton.transducer.impl.CompactMealy;
31+
import net.automatalib.util.automaton.Automata;
32+
import net.automatalib.util.automaton.random.RandomAutomata;
33+
import net.automatalib.word.Word;
34+
import org.testng.Assert;
35+
import org.testng.annotations.Test;
36+
37+
public class LSharpMealyTest {
38+
39+
/**
40+
* Checks that {@link LSharpMealy#getHypothesisModel()} is free of side-effects. For details, see <a
41+
* href="https://github.com/LearnLib/learnlib/issues/145">issue 145</a>.
42+
*/
43+
@Test
44+
public void testIssue145() {
45+
Alphabet<Integer> alphabet = Alphabets.integers(0, 1);
46+
CompactMealy<Integer, Character> mealy =
47+
RandomAutomata.randomMealy(new Random(42), 20, alphabet, Alphabets.characters('a', 'c'));
48+
49+
ValidatingOracle<Integer, Character> mqo =
50+
new ValidatingOracle<>(new SULAdaptiveOracle<>(new MealySimulatorSUL<>(mealy)));
51+
MealySimulatorEQOracle<Integer, Character> eqo = new MealySimulatorEQOracle<>(mealy);
52+
LSharpMealy<Integer, Character> learner = new LSharpMealyBuilder<Integer, Character>().withAlphabet(alphabet)
53+
.withOracle(mqo)
54+
.withRandom(new Random(42))
55+
.create();
56+
57+
mqo.mutable = true;
58+
learner.startLearning();
59+
mqo.mutable = false;
60+
MealyMachine<?, Integer, ?, Character> hyp = learner.getHypothesisModel();
61+
DefaultQuery<Integer, Word<Character>> cex;
62+
63+
while ((cex = eqo.findCounterExample(hyp, alphabet)) != null) {
64+
mqo.mutable = true;
65+
learner.refineHypothesis(cex);
66+
mqo.mutable = false;
67+
hyp = learner.getHypothesisModel();
68+
}
69+
70+
Assert.assertTrue(Automata.testEquivalence(mealy, learner.getHypothesisModel(), alphabet));
71+
}
72+
73+
private static final class ValidatingOracle<I, O> implements AdaptiveMembershipOracle<I, O> {
74+
75+
private final AdaptiveMembershipOracle<I, O> delegate;
76+
private boolean mutable;
77+
78+
ValidatingOracle(AdaptiveMembershipOracle<I, O> delegate) {
79+
this.delegate = delegate;
80+
this.mutable = false;
81+
}
82+
83+
@Override
84+
public void processQueries(Collection<? extends AdaptiveQuery<I, O>> adaptiveQueries) {
85+
Assert.assertTrue(mutable);
86+
this.delegate.processQueries(adaptiveQueries);
87+
88+
}
89+
}
90+
91+
}

0 commit comments

Comments
 (0)