All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.learnlib.algorithm.observationpack.vpa.AbstractVPALearner Maven / Gradle / Ivy

Go to download

This artifact provides the implementation of the VPA adaption of the Observation-Pack learning algorithm as discussed in the PhD thesis "Foundations of Active Automata Learning: An Algorithmic Perspective" (https://dx.doi.org/10.17877/DE290R-16359) by Malte Isberner.

The newest version!
/* 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 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()); } }