Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* LearnLib now requires Java 17 at runtime.
* Statistics collection has received a major rework. Previously, classes would implement the `StatisticCollector` interface and return a `StatisticData` object which 1) only allows for describing a very limited amount of data, and 2) requires you to keep track of all the objects that collect data. This approach has been *replaced* by a new `StatisticsService`. Instances of this service can be obtained similar to a logger via `Statistics.getService()` and require you to provide an implementation of this service on the classpath (a default one is provided by the `learnlib-statistics` module). The new service allows arbitrary components to collect various data which can be conveniently extracted based on the new `StatisticsKey`s used by the components. For more details on advanced scenarios (such as multi-threaded benchmarking), see the documentation of the respective classes. While this may require you to adjust the way you are collecting statistics, all functionality from beforehand should still be available.
* `SimpleProfiler` has been replaced by the new clock-based statistics.
* Most learners now more rigorously implement the `LearningAlgorithm` contract that, e.g., duplicate invocations of `startLearning` or calling `refineHypothesis` / `getHypothesisModel` before `startLearning` throw `IllegalStateException`s.
* The `generateTestWords` method of `AbstractTestWordEQOracle` now needs to be public.
* 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`).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.Set;

import de.learnlib.AccessSequenceTransformer;
import de.learnlib.LearnerStateTracker;
import de.learnlib.Resumable;
import de.learnlib.algorithm.LearningAlgorithm;
import de.learnlib.algorithm.adt.adt.ADT;
Expand Down Expand Up @@ -84,7 +85,8 @@ public class ADTLearner<I, O> implements LearningAlgorithm.MealyLearner<I, O>,
PartialTransitionAnalyzer<ADTState<I, O>, I>,
AccessSequenceTransformer<I>,
SupportsGrowingAlphabet<I>,
Resumable<ADTLearnerState<ADTState<I, O>, I, O>> {
Resumable<ADTLearnerState<ADTState<I, O>, I, O>>,
LearnerStateTracker {

private static final Logger LOGGER = LoggerFactory.getLogger(ADTLearner.class);

Expand Down Expand Up @@ -138,6 +140,7 @@ public ADTLearner(Alphabet<I> alphabet,

@Override
public void startLearning() {
requireLearningProcessNotStarted();

final ADTState<I, O> initialState = this.hypothesis.addInitialState();
initialState.setAccessSequence(Word.epsilon());
Expand Down Expand Up @@ -315,9 +318,15 @@ private ADTNode<ADTState<I, O>, I, O> findNodeForState(ADTState<I, O> state) {

@Override
public MealyMachine<?, I, ?, O> getHypothesisModel() {
requireLearningProcessStarted();
return this.hypothesis;
}

@Override
public boolean hasLearningProcessStarted() {
return !hypothesis.getStates().isEmpty();
}

/**
* Close all pending open transitions.
*/
Expand Down Expand Up @@ -852,12 +861,6 @@ private List<ADTTransition<I, O>> getIncomingNonSpanningTreeTransitions(ADTState
return result;
}

private void requireLearningProcessStarted() {
if (hypothesis.getStates().isEmpty()) {
throw new IllegalStateException("Learning process has not been started");
}
}

public ADT<ADTState<I, O>, I, O> getADT() {
return adt;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Set;

import de.learnlib.AccessSequenceTransformer;
import de.learnlib.LearnerStateTracker;
import de.learnlib.Resumable;
import de.learnlib.algorithm.GlobalSuffixLearner.GlobalSuffixLearnerMealy;
import de.learnlib.algorithm.LearningAlgorithm.MealyLearner;
Expand Down Expand Up @@ -61,7 +62,8 @@ public class MealyDHC<I, O> implements MealyLearner<I, O>,
AccessSequenceTransformer<I>,
GlobalSuffixLearnerMealy<I, O>,
SupportsGrowingAlphabet<I>,
Resumable<MealyDHCState<I, O>> {
Resumable<MealyDHCState<I, O>>,
LearnerStateTracker {

private final MembershipOracle<I, Word<O>> oracle;
private final Alphabet<I> alphabet;
Expand Down Expand Up @@ -120,29 +122,28 @@ public Collection<Word<I>> getGlobalSuffixes() {

@Override
public boolean addGlobalSuffixes(Collection<? extends Word<I>> newGlobalSuffixes) {
checkInternalState();
requireLearningProcessStarted();

return addSuffixesUnchecked(newGlobalSuffixes);
}

private void checkInternalState() {
if (hypothesis == null) {
throw new IllegalStateException("No hypothesis learned yet");
}
}

protected boolean addSuffixesUnchecked(Collection<? extends Word<I>> newSuffixes) {
int oldSize = hypothesis.size();

splitters.addAll(newSuffixes);

startLearning();
startLearningInternal();

return hypothesis.size() != oldSize;
}

@Override
public void startLearning() {
requireLearningProcessNotStarted();
startLearningInternal();
}

private void startLearningInternal() {
// initialize structure to store state output signatures
Map<List<Word<O>>, Integer> signatures = new HashMap<>();

Expand Down Expand Up @@ -233,7 +234,7 @@ private void scheduleSuccessors(QueueElement<I, O> elem,

@Override
public boolean refineHypothesis(DefaultQuery<I, Word<O>> ceQuery) {
checkInternalState();
requireLearningProcessStarted();

if (hypothesis.computeSuffixOutput(ceQuery.getPrefix(), ceQuery.getSuffix()).equals(ceQuery.getOutput())) {
return false;
Expand All @@ -246,10 +247,15 @@ public boolean refineHypothesis(DefaultQuery<I, Word<O>> ceQuery) {

@Override
public CompactMealy<I, O> getHypothesisModel() {
checkInternalState();
requireLearningProcessStarted();
return hypothesis;
}

@Override
public boolean hasLearningProcessStarted() {
return hypothesis != null;
}

@Override
public void addAlphabetSymbol(I symbol) {

Expand All @@ -275,7 +281,9 @@ public void addAlphabetSymbol(I symbol) {

this.splitters = newSplitters;

this.startLearning();
if (hasLearningProcessStarted()) {
this.startLearningInternal();
}
}
}

Expand All @@ -293,7 +301,7 @@ public void resume(MealyDHCState<I, O> state) {

@Override
public Word<I> transformAccessSequence(Word<I> word) {
checkInternalState();
requireLearningProcessStarted();
Integer state = hypothesis.getState(word);
assert state != null;
return assembleAccessSequence(accessSequences.get(state));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.function.BooleanSupplier;

import de.learnlib.AccessSequenceTransformer;
import de.learnlib.LearnerStateTracker;
import de.learnlib.Resumable;
import de.learnlib.acex.AbstractBaseCounterexample;
import de.learnlib.acex.AcexAnalyzer;
Expand Down Expand Up @@ -59,7 +60,8 @@
public class KearnsVaziraniDFA<I> implements DFALearner<I>,
AccessSequenceTransformer<I>,
SupportsGrowingAlphabet<I>,
Resumable<KearnsVaziraniDFAState<I>> {
Resumable<KearnsVaziraniDFAState<I>>,
LearnerStateTracker {

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

Expand Down Expand Up @@ -113,6 +115,7 @@ public KearnsVaziraniDFA(Alphabet<I> alphabet,

@Override
public void startLearning() {
requireLearningProcessNotStarted();
initialize();
}

Expand All @@ -138,6 +141,11 @@ public DFA<?, I> getHypothesisModel() {
return hypothesis;
}

@Override
public boolean hasLearningProcessStarted() {
return this.hypothesis.size() != 0;
}

public BinaryDTree<I, StateInfo<I, Boolean>> getDiscriminationTree() {
return discriminationTree;
}
Expand Down Expand Up @@ -315,12 +323,6 @@ private List<StateInfo<I, Boolean>> sift(List<AbstractWordBasedDTNode<I, Boolean
return result;
}

private void requireLearningProcessStarted() {
if (hypothesis.size() == 0) {
throw new IllegalStateException("Learning process has not been started");
}
}

@Override
public Word<I> transformAccessSequence(Word<I> word) {
requireLearningProcessStarted();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.Objects;

import de.learnlib.AccessSequenceTransformer;
import de.learnlib.LearnerStateTracker;
import de.learnlib.Resumable;
import de.learnlib.acex.AbstractBaseCounterexample;
import de.learnlib.acex.AcexAnalyzer;
Expand Down Expand Up @@ -62,7 +63,8 @@
public class KearnsVaziraniMealy<I, O> implements MealyLearner<I, O>,
AccessSequenceTransformer<I>,
SupportsGrowingAlphabet<I>,
Resumable<KearnsVaziraniMealyState<I, O>> {
Resumable<KearnsVaziraniMealyState<I, O>>,
LearnerStateTracker {

private static final Logger LOGGER = LoggerFactory.getLogger(KearnsVaziraniMealy.class);

Expand Down Expand Up @@ -116,6 +118,7 @@ public KearnsVaziraniMealy(Alphabet<I> alphabet,

@Override
public void startLearning() {
requireLearningProcessNotStarted();
initialize();
}

Expand Down Expand Up @@ -143,6 +146,11 @@ public boolean refineHypothesis(DefaultQuery<I, Word<O>> ceQuery) {
return hypothesis;
}

@Override
public boolean hasLearningProcessStarted() {
return this.hypothesis.size() != 0;
}

public MultiDTree<I, Word<O>, StateInfo<I, Word<O>>> getDiscriminationTree() {
return discriminationTree;
}
Expand Down Expand Up @@ -343,12 +351,6 @@ private List<StateInfo<I, Word<O>>> sift(List<AbstractWordBasedDTNode<I, Word<O>
return result;
}

private void requireLearningProcessStarted() {
if (hypothesis.size() == 0) {
throw new IllegalStateException("Learning process has not been started");
}
}

@Override
public Word<I> transformAccessSequence(Word<I> word) {
requireLearningProcessStarted();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Set;

import de.learnlib.AccessSequenceTransformer;
import de.learnlib.LearnerStateTracker;
import de.learnlib.Resumable;
import de.learnlib.datastructure.observationtable.OTLearner;
import de.learnlib.datastructure.observationtable.ObservationTable;
Expand All @@ -47,7 +48,8 @@ abstract class AbstractLLambda<M extends SuffixOutput<I, D>, I, D> implements OT
AccessSequenceTransformer<I>,
SupportsGrowingAlphabet<I>,
Resumable<LLambdaState<I, D>>,
FiniteRepresentation {
FiniteRepresentation,
LearnerStateTracker {

private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLLambda.class);

Expand Down Expand Up @@ -82,6 +84,7 @@ abstract class AbstractLLambda<M extends SuffixOutput<I, D>, I, D> implements OT

@Override
public void startLearning() {
requireLearningProcessNotStarted();
initTable();
learnLoop();
}
Expand Down Expand Up @@ -116,6 +119,11 @@ public boolean refineHypothesis(DefaultQuery<I, D> counterexample) {
return refined;
}

@Override
public boolean hasLearningProcessStarted() {
return !rows.isEmpty();
}

private void initTable() {
Word<I> epsilon = Word.epsilon();
List<D> rowData = initRow(epsilon);
Expand Down Expand Up @@ -295,12 +303,6 @@ private void addShortPrefix(Word<I> shortPrefix) {
}
}

protected void requireLearningProcessStarted() {
if (rows.isEmpty()) {
throw new IllegalStateException("Learning process has not been started");
}
}

@Override
public void addAlphabetSymbol(I symbol) {
if (!this.alphabet.containsSymbol(symbol)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Objects;

import de.learnlib.AccessSequenceTransformer;
import de.learnlib.LearnerStateTracker;
import de.learnlib.Resumable;
import de.learnlib.algorithm.LearningAlgorithm;
import de.learnlib.algorithm.lambda.ttt.dt.AbstractDecisionTree;
Expand All @@ -45,7 +46,8 @@ public abstract class AbstractTTTLambda<M extends SuffixOutput<I, D>, I, D> impl
AccessSequenceTransformer<I>,
SupportsGrowingAlphabet<I>,
Resumable<TTTLambdaState<I, D>>,
FiniteRepresentation {
FiniteRepresentation,
LearnerStateTracker {

private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTTTLambda.class);

Expand Down Expand Up @@ -75,6 +77,7 @@ protected AbstractTTTLambda(Alphabet<I> alphabet, MembershipOracle<I, D> mqs, Me

@Override
public void startLearning() {
requireLearningProcessNotStarted();
dtree().sift(mqs, ptree.root());
makeConsistent(mqs);
started = true;
Expand Down Expand Up @@ -110,6 +113,11 @@ public boolean refineHypothesis(DefaultQuery<I, D> counterexample) {
return refined;
}

@Override
public boolean hasLearningProcessStarted() {
return started;
}

@Override
public void addAlphabetSymbol(I symbol) {
if (!this.alphabet.containsSymbol(symbol)) {
Expand Down Expand Up @@ -214,12 +222,6 @@ private void analyzeCounterexample(DefaultQuery<I, D> counterexample, Deque<Defa
ua.makeShortPrefix(mqs);
}

private void requireLearningProcessStarted() {
if (!started) {
throw new IllegalStateException("Learning process has not been started");
}
}

@Override
public TTTLambdaState<I, D> suspend() {
return new TTTLambdaState<>(strie, ptree, dtree(), started);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ protected int maxSearchIndex(int ceLength) {

@Override
public DFA<?, I> getHypothesisModel() {
requireLearningProcessStarted();
return hypothesis;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ protected int maxSearchIndex(int ceLength) {

@Override
public MealyMachine<?, I, ?, O> getHypothesisModel() {
requireLearningProcessStarted();
return hypothesis;
}

Expand Down
Loading
Loading