/*
* Copyright (C) 2018 schaetzc@tf.uni-freiburg.de
* Copyright (C) 2009-2018 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.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import de.uni_freiburg.informatik.ultimate.automata.AutomataLibraryServices;
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.Transition;
import de.uni_freiburg.informatik.ultimate.util.datastructures.DataStructureUtils;
import de.uni_freiburg.informatik.ultimate.util.datastructures.ImmutableSet;
/**
* Copies a Petri net N partially, creating a sub-net N'.
*
* Given a Petri net N and a subset T' ⊆ T of its transitions, creates a Petri net N' with transitions T' and required
* places (see {@link #requiredPlaces()}) only.
*
* @author schaetzc@tf.uni-freiburg.de
*
* @param
* Type of letters in alphabet of Petri net
* @param
* Type of places in Petri net
*/
public final class CopySubnet {
/** The Petri Net for which we copy partially to create a sub-net. */
private final IPetriNet mSuperNet;
private final boolean mKeepSuccessorPlaces;
private final Set> mTransitionSubset;
private final BoundedPetriNet mResult;
/**
* Copies a net partially, creating a sub-net.
*
* @param services
* Services for logging and so on
* @param superNet
* Petri net N to be copied partially
* @param transitionSubset
* Subset of transitions of net N forming the transitions of sub-net N'
* @param keepSuccessorPlaces
* Whether or not to keep successor places for all included transitions. Setting this to false may result
* in transitions with an empty post-set.
*/
private CopySubnet(final AutomataLibraryServices services, final IPetriNet superNet,
final Set> transitionSubset, final Set newAlphabet,
final boolean keepSuccessorPlaces) {
mSuperNet = superNet;
mKeepSuccessorPlaces = keepSuccessorPlaces;
mTransitionSubset = transitionSubset;
final boolean constantTokenAmount = false;
mResult = new BoundedPetriNet<>(services, newAlphabet, constantTokenAmount);
copySubnet();
}
/**
* Copies a net partially, creating a sub-net. The sub-net is defined in terms of transitions. Places that are no
* longer required are excluded automatically.
*
* @param
* Type of the transition labels in the old and new petri net
* @param
* Type of the places in the old and new petri net
* @param services
* Services for logging and so on
* @param superNet
* Petri net N to be copied partially
* @param transitionSubset
* Subset of transitions of net N forming the transitions of sub-net N'
* @param newAlphabet
* New alphabet of sub-net N'. The new alphabet can be a subset or superset of the old alphabet, however,
* all labels from {@code transitionSubset} have to be included.
* @param keepSuccessorPlaces
* Whether or not to keep successor places for all included transitions. Setting this to false may result
* in transitions with an empty post-set.
* @return Subnet N'
*/
public static BoundedPetriNet copy(final AutomataLibraryServices services,
final IPetriNet superNet, final Set> transitionSubset,
final Set newAlphabet, final boolean keepSuccessorPlaces) {
return new CopySubnet<>(services, superNet, transitionSubset, newAlphabet, keepSuccessorPlaces).getResult();
}
/**
* Copies a net partially, creating a sub-net. The sub-net is defined in terms of transitions. Places that are no
* longer required are excluded automatically.
*
* @param
* Type of the transition labels in the old and new petri net
* @param
* Type of the places in the old and new petri net
* @param services
* Services for logging and so on
* @param superNet
* Petri net N to be copied partially
* @param transitionSubset
* Subset of transitions of net N forming the transitions of sub-net N'
* @param newAlphabet
* New alphabet of sub-net N'. The new alphabet can be a subset or superset of the old alphabet, however,
* all labels from {@code transitionSubset} have to be included.
* @return Subnet N'
*/
public static BoundedPetriNet copy(final AutomataLibraryServices services,
final IPetriNet superNet, final Set> transitionSubset,
final Set newAlphabet) {
return copy(services, superNet, transitionSubset, newAlphabet, false);
}
/**
* Copies a net partially, creating a sub-net. The sub-net is defined in terms of transitions. Places that are no
* longer required are excluded automatically. The alphabet stays the same, even if some letters are no longer used.
*
* @param
* Type of the transition labels in the old and new petri net
* @param
* Type of the places in the old and new petri net
* @param services
* Services for logging and so on
* @param superNet
* Petri net N to be copied partially
* @param transitionSubset
* Subset of transitions of net N forming the transitions of sub-net N'
* @return Subnet N'
*/
public static BoundedPetriNet copy(final AutomataLibraryServices services,
final IPetriNet superNet, final Set> transitionSubset) {
return copy(services, superNet, transitionSubset, superNet.getAlphabet());
}
/**
* Returns the result of the operation modeled by this class, see documentation of {@link CopySubnet}.
*
* @return Sub-net
*/
public BoundedPetriNet getResult() {
return mResult;
}
private void copySubnet() {
requiredPlaces().forEach(this::rebuildPlace);
mTransitionSubset.forEach(this::rebuildTransition);
}
private void rebuildPlace(final PLACE place) {
final boolean isInitial = mSuperNet.getInitialPlaces().contains(place);
final boolean isAccepting = mSuperNet.getAcceptingPlaces().contains(place);
final boolean newlyAdded = mResult.addPlace(place, isInitial, isAccepting);
if (!newlyAdded) {
throw new AssertionError("Must not add place twice: " + place);
}
}
private void rebuildTransition(final Transition trans) {
final Set succ = DataStructureUtils.intersection(trans.getSuccessors(), mResult.getPlaces());
mResult.addTransition(trans.getSymbol(), trans.getPredecessors(), ImmutableSet.of(succ));
}
/**
* Returns a the required places in a sub-net N' of a Petri net N. Sub-net N' has the same places as N, but only
* some transitions.
*
* A place p is required in N' iff p is predecessor of some transition in N', or p is accepting and successor of
* some transition in N', or p is accepting and initial in N'.
*
* @param net
* Petri net N
* @param transitionSubset
* transitions of N'
* @return required places in N'
*
* @return Superset of the required places
*/
private Set requiredPlaces() {
final Set requiredPlaces = new HashSet<>();
for (final Transition trans : mTransitionSubset) {
requiredPlaces.addAll(trans.getPredecessors());
// successor places are only really required
// if they are predecessors of another reachable transition
// or if they are accepting
if (mKeepSuccessorPlaces) {
requiredPlaces.addAll(trans.getSuccessors());
}
}
acceptingSuccPlaces().forEach(requiredPlaces::add);
// one of all always accepting places would be sufficient, but not deterministic
alwaysAcceptingPlaces().forEach(requiredPlaces::add);
return requiredPlaces;
}
/**
* Returns all accepting places that are also a successor of at least one transition from a given set of transition.
*
* @return Successor places of T' that are also accepting
*/
private Stream acceptingSuccPlaces() {
return mSuperNet.getAcceptingPlaces().stream().filter(accPlace -> DataStructureUtils
.haveNonEmptyIntersection(mSuperNet.getPredecessors(accPlace), mTransitionSubset));
}
/**
* Returns all places that are accepting, initial, and not connected to any transition in a sub-net N' of a Petri
* net N. Sub-net N' has the same places as N, but only some transitions.
*
* The returned places are only a subset of the places which are always accepting. Places that are always accepting
* because their outgoing transitions can never fire are not considered. Places that are always accepting because
* their outgoing transitions are also incoming are not considered.
*
* @return subset of the always accepting places in N'
*/
private Stream alwaysAcceptingPlaces() {
return acceptingInitialPlaces(mSuperNet).filter(accIniPlace -> DataStructureUtils
.haveEmptyIntersection(mSuperNet.getSuccessors(accIniPlace), mTransitionSubset));
}
/**
* Returns all places that are accepting and initial.
*
* @return accepting initial places of N
*/
private static Stream acceptingInitialPlaces(final IPetriNet net) {
return net.getAcceptingPlaces().stream().filter(net.getInitialPlaces()::contains);
}
}