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-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 target;

    public NearLinearEquivalenceTest(UniversalDeterministicAutomaton target) {
        this.target = target;
    }

    public static  Word findSeparatingWord(final UniversalDeterministicAutomaton target,
                                                       final S init1,
                                                       final S init2,
                                                       final Collection 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 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 other,
                                      Collection inputs) {
        return findSeparatingWord(target, other, inputs);
    }

    public static  Word findSeparatingWord(UniversalDeterministicAutomaton target,
                                                               UniversalDeterministicAutomaton other,
                                                               Collection inputs) {
        return findSeparatingWord(target, other, inputs, false);
    }

    public static  Word findSeparatingWord(UniversalDeterministicAutomaton target,
                                                               UniversalDeterministicAutomaton other,
                                                               Collection 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;
        }
    }
}