/* * 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.INwaOutgoingLetterAndTransitionProvider; import de.uni_freiburg.informatik.ultimate.automata.nestedword.VpAlphabet; import de.uni_freiburg.informatik.ultimate.automata.nestedword.transitions.OutgoingCallTransition; import de.uni_freiburg.informatik.ultimate.automata.nestedword.transitions.OutgoingInternalTransition; import de.uni_freiburg.informatik.ultimate.automata.nestedword.transitions.OutgoingReturnTransition; import de.uni_freiburg.informatik.ultimate.automata.statefactory.IBlackWhiteStateFactory; import de.uni_freiburg.informatik.ultimate.automata.statefactory.IEmptyStackStateFactory; import de.uni_freiburg.informatik.ultimate.automata.statefactory.IStateFactory; /** * lazy translation of a Rabin automaton into an equivalent Büchi automaton. Do not use Rabin States for operations on * this automaton! * * @author Philipp Müller (pm251@venus.uni-freiburg.de) * * @param * letter type * @param * state type * @param * state factory with IBlackWhiteState & IEmptyStackState functionality * */ public class Rabin2BuchiAutomaton & IEmptyStackStateFactory> implements INwaOutgoingLetterAndTransitionProvider { private final IRabinAutomaton mRabinAutomaton; // black states ~ nonFinite, white states ~ Finite private final FACTORY mFiniteOrNonFiniteStateFactory; // references for a reversal of the BlackWhite modification private final HashMap mBuchi2Rabin = new HashMap<>(); private final HashSet mInitialSet = new HashSet<>(); private final HashSet mNonFiniteSet = new HashSet<>(); /** * Initializes lazy Buchi conversion for a Rabin automaton using the given BWStateFactory * * @param automaton * the Rabin automaton to be converted * @param finiteOrNonFiniteStateFactory * a factory mapping a set of states to two non intersecting sets of states */ public Rabin2BuchiAutomaton(final IRabinAutomaton automaton, final FACTORY finiteOrNonFiniteStateFactory) { mRabinAutomaton = automaton; mFiniteOrNonFiniteStateFactory = finiteOrNonFiniteStateFactory; for (final STATE init : automaton.getInitialStates()) { mInitialSet.add(getFiniteVariant(init)); final STATE nonFiniteCandidate = getNonFiniteVariant(init); if (nonFiniteCandidate != null) { mInitialSet.add(nonFiniteCandidate); } } } @Override public VpAlphabet getVpAlphabet() { return new VpAlphabet<>(mRabinAutomaton.getAlphabet()); } @Override public Iterable getInitialStates() { return mInitialSet; } @Override public boolean isInitial(final STATE state) { return mInitialSet.contains(state); } /** * Computes if a state is accepting (Final means accepting in this context) only returns a boolean iff the state is * reached through lazy construction * * @param state * state which acceptance should be evaluated * @return */ @Override public boolean isFinal(final STATE state) { return mNonFiniteSet.contains(state) && mRabinAutomaton.isAccepting(mBuchi2Rabin.get(state)); } @Override public int size() { // Maybe compute this eager ? return mBuchi2Rabin.size(); } @Override public String sizeInformation() { return "Number of distinct computed states so far: " + size(); } @Override public Iterable> internalSuccessors(final STATE state, final LETTER letter) { return getSucessors(state, mRabinAutomaton.getSuccessors(mBuchi2Rabin.get(state), letter)); } @Override public Iterable> internalSuccessors(final STATE state) { return getSucessors(state, mRabinAutomaton.getSuccessors(mBuchi2Rabin.get(state))); } /** * a helper method for internalSucessors * * @param buchiState * the state which is the root for all transitions in the Buchi automaton * @param rabinTransitions * the transitions for the parent of this buchiState in the Rabin automaton which should be considered * @return the derived transitions for the Buchi automaton */ private Iterable> getSucessors(final STATE buchiState, final Iterable> rabinTransitions) { final ArrayList> result = new ArrayList<>(); // if the state is non-finite it can only have nonFinite successors if (mNonFiniteSet.contains(buchiState)) { for (final OutgoingInternalTransition transition : rabinTransitions) { final STATE succ = getNonFiniteVariant(transition.getSucc()); if (succ != null) { result.add(new OutgoingInternalTransition<>(transition.getLetter(), succ)); } } return result; } for (final OutgoingInternalTransition transition : rabinTransitions) { final STATE rabinSucc = transition.getSucc(); result.add(new OutgoingInternalTransition<>(transition.getLetter(), getFiniteVariant(rabinSucc))); final STATE nonFiniteVariant = getNonFiniteVariant(rabinSucc); if (nonFiniteVariant != null) { // we only consider accepting states as entries into the non-finite subautomaton, this removes // cul-de-sac paths without accepting components from being computed without necessity if (isFinal(nonFiniteVariant)) { result.add(new OutgoingInternalTransition<>(transition.getLetter(), nonFiniteVariant)); } } } return result; } @Override public IStateFactory getStateFactory() { return mFiniteOrNonFiniteStateFactory; } /** * creates a finite variant of a state if not already created and returns this variant * * @param rabinState * a state from the original Rabin automaton * @return the finite variant in the here computed Buchi automaton */ private STATE getFiniteVariant(final STATE rabinState) { final STATE finiteVariant = mFiniteOrNonFiniteStateFactory.getWhiteContent(rabinState); if (mBuchi2Rabin.containsKey(finiteVariant)) { return finiteVariant; } mBuchi2Rabin.put(finiteVariant, rabinState); return finiteVariant; } /** * creates a nonFinite variant of a state if not already created and returns this variant * * @param rabinState * a state from the original Rabin automaton * @return the nonFinite variant in the here computed Buchi automaton */ private STATE getNonFiniteVariant(final STATE rabinState) { final STATE nonFiniteVariant = mFiniteOrNonFiniteStateFactory.getBlackContent(rabinState); if (mNonFiniteSet.contains(nonFiniteVariant)) { return nonFiniteVariant; } if (!mRabinAutomaton.isFinite(rabinState)) { mBuchi2Rabin.put(nonFiniteVariant, rabinState); mNonFiniteSet.add(nonFiniteVariant); return nonFiniteVariant; } // A Finite state can never produce a nonFinite Buchi variant return null; } @Override public STATE getEmptyStackState() { // There is no stack in Rabin automata return mFiniteOrNonFiniteStateFactory.createEmptyStackState(); } @Override public Iterable> callSuccessors(final STATE state, final LETTER letter) { // There are no call Transitions in Rabin automata return Set.of(); } @Override public Iterable> returnSuccessors(final STATE state, final STATE hier, final LETTER letter) { // There are no return Transitions in Rabin automata return Set.of(); } }