/* * 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.Objects; import java.util.Set; import java.util.stream.Collectors; import de.uni_freiburg.informatik.ultimate.automata.AutomataLibraryException; import de.uni_freiburg.informatik.ultimate.automata.AutomataLibraryServices; import de.uni_freiburg.informatik.ultimate.automata.AutomataOperationCanceledException; import de.uni_freiburg.informatik.ultimate.automata.AutomataOperationStatistics; import de.uni_freiburg.informatik.ultimate.automata.StatisticsType; import de.uni_freiburg.informatik.ultimate.automata.nestedword.INwaInclusionStateFactory; import de.uni_freiburg.informatik.ultimate.automata.petrinet.IPetriNet; import de.uni_freiburg.informatik.ultimate.automata.petrinet.PetriNetNot1SafeException; import de.uni_freiburg.informatik.ultimate.automata.petrinet.UnaryNetOperation; 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.petrinet.unfolding.BranchingProcess; import de.uni_freiburg.informatik.ultimate.automata.petrinet.unfolding.Event; import de.uni_freiburg.informatik.ultimate.automata.petrinet.unfolding.FinitePrefix; import de.uni_freiburg.informatik.ultimate.automata.statefactory.IPetriNet2FiniteAutomatonStateFactory; import de.uni_freiburg.informatik.ultimate.core.lib.exceptions.RunningTaskInfo; /** * Removes unreachable nodes of a Petri Net preserving its behavior. Nodes are either transitions or places. *

* A transition is unreachable iff it can never fire (because there is no reachable marking covering all of its * preceding places). *

* A place is unreachable iff it is not covered by any reachable marking. *

* This operation may also remove some of the reachable places if they are not needed. Required are only *

* * @author schaetzc@tf.uni-freiburg.de * * @param * Type of letters in alphabet of Petri net * @param * Type of places in Petri net * @param * Type of factory needed to check the result of this operation in {@link #checkResult(CRSF)} */ public class RemoveUnreachable & INwaInclusionStateFactory> extends UnaryNetOperation { private static final boolean DEBUG_COMPUTE_REMOVED_TRANSITIONS = false; private final IPetriNet mOperand; /** {@link #mOperand} with only reachable transitions and required places. */ private final BoundedPetriNet mResult; private final Set> mReachableTransitions; public RemoveUnreachable(final AutomataLibraryServices services, final IPetriNet operand) throws AutomataOperationCanceledException, PetriNetNot1SafeException { this(services, operand, null); } /** * Copies the reachable parts of a net. * * @param operand * Petri net to be copied such that only reachable nodes remain. * @param reachableTransitions * The reachable transitions (or a superset) of {@code operand}. Can be computed from an existing finite * prefix using {@link #reachableTransitions(BranchingProcess)} or automatically when using * {@link #RemoveUnreachable(AutomataLibraryServices, BoundedPetriNet)}. * * @throws AutomataOperationCanceledException * The operation was canceled * @throws PetriNetNot1SafeException */ public RemoveUnreachable(final AutomataLibraryServices services, final IPetriNet operand, final Set> reachableTransitions) throws AutomataOperationCanceledException, PetriNetNot1SafeException { super(services); mOperand = operand; if (mLogger.isInfoEnabled()) { mLogger.info(startMessage()); } mReachableTransitions = (reachableTransitions == null ? computeReachableTransitions() : reachableTransitions); if (DEBUG_COMPUTE_REMOVED_TRANSITIONS) { final Set> removedTransitions = operand.getTransitions().stream() .filter(x -> !mReachableTransitions.contains(x)).collect(Collectors.toSet()); if (!removedTransitions.isEmpty()) { mLogger.info("Removed transitions: " + removedTransitions); } } mResult = CopySubnet.copy(services, mOperand, mReachableTransitions); if (mLogger.isInfoEnabled()) { mLogger.info(exitMessage()); } } @Override public String exitMessage() { final StringBuilder sb = new StringBuilder(); sb.append("Finished " + getOperationName() + "."); sb.append(" "); sb.append("Removed "); sb.append(computeRemovedPlaces() + " of " + mOperand.getPlaces().size() + " places"); sb.append(", "); sb.append(computeRemovedTransitions() + " of " + mOperand.getTransitions().size() + " transitions"); sb.append(", "); sb.append(computeRemovedFlow() + " of " + mOperand.flowSize() + " flow."); return sb.toString(); } private Set> computeReachableTransitions() throws AutomataOperationCanceledException, PetriNetNot1SafeException { try { final BranchingProcess finPre = new FinitePrefix<>(mServices, mOperand).getResult(); return reachableTransitions(finPre); } catch (final AutomataOperationCanceledException aoce) { final RunningTaskInfo rti = new RunningTaskInfo(getClass(), "analyzing net that has " + mOperand.getPlaces().size() + " and " + mOperand.getTransitions().size() + " transistions."); aoce.addRunningTaskInfo(rti); throw aoce; } } /** * From a complete finite prefix compute the reachable transitions of the original Petri net. A transition t is * reachable iff there is a reachable marking enabling t. * * @param finPre * complete finite Prefix of a Petri net N * @return reachable transitions in N */ public static Set> reachableTransitions(final BranchingProcess finPre) { return finPre.getEvents().stream().map(Event::getTransition) // finPre contains dummy root-event which does not correspond to any transition .filter(Objects::nonNull).collect(Collectors.toSet()); } @Override public BoundedPetriNet getResult() { return mResult; } @Override protected IPetriNet getOperand() { return mOperand; } private int computeRemovedPlaces() { return mOperand.getPlaces().size() - mResult.getPlaces().size(); } private int computeRemovedTransitions() { return mOperand.getTransitions().size() - mResult.getTransitions().size(); } private int computeRemovedFlow() { return mOperand.flowSize() - mResult.flowSize(); } @Override public boolean checkResult(final CRSF stateFactory) throws AutomataLibraryException { if (mLogger.isInfoEnabled()) { mLogger.info("Testing correctness of " + getOperationName()); } final boolean correct = PetriNetUtils.isEquivalent(mServices, stateFactory, mOperand, mResult); if (mLogger.isInfoEnabled()) { mLogger.info("Finished testing correctness of " + getOperationName()); } return correct; } @Override public AutomataOperationStatistics getAutomataOperationStatistics() { final AutomataOperationStatistics statistics = new AutomataOperationStatistics(); statistics.addKeyValuePair(StatisticsType.PETRI_REMOVED_PLACES, computeRemovedPlaces()); statistics.addKeyValuePair(StatisticsType.PETRI_REMOVED_TRANSITIONS, computeRemovedTransitions()); statistics.addKeyValuePair(StatisticsType.PETRI_REMOVED_FLOW, computeRemovedFlow()); statistics.addKeyValuePair(StatisticsType.PETRI_ALPHABET, mResult.getAlphabet().size()); statistics.addKeyValuePair(StatisticsType.PETRI_PLACES, mResult.getPlaces().size()); statistics.addKeyValuePair(StatisticsType.PETRI_TRANSITIONS, mResult.getTransitions().size()); statistics.addKeyValuePair(StatisticsType.PETRI_FLOW, mResult.flowSize()); return statistics; } }