
de.learnlib.algorithm.observationpack.vpa.AbstractVPALearner Maven / Gradle / Ivy
Show all versions of learnlib-observation-pack-vpa Show documentation
/* Copyright (C) 2013-2023 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.algorithm.observationpack.vpa;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import de.learnlib.AccessSequenceProvider;
import de.learnlib.algorithm.LearningAlgorithm;
import de.learnlib.algorithm.observationpack.vpa.hypothesis.AbstractHypTrans;
import de.learnlib.algorithm.observationpack.vpa.hypothesis.ContextPair;
import de.learnlib.algorithm.observationpack.vpa.hypothesis.DTNode;
import de.learnlib.algorithm.observationpack.vpa.hypothesis.DTree;
import de.learnlib.algorithm.observationpack.vpa.hypothesis.HypIntTrans;
import de.learnlib.algorithm.observationpack.vpa.hypothesis.HypLoc;
import de.learnlib.algorithm.observationpack.vpa.hypothesis.HypRetTrans;
import de.learnlib.algorithm.observationpack.vpa.hypothesis.OneSEVPAHypothesis;
import de.learnlib.algorithm.observationpack.vpa.hypothesis.TransList;
import de.learnlib.oracle.MembershipOracle;
import de.learnlib.query.DefaultQuery;
import net.automatalib.alphabet.VPAlphabet;
import net.automatalib.automaton.vpa.OneSEVPA;
import net.automatalib.common.smartcollection.ElementReference;
import net.automatalib.common.smartcollection.UnorderedCollection;
import net.automatalib.word.Word;
/**
* @param
* input alphabet type
*/
public abstract class AbstractVPALearner implements LearningAlgorithm, I, Boolean> {
protected final VPAlphabet alphabet;
protected final MembershipOracle oracle;
protected final DTree dtree;
protected final OneSEVPAHypothesis hypothesis;
protected final TransList openTransitions = new TransList<>();
public AbstractVPALearner(VPAlphabet alphabet, MembershipOracle oracle) {
this.alphabet = alphabet;
this.oracle = oracle;
this.dtree = new DTree<>(oracle);
dtree.getRoot().split(new ContextPair<>(Word.epsilon(), Word.epsilon()), false, true);
this.hypothesis = new OneSEVPAHypothesis<>(alphabet);
}
@Override
public void startLearning() {
HypLoc initLoc = hypothesis.initialize();
DTNode leaf = dtree.sift(initLoc.getAccessSequence());
link(leaf, initLoc);
initializeLocation(initLoc);
closeTransitions();
}
@Override
public boolean refineHypothesis(DefaultQuery ceQuery) {
if (hypothesis.computeSuffixOutput(ceQuery.getPrefix(), ceQuery.getSuffix()).equals(ceQuery.getOutput())) {
return false;
}
while (refineHypothesisSingle(ceQuery)) {}
return true;
}
protected abstract boolean refineHypothesisSingle(DefaultQuery ceQuery);
@Override
public OneSEVPA, I> getHypothesisModel() {
return hypothesis;
}
public DTree getDiscriminationTree() {
return dtree;
}
protected static void link(DTNode leaf, HypLoc loc) {
assert leaf.isLeaf();
leaf.setData(loc);
loc.setLeaf(leaf);
}
protected void initializeLocation(HypLoc loc) {
final Boolean subtreeLabel = dtree.getRoot().subtreeLabel(loc.getLeaf());
assert subtreeLabel != null;
loc.setAccepting(subtreeLabel);
for (int i = 0; i < alphabet.getNumInternals(); i++) {
I intSym = alphabet.getInternalSymbol(i);
HypIntTrans trans = new HypIntTrans<>(loc, intSym);
loc.setInternalTransition(i, trans);
openTransitions.add(trans);
}
loc.updateStackAlphabetSize(hypothesis.getNumStackSymbols());
for (int i = 0; i < alphabet.getNumCalls(); i++) {
I callSym = alphabet.getCallSymbol(i);
int myStackSym = hypothesis.encodeStackSym(loc, i);
for (HypLoc stackLoc : hypothesis.getLocations()) {
stackLoc.updateStackAlphabetSize(hypothesis.getNumStackSymbols());
int stackSym = hypothesis.encodeStackSym(stackLoc, i);
for (int j = 0; j < alphabet.getNumReturns(); j++) {
I retSym = alphabet.getReturnSymbol(j);
HypRetTrans trans = new HypRetTrans<>(loc, retSym, callSym, stackLoc);
loc.setReturnTransition(j, stackSym, trans);
openTransitions.add(trans);
if (loc != stackLoc) {
HypRetTrans retTrans = new HypRetTrans<>(stackLoc, retSym, callSym, loc);
stackLoc.setReturnTransition(j, myStackSym, retTrans);
openTransitions.add(retTrans);
}
}
}
}
}
protected void closeTransitions() {
UnorderedCollection> newStateNodes = new UnorderedCollection<>();
do {
newStateNodes.addAll(closeTransitions(openTransitions, false));
if (!newStateNodes.isEmpty()) {
addNewStates(newStateNodes);
}
} while (!openTransitions.isEmpty());
}
/**
* Ensures that the specified transitions point to a leaf-node. If a transition is a tree transition, this method
* has no effect.
*
* The provided transList is consumed in this process.
*
* If a transition needs sifting, the reached leaf node will be collected in the returned collection.
*
* @param transList
* the list of transitions
*
* @return a collection containing the reached leaves of transitions that needed sifting
*/
private List> closeTransitions(TransList transList, boolean hard) {
final List> transToSift = new ArrayList<>(transList.size());
AbstractHypTrans t;
while ((t = transList.poll()) != null) {
if (!t.isTree()) {
transToSift.add(t);
}
}
if (transToSift.isEmpty()) {
return Collections.emptyList();
}
final Iterator> leavesIter = updateDTTargets(transToSift, hard).iterator();
final List> result = new ArrayList<>(transToSift.size());
for (AbstractHypTrans transition : transToSift) {
final DTNode node = leavesIter.next();
if (node.isLeaf() && node.getData() == null && transition.getNextElement() == null) {
result.add(node);
}
}
assert !leavesIter.hasNext();
return result;
}
private void addNewStates(UnorderedCollection> newStateNodes) {
DTNode minTransNode = null;
AbstractHypTrans minTrans = null;
int minAsLen = Integer.MAX_VALUE;
ElementReference minTransNodeRef = null;
for (ElementReference ref : newStateNodes.references()) {
DTNode newStateNode = newStateNodes.get(ref);
for (AbstractHypTrans trans : newStateNode.getIncoming()) {
Word as = trans.getAccessSequence();
int asLen = as.length();
if (asLen < minAsLen) {
minTransNode = newStateNode;
minTrans = trans;
minAsLen = asLen;
minTransNodeRef = ref;
}
}
}
assert minTransNode != null;
newStateNodes.remove(minTransNodeRef);
assert minTrans.getNonTreeTarget().getData() == null;
HypLoc newLoc = makeTree(minTrans);
link(minTransNode, newLoc);
initializeLocation(newLoc);
}
protected List> updateDTTargets(List> trans, boolean hard) {
final List> nodes = new ArrayList<>(trans.size());
final List> prefixes = new ArrayList<>(trans.size());
for (AbstractHypTrans t : trans) {
if (!t.isTree()) {
DTNode start = t.getNonTreeTarget();
if (start == null) {
t.setNonTreeTarget(dtree.getRoot());
start = dtree.getRoot();
}
nodes.add(start);
prefixes.add(t.getAccessSequence());
}
}
final Iterator> leavesIter = dtree.sift(nodes, prefixes, hard).iterator();
final List> result = new ArrayList<>(trans.size());
for (AbstractHypTrans t : trans) {
if (t.isTree()) {
result.add(t.getTargetNode());
} else {
final DTNode leaf = leavesIter.next();
t.setNonTreeTarget(leaf);
leaf.addIncoming(t);
result.add(leaf);
}
}
assert !leavesIter.hasNext();
return result;
}
protected HypLoc makeTree(AbstractHypTrans trans) {
assert !trans.isTree();
HypLoc newLoc = createLocation(trans);
trans.makeTree(newLoc);
return newLoc;
}
protected HypLoc createLocation(AbstractHypTrans trans) {
return hypothesis.createLocation(false, trans);
}
protected Boolean query(AccessSequenceProvider asp, ContextPair context) {
return oracle.answerQuery(context.getPrefix().concat(asp.getAccessSequence()), context.getSuffix());
}
}