Skip to content

Commit 692cf7b

Browse files
committed
make most regular learners implement AccessSequenceTransformer
1 parent a876054 commit 692cf7b

80 files changed

Lines changed: 1999 additions & 765 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1212
* Added the L<sup>s</sup> active learning algorithm for Mealy machines (thanks to [Wolffhardt Schwabe](https://github.com/stateMachinist)).
1313
* Added an `EarlyExitEQOracle` which for a given `AdaptiveMembershipOracle` and `TestWordGenerator` stops the evaluation of (potentially long) Mealy-based equivalence tests as soon as a mismatch with the hypothesis is detected, potentially improving the symbol performance of the given equivalence oracle.
1414
* Both lambda learners (`LLambda{DFA,Mealy}` and `TTTLambda{DFA,Mealy}`) now support the `Resumable` interface.
15-
* The `LLambda{DFA,Mealy}` learners now implement the `OTLearner` interface to export their internal knowledge via an `ObservationTable`.
15+
* The `LLambda{DFA,Mealy}` learners now implement the `OTLearner` interface to export their internal knowledge via an `ObservationTable`.
16+
* Most regular learner no implement `AccessSequenceTransformer` as a means to extract representatives of hypothesis states.
1617

1718
### Changed
1819

@@ -22,6 +23,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
2223
* The `generateTestWords` method of `AbstractTestWordEQOracle` now needs to be public.
2324
* The classes of `de.learnlib.testsupport.it.learner` have been split into the packages `de.learnlib.testsupport.it{,testcase,util,variant}` in the same module (`de.learnlib.testsupport:learnlib-learner-it-support`).
2425

26+
### Removed
27+
28+
* All *adapters* from the `learnlib-procedural` learner have been removed to due main learners implementing `AccessSequenceTransformer` now. Use the constructors of the main learners instead.
29+
2530
### Fixed
2631

2732
* The `de.learnlib.algorithm.adt` module now correctly `exports` the `de.learnlib.algorithm.adt.config.model.*` packages.

algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADTLearner.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Queue;
3030
import java.util.Set;
3131

32+
import de.learnlib.AccessSequenceTransformer;
3233
import de.learnlib.Resumable;
3334
import de.learnlib.algorithm.LearningAlgorithm;
3435
import de.learnlib.algorithm.adt.adt.ADT;
@@ -70,6 +71,9 @@
7071

7172
/**
7273
* The main learning algorithm.
74+
* <p>
75+
* <b>Implementation note:</b> this learner uses the {@link AccessSequenceTransformer} interface to provide access to
76+
* the representatives of the states of the current hypothesis model.
7377
*
7478
* @param <I>
7579
* input symbol type
@@ -78,6 +82,7 @@
7882
*/
7983
public class ADTLearner<I, O> implements LearningAlgorithm.MealyLearner<I, O>,
8084
PartialTransitionAnalyzer<ADTState<I, O>, I>,
85+
AccessSequenceTransformer<I>,
8186
SupportsGrowingAlphabet<I>,
8287
Resumable<ADTLearnerState<ADTState<I, O>, I, O>> {
8388

@@ -148,6 +153,7 @@ public void startLearning() {
148153

149154
@Override
150155
public boolean refineHypothesis(DefaultQuery<I, Word<O>> ce) {
156+
requireLearningProcessStarted();
151157

152158
if (!MQUtil.isCounterexample(ce, this.hypothesis)) {
153159
return false;
@@ -409,6 +415,15 @@ public boolean isTransitionDefined(ADTState<I, O> state, I input) {
409415
return !transition.needsSifting();
410416
}
411417

418+
@Override
419+
public Word<I> transformAccessSequence(Word<I> word) {
420+
requireLearningProcessStarted();
421+
422+
final ADTState<I, O> state = this.hypothesis.getState(word);
423+
assert state != null;
424+
return state.getAccessSequence();
425+
}
426+
412427
@Override
413428
public void addAlphabetSymbol(I symbol) {
414429

@@ -837,6 +852,12 @@ private List<ADTTransition<I, O>> getIncomingNonSpanningTreeTransitions(ADTState
837852
return result;
838853
}
839854

855+
private void requireLearningProcessStarted() {
856+
if (hypothesis.getStates().isEmpty()) {
857+
throw new IllegalStateException("Learning process has not been started");
858+
}
859+
}
860+
840861
public ADT<ADTState<I, O>, I, O> getADT() {
841862
return adt;
842863
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/* Copyright (C) 2013-2026 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.adt.learner;
17+
18+
import java.util.ArrayList;
19+
import java.util.Collection;
20+
import java.util.List;
21+
import java.util.Random;
22+
import java.util.Set;
23+
24+
import de.learnlib.algorithm.adt.adt.ADT;
25+
import de.learnlib.algorithm.adt.adt.ADTLeafNode;
26+
import de.learnlib.algorithm.adt.adt.ADTNode;
27+
import de.learnlib.algorithm.adt.automaton.ADTState;
28+
import de.learnlib.algorithm.adt.util.ADTUtil;
29+
import de.learnlib.driver.simulator.MealySimulatorSUL;
30+
import de.learnlib.oracle.AdaptiveMembershipOracle;
31+
import de.learnlib.oracle.membership.SULAdaptiveOracle;
32+
import de.learnlib.testsupport.AbstractLearnerASTTest;
33+
import net.automatalib.alphabet.Alphabet;
34+
import net.automatalib.alphabet.impl.Alphabets;
35+
import net.automatalib.automaton.transducer.MealyMachine;
36+
import net.automatalib.util.automaton.random.RandomAutomata;
37+
import net.automatalib.word.Word;
38+
39+
public class ADTASTTest
40+
extends AbstractLearnerASTTest<ADTLearner<Integer, Character>, MealyMachine<?, Integer, ?, Character>, AdaptiveMembershipOracle<Integer, Character>, Integer, Word<Character>> {
41+
42+
@Override
43+
protected Alphabet<Integer> getInitialAlphabet() {
44+
return Alphabets.integers(1, 5);
45+
}
46+
47+
@Override
48+
protected MealyMachine<?, Integer, ?, Character> getSUL(Alphabet<Integer> alphabet) {
49+
return RandomAutomata.randomMealy(new Random(42), 15, alphabet, Alphabets.characters('a', 'f'));
50+
}
51+
52+
@Override
53+
protected AdaptiveMembershipOracle<Integer, Character> getOracle(MealyMachine<?, Integer, ?, Character> target) {
54+
return new SULAdaptiveOracle<>(new MealySimulatorSUL<>(target));
55+
}
56+
57+
@Override
58+
protected ADTLearner<Integer, Character> getLearner(AdaptiveMembershipOracle<Integer, Character> oracle,
59+
Alphabet<Integer> alphabet) {
60+
return new ADTLearnerBuilder<Integer, Character>().withAlphabet(alphabet).withOracle(oracle).create();
61+
}
62+
63+
@Override
64+
protected Collection<Word<Integer>> getTrueRepresentatives() {
65+
final ADT<ADTState<Integer, Character>, Integer, Character> adt = learner.getADT();
66+
final Set<ADTNode<ADTState<Integer, Character>, Integer, Character>> leaves =
67+
ADTUtil.collectLeaves(adt.getRoot());
68+
69+
final List<Word<Integer>> result = new ArrayList<>(leaves.size());
70+
71+
for (ADTNode<ADTState<Integer, Character>, Integer, Character> l : leaves) {
72+
if (l instanceof ADTLeafNode<ADTState<Integer, Character>, Integer, Character> leaf) {
73+
result.add(leaf.getState().getAccessSequence());
74+
}
75+
}
76+
77+
return result;
78+
}
79+
}

algorithms/active/dhc/src/main/java/de/learnlib/algorithm/dhc/mealy/MealyDHC.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@
4848

4949
/**
5050
* The DHC learner.
51+
* <p>
52+
* <b>Implementation note:</b> this learner uses the {@link AccessSequenceTransformer} interface to provide access
53+
* to the representatives of the states of the current hypothesis model.
5154
*
5255
* @param <I>
5356
* input symbol type
@@ -291,7 +294,8 @@ public void resume(MealyDHCState<I, O> state) {
291294
@Override
292295
public Word<I> transformAccessSequence(Word<I> word) {
293296
checkInternalState();
294-
Integer state = hypothesis.getSuccessor(hypothesis.getInitialState(), word);
297+
Integer state = hypothesis.getState(word);
298+
assert state != null;
295299
return assembleAccessSequence(accessSequences.get(state));
296300
}
297301

algorithms/active/procedural/src/main/java/de/learnlib/algorithm/procedural/adapter/mealy/LLambdaAdapterMealy.java renamed to algorithms/active/dhc/src/test/java/de/learnlib/algorithm/dhc/mealy/MealyDHCASTTest.java

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,18 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package de.learnlib.algorithm.procedural.adapter.mealy;
16+
package de.learnlib.algorithm.dhc.mealy;
1717

18-
import de.learnlib.algorithm.lambda.lstar.LLambdaMealy;
19-
import de.learnlib.oracle.MembershipOracle;
18+
import de.learnlib.oracle.MembershipOracle.MealyMembershipOracle;
19+
import de.learnlib.testsupport.AbstractLearnerASTMealyTest;
2020
import net.automatalib.alphabet.Alphabet;
21-
import net.automatalib.word.Word;
2221

23-
/**
24-
* Adapter for using {@link LLambdaMealy} as a procedural learner.
25-
*
26-
* @param <I>
27-
* input symbol type
28-
* @param <O>
29-
* output symbol type
30-
*/
31-
public class LLambdaAdapterMealy<I, O> extends LLambdaMealy<I, O> {
22+
public class MealyDHCASTTest extends AbstractLearnerASTMealyTest<MealyDHC<Character, Character>> {
3223

33-
public LLambdaAdapterMealy(Alphabet<I> alphabet, MembershipOracle<I, Word<O>> oracle) {
34-
super(alphabet, oracle);
24+
@Override
25+
protected MealyDHC<Character, Character> getLearner(MealyMembershipOracle<Character, Character> oracle,
26+
Alphabet<Character> alphabet) {
27+
return new MealyDHC<>(alphabet, oracle);
3528
}
29+
3630
}

algorithms/active/kearns-vazirani/src/main/java/de/learnlib/algorithm/kv/dfa/KearnsVaziraniDFA.java

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.List;
2424
import java.util.function.BooleanSupplier;
2525

26+
import de.learnlib.AccessSequenceTransformer;
2627
import de.learnlib.Resumable;
2728
import de.learnlib.acex.AbstractBaseCounterexample;
2829
import de.learnlib.acex.AcexAnalyzer;
@@ -48,12 +49,17 @@
4849
/**
4950
* The Kearns/Vazirani algorithm for learning DFA, as described in the book "An Introduction to Computational Learning
5051
* Theory" by Michael Kearns and Umesh Vazirani.
52+
* <p>
53+
* <b>Implementation note:</b> this learner uses the {@link AccessSequenceTransformer} interface to provide access to
54+
* the representatives of the states of the current hypothesis model.
5155
*
5256
* @param <I>
5357
* input symbol type
5458
*/
55-
public class KearnsVaziraniDFA<I>
56-
implements DFALearner<I>, SupportsGrowingAlphabet<I>, Resumable<KearnsVaziraniDFAState<I>> {
59+
public class KearnsVaziraniDFA<I> implements DFALearner<I>,
60+
AccessSequenceTransformer<I>,
61+
SupportsGrowingAlphabet<I>,
62+
Resumable<KearnsVaziraniDFAState<I>> {
5763

5864
private static final Logger LOGGER = LoggerFactory.getLogger(KearnsVaziraniDFA.class);
5965

@@ -65,6 +71,21 @@ public class KearnsVaziraniDFA<I>
6571
protected List<StateInfo<I, Boolean>> stateInfos = new ArrayList<>();
6672
private CompactDFA<I> hypothesis;
6773

74+
/**
75+
* Constructor.
76+
*
77+
* @param alphabet
78+
* the learning alphabet
79+
* @param oracle
80+
* the membership oracle
81+
*/
82+
public KearnsVaziraniDFA(Alphabet<I> alphabet, MembershipOracle<I, Boolean> oracle) {
83+
this(alphabet,
84+
oracle,
85+
BuilderDefaults.repeatedCounterexampleEvaluation(),
86+
BuilderDefaults.counterexampleAnalyzer());
87+
}
88+
6889
/**
6990
* Constructor.
7091
*
@@ -97,9 +118,7 @@ public void startLearning() {
97118

98119
@Override
99120
public boolean refineHypothesis(DefaultQuery<I, Boolean> ceQuery) {
100-
if (hypothesis.size() == 0) {
101-
throw new IllegalStateException("Not initialized");
102-
}
121+
requireLearningProcessStarted();
103122
Word<I> input = ceQuery.getInput();
104123
boolean output = ceQuery.getOutput();
105124
if (!refineHypothesisSingle(input, output)) {
@@ -115,9 +134,7 @@ public boolean refineHypothesis(DefaultQuery<I, Boolean> ceQuery) {
115134

116135
@Override
117136
public DFA<?, I> getHypothesisModel() {
118-
if (hypothesis.size() == 0) {
119-
throw new IllegalStateException("Not started");
120-
}
137+
requireLearningProcessStarted();
121138
return hypothesis;
122139
}
123140

@@ -298,6 +315,22 @@ private List<StateInfo<I, Boolean>> sift(List<AbstractWordBasedDTNode<I, Boolean
298315
return result;
299316
}
300317

318+
private void requireLearningProcessStarted() {
319+
if (hypothesis.size() == 0) {
320+
throw new IllegalStateException("Learning process has not been started");
321+
}
322+
}
323+
324+
@Override
325+
public Word<I> transformAccessSequence(Word<I> word) {
326+
requireLearningProcessStarted();
327+
328+
final Integer state = hypothesis.getState(word);
329+
assert state != null;
330+
final StateInfo<I, Boolean> stateInfo = stateInfos.get(state);
331+
return stateInfo.accessSequence;
332+
}
333+
301334
@Override
302335
public void addAlphabetSymbol(I symbol) {
303336

0 commit comments

Comments
 (0)