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

net.automatalib.incremental.mealy.tree.IncrementalMealyTreeBuilder Maven / Gradle / Ivy

Go to download

A library for incremental automata construction. This artifact contains algorithms for incrementally constructing DFAs (prefix-closed and non-prefix-closed), Mealy machines, and Moore machines from a finite, incrementally growing set of example inputs/outputs.

There is a newer version: 0.11.0
Show newest version
/* Copyright (C) 2013-2018 TU Dortmund
 * This file is part of AutomataLib, http://www.automatalib.net/.
 *
 * 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 net.automatalib.incremental.mealy.tree;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.google.common.base.Objects;
import com.google.common.collect.Iterators;
import net.automatalib.automata.transout.MealyMachine;
import net.automatalib.incremental.ConflictException;
import net.automatalib.incremental.mealy.AbstractIncrementalMealyBuilder;
import net.automatalib.ts.transout.MealyTransitionSystem;
import net.automatalib.util.graphs.traversal.GraphTraversal;
import net.automatalib.visualization.VisualizationHelper;
import net.automatalib.visualization.helper.DelegateVisualizationHelper;
import net.automatalib.words.Alphabet;
import net.automatalib.words.Word;
import net.automatalib.words.WordBuilder;

public class IncrementalMealyTreeBuilder extends AbstractIncrementalMealyBuilder {

    private final Node root;

    public IncrementalMealyTreeBuilder(Alphabet inputAlphabet) {
        super(inputAlphabet);
        this.root = new Node<>(inputAlphabet.size());
    }

    @Override
    public boolean lookup(Word word, List output) {
        Node curr = root;

        for (I sym : word) {
            int symIdx = inputAlphabet.getSymbolIndex(sym);
            Edge edge = curr.getEdge(symIdx);
            if (edge == null) {
                return false;
            }
            output.add(edge.getOutput());
            curr = edge.getTarget();
        }

        return true;
    }

    @Override
    public void insert(Word input, Word outputWord) throws ConflictException {
        Node curr = root;

        Iterator outputIt = outputWord.iterator();
        for (I sym : input) {
            int symIdx = inputAlphabet.getSymbolIndex(sym);
            O out = outputIt.next();
            Edge edge = curr.getEdge(symIdx);
            if (edge == null) {
                curr = insertNode(curr, symIdx, out);
            } else {
                if (!Objects.equal(out, edge.getOutput())) {
                    throw new ConflictException();
                }
                curr = curr.getSuccessor(symIdx);
            }
        }
    }

    private Node insertNode(Node parent, int symIdx, O output) {
        Node succ = new Node<>(inputAlphabet.size());
        Edge edge = new Edge<>(output, succ);
        parent.setEdge(symIdx, edge);
        return succ;
    }

    @Override
    public GraphView asGraph() {
        return new GraphView();
    }

    @Override
    public TransitionSystemView asTransitionSystem() {
        return new TransitionSystemView();
    }

    @Override
    public Word findSeparatingWord(MealyMachine target,
                                      Collection inputs,
                                      boolean omitUndefined) {
        return doFindSeparatingWord(target, inputs, omitUndefined);
    }

    private  Word doFindSeparatingWord(MealyMachine target,
                                                Collection inputs,
                                                boolean omitUndefined) {
        Deque> dfsStack = new ArrayDeque<>();

        dfsStack.push(new Record<>(target.getInitialState(), root, null, inputs.iterator()));

        while (!dfsStack.isEmpty()) {
            Record rec = dfsStack.peek();
            if (!rec.inputIt.hasNext()) {
                dfsStack.pop();
                continue;
            }
            I input = rec.inputIt.next();
            int inputIdx = inputAlphabet.getSymbolIndex(input);

            Edge edge = rec.treeNode.getEdge(inputIdx);
            if (edge == null) {
                continue;
            }

            T trans = target.getTransition(rec.automatonState, input);
            if (omitUndefined && trans == null) {
                continue;
            }
            if (trans == null || !Objects.equal(target.getTransitionOutput(trans), edge.getOutput())) {

                WordBuilder wb = new WordBuilder<>(dfsStack.size());
                wb.append(input);
                dfsStack.pop();

                while (!dfsStack.isEmpty()) {
                    wb.append(rec.incomingInput);
                    rec = dfsStack.pop();
                }
                return wb.reverse().toWord();
            }

            dfsStack.push(new Record<>(target.getSuccessor(trans), edge.getTarget(), input, inputs.iterator()));
        }

        return null;
    }

    @Override
    public boolean hasDefinitiveInformation(Word word) {
        Node curr = root;

        Iterator symIt = word.iterator();
        while (symIt.hasNext() && curr != null) {
            int symIdx = inputAlphabet.getSymbolIndex(symIt.next());
            curr = curr.getSuccessor(symIdx);
        }
        return (curr != null);
    }

    private static final class Record {

        private final S automatonState;
        private final Node treeNode;
        private final I incomingInput;
        private final Iterator inputIt;

        Record(S automatonState, Node treeNode, I incomingInput, Iterator inputIt) {
            this.automatonState = automatonState;
            this.treeNode = treeNode;
            this.inputIt = inputIt;
            this.incomingInput = incomingInput;
        }
    }

    public class GraphView extends AbstractGraphView, AnnotatedEdge> {

        @Override
        public Collection> getNodes() {
            List> result = new ArrayList<>();
            Iterators.addAll(result, GraphTraversal.dfIterator(this, Collections.singleton(root)));
            return result;
        }

        @Override
        public Collection> getOutgoingEdges(Node node) {
            List> result = new ArrayList<>();
            for (int i = 0; i < inputAlphabet.size(); i++) {
                Edge edge = node.getEdge(i);
                if (edge != null) {
                    result.add(new AnnotatedEdge<>(edge, inputAlphabet.getSymbol(i)));
                }
            }
            return result;
        }

        @Override
        public Node getTarget(AnnotatedEdge edge) {
            return edge.getTarget();
        }

        @Override
        @Nullable
        public I getInputSymbol(AnnotatedEdge edge) {
            return edge.getInput();
        }

        @Override
        @Nullable
        public O getOutputSymbol(AnnotatedEdge edge) {
            return edge.getOutput();
        }

        @Override
        @Nonnull
        public Node getInitialNode() {
            return root;
        }

        @Override
        public VisualizationHelper, AnnotatedEdge> getVisualizationHelper() {
            return new DelegateVisualizationHelper, AnnotatedEdge>(super.getVisualizationHelper()) {

                private int id;

                @Override
                public boolean getNodeProperties(Node node, Map properties) {
                    if (!super.getNodeProperties(node, properties)) {
                        return false;
                    }
                    properties.put(NodeAttrs.LABEL, "n" + (id++));
                    return true;
                }
            };
        }

    }

    public class TransitionSystemView implements MealyTransitionSystem, I, Edge, O> {

        @Override
        public Edge getTransition(Node state, I input) {
            int inputIdx = inputAlphabet.getSymbolIndex(input);
            return state.getEdge(inputIdx);
        }

        @Override
        public Node getSuccessor(Edge transition) {
            return transition.getTarget();
        }

        @Override
        public Node getInitialState() {
            return root;
        }

        @Override
        public O getTransitionOutput(Edge transition) {
            return transition.getOutput();
        }
    }
}