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

de.learnlib.algorithm.ttt.dfa.PrefixTTTLearnerDFA Maven / Gradle / Ivy

Go to download

This artifact provides the implementation of the TTT algorithm as described in the paper "The TTT Algorithm: A Redundancy-Free Approach to Active Automata Learning" (https://doi.org/10.1007/978-3-319-11164-3_26) by Malte Isberner, Falk Howar, and Bernhard Steffen.

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.ttt.dfa;

import java.util.Iterator;

import com.google.common.collect.AbstractIterator;
import de.learnlib.acex.AbstractCounterexample;
import de.learnlib.acex.AcexAnalyzer;
import de.learnlib.algorithm.ttt.base.TTTState;
import de.learnlib.algorithm.ttt.base.TTTTransition;
import de.learnlib.oracle.MembershipOracle;
import de.learnlib.query.DefaultQuery;
import net.automatalib.alphabet.Alphabet;
import net.automatalib.common.smartcollection.ArrayStorage;
import net.automatalib.word.Word;

public class PrefixTTTLearnerDFA extends TTTLearnerDFA {

    private final ExtDTNode unlabeledList = new ExtDTNode<>();

    public PrefixTTTLearnerDFA(Alphabet alphabet, MembershipOracle oracle, AcexAnalyzer analyzer) {
        super(alphabet, oracle, analyzer, ExtDTNode::new);
    }

    @Override
    public boolean refineHypothesis(DefaultQuery ceQuery) {
        boolean refined = refineHypothesisSingle(ceQuery);
        if (!refined) {
            return false;
        }

        while (refineHypothesisSingle(ceQuery)) {}

        return true;
    }

    @Override
    protected boolean refineHypothesisSingle(DefaultQuery ceQuery) {
        if (getHypothesisModel().computeSuffixOutput(ceQuery.getPrefix(), ceQuery.getSuffix())
                                .equals(ceQuery.getOutput())) {
            return false;
        }

        Word ceWord = ceQuery.getInput();
        int currReachInconsLength = ceWord.length();

        EasyTTTPrefAcex acex = new EasyTTTPrefAcex(ceWord);
        do {
            acex.update(currReachInconsLength);
            int breakpoint = analyzer.analyzeAbstractCounterexample(acex, 0, currReachInconsLength);
            ExtDTNode toSplit = acex.getHypNode(breakpoint);
            TTTState splitState = toSplit.getData();
            ExtDTNode lca = acex.getLCA(breakpoint + 1);
            I sym = ceWord.getSymbol(breakpoint);
            Word newDiscr = lca.getDiscriminator().prepend(sym);
            ExtDTNode succHyp = acex.getHypNode(breakpoint + 1);
            Boolean hypOut = lca.subtreeLabel(succHyp);
            assert hypOut != null;
            openTransitions.insertAllIncoming(toSplit.getIncoming());
            ExtDTNode.SplitResult splitResult = toSplit.split(newDiscr, hypOut, !hypOut);
            link(splitResult.nodeOld, splitState);
            ExtDTNode extUnlabeled = (ExtDTNode) splitResult.nodeNew;
            extUnlabeled.tempPrefix = currReachInconsLength;
            unlabeledList.addUnlabeled(extUnlabeled);
            closeTransitions();

            currReachInconsLength = findMinReachIncons();
        } while (currReachInconsLength != -1);

        return true;
    }

    @Override
    protected TTTState makeTree(TTTTransition trans) {
        ExtDTNode node = (ExtDTNode) trans.getNonTreeTarget();
        if (node.tempPrefix != -1) {
            node.removeFromUnlabeledList();
        }
        return super.makeTree(trans);
    }

    private int findMinReachIncons() {
        int minLength = -1;
        for (ExtDTNode n : unlabeledList.unlabeled()) {
            int len = n.tempPrefix;
            if (minLength == -1 || len < minLength) {
                minLength = len;
            }
        }
        return minLength;
    }

    protected static class ExtDTNode extends TTTDTNodeDFA {

        private ExtDTNode prevUnlabeled, nextUnlabeled;
        private int tempPrefix = -1;

        public ExtDTNode() {
            // default constructor
        }

        public ExtDTNode(ExtDTNode parent, Boolean parentOut) {
            super(parent, parentOut);
        }

        public void removeFromUnlabeledList() {
            prevUnlabeled.nextUnlabeled = nextUnlabeled;
            if (nextUnlabeled != null) {
                nextUnlabeled.prevUnlabeled = prevUnlabeled;
            }
        }

        @Override
        protected ExtDTNode createChild(Boolean outcome, TTTState data) {
            return new ExtDTNode<>(this, outcome);
        }

        public boolean hasUnlabeled() {
            return nextUnlabeled != null;
        }

        public void addUnlabeled(ExtDTNode node) {
            node.nextUnlabeled = nextUnlabeled;
            if (nextUnlabeled != null) {
                nextUnlabeled.prevUnlabeled = node;
            }
            node.prevUnlabeled = this;
            this.nextUnlabeled = node;
        }

        public Iterable> unlabeled() {
            return this::unlabeledIterator;
        }

        public Iterator> unlabeledIterator() {
            return new UnlabeledIterator<>(this);
        }

        private static class UnlabeledIterator extends AbstractIterator> {

            private ExtDTNode curr;

            UnlabeledIterator(ExtDTNode curr) {
                this.curr = curr;
            }

            @Override
            protected ExtDTNode computeNext() {
                curr = curr.nextUnlabeled;
                if (curr == null) {
                    return endOfData();
                }
                return curr;
            }
        }
    }

    private final class EasyTTTPrefAcex implements AbstractCounterexample {

        private final Word ceWord;
        private final ArrayStorage> hypNodes;
        private final ArrayStorage> siftNodes;

        EasyTTTPrefAcex(Word ceWord) {
            this.ceWord = ceWord;
            this.hypNodes = new ArrayStorage<>(ceWord.length() + 1);
            this.siftNodes = new ArrayStorage<>(ceWord.length() + 1);

            update(ceWord.length());
        }

        public void update(int len) {
            TTTStateDFA curr = (TTTStateDFA) hypothesis.getInitialState();
            assert curr != null;
            hypNodes.set(0, (ExtDTNode) curr.getDTLeaf());
            siftNodes.set(0, (ExtDTNode) curr.getDTLeaf());

            boolean wasTree = true;
            for (int i = 0; i < len; i++) {
                I sym = ceWord.getSymbol(i);
                TTTTransition trans = hypothesis.getInternalTransition(curr, sym);
                curr = (TTTStateDFA) trans.getTarget();

                hypNodes.set(i + 1, (ExtDTNode) curr.getDTLeaf());
                if (wasTree) {
                    siftNodes.set(i + 1, (ExtDTNode) curr.getDTLeaf());
                    if (!trans.isTree()) {
                        wasTree = false;
                    }
                }

            }
        }

        @Override
        public int getLength() {
            return ceWord.length() + 1;
        }

        @Override
        public boolean checkEffects(Boolean eff1, Boolean eff2) {
            return !eff1 || eff2;
        }

        @Override
        public Boolean effect(int index) {
            ExtDTNode hypNode = hypNodes.get(index);
            ExtDTNode siftNode = siftNodes.get(index);
            if (siftNode == null) {
                siftNode = (ExtDTNode) dtree.getRoot();
            }

            ExtDTNode lca = (ExtDTNode) dtree.leastCommonAncestor(hypNode, siftNode);
            Word cePref = ceWord.prefix(index);
            while (lca == siftNode && siftNode != hypNode) {
                Boolean out = oracle.answerQuery(cePref, siftNode.getDiscriminator());
                siftNode = (ExtDTNode) siftNode.getChild(out);
                lca = (ExtDTNode) dtree.leastCommonAncestor(hypNode, siftNode);
            }
            siftNodes.set(index, siftNode);

            return siftNode == hypNode;
        }

        public ExtDTNode getLCA(int index) {
            return (ExtDTNode) dtree.leastCommonAncestor(hypNodes.get(index), siftNodes.get(index));
        }

        public ExtDTNode getHypNode(int index) {
            return hypNodes.get(index);
        }

    }

}