/* * (C) Copyright 2005 Arnaud Bailly (arnaud.oqube@gmail.com), * Yves Roos (yroos@lifl.fr) and others. * * 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 rationals.properties; import rationals.Automaton; import rationals.Builder; import rationals.State; import rationals.Transition; import rationals.transformations.StatesCouple; import rationals.transformations.TransformationsToolBox; import java.util.*; /** * A class that compute trace equivalence relation between two states. This * class checks whether two states from two automata are trace equivalent, which * simply means they recognize the same prefix of languages. * <p> * This class effectively computes the deterministic form of the two given * automata. * * @version $Id: TraceEquivalence.java 2 2006-08-24 14:41:48Z oqube $ */ public class TraceEquivalence<L, Tr extends Transition<L>, T extends Builder<L, Tr, T>> implements Relation<L, Tr, T> { private Automaton<L, Tr, T> a1; private Automaton<L, Tr, T> a2; private List<L> errorTrace; /* * (non-Javadoc) * * @see rationals.tests.Relation#setAutomata(rationals.Automaton, * rationals.Automaton) */ public void setAutomata(Automaton<L, Tr, T> a1, Automaton<L, Tr, T> a2) { this.a1 = a1; this.a2 = a2; } /* * (non-Javadoc) * * @see rationals.tests.Relation#equivalence(rationals.State, * rationals.State) */ public boolean equivalence(State q0a, State q0b) { /* compute epsilon closures on states */ Set<State> nsa = a1.getStateFactory().stateSet(); Set<State> nsb = a2.getStateFactory().stateSet(); nsa.add(q0a); nsb.add(q0b); /* check equivalence on sets */ return equivalence(nsa, nsb); } /* * (non-Javadoc) * * @see rationals.properties.Relation#equivalence(java.util.Set, * java.util.Set) */ public boolean equivalence(Set<State> nsa, Set<State> nsb) { /* sets of explored states */ Stack<StatesCouple> todo = new Stack<>(); /* current traces for failure */ Stack<L> labels = new Stack<>(); Stack<L> trace = new Stack<>(); Set<StatesCouple> done = new HashSet<>(); todo.push(new StatesCouple(nsa, nsb)); labels.push(null); // Needed to avoid empty stack do { StatesCouple cpl = todo.pop(); L lbl = labels.pop(); Set<State> sa = TransformationsToolBox.epsilonClosure(cpl.sa, a1); Set<State> sb = TransformationsToolBox.epsilonClosure(cpl.sb, a2); if (done.contains(cpl)) { L top = trace.peek(); // Bug fix: two different transitions to the same state can cause the trace to become empty if (top == null ? lbl == null : top.equals(lbl)) trace.pop(); continue; } else { trace.push(lbl); } done.add(cpl); /* compute set of transitions */ List<Transition<L>> tas = new ArrayList<>(a1.delta(sa)); List<Transition<L>> tbs = new ArrayList<>(a2.delta(sb)); /* map from letters to set of states */ Map<L, Set<State>> am = new HashMap<>(); Map<L, Set<State>> bm = new HashMap<>(); /* compute set of states reached for each letter */ mapAlphabet(tas, am, a1); mapAlphabet(tbs, bm, a2); Iterator<Map.Entry<L, Set<State>>> it2 = am.entrySet().iterator(); while (it2.hasNext()) { Map.Entry<L, Set<State>> me = it2.next(); L l = me.getKey(); Set<State> as = me.getValue(); Set<State> bs = bm.remove(l); if (bs == null) { this.errorTrace = trace; this.errorTrace.add(l); return false; } StatesCouple sc = new StatesCouple(as, bs); todo.push(sc); labels.push(l); } if (!bm.isEmpty()) { this.errorTrace = trace; this.errorTrace.addAll(bm.keySet()); return false; } } while (!todo.isEmpty()); return true; } /** * @param tas * @param am */ public void mapAlphabet(List<Transition<L>> tas, Map<L, Set<State>> am, Automaton<L, Tr, T> a) { /* compute set of states for each letter */ while (!tas.isEmpty()) { Transition<L> tr = tas.remove(0); L l = tr.label(); if (l == null) continue; Set<State> as = am.get(l); if (as == null) { as = a.getStateFactory().stateSet(); am.put(l, as); } as.add(tr.end()); } } /** * @return Returns the errorTrace. */ public List<L> getErrorTrace() { return errorTrace; } }