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) 2014 TU Dortmund
 * This file is part of AutomataLib, http://www.automatalib.net/.
 * 
 * AutomataLib is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 3.0 as published by the Free Software Foundation.
 * 
 * AutomataLib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with AutomataLib; if not, see
 * http://www.gnu.de/documents/lgpl.en.html.
 */
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 net.automatalib.automata.transout.MealyMachine;
import net.automatalib.graphs.dot.DelegateDOTHelper;
import net.automatalib.graphs.dot.GraphDOTHelper;
import net.automatalib.incremental.ConflictException;
import net.automatalib.incremental.mealy.AbstractIncrementalMealyBuilder;
import net.automatalib.util.graphs.traversal.GraphTraversal;
import net.automatalib.words.Alphabet;
import net.automatalib.words.Word;
import net.automatalib.words.WordBuilder;

import com.google.common.base.Objects;
import com.google.common.collect.Iterators;

public class IncrementalMealyTreeBuilder extends AbstractIncrementalMealyBuilder {
	
	private static final class Record {
		private final S automatonState;
		private final Node treeNode;
		private final I incomingInput;
		private final Iterator inputIt;
		
		public 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
		@Nonnull
		public Node getInitialNode() {
			return root;
		}
		@Override
		@Nullable
		public I getInputSymbol(AnnotatedEdge edge) {
			return edge.getInput();
		}
		@Override
		@Nullable
		public O getOutputSymbol(AnnotatedEdge edge) {
			return edge.getOutput();
		}
		@Override
		public GraphDOTHelper, AnnotatedEdge> getGraphDOTHelper() {
			return new DelegateDOTHelper,AnnotatedEdge>(super.getGraphDOTHelper()) {
				private int id = 0;
				@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 extends AbstractTransitionSystemView, Edge> {
		@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();
		}
	}
	
	
	private final Node root;
	
	public IncrementalMealyTreeBuilder(Alphabet inputAlphabet) {
		super(inputAlphabet);
		this.root = new Node<>(inputAlphabet.size());
	}

	/*
	 * (non-Javadoc)
	 * @see net.automatalib.incremental.mealy.IncrementalMealyBuilder#insert(net.automatalib.words.Word, net.automatalib.words.Word)
	 */
	@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);
			}
		}
	}
	
	/*
	 * (non-Javadoc)
	 * @see net.automatalib.incremental.mealy.IncrementalMealyBuilder#lookup(net.automatalib.words.Word, java.util.List)
	 */
	@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;
	}

	/*
	 * (non-Javadoc)
	 * @see net.automatalib.incremental.IncrementalConstruction#findSeparatingWord(java.lang.Object, java.util.Collection, boolean)
	 */
	@Override
	public Word findSeparatingWord(MealyMachine target,
			Collection inputs, boolean omitUndefined) {
		return doFindSeparatingWord(target, inputs, omitUndefined);
	}


	/*
	 * (non-Javadoc)
	 * @see net.automatalib.incremental.mealy.AbstractIncrementalMealyBuilder#hasDefinitiveInformation(net.automatalib.words.Word)
	 */
	@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  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();
				do {
					wb.append(rec.incomingInput);
					rec = dfsStack.pop();
				} while(!dfsStack.isEmpty());
				return wb.reverse().toWord();
			}
			
			dfsStack.push(new Record<>(target.getSuccessor(trans), edge.getTarget(), input, inputs.iterator()));
		}
		
		return null;
	}
	
	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 TransitionSystemView asTransitionSystem() {
		return new TransitionSystemView();
	}
	@Override
	public GraphView asGraph() {
		return new GraphView();
	}
}