/* * Copyright (C) 2011-2015 Julian Jarecki (jareckij@informatik.uni-freiburg.de) * Copyright (C) 2011-2015 Matthias Heizmann (heizmann@informatik.uni-freiburg.de) * Copyright (C) 2009-2015 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.netdatastructures; 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.AutomatonDefinitionPrinter; import de.uni_freiburg.informatik.ultimate.automata.LibraryIdentifiers; import de.uni_freiburg.informatik.ultimate.automata.nestedword.INestedWordAutomaton; 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.IPetriNetAndAutomataInclusionStateFactory; import de.uni_freiburg.informatik.ultimate.automata.petrinet.Marking; import de.uni_freiburg.informatik.ultimate.automata.petrinet.TransitionUnifier; import de.uni_freiburg.informatik.ultimate.automata.petrinet.operations.IsEquivalent; import de.uni_freiburg.informatik.ultimate.core.model.services.ILogger; import de.uni_freiburg.informatik.ultimate.util.datastructures.ImmutableSet; import de.uni_freiburg.informatik.ultimate.util.datastructures.relation.HashRelation; /** * Models 1-bounded petri nets with accepting places. Boundedness is only assumed, not checked! *

* A petri net is n-bounded iff at all times each place has at most n tokens. A petri net with accepting places accepts * a marking m iff m contains at least one accepting place. * * @author Julian Jarecki (jareckij@informatik.uni-freiburg.de) * @author Matthias Heizmann (heizmann@informatik.uni-freiburg.de) * * @param * Type of letters from the alphabet used to label transitions * @param * place content type */ public final class BoundedPetriNet implements IPetriNet { private final AutomataLibraryServices mServices; private final ILogger mLogger; private final Set mAlphabet; private final Set mPlaces = new HashSet<>(); private final Set mInitialPlaces = new HashSet<>(); private final Set mAcceptingPlaces = new HashSet<>(); private final Set> mTransitions = new HashSet<>(); private final Set mTransitionIds = new HashSet<>(); private final TransitionUnifier mTransitionUnifier = new TransitionUnifier<>(); /** * Map each place to its incoming transitions. Redundant to {@link #mTransitions} for better performance. */ private final HashRelation> mPredecessors = new HashRelation<>(); /** * Map each place to its outgoing transitions. Redundant to {@link #mTransitions} for better performance. */ private final HashRelation> mSuccessors = new HashRelation<>(); /** * If true the number of tokens in this petri net is constant. Formally: There is a natural number n such that every * reachable marking consists of n places. */ private final boolean mConstantTokenAmount; private int mSizeOfFlowRelation; /** * Standard constructor. * * @param services * Ultimate services * @param alphabet * Alphabet of this net, used to label transitions * @param constantTokenAmount * Number of tokens in this net is constant (does not change after any firing sequence) */ public BoundedPetriNet(final AutomataLibraryServices services, final Set alphabet, final boolean constantTokenAmount) { mServices = services; mLogger = mServices.getLoggingService().getLogger(LibraryIdentifiers.PLUGIN_ID); mAlphabet = alphabet; mConstantTokenAmount = constantTokenAmount; } /** * Constructor from a nested word automaton. * * @param services * Ultimate services * @param nwa * nested word automaton * @throws AutomataLibraryException * if inclusion check in assertion fails */ public BoundedPetriNet(final AutomataLibraryServices services, final INestedWordAutomaton nwa) { this(services, nwa.getVpAlphabet().getInternalAlphabet(), true); final Map state2place = new HashMap<>(); for (final PLACE nwaState : nwa.getStates()) { final boolean isInitial = nwa.isInitial(nwaState); final boolean isAccepting = nwa.isFinal(nwaState); final boolean newlyAdded = addPlace(nwaState, isInitial, isAccepting); if (!newlyAdded) { throw new AssertionError("Automaton must not contain state twice: " + nwaState); } state2place.put(nwaState, nwaState); } for (final PLACE content : nwa.getStates()) { final ImmutableSet predPlace = ImmutableSet.singleton(state2place.get(content)); for (final OutgoingInternalTransition trans : nwa.internalSuccessors(content)) { final ImmutableSet succPlace = ImmutableSet.singleton(state2place.get(trans.getSucc())); addTransition(trans.getLetter(), predPlace, succPlace); } } assert constantTokenAmountGuaranteed(); } /** * If {@link #mConstantTokenAmount} is set, check that amount of tokens is preserved for all initial markings* and * all firing sequences. *

* * Depending on the initial marking and reachability of some transitions, this method may return false even though * {@link #mConstantTokenAmount} was set correctly to true. * * @return {@link #mConstantTokenAmount} implies all (not only the reachable) transitions preserve the token amount. */ private boolean constantTokenAmountGuaranteed() { return !constantTokenAmount() || transitionsPreserveTokenAmount(); } private boolean transitionsPreserveTokenAmount() { return mTransitions.parallelStream() .allMatch(transition -> transition.getPredecessors().size() == transition.getSuccessors().size()); } public boolean checkResult(final INestedWordAutomaton nwa, final IPetriNetAndAutomataInclusionStateFactory stateFactory) throws AutomataLibraryException { if (mLogger.isInfoEnabled()) { mLogger.info("Testing correctness of constructor" + getClass().getSimpleName()); } final boolean correct = new IsEquivalent<>(mServices, stateFactory, this, nwa).getResult(); if (mLogger.isInfoEnabled()) { mLogger.info("Finished testing correctness of constructor " + getClass().getSimpleName()); } return correct; } /** * Adds a place. * * @param place * place to be added * @param isInitial * {@code true} iff the place is initial * @param isAccepting * {@code true} iff the place is final * @return true iff the place was not already contained */ public boolean addPlace(final PLACE place, final boolean isInitial, final boolean isAccepting) { final boolean addedForFirstTime = mPlaces.add(place); if (addedForFirstTime) { if (isInitial) { mInitialPlaces.add(place); } if (isAccepting) { mAcceptingPlaces.add(place); } } else { if (mInitialPlaces.contains(place) != isInitial) { throw new IllegalArgumentException( "Place " + place + " was already added with different isInitial status"); } if (mAcceptingPlaces.contains(place) != isAccepting) { throw new IllegalArgumentException( "Place " + place + " was already added with different isAccepting status"); } } return addedForFirstTime; } /** * Adds a transition. * * @param letter * symbol * @param preds * predecessor places * @param succs * successor places * @return the newly added transition */ public Transition addTransition(final LETTER letter, final ImmutableSet preds, final ImmutableSet succs, final int transitionId) { assert mAlphabet.contains(letter) : "Letter not in alphabet: " + letter; if (mTransitionIds.contains(transitionId)) { throw new IllegalArgumentException("Transition with id " + transitionId + " was already added."); } final Transition transition = new Transition<>(letter, preds, succs, transitionId); final Transition existingTransition = mTransitionUnifier.findOrRegister(transition); if (existingTransition != null) { return existingTransition; } for (final PLACE predPlace : preds) { assert mPlaces.contains(predPlace) : "Place not in net: " + predPlace; mSuccessors.addPair(predPlace, transition); } for (final PLACE succPlace : succs) { assert mPlaces.contains(succPlace) : "Place not in net: " + succPlace; mPredecessors.addPair(succPlace, transition); } mTransitions.add(transition); mTransitionIds.add(transition.getTotalOrderId()); mSizeOfFlowRelation += (preds.size() + succs.size()); return transition; } public Transition addTransition(final LETTER letter, final ImmutableSet preds, final ImmutableSet succs) { return addTransition(letter, preds, succs, mTransitions.size()); } @Override public Set getAlphabet() { return mAlphabet; } @Override public Set getPlaces() { return mPlaces; } @Override public Set getInitialPlaces() { return mInitialPlaces; } @Override public Set getAcceptingPlaces() { return mAcceptingPlaces; } @Override public Set> getTransitions() { return mTransitions; } /** @return Outgoing transitions of given place. */ @Override public Set> getSuccessors(final PLACE place) { return mSuccessors.getImage(place); } /** @return Incoming transitions of given place. */ @Override public Set> getPredecessors(final PLACE place) { return mPredecessors.getImage(place); } /** * @return {@code true} if the number of tokens in the net is constant (= size of initial marking) during every run * of the net. */ public boolean constantTokenAmount() { return mConstantTokenAmount; } @Override public boolean isAccepting(final Marking marking) { for (final PLACE place : marking) { if (getAcceptingPlaces().contains(place)) { return true; } } return false; } @Override public int size() { return mSizeOfFlowRelation; } @Override public String sizeInformation() { return "has " + mPlaces.size() + " places, " + mTransitions.size() + " transitions, " + mSizeOfFlowRelation + " flow"; } /** @return Number of edges in this net. */ @Override public int flowSize() { return mPredecessors.size() + mSuccessors.size(); } @Override public boolean isAccepting(final PLACE place) { if (!mPlaces.contains(place)) { throw new IllegalArgumentException("unknown place " + place); } return mAcceptingPlaces.contains(place); } @Override public String toString() { return AutomatonDefinitionPrinter.toString(mServices, "net", this); } }