de.learnlib.algorithms.nlstar.NLStarLearner Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of learnlib-nlstar Show documentation
Show all versions of learnlib-nlstar Show documentation
This artifact provides the implementation of the NL* learning algorithm as described in the paper "Angluin-Style
Learning of NFA" (http://ijcai.org/Proceedings/09/Papers/170.pdf) by Benedikt Bollig, Peter Habermehl,
Carsten Kern, and Martin Leucker.
/* Copyright (C) 2013-2020 TU Dortmund
* This file is part of LearnLib, http://www.learnlib.de/.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.learnlib.algorithms.nlstar;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.github.misberner.buildergen.annotations.GenerateBuilder;
import de.learnlib.api.algorithm.NFALearner;
import de.learnlib.api.oracle.MembershipOracle;
import de.learnlib.api.query.DefaultQuery;
import de.learnlib.util.MQUtil;
import net.automatalib.automata.fsa.impl.compact.CompactDFA;
import net.automatalib.automata.fsa.impl.compact.CompactNFA;
import net.automatalib.util.automata.fsa.NFAs;
import net.automatalib.words.Alphabet;
import net.automatalib.words.Word;
/**
* The NL* algorithm, as described in the paper "Angluin-Style
* Learning of NFA" by B. Bollig et al. (IJCAI'09).
*
* @param
* input symbol type
*
* @author Malte Isberner
*/
public class NLStarLearner implements NFALearner {
private final Alphabet alphabet;
private final ObservationTable table;
private CompactNFA hypothesis;
/**
* Constructor.
*
* @param alphabet
* the input alphabet
* @param oracle
* the membership oracle
*/
@GenerateBuilder
public NLStarLearner(Alphabet alphabet, MembershipOracle oracle) {
this.alphabet = alphabet;
this.table = new ObservationTable<>(alphabet, oracle);
}
@Override
public void startLearning() {
if (hypothesis != null) {
throw new IllegalStateException();
}
List>> unclosed = table.initialize();
completeConsistentTable(unclosed);
constructHypothesis();
}
/**
* Retrieves a view of this learner as a DFA learner. The DFA is obtained by determinizing and minimizing the NFA
* hypothesis.
*
* @return a DFA learner view of this learner
*
* @see #getDeterminizedHypothesis()
*/
public DFALearner asDFALearner() {
return new DFALearner() {
@Override
public String toString() {
return NLStarLearner.this.toString();
}
@Override
public void startLearning() {
NLStarLearner.this.startLearning();
}
@Override
public boolean refineHypothesis(DefaultQuery ceQuery) {
return NLStarLearner.this.refineHypothesis(ceQuery);
}
@Override
public CompactDFA getHypothesisModel() {
return NLStarLearner.this.getDeterminizedHypothesis();
}
};
}
/**
* Retrieves a deterministic version of the hypothesis. The DFA is obtained through {@link
* NFAs#determinize(net.automatalib.automata.fsa.NFA)}.
*
* @return a deterministic version of the hypothesis
*/
public CompactDFA getDeterminizedHypothesis() {
if (hypothesis == null) {
throw new IllegalStateException();
}
return NFAs.determinize(hypothesis);
}
private void completeConsistentTable(List>> initialUnclosed) {
List>> unclosed = initialUnclosed;
Inconsistency incons;
do {
while (!unclosed.isEmpty()) {
unclosed = fixUnclosed(unclosed);
}
incons = table.findInconsistency();
if (incons != null) {
unclosed = fixInconsistency(incons);
}
} while (!unclosed.isEmpty() || incons != null);
}
private List>> fixUnclosed(List>> unclosed) {
List> newShort = new ArrayList<>(unclosed.size());
for (List> unclosedClass : unclosed) {
newShort.add(unclosedClass.get(0));
}
return table.makeUpper(newShort);
}
private List>> fixInconsistency(Inconsistency incons) {
I sym = alphabet.getSymbol(incons.getSymbolIdx());
Word oldSuffix = table.getSuffix(incons.getSuffixIdx());
Word newSuffix = oldSuffix.prepend(sym);
return table.addSuffix(newSuffix);
}
@Override
public boolean refineHypothesis(DefaultQuery ceQuery) {
if (hypothesis == null) {
throw new IllegalStateException();
}
boolean refined = false;
while (MQUtil.isCounterexample(ceQuery, hypothesis)) {
Word ceWord = ceQuery.getInput();
List>> unclosed = table.addSuffixes(ceWord.suffixes(false));
completeConsistentTable(unclosed);
constructHypothesis();
refined = true;
}
return refined;
}
private void constructHypothesis() {
hypothesis = new CompactNFA<>(alphabet);
int[] stateMap = new int[table.getNumUpperRows()];
Arrays.fill(stateMap, -1);
List> upperPrimes = table.getUpperPrimes();
for (Row row : upperPrimes) {
int state = hypothesis.addIntState(row.getContent(0));
stateMap[row.getUpperId()] = state;
}
Row firstRow = table.getUpperRow(0);
if (firstRow.isPrime()) {
int state = stateMap[firstRow.getUpperId()];
hypothesis.setInitial(state, true);
} else {
for (Row row : table.getCoveredRows(firstRow)) {
if (row.isPrime()) {
int state = stateMap[row.getUpperId()];
hypothesis.setInitial(state, true);
}
}
}
// Transition relation
for (Row row : upperPrimes) {
int state = stateMap[row.getUpperId()];
for (int i = 0; i < alphabet.size(); i++) {
Row succRow = row.getSuccessorRow(i);
if (succRow.isPrime()) {
int succState = stateMap[succRow.getUpperId()];
hypothesis.addTransition(state, i, succState);
} else {
for (Row r : succRow.getCoveredRows()) {
if (r.isPrime()) {
int succState = stateMap[r.getUpperId()];
hypothesis.addTransition(state, i, succState);
}
}
}
}
}
}
@Override
public CompactNFA getHypothesisModel() {
if (hypothesis == null) {
throw new IllegalStateException();
}
return hypothesis;
}
public ObservationTable getObservationTable() {
return table;
}
}