/* * Copyright (C) 2023 Philipp Müller (pm251@venus.uni-freiburg.de) * Copyright (C) 2023 University of Freiburg * * This file is part of the ULTIMATE Automata Library. * * The ULTIMATE Automata Library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ULTIMATE Automata Library 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 the ULTIMATE Automata Library. If not, see . * * Additional permission under GNU GPL version 3 section 7: * If you modify the ULTIMATE Automata Library, or any covered work, by linking * or combining it with Eclipse RCP (or a modified version of Eclipse RCP), * containing parts covered by the terms of the Eclipse Public License, the * licensors of the ULTIMATE Automata Library grant you additional permission * to convey the resulting work. */ package de.uni_freiburg.informatik.ultimate.automata.rabin; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import de.uni_freiburg.informatik.ultimate.automata.nestedword.transitions.OutgoingInternalTransition; import de.uni_freiburg.informatik.ultimate.automata.statefactory.IBuchiIntersectStateFactory; import de.uni_freiburg.informatik.ultimate.util.datastructures.relation.Triple; /** * A class that lazyly constructs the intersection production with the method described in: J. Esparza, “Automata theory * - an algorithmic approach.” https://www7.in.tum.de/esparza/autoskript.pdf. Aug 26, 2017. The construction is found on * page 247. All product-states that have at least one finite state as a parent are finite in Rabin automata. You can * imagine removing all finite parents at one point, this results in removing their children at the same point. * * @author Philipp Müller (pm251@venus.uni-freiburg.de) * * @param * type of letter * @param * type of state */ public class RabinIntersectionWorstCaseOptimal implements IRabinAutomaton { private final IRabinAutomaton mFirstAutomaton; private final IRabinAutomaton mSecondAutomaton; private final IBuchiIntersectStateFactory mFactory; private Set mInitialStates; private final HashMap> mAutomatonMap = new HashMap<>(); /** * implementation that lazyly constructs the intersection of two Rabin automata * * @param firstAutomaton * first Rabin automaton to intersect * @param secondAutomaton * second Rabin automaton to intersect * @param factory * some factory */ public RabinIntersectionWorstCaseOptimal(final IRabinAutomaton firstAutomaton, final IRabinAutomaton secondAutomaton, final IBuchiIntersectStateFactory factory) { mFirstAutomaton = firstAutomaton; mSecondAutomaton = secondAutomaton; mFactory = factory; } @Override public Set getAlphabet() { assert mFirstAutomaton.getAlphabet().equals(mSecondAutomaton.getAlphabet()); return mFirstAutomaton.getAlphabet(); } @Override public int size() { return (mFirstAutomaton.size() * mSecondAutomaton.size()) * 2; } @Override public String sizeInformation() { return "Number of states: " + size() + "\n" + "The number of lazyly constructed reachable states may be smaller"; } private Set constructInitialStates() { final Set result = new HashSet<>(); for (final STATE firstInitial : mFirstAutomaton.getInitialStates()) { for (final STATE secondInitial : mSecondAutomaton.getInitialStates()) { result.add(getProducedState(firstInitial, secondInitial, false)); } } return result; } @Override public Set getInitialStates() { if (mInitialStates == null) { mInitialStates = constructInitialStates(); } return mInitialStates; } @Override public boolean isAccepting(final STATE state) { final Triple originalStateInformation = mAutomatonMap.get(state); return (!originalStateInformation.getThird() && mFirstAutomaton.isAccepting(originalStateInformation.getFirst())); } @Override public boolean isFinite(final STATE state) { final Triple originalStateInformation = mAutomatonMap.get(state); return (mFirstAutomaton.isFinite(originalStateInformation.getFirst()) || mSecondAutomaton.isFinite(originalStateInformation.getSecond())); } @Override public Iterable> getSuccessors(final STATE state) { final Triple originalStateInformation = mAutomatonMap.get(state); return getComponentalSuccessors(mFirstAutomaton.getSuccessors(originalStateInformation.getFirst()), originalStateInformation); } @Override public Iterable> getSuccessors(final STATE state, final LETTER letter) { final Triple originalStateInformation = mAutomatonMap.get(state); return getComponentalSuccessors(mFirstAutomaton.getSuccessors(originalStateInformation.getFirst(), letter), originalStateInformation); } /** * Constructs product states for different subautomata used in this product construction * * @param first * state from mFirstAutomaton * @param second * state from mSecondAutomaton * @param acceptedOnlyFirst * referencing the subautomaton, if true then the first automaton has accepted before reaching this * state, but we await acceptance of the second automaton * @return a state which uniquely incorporates all parameters */ private STATE getProducedState(final STATE first, final STATE second, final boolean acceptedOnlyFirst) { final STATE result = mFactory.intersectBuchi(first, second, acceptedOnlyFirst ? 1 : 2); mAutomatonMap.computeIfAbsent(result, x -> new Triple<>(first, second, acceptedOnlyFirst)); return result; } /** * a indirect helper function for getSuccessors, this contains the logic for the product subautomaton construction * * @param firstTransitionList * a subset of valid transitions for one (active) state in the first automaton * @param secondState * a active state in the second automaton * @param acceptedOnlyFirst * referencing the subautomaton in which the successors will be * @return a list of corresponding transitions in the intersection automaton */ private ArrayList> getRelatedSuccessors( final Iterable> firstTransitionList, final STATE secondState, final boolean acceptedOnlyFirst) { final ArrayList> result = new ArrayList<>(); for (final OutgoingInternalTransition transitionFirst : firstTransitionList) { final LETTER letter = transitionFirst.getLetter(); for (final OutgoingInternalTransition transitionSecond : mSecondAutomaton .getSuccessors(secondState, letter)) { result.add(new OutgoingInternalTransition<>(letter, getProducedState(transitionFirst.getSucc(), transitionSecond.getSucc(), acceptedOnlyFirst))); } } return result; } /** * a helper method for getSuccessors, this contains the logic on the connections in and between components * * @param transitionsFromFirst * a list with transitions that are valid for one (active) state in the first automaton * @param originalStateInformation * the content of mAutomatonMap for the origin * @return a list of corresponding transitions in the intersection automaton */ private ArrayList> getComponentalSuccessors( final Iterable> transitionsFromFirst, final Triple originalStateInformation) { boolean hasAcceptedFirst = originalStateInformation.getThird(); if (hasAcceptedFirst) { if (mSecondAutomaton.isAccepting(originalStateInformation.getSecond())) { hasAcceptedFirst = false; } } else if (mFirstAutomaton.isAccepting(originalStateInformation.getFirst()) && !mSecondAutomaton.isAccepting(originalStateInformation.getSecond())) { hasAcceptedFirst = true; } return getRelatedSuccessors(transitionsFromFirst, originalStateInformation.getSecond(), hasAcceptedFirst); } }