/*
* 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.unfolding;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import de.uni_freiburg.informatik.ultimate.automata.AutomataLibraryServices;
import de.uni_freiburg.informatik.ultimate.automata.AutomataOperationCanceledException;
import de.uni_freiburg.informatik.ultimate.automata.AutomatonDefinitionPrinter;
import de.uni_freiburg.informatik.ultimate.automata.IAutomaton;
import de.uni_freiburg.informatik.ultimate.automata.petrinet.IPetriNetSuccessorProvider;
import de.uni_freiburg.informatik.ultimate.automata.petrinet.Marking;
import de.uni_freiburg.informatik.ultimate.automata.petrinet.PetriNetNot1SafeException;
import de.uni_freiburg.informatik.ultimate.automata.petrinet.netdatastructures.Transition;
import de.uni_freiburg.informatik.ultimate.automata.petrinet.visualization.BranchingProcessToUltimateModel;
import de.uni_freiburg.informatik.ultimate.core.model.models.IElement;
import de.uni_freiburg.informatik.ultimate.util.datastructures.relation.HashRelation;
/**
* @author Julian Jarecki (jareckij@informatik.uni-freiburg.de)
* @author Matthias Heizmann (heizmann@informatik.uni-freiburg.de)
* @param
* symbol type
* @param
* place content type
*/
public final class BranchingProcess implements IAutomaton {
/**
* Before 2019-10-20 we added cut-off events and their successor conditions to the co-relation. This is not
* necessary for the computation of the finite prefix (maybe necessary for other applications) and we might be able
* to save some time. See Issue #448. https://github.com/ultimate-pa/ultimate/issues/448
*/
private static final boolean ADD_CUTOFF_EVENTS_TO_CORELATION = true;
private final AutomataLibraryServices mServices;
private final Collection> mConditions;
private final Collection> mEvents;
private final ICoRelation mCoRelation;
private final HashRelation> mPlace2Conds;
/**
* Dummy root event with all initial conditions as successors. Unlike all other events in this branching process,
* the root does not correspond to any transition of {@link #mNet}.
*/
private final Event mDummyRoot;
/**
* Net associated with this branching process. Places of this branching process correspond to places of the net.
* Events of this branching process correspond to transitions of the net.
*/
private final IPetriNetSuccessorProvider mNet;
/**
* The input is a {@link IPetriNetSuccessorProvider} and does not provide the predecessor transitions of places. We
* use this relation to store the information that we observed while computing the finite prefix.
*/
private final HashRelation> mYetKnownPredecessorTransitions = new HashRelation<>();
private final ConfigurationOrder mOrder;
private int mConditionSerialnumberCounter;
/**
* Relation between the hashcode of {@link Event} {@link Marking}s and all non-cut-off Events that have this
* {@link Marking}. Hashcode is the key, allows us to check find cut-off events more quickly.
*
* 2019-11-16 Matthias: I have some doubts that this optimization (hashcode instead of {@link Marking}) brings a
* measureable speedup but it makes the code more complicated. I case we have total {@link ConfigurationOrder} the
* image of the relation has size one and hence we could use a map instead of a relation. I guess that using a map
* instead of a relation will not bring a significant speedup and will only reduce the memory consumption by 64 byes
* (initial size of HashSet) per non-cut-off event.
*
*/
private final HashRelation> mMarkingNonCutoffEventRelation = new HashRelation<>();
/**
* #Backfolding Temporary boolean flag for testing our computation of a "finite comprehensive prefix".
*/
private final boolean mNewFiniteComprehensivePrefixMode = false;
private final boolean mUseFirstbornCutoffCheck;
public Set> mCutoffEvents = new HashSet<>();
public BranchingProcess(final AutomataLibraryServices services, final IPetriNetSuccessorProvider net,
final ConfigurationOrder order, final boolean useCutoffChekingPossibleExtention,
final boolean useB32Optimization) throws PetriNetNot1SafeException {
mServices = services;
mNet = net;
mOrder = order;
mPlace2Conds = new HashRelation<>();
mConditions = new HashSet<>();
mEvents = new HashSet<>();
if (useB32Optimization) {
mCoRelation = new ConditionEventsCoRelationB32<>(this);
} else {
mCoRelation = new ConditionEventsCoRelation<>(this);
}
mUseFirstbornCutoffCheck = useCutoffChekingPossibleExtention;
// add a dummy event as root. its successors are the initial conditions.
mDummyRoot = new Event<>(this);
// mCoRelation.initialize(mDummyRoot.getSuccessorConditions());
addEvent(mDummyRoot);
}
/**
* @return dummy root event with all initial conditions as successors. Is not associated with any transition from
* the net.
*/
public Event getDummyRoot() {
return mDummyRoot;
}
public Condition constructCondition(final Event predecessor, final PLACE place) {
return new Condition<>(predecessor, place, mConditionSerialnumberCounter++);
}
/**
* Adds an Event to the Branching Process with all outgoing Conditions.
*
* updates the Co-Relation.
*
*
* @param event
* event
* @return true iff some successor of e corresponds to an accepting place
* @throws PetriNetNot1SafeException
*/
boolean addEvent(final Event event) throws PetriNetNot1SafeException {
if (event != getDummyRoot()) {
for (final Condition c : event.getSuccessorConditions()) {
mYetKnownPredecessorTransitions.addPair(c.getPlace(), event.getTransition());
}
}
if (event.isCutoffEvent()) {
mCutoffEvents.add(event);
}
event.setSerialNumber(mEvents.size());
mEvents.add(event);
if (!mUseFirstbornCutoffCheck && !event.isCutoffEvent()) {
mMarkingNonCutoffEventRelation.addPair(event.getMark().hashCode(), event);
}
for (final Condition c : event.getPredecessorConditions()) {
assert !c.getPredecessorEvent().isCutoffEvent() : "Cut-off events must not have successors.";
c.addSuccesssor(event);
}
boolean someSuccessorIsAccepting = false;
for (final Condition c : event.getSuccessorConditions()) {
mConditions.add(c);
mPlace2Conds.addPair(c.getPlace(), c);
if (mNet.isAccepting(c.getPlace())) {
someSuccessorIsAccepting = true;
}
}
if (ADD_CUTOFF_EVENTS_TO_CORELATION || !event.isCutoffEvent()) {
mCoRelation.update(event);
}
return someSuccessorIsAccepting;
}
/**
* Checks if a new event {@code event}, with regards to {@code order} is a cut-off event. In that case, companions
* are computed as a side-effect.
*
* @param event
* event
* @param order
* order
* @param sameTransitionCutOff
* {@code true} iff events belong to the same transition
* @return true iff event is cut-off
* @see Event#checkCutOffAndSetCompanion(Event, Comparator, boolean)
*/
public boolean isCutoffEvent(final Event event, final Comparator> order,
final boolean sameTransitionCutOff) {
for (final Event ev : mMarkingNonCutoffEventRelation.getImage(event.getMark().hashCode())) {
if (mNewFiniteComprehensivePrefixMode) {
if (event.checkCutOffAndSetCompanionForComprehensivePrefix(ev, order, sameTransitionCutOff)) {
return true;
}
} else if (event.checkCutOffAndSetCompanion(ev, order, sameTransitionCutOff)) {
return true;
}
}
return false;
}
public Set> getCutoffEvents() {
return mCutoffEvents;
}
/**
* @param place
* place
* @return all conditions c s.t. p is the corresponding place of c.
*/
public Set> place2cond(final PLACE place) {
return mPlace2Conds.getImage(place);
}
public ICoRelation getCoRelation() {
return mCoRelation;
}
public Collection> getConditions() {
return mConditions;
}
public Collection> getEvents() {
return mEvents;
}
public Set> getConditions(final PLACE p) {
return Collections.unmodifiableSet(mPlace2Conds.getImage(p));
}
/**
* @return The initial conditions.
*/
public Collection> initialConditions() {
return mDummyRoot.getSuccessorConditions();
}
/**
* Returns all minimal events of this branching process with respect to the causal order. An event is causally
* minimal iff all its predecessors are initial conditions. Events with a non-initial preceding condition c cannot
* be minimal. Because c is non-initial it has to be preceded by another event which is causally smaller.
*
* @return The causally minimal events
*/
public Collection> minEvents() {
final Set> eventsConnectedToInitConditions = new HashSet<>();
for (final Condition initCondition : initialConditions()) {
eventsConnectedToInitConditions.addAll(initCondition.getSuccessorEvents());
}
final Set> minEvents = new HashSet<>();
for (final Event succEvent : eventsConnectedToInitConditions) {
if (initialConditions().containsAll(succEvent.getPredecessorConditions())) {
minEvents.add(succEvent);
}
}
return minEvents;
}
/**
* Returns the net associated with this branching process.
*
* @return Net associated with this branching process
*/
public IPetriNetSuccessorProvider getNet() {
return mNet;
}
/**
* Check if the Conditions c1 and c2 are in causal relation. Conditions c1 and c2 are in causal relation if
*
*
c1 != c2 and c1 is ancestor of c2
*
or c1 != c2 and c2 is ancestor of c1
*
*
* @param c1
* first condition
* @param c2
* second condition
* @return {@code true} iff the conditions are in causal relation
*/
public boolean inCausalRelation(final Condition c1, final Condition c2) {
if (c1 == c2) {
return false;
}
final Set