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

net.automatalib.util.automata.equivalence.NearLinearEquivalenceTest Maven / Gradle / Ivy

Go to download

This artifact provides various common utility operations for analyzing and manipulating automata and graphs, such as traversal, minimization and copying.

There is a newer version: 0.11.0
Show newest version
/* Copyright (C) 2013 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.util.automata.equivalence;

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Objects;
import java.util.Queue;

import net.automatalib.automata.UniversalDeterministicAutomaton;
import net.automatalib.automata.concepts.StateIDs;
import net.automatalib.commons.util.UnionFind;
import net.automatalib.words.Word;
import net.automatalib.words.WordBuilder;

public class NearLinearEquivalenceTest {
	
	private static final class Record {
		private final S state1;
		private final S2 state2;
		private final I reachedBy;
		private final Record reachedFrom;
		private final int depth;
		
		public Record(S state1, S2 state2) {
			this(state1, state2, null, null);
		}
		
		public Record(S state1, S2 state2, I reachedBy, Record reachedFrom) {
			this.state1 = state1;
			this.state2 = state2;
			this.reachedBy = reachedBy;
			this.reachedFrom = reachedFrom;
			this.depth = (reachedFrom != null) ? reachedFrom.depth + 1 : 0;
		}
	}
	
	private final UniversalDeterministicAutomaton target;
	
	public NearLinearEquivalenceTest(UniversalDeterministicAutomaton target) {
		this.target = target;
	}
	
	public Word findSeparatingWord(UniversalDeterministicAutomaton other, Collection inputs) {
		return findSeparatingWord(target, other, inputs);
	}
	
	public static  Word findSeparatingWord(UniversalDeterministicAutomaton target,
			UniversalDeterministicAutomaton other, Collection inputs) {
		int targetStates = target.size();
		UnionFind uf = new UnionFind(targetStates + other.size());

		S init1 = target.getInitialState();
		S2 init2 = other.getInitialState();
		
		Object sprop1 = target.getStateProperty(init1);
		Object sprop2 = other.getStateProperty(init2);
		
		if(!Objects.equals(sprop1, sprop2))
			return Word.epsilon();
		
		StateIDs targetStateIds = target.stateIDs();
		StateIDs otherStateIds = other.stateIDs();
		
		int id1 = targetStateIds.getStateId(init1);
		int id2 = otherStateIds.getStateId(init2) + targetStates;
		
		uf.link(id1, id2);
		
		Queue> queue = new ArrayDeque>();
		
		queue.offer(new Record(init1, init2));
		
		I lastSym = null;
		
		Record current;
		
explore:while((current = queue.poll()) != null) {
			S state1 = current.state1;
			S2 state2 = current.state2;
			
			for(I sym : inputs) {
				T trans1 = target.getTransition(state1, sym);
				T2 trans2 = other.getTransition(state2, sym);
				
				if(trans1 == null) {
					if(trans2 == null)
						continue;
					lastSym = sym;
					break explore;
				}
				else if(trans2 == null) {
					lastSym = sym;
					break explore;
				}
				
				Object tprop1 = target.getTransitionProperty(trans1);
				Object tprop2 = other.getTransitionProperty(trans2);
				
				if(!Objects.equals(tprop1, tprop2)) {
					lastSym = sym;
					break explore;
				}
				
				S succ1 = target.getSuccessor(trans1);
				S2 succ2 = other.getSuccessor(trans2);
				
				id1 = targetStateIds.getStateId(succ1);
				id2 = otherStateIds.getStateId(succ2) + targetStates;
				
				int r1 = uf.find(id1), r2 = uf.find(id2);
				
				if(r1 == r2)
					continue;
				
				
				sprop1 = target.getStateProperty(succ1);
				sprop2 = other.getStateProperty(succ2);
				
				if(!Objects.equals(sprop1, sprop2)) {
					lastSym = sym;
					break explore;
				}
				
				uf.link(r1, r2);
				
				queue.offer(new Record<>(succ1, succ2, sym, current));
			}
        }
		
		if(current == null)
			return null;
		
		int ceLength = current.depth;
		if(lastSym != null)
			ceLength++;
		
		WordBuilder wb = new WordBuilder(null, ceLength);
		
		int index = ceLength;
		
		if(lastSym != null)
			wb.setSymbol(--index, lastSym);
		
		while(current.reachedFrom != null) {
			wb.setSymbol(--index, current.reachedBy);
			current = current.reachedFrom;
		}
		
		return wb.toWord();
	}
	
	
	public static  Word findSeparatingWord(UniversalDeterministicAutomaton target,
			S init1, S init2, Collection inputs) {
		UnionFind uf = new UnionFind(target.size());
		
		Object sprop1 = target.getStateProperty(init1);
		Object sprop2 = target.getStateProperty(init2);
		
		if(!Objects.equals(sprop1, sprop2))
			return Word.epsilon();
		
		StateIDs stateIds = target.stateIDs();
		
		int id1 = stateIds.getStateId(init1), id2 = stateIds.getStateId(init2);
		
		uf.link(id1, id2);
		
		Queue> queue = new ArrayDeque>();
		
		queue.offer(new Record(init1, init2));
		
		I lastSym = null;
		Record current;
		
explore:while((current = queue.poll()) != null) {
			S state1 = current.state1;
			S state2 = current.state2;
			
			for(I sym : inputs) {
				T trans1 = target.getTransition(state1, sym);
				T trans2 = target.getTransition(state2, sym);
				
				if(trans1 == null) {
					if(trans2 == null)
						continue;
					lastSym = sym;
					break explore;
				}
				else if(trans2 == null) {
					lastSym = sym;
					break explore;
				}
				
				Object tprop1 = target.getTransitionProperty(trans1);
				Object tprop2 = target.getTransitionProperty(trans2);
				
				if(!Objects.equals(tprop1, tprop2)) {
					lastSym = sym;
					break explore;
				}
				
				S succ1 = target.getSuccessor(trans1);
				S succ2 = target.getSuccessor(trans2);
				
				id1 = stateIds.getStateId(succ1);
				id2 = stateIds.getStateId(succ2);
				
				int r1 = uf.find(id1), r2 = uf.find(id2);
				
				if(r1 == r2)
					continue;
				
				
				sprop1 = target.getStateProperty(succ1);
				sprop2 = target.getStateProperty(succ2);
				
				if(!Objects.equals(sprop1, sprop2)) {
					lastSym = sym;
					break explore;
				}
				
				uf.link(r1, r2);
				
				queue.offer(new Record<>(succ1, succ2, sym, current));
			}
        }
		
		if(current == null)
			return null;
		
		int ceLength = current.depth;
		if(lastSym != null)
			ceLength++;
		
		WordBuilder wb = new WordBuilder(null, ceLength);
		
		int index = ceLength;
		
		if(lastSym != null)
			wb.setSymbol(--index, lastSym);
		
		while(current.reachedFrom != null) {
			wb.setSymbol(--index, current.reachedBy);
			current = current.reachedFrom;
		}
		
		return wb.toWord();
	}
}