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

net.automatalib.util.automata.equivalence.DeterministicEquivalenceTest 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.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;

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

public class DeterministicEquivalenceTest {

    private static final int MAP_THRESHOLD = 10000;
    private final UniversalDeterministicAutomaton reference;

    public DeterministicEquivalenceTest(UniversalDeterministicAutomaton reference) {
        this.reference = reference;
    }

    public Word findSeparatingWord(UniversalDeterministicAutomaton other,
                                      Collection inputs) {
        return findSeparatingWord(reference, other, inputs);
    }

    public static  Word findSeparatingWord(UniversalDeterministicAutomaton reference,
                                                               UniversalDeterministicAutomaton other,
                                                               Collection inputs) {
        int refSize = reference.size();
        int totalStates = refSize * other.size();

        if (totalStates < 0 || totalStates > MAP_THRESHOLD) {
            return findSeparatingWordLarge(reference, other, inputs);
        }

        S refInit = reference.getInitialState();
        S2 otherInit = other.getInitialState();

        Object refStateProp = reference.getStateProperty(refInit), otherStateProp = other.getStateProperty(otherInit);

        if (!Objects.equals(refStateProp, otherStateProp)) {
            return Word.epsilon();
        }

        Queue> bfsQueue = new ArrayDeque<>();
        bfsQueue.add(new StatePair<>(refInit, otherInit));

        StateIDs refStateIds = reference.stateIDs();
        StateIDs otherStateIds = other.stateIDs();

        StatePair currPair;
        int lastId = otherStateIds.getStateId(otherInit) * refSize + refStateIds.getStateId(refInit);

        @SuppressWarnings("unchecked")
        Pred[] preds = new Pred[totalStates];
        preds[lastId] = new Pred<>(-1, null);

        int currDepth = 0;
        int inCurrDepth = 1;
        int inNextDepth = 0;

        I lastSym = null;

        bfs:
        while ((currPair = bfsQueue.poll()) != null) {
            S refState = currPair.ref;
            S2 otherState = currPair.other;

            int currId = otherStateIds.getStateId(otherState) * refSize + refStateIds.getStateId(refState);
            lastId = currId;

            for (I in : inputs) {
                lastSym = in;
                T refTrans = reference.getTransition(refState, in);
                T2 otherTrans = other.getTransition(otherState, in);

                if ((refTrans == null || otherTrans == null) && refTrans != otherTrans) {
                    break bfs;
                }

                Object refProp = reference.getTransitionProperty(refTrans);
                Object otherProp = other.getTransitionProperty(otherTrans);
                if (!Objects.equals(refProp, otherProp)) {
                    break bfs;
                }

                S refSucc = reference.getSuccessor(refTrans);
                S2 otherSucc = other.getSuccessor(otherTrans);

                int succId = otherStateIds.getStateId(otherSucc) * refSize + refStateIds.getStateId(refSucc);

                if (preds[succId] == null) {
                    refStateProp = reference.getStateProperty(refSucc);
                    otherStateProp = other.getStateProperty(otherSucc);

                    if (!Objects.equals(refStateProp, otherStateProp)) {
                        break bfs;
                    }

                    preds[succId] = new Pred<>(currId, in);
                    bfsQueue.add(new StatePair<>(refSucc, otherSucc));
                    inNextDepth++;
                }
            }

            lastSym = null;

            // Next level in BFS reached
            if (--inCurrDepth == 0) {
                inCurrDepth = inNextDepth;
                inNextDepth = 0;
                currDepth++;
            }
        }

        if (lastSym == null) {
            return null;
        }

        WordBuilder sep = new WordBuilder<>(null, currDepth + 1);
        int index = currDepth;
        sep.setSymbol(index--, lastSym);

        Pred pred = preds[lastId];
        I sym = pred.symbol;
        while (sym != null) {
            sep.setSymbol(index--, sym);
            pred = preds[pred.id];
            sym = pred.symbol;
        }

        return sep.toWord();
    }

    public static  Word findSeparatingWordLarge(UniversalDeterministicAutomaton reference,
                                                                    UniversalDeterministicAutomaton other,
                                                                    Collection inputs) {
        S refInit = reference.getInitialState();
        S2 otherInit = other.getInitialState();

        Object refStateProp = reference.getStateProperty(refInit), otherStateProp = other.getStateProperty(otherInit);

        if (!Objects.equals(refStateProp, otherStateProp)) {
            return Word.epsilon();
        }

        Queue> bfsQueue = new ArrayDeque<>();
        bfsQueue.add(new StatePair<>(refInit, otherInit));

        int refSize = reference.size();

        StateIDs refStateIds = reference.stateIDs();
        StateIDs otherStateIds = other.stateIDs();

        StatePair currPair;
        int lastId = otherStateIds.getStateId(otherInit) * refSize + refStateIds.getStateId(refInit);

        //TIntObjectMap> preds = new TIntObjectHashMap<>();
        Map> preds = new HashMap<>(); // TODO: replace by primitive specialization
        preds.put(lastId, new Pred<>(-1, null));

        int currDepth = 0;
        int inCurrDepth = 1;
        int inNextDepth = 0;

        I lastSym = null;

        bfs:
        while ((currPair = bfsQueue.poll()) != null) {
            S refState = currPair.ref;
            S2 otherState = currPair.other;

            int currId = otherStateIds.getStateId(otherState) * refSize + refStateIds.getStateId(refState);
            lastId = currId;

            for (I in : inputs) {
                lastSym = in;
                T refTrans = reference.getTransition(refState, in);
                T2 otherTrans = other.getTransition(otherState, in);

                if ((refTrans == null || otherTrans == null) && refTrans != otherTrans) {
                    break bfs;
                }

                Object refProp = reference.getTransitionProperty(refTrans);
                Object otherProp = other.getTransitionProperty(otherTrans);
                if (!Objects.equals(refProp, otherProp)) {
                    break bfs;
                }

                S refSucc = reference.getSuccessor(refTrans);
                S2 otherSucc = other.getSuccessor(otherTrans);

                int succId = otherStateIds.getStateId(otherSucc) * refSize + refStateIds.getStateId(refSucc);

                if (preds.get(succId) == null) {
                    refStateProp = reference.getStateProperty(refSucc);
                    otherStateProp = other.getStateProperty(otherSucc);

                    if (!Objects.equals(refStateProp, otherStateProp)) {
                        break bfs;
                    }

                    preds.put(succId, new Pred<>(currId, in));
                    bfsQueue.add(new StatePair<>(refSucc, otherSucc));
                    inNextDepth++;
                }
            }

            lastSym = null;

            // Next level in BFS reached
            if (--inCurrDepth == 0) {
                inCurrDepth = inNextDepth;
                inNextDepth = 0;
                currDepth++;
            }
        }

        if (lastSym == null) {
            return null;
        }

        WordBuilder sep = new WordBuilder<>(null, currDepth + 1);
        int index = currDepth;
        sep.setSymbol(index--, lastSym);

        Pred pred = preds.get(lastId);
        I sym = pred.symbol;
        while (sym != null) {
            sep.setSymbol(index--, sym);
            pred = preds.get(pred.id);
            sym = pred.symbol;
        }

        return sep.toWord();
    }

    private static final class StatePair {

        public final S ref;
        public final S2 other;

        StatePair(S ref, S2 other) {
            this.ref = ref;
            this.other = other;
        }
    }

    private static final class Pred {

        public final int id;
        public final I symbol;

        Pred(int id, I input) {
            this.id = id;
            this.symbol = input;
        }
    }
}