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

de.learnlib.algorithms.adt.model.ObservationTree Maven / Gradle / Ivy

/* Copyright (C) 2013-2018 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.adt.model;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

import javax.annotation.ParametersAreNonnullByDefault;

import de.learnlib.algorithms.adt.adt.ADTNode;
import de.learnlib.algorithms.adt.util.ADTUtil;
import net.automatalib.automata.transout.impl.FastMealy;
import net.automatalib.automata.transout.impl.FastMealyState;
import net.automatalib.commons.util.Pair;
import net.automatalib.util.automata.equivalence.NearLinearEquivalenceTest;
import net.automatalib.words.Alphabet;
import net.automatalib.words.Word;

/**
 * A class, that stores observations of the system under learning in a tree-like structure. Can be used to 
  • * Store output behavior information about the system under learning
  • Query output behavior of the system * under learning if it has been stored before (i.e. cache)
  • Find separating words of hypothesis states based * on the stored output behavior information
* * @param * (hypothesis) state type * @param * input alphabet type * @param * output alphabet type * * @author frohme */ @ParametersAreNonnullByDefault public class ObservationTree { private final Alphabet alphabet; private final FastMealy observationTree; private final Map> nodeToObservationMap; public ObservationTree(final Alphabet alphabet) { this.alphabet = alphabet; this.observationTree = new FastMealy<>(alphabet); this.nodeToObservationMap = new HashMap<>(); } public FastMealy getObservationTree() { return observationTree; } /** * Initialize the observation tree with initial hypothesis state. Usually used during {@link * de.learnlib.api.algorithm.LearningAlgorithm#startLearning()} * * @param state * the initial state of the hypothesis */ public void initialize(final S state) { final FastMealyState init = this.observationTree.addInitialState(); this.nodeToObservationMap.put(state, init); } /** * Extended initialization method, that allows to initialize the observation tree with several hypothesis states. * * @param states * The hypothesis states to initialize the observation tree with * @param asFunction * Function to compute the access sequence of a node * @param outputFunction * Function to compute the output of the access sequences */ public void initialize(final Collection states, final Function> asFunction, final Function, Word> outputFunction) { final FastMealyState init = this.observationTree.addInitialState(); for (final S s : states) { final Word as = asFunction.apply(s); final FastMealyState treeNode = this.addTrace(init, as, outputFunction.apply(as)); this.nodeToObservationMap.put(s, treeNode); } } /** * Store input/output information about a hypothesis state in the internal data structure. * * @param state * the hypothesis state for which information should be stored * @param input * the input sequence applied when in the given state * @param output * the observed output sequence */ public void addTrace(final S state, final Word input, final Word output) { this.addTrace(this.nodeToObservationMap.get(state), input, output); } private FastMealyState addTrace(final FastMealyState state, final Word input, final Word output) { assert input.length() == output.length() : "Traces differ in length"; final Iterator inputIter = input.iterator(); final Iterator outputIter = output.iterator(); FastMealyState iter = state; while (inputIter.hasNext()) { final I nextInput = inputIter.next(); final O nextOuput = outputIter.next(); final FastMealyState nextState; if (this.observationTree.getTransition(iter, nextInput) == null) { nextState = this.observationTree.addState(); this.observationTree.addTransition(iter, nextInput, nextState, nextOuput); } else { assert this.observationTree.getOutput(iter, nextInput).equals(nextOuput) : "Inconsistent observations"; nextState = this.observationTree.getSuccessor(iter, nextInput); } iter = nextState; } return iter; } /** * See {@link #addState(Object, Word, Object)}. Convenience method that stores all information that the traces of * the given {@link ADTNode} holds. * * @param state * the hypothesis state for which information should be stored * @param adtNode * the {@link ADTNode} whose traces should be stored */ public void addTrace(final S state, final ADTNode adtNode) { final FastMealyState internalState = this.nodeToObservationMap.get(state); ADTNode adsIter = adtNode; while (adsIter != null) { final Pair, Word> trace = ADTUtil.buildTraceForNode(adsIter); this.addTrace(internalState, trace.getFirst(), trace.getSecond()); adsIter = ADTUtil.getStartOfADS(adsIter).getParent(); } } /** * Registers a new hypothesis state at the observation tree. It is expected to register states in the order of their * discovery, meaning whenever a new state is added, information about all prefixes of its access sequence are * already stored. Therefore providing only the output of the last symbol of its access sequence is sufficient. * * @param newState * the hypothesis state in question * @param accessSequence * the access sequence of the hypothesis state in the system under learning * @param output * the output of the last symbol of the access sequence. */ public void addState(final S newState, final Word accessSequence, final O output) { final Word prefix = accessSequence.prefix(accessSequence.length() - 1); final I sym = accessSequence.lastSymbol(); final FastMealyState pred = this.observationTree.getSuccessor(this.observationTree.getInitialState(), prefix); final FastMealyState target; if (pred.getTransition(alphabet.getSymbolIndex(sym)) == null) { target = this.observationTree.addState(); this.observationTree.addTransition(pred, sym, target, output); } else { target = this.observationTree.getSuccessor(pred, sym); } this.nodeToObservationMap.put(newState, target); } /** * Find a separating word for two hypothesis states, after applying given input sequence first. * * @param s1 * first state * @param s2 * second state * @param prefix * input sequence * * @return A {@link Word} separating the two states reached after applying the prefix to s1 and s2. {@code * Optional.empty()} if not separating word exists. */ public Optional> findSeparatingWord(final S s1, final S s2, final Word prefix) { final FastMealyState n1 = this.nodeToObservationMap.get(s1); final FastMealyState n2 = this.nodeToObservationMap.get(s2); final FastMealyState s1Succ = this.observationTree.getSuccessor(n1, prefix); final FastMealyState s2Succ = this.observationTree.getSuccessor(n2, prefix); if (s1Succ != null && s2Succ != null) { final Word sepWord = NearLinearEquivalenceTest.findSeparatingWord(this.observationTree, s1Succ, s2Succ, alphabet, true); if (sepWord != null) { return Optional.of(sepWord); } } return Optional.empty(); } /** * Find a separating word for two hypothesis states. * * @param s1 * first state * @param s2 * second state * * @return A {@link Word} separating the two words. {@code null} if no such word is found. */ public Word findSeparatingWord(final S s1, final S s2) { final FastMealyState n1 = this.nodeToObservationMap.get(s1); final FastMealyState n2 = this.nodeToObservationMap.get(s2); return NearLinearEquivalenceTest.findSeparatingWord(this.observationTree, n1, n2, this.alphabet, true); } /** * Computes the output of the system under learning when applying the given input sequence in the given hypothesis * state. Requires the input/output behavior information to be stored before. * * @param s * the hypothesis state * @param input * the input sequence of interest * * @return the previously stored output behavior of the system under learning */ public Word trace(final S s, final Word input) { final FastMealyState q = this.nodeToObservationMap.get(s); return this.observationTree.computeStateOutput(q, input); } }