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

de.learnlib.algorithm.observationpack.vpa.OPLearnerVPA 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.List;

import com.github.misberner.buildergen.annotations.GenerateBuilder;
import com.google.common.collect.Iterables;
import de.learnlib.acex.AbstractBaseCounterexample;
import de.learnlib.acex.AcexAnalyzer;
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.HypLoc;
import de.learnlib.oracle.MembershipOracle.DFAMembershipOracle;
import de.learnlib.query.DefaultQuery;
import net.automatalib.alphabet.VPAlphabet;
import net.automatalib.automaton.vpa.StackContents;
import net.automatalib.automaton.vpa.State;
import net.automatalib.word.Word;
import net.automatalib.word.WordBuilder;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * @param 
 *         input alphabet type
 */
public class OPLearnerVPA extends AbstractVPALearner {

    protected final AcexAnalyzer analyzer;

    @GenerateBuilder
    public OPLearnerVPA(VPAlphabet alphabet, DFAMembershipOracle oracle, AcexAnalyzer analyzer) {
        super(alphabet, oracle);
        this.analyzer = analyzer;
    }

    protected State> getDefinitiveSuccessor(State> baseState, Word suffix) {
        return hypothesis.getSuccessor(baseState, suffix);
    }

    protected Word transformAccessSequence(State> state) {
        return transformAccessSequence(state.getStackContents(), state.getLocation());
    }

    protected Word transformAccessSequence(@Nullable StackContents contents) {
        return transformAccessSequence(contents, hypothesis.getInitialLocation());
    }

    protected Word transformAccessSequence(@Nullable StackContents contents, HypLoc loc) {
        List stackElems = new ArrayList<>();
        if (contents != null) {
            StackContents iter = contents;
            while (iter != null) {
                stackElems.add(iter.peek());
                iter = iter.pop();
            }
        }
        WordBuilder wb = new WordBuilder<>();
        for (int i = stackElems.size() - 1; i >= 0; i--) {
            int elem = stackElems.get(i);
            HypLoc stackLoc = hypothesis.getStackLoc(elem);
            wb.append(stackLoc.getAccessSequence());
            I callSym = hypothesis.getCallSym(elem);
            wb.append(callSym);
        }
        wb.append(loc.getAccessSequence());
        return wb.toWord();
    }

    @Override
    protected boolean refineHypothesisSingle(DefaultQuery ceQuery) {
        Word ceWord = ceQuery.getInput();
        boolean hypOut = hypothesis.computeOutput(ceWord);
        if (hypOut == ceQuery.getOutput()) {
            return false;
        }
        PrefixTransformAcex acex = new PrefixTransformAcex(Word.epsilon(), new ContextPair<>(Word.epsilon(), ceWord));
        acex.setEffect(0, !hypOut);
        acex.setEffect(acex.getLength() - 1, hypOut);

        int breakpoint = analyzer.analyzeAbstractCounterexample(acex);

        Word prefix = ceWord.prefix(breakpoint);
        I act = ceWord.getSymbol(breakpoint);
        Word suffix = ceWord.subWord(breakpoint + 1);

        State> state = hypothesis.getState(prefix);
        assert state != null;
        State> succState = hypothesis.getSuccessor(state, act);
        assert succState != null;

        ContextPair context = new ContextPair<>(transformAccessSequence(succState.getStackContents()), suffix);

        AbstractHypTrans trans = hypothesis.getInternalTransition(state, act);
        assert trans != null;

        HypLoc newLoc = makeTree(trans);
        DTNode oldDtNode = succState.getLocation().getLeaf();
        openTransitions.addAll(oldDtNode.getIncoming());
        DTNode.SplitResult children = oldDtNode.split(context, acex.effect(breakpoint), acex.effect(breakpoint + 1));
        link(children.nodeOld, newLoc);
        link(children.nodeNew, succState.getLocation());
        initializeLocation(trans.getTreeTarget());

        closeTransitions();

        return true;
    }

    protected class PrefixTransformAcex extends AbstractBaseCounterexample {

        private final Word suffix;

        private final State> baseState;

        public PrefixTransformAcex(Word word, ContextPair context) {
            super(context.getSuffix().length() + 1);
            this.suffix = context.getSuffix();
            this.baseState = hypothesis.getState(Iterables.concat(context.getPrefix(), word));
        }

        public State> getBaseState() {
            return baseState;
        }

        public Word getSuffix() {
            return suffix;
        }

        @Override
        public boolean checkEffects(Boolean eff1, Boolean eff2) {
            return eff1.equals(eff2);
        }

        @Override
        protected Boolean computeEffect(int index) {
            Word suffPref = suffix.prefix(index);
            State> state = getDefinitiveSuccessor(baseState, suffPref);
            Word suffSuff = suffix.subWord(index);

            return oracle.answerQuery(transformAccessSequence(state), suffSuff);
        }

    }

}