net.automatalib.util.automata.equivalence.NearLinearEquivalenceTest Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of automata-util Show documentation
Show all versions of automata-util Show documentation
This artifact provides various common utility operations for analyzing and manipulating
automata and graphs, such as traversal, minimization and copying.
/* Copyright (C) 2013-2019 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.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.InputAlphabetHolder;
import net.automatalib.automata.concepts.StateIDs;
import net.automatalib.commons.util.IntDisjointSets;
import net.automatalib.commons.util.UnionFindRemSP;
import net.automatalib.words.Alphabet;
import net.automatalib.words.Word;
import net.automatalib.words.WordBuilder;
public class NearLinearEquivalenceTest {
private final UniversalDeterministicAutomaton, I, ?, ?, ?> target;
public NearLinearEquivalenceTest(UniversalDeterministicAutomaton, I, ?, ?, ?> target) {
this.target = target;
}
public static Word findSeparatingWord(final UniversalDeterministicAutomaton target,
final S init1,
final S init2,
final Collection extends I> inputs) {
return findSeparatingWord(target, init1, init2, inputs, false);
}
/**
* Find a separating word for two states in a given automaton.
*
* @param target
* the automaton
* @param init1
* the first state
* @param init2
* the second state
* @param inputs
* the inputs to consider for a separating word
* @param ignoreUndefinedTransitions
* if {@code true}, undefined transitions are not considered to distinguish two states, if {@code false} an
* undefined and defined transition are considered to distinguish two states
* @param
* automaton state type
* @param
* input alphabet type
* @param
* automaton transition type
*
* @return A word separating the two states, {@code null} if no such word can be found
*/
public static Word findSeparatingWord(final UniversalDeterministicAutomaton target,
final S init1,
final S init2,
final Collection extends I> inputs,
final boolean ignoreUndefinedTransitions) {
Object sprop1 = target.getStateProperty(init1);
Object sprop2 = target.getStateProperty(init2);
if (!Objects.equals(sprop1, sprop2)) {
return Word.epsilon();
}
IntDisjointSets uf = new UnionFindRemSP(target.size());
StateIDs stateIds = target.stateIDs();
int id1 = stateIds.getStateId(init1), id2 = stateIds.getStateId(init2);
uf.link(id1, id2);
Queue> queue = new ArrayDeque<>();
queue.add(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 (ignoreUndefinedTransitions && (trans1 == null || trans2 == null)) {
continue;
} else 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.add(new Record<>(succ1, succ2, sym, current));
}
}
if (current == null) {
return null;
}
int position = current.depth + 1;
WordBuilder wb = new WordBuilder<>(null, position);
wb.setSymbol(--position, lastSym);
while (current.reachedFrom != null) {
wb.setSymbol(--position, current.reachedBy);
current = current.reachedFrom;
}
return wb.toWord();
}
public Word findSeparatingWord(UniversalDeterministicAutomaton, I, ?, ?, ?> other,
Collection extends I> inputs) {
return findSeparatingWord(target, other, inputs);
}
public static Word findSeparatingWord(UniversalDeterministicAutomaton target,
UniversalDeterministicAutomaton other,
Collection extends I> inputs) {
return findSeparatingWord(target, other, inputs, false);
}
public static Word findSeparatingWord(UniversalDeterministicAutomaton target,
UniversalDeterministicAutomaton other,
Collection extends I> inputs,
boolean ignoreUndefinedTransitions) {
if (inputs instanceof Alphabet && target instanceof InputAlphabetHolder &&
other instanceof InputAlphabetHolder) {
@SuppressWarnings("unchecked")
Alphabet alphabet = (Alphabet) inputs;
@SuppressWarnings("unchecked")
Alphabet targetAlphabet = ((InputAlphabetHolder) target).getInputAlphabet();
if (alphabet.equals(targetAlphabet)) {
@SuppressWarnings("unchecked")
Alphabet otherAlphabet = ((InputAlphabetHolder) other).getInputAlphabet();
if (alphabet.equals(otherAlphabet)) {
return findSeparatingWord(target, other, alphabet, ignoreUndefinedTransitions);
}
}
}
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();
}
int targetStates = target.size();
IntDisjointSets uf = new UnionFindRemSP(targetStates + other.size());
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.add(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 (ignoreUndefinedTransitions && (trans1 == null || trans2 == null)) {
continue;
} else 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;
if (!uf.union(id1, id2)) {
continue;
}
sprop1 = target.getStateProperty(succ1);
sprop2 = other.getStateProperty(succ2);
if (!Objects.equals(sprop1, sprop2)) {
lastSym = sym;
break explore;
}
queue.add(new Record<>(succ1, succ2, sym, current));
}
}
if (current == null) {
return null;
}
int position = current.depth + 1;
WordBuilder wb = new WordBuilder<>(null, position);
wb.setSymbol(--position, lastSym);
while (current.reachedFrom != null) {
wb.setSymbol(--position, current.reachedBy);
current = current.reachedFrom;
}
return wb.toWord();
}
public static Word findSeparatingWord(UniversalDeterministicAutomaton target,
UniversalDeterministicAutomaton other,
Alphabet inputs) {
return findSeparatingWord(target, other, inputs, false);
}
public static Word findSeparatingWord(UniversalDeterministicAutomaton target,
UniversalDeterministicAutomaton other,
Alphabet inputs,
boolean ignoreUndefinedTransitions) {
UniversalDeterministicAutomaton.FullIntAbstraction absTarget = target.fullIntAbstraction(inputs);
UniversalDeterministicAutomaton.FullIntAbstraction absOther = other.fullIntAbstraction(inputs);
int init1 = absTarget.getIntInitialState();
int init2 = absOther.getIntInitialState();
Object sprop1 = absTarget.getStateProperty(init1);
Object sprop2 = absOther.getStateProperty(init2);
if (!Objects.equals(sprop1, sprop2)) {
return Word.epsilon();
}
int targetStates = target.size();
IntDisjointSets uf = new UnionFindRemSP(targetStates + other.size());
int id1 = init1;
int id2 = targetStates + init2;
uf.link(id1, id2);
Queue queue = new ArrayDeque<>();
queue.add(new IntRecord(init1, init2));
int lastSym = -1;
int numInputs = inputs.size();
IntRecord current;
explore:
while ((current = queue.poll()) != null) {
int state1 = current.state1;
int state2 = current.state2;
for (int sym = 0; sym < numInputs; sym++) {
T trans1 = absTarget.getTransition(state1, sym);
T2 trans2 = absOther.getTransition(state2, sym);
if (ignoreUndefinedTransitions && (trans1 == null || trans2 == null)) {
continue;
} else 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;
}
int succ1 = absTarget.getIntSuccessor(trans1);
int succ2 = absOther.getIntSuccessor(trans2);
id1 = succ1;
id2 = succ2 + targetStates;
if (!uf.union(id1, id2)) {
continue;
}
sprop1 = absTarget.getStateProperty(succ1);
sprop2 = absOther.getStateProperty(succ2);
if (!Objects.equals(sprop1, sprop2)) {
lastSym = sym;
break explore;
}
queue.add(new IntRecord(succ1, succ2, sym, current));
}
}
if (current == null) {
return null;
}
int position = current.depth + 1;
WordBuilder wb = new WordBuilder<>(null, position);
wb.setSymbol(--position, inputs.getSymbol(lastSym));
while (current.reachedFrom != null) {
wb.setSymbol(--position, inputs.getSymbol(current.reachedBy));
current = current.reachedFrom;
}
return wb.toWord();
}
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;
Record(S state1, S2 state2) {
this(state1, state2, null, null);
}
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 static final class IntRecord {
private final int state1;
private final int state2;
private final int reachedBy;
private final IntRecord reachedFrom;
private final int depth;
IntRecord(int state1, int state2) {
this(state1, state2, -1, null);
}
IntRecord(int state1, int state2, int reachedBy, IntRecord reachedFrom) {
this.state1 = state1;
this.state2 = state2;
this.reachedBy = reachedBy;
this.reachedFrom = reachedFrom;
this.depth = (reachedFrom != null) ? reachedFrom.depth + 1 : 0;
}
}
}