/* * Copyright (C) 2022-2023 Daniel Küchler (kuechlerdaniel33@gmail.com) * Copyright (C) 2022-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.petrinet.operations; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import de.uni_freiburg.informatik.ultimate.automata.AutomataLibraryException; import de.uni_freiburg.informatik.ultimate.automata.AutomataLibraryServices; import de.uni_freiburg.informatik.ultimate.automata.GeneralOperation; import de.uni_freiburg.informatik.ultimate.automata.nestedword.INestedWordAutomaton; import de.uni_freiburg.informatik.ultimate.automata.nestedword.INwaInclusionStateFactory; import de.uni_freiburg.informatik.ultimate.automata.nestedword.INwaOutgoingLetterAndTransitionProvider; import de.uni_freiburg.informatik.ultimate.automata.nestedword.buchi.NestedLassoWord; import de.uni_freiburg.informatik.ultimate.automata.nestedword.operations.IsIncludedBuchi; import de.uni_freiburg.informatik.ultimate.automata.nestedword.reachablestates.NestedWordAutomatonReachableStates; import de.uni_freiburg.informatik.ultimate.automata.nestedword.transitions.OutgoingInternalTransition; import de.uni_freiburg.informatik.ultimate.automata.petrinet.IPetriNet; import de.uni_freiburg.informatik.ultimate.automata.petrinet.netdatastructures.BoundedPetriNet; import de.uni_freiburg.informatik.ultimate.automata.petrinet.netdatastructures.PetriNetUtils; import de.uni_freiburg.informatik.ultimate.automata.petrinet.netdatastructures.Transition; import de.uni_freiburg.informatik.ultimate.automata.statefactory.IBlackWhiteStateFactory; import de.uni_freiburg.informatik.ultimate.automata.statefactory.IBuchiIntersectStateFactory; import de.uni_freiburg.informatik.ultimate.automata.statefactory.IPetriNet2FiniteAutomatonStateFactory; import de.uni_freiburg.informatik.ultimate.core.model.services.ILogger; import de.uni_freiburg.informatik.ultimate.util.datastructures.ImmutableSet; /** * Creates intersection of Buchi Petri net and buchi automata (eagerly). * * @param * @param * * @author Daniel Küchler (kuechlerdaniel33@gmail.com) */ public class BuchiIntersect extends GeneralOperation> { private final IPetriNet mPetriNet; private final INestedWordAutomaton mBuchiAutomata; private final IBlackWhiteStateFactory mLabeledBuchiPlaceFactory; private final Map mInputQGetQ1 = new HashMap<>(); private final Map mInputQGetQ2 = new HashMap<>(); private final Map mInputQ2GetQ = new HashMap<>(); private final BoundedPetriNet mIntersectionNet; public BuchiIntersect(final AutomataLibraryServices services, final IBlackWhiteStateFactory factory, final IPetriNet petriNet, final INestedWordAutomaton buchiAutomata) { super(services); mPetriNet = petriNet; mBuchiAutomata = buchiAutomata; mLogger.info(startMessage()); if (buchiAutomata.getInitialStates().size() != 1) { throw new IllegalArgumentException("Buchi with multiple initial states not supported."); } mLabeledBuchiPlaceFactory = factory; mIntersectionNet = new BoundedPetriNet<>(services, petriNet.getAlphabet(), false); constructIntersectionNet(); mLogger.info(exitMessage()); } private final void constructIntersectionNet() { addPlacesToIntersectionNet(); addTransitionsToIntersectionNet(); } private final void addPlacesToIntersectionNet() { addOriginalPetriPlaces(); addOriginalBuchiPlaces(); } private final void addOriginalPetriPlaces() { for (final PLACE place : mPetriNet.getPlaces()) { mIntersectionNet.addPlace(place, mPetriNet.getInitialPlaces().contains(place), false); } } private final void addOriginalBuchiPlaces() { for (final PLACE place : mBuchiAutomata.getStates()) { final PLACE stateOnePlace = mLabeledBuchiPlaceFactory.getWhiteContent(place); final PLACE stateTwoPlace = mLabeledBuchiPlaceFactory.getBlackContent(place); mIntersectionNet.addPlace(stateOnePlace, mBuchiAutomata.isInitial(place), false); mIntersectionNet.addPlace(stateTwoPlace, false, mBuchiAutomata.isFinal(place)); mInputQGetQ1.put(place, stateOnePlace); mInputQGetQ2.put(place, stateTwoPlace); mInputQ2GetQ.put(stateTwoPlace, place); } } private final void addTransitionsToIntersectionNet() { for (final Transition petriTransition : mPetriNet.getTransitions()) { for (final PLACE buchiPlace : mBuchiAutomata.getStates()) { for (final OutgoingInternalTransition buchiTransition : mBuchiAutomata .internalSuccessors(buchiPlace, petriTransition.getSymbol())) { addStateOneAndTwoTransition(petriTransition, buchiTransition, buchiPlace); } } } } private final void addStateOneAndTwoTransition(final Transition petriTransition, final OutgoingInternalTransition buchiTransition, final PLACE buchiPredecessor) { Set predecessorSet = getTransitionPredecessors(petriTransition, buchiPredecessor, true); Set successorSet = getTransitionSuccessors(petriTransition, buchiTransition, predecessorSet, true); mIntersectionNet.addTransition(petriTransition.getSymbol(), ImmutableSet.of(predecessorSet), ImmutableSet.of(successorSet)); predecessorSet = getTransitionPredecessors(petriTransition, buchiPredecessor, false); successorSet = getTransitionSuccessors(petriTransition, buchiTransition, predecessorSet, false); mIntersectionNet.addTransition(petriTransition.getSymbol(), ImmutableSet.of(predecessorSet), ImmutableSet.of(successorSet)); } private final Set getTransitionPredecessors(final Transition petriTransition, final PLACE buchiPredecessor, final boolean mBuildingStateOneTransition) { final Set predecessorSet = new HashSet<>(petriTransition.getPredecessors()); if (mBuildingStateOneTransition) { predecessorSet.add(mInputQGetQ1.get(buchiPredecessor)); } else { predecessorSet.add(mInputQGetQ2.get(buchiPredecessor)); } return predecessorSet; } private final Set getTransitionSuccessors(final Transition petriTransition, final OutgoingInternalTransition buchiTransition, final Set predecessorSet, final boolean mBuildingStateOneTransition) { final Set successorSet = new HashSet<>(petriTransition.getSuccessors()); if (mBuildingStateOneTransition) { successorSet.add(getQSuccesorForStateOneTransition(petriTransition, buchiTransition)); } else { successorSet.add(getQSuccesorForStateTwoTransition(predecessorSet, buchiTransition)); } return successorSet; } private PLACE getQSuccesorForStateOneTransition(final Transition petriTransition, final OutgoingInternalTransition buchiTransition) { for (final PLACE place : petriTransition.getSuccessors()) { if (mPetriNet.getAcceptingPlaces().contains(place)) { return mInputQGetQ2.get(buchiTransition.getSucc()); } } return mInputQGetQ1.get(buchiTransition.getSucc()); } private PLACE getQSuccesorForStateTwoTransition(final Set predecessorSet, final OutgoingInternalTransition buchiTransition) { for (final PLACE place : predecessorSet) { if (mInputQ2GetQ.containsKey(place) && mBuchiAutomata.getFinalStates().contains(mInputQ2GetQ.get(place))) { return mInputQGetQ1.get(buchiTransition.getSucc()); } } return mInputQGetQ2.get(buchiTransition.getSucc()); } @Override public String startMessage() { return "Starting Intersection"; } @Override public String exitMessage() { return "Exiting Intersection"; } @Override public IPetriNet getResult() { return mIntersectionNet; } @SuppressWarnings("unchecked") @Override public boolean checkResult(final IPetriNet2FiniteAutomatonStateFactory stateFactory) throws AutomataLibraryException { final INwaOutgoingLetterAndTransitionProvider operandAsNwa = (new BuchiPetriNet2FiniteAutomaton<>(mServices, stateFactory, (IBlackWhiteStateFactory) stateFactory, mPetriNet)).getResult(); final INwaOutgoingLetterAndTransitionProvider resultAsNwa = (new BuchiPetriNet2FiniteAutomaton<>(mServices, stateFactory, (IBlackWhiteStateFactory) stateFactory, mIntersectionNet)).getResult(); final NestedWordAutomatonReachableStates automatonIntersection = new de.uni_freiburg.informatik.ultimate.automata.nestedword.buchi.BuchiIntersect<>(mServices, (IBuchiIntersectStateFactory) stateFactory, operandAsNwa, mBuchiAutomata).getResult(); final IsIncludedBuchi isSubset = new IsIncludedBuchi<>(mServices, (INwaInclusionStateFactory) stateFactory, resultAsNwa, automatonIntersection); if (!isSubset.getResult()) { final NestedLassoWord ctx = isSubset.getCounterexample().getNestedLassoWord(); final ILogger logger = mServices.getLoggingService().getLogger(PetriNetUtils.class); logger.error("Intersection recognizes incorrect word : " + ctx); } final IsIncludedBuchi isSuperset = new IsIncludedBuchi<>(mServices, (INwaInclusionStateFactory) stateFactory, automatonIntersection, resultAsNwa); if (!isSuperset.getResult()) { final NestedLassoWord ctx = isSuperset.getCounterexample().getNestedLassoWord(); final ILogger logger = mServices.getLoggingService().getLogger(PetriNetUtils.class); logger.error("Intersection not recognizing word of correct intersection : " + ctx); } return isSubset.getResult() && isSuperset.getResult(); } }