/* * Copyright (C) 2016 Mostafa M.A. (mostafa.amin93@gmail.com) * Copyright (C) 2014-2016 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.tree.operations; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; 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.IOperation; import de.uni_freiburg.informatik.ultimate.automata.statefactory.IMergeStateFactory; import de.uni_freiburg.informatik.ultimate.automata.statefactory.IStateFactory; import de.uni_freiburg.informatik.ultimate.automata.tree.IRankedLetter; import de.uni_freiburg.informatik.ultimate.automata.tree.ITreeAutomatonBU; import de.uni_freiburg.informatik.ultimate.automata.tree.TreeAutomatonBU; import de.uni_freiburg.informatik.ultimate.automata.tree.TreeAutomatonRule; /** * Determinize a given bottom-up tree automaton. * * @author mostafa (mostafa.amin93@gmail.com) * * @param * letter of the tree automaton. * @param * state of the tree automaton. */ public class Determinize extends GeneralOperation> implements IOperation> { private final ITreeAutomatonBU mTreeAutomaton; private final IMergeStateFactory mStateFactoryMerge; private final Map, STATE> mReducedStates; protected final ITreeAutomatonBU mResult; /*** * Compute the deterministic equivalent automaton of a given Bottom-up Tree * Automaton * * @param services * @param factory * @param tree * The given tree automaton */ public > Determinize( final AutomataLibraryServices services, final SF factory, final ITreeAutomatonBU tree) { super(services); mReducedStates = new HashMap<>(); mStateFactoryMerge = factory; mTreeAutomaton = tree; if (new isDetereministic<>(services, tree).getResult()) { mResult = tree; } else { mResult = computeDeterminize(); } } private STATE reduceState(final Set key) { if (!mReducedStates.containsKey(key)) { mReducedStates.put(key, mStateFactoryMerge.merge(key)); } return mReducedStates.get(key); } @Override public String startMessage() { return "Starting determinization"; } @Override public String exitMessage() { return "Exiting determinization"; } private TreeAutomatonBU constructFromRules( final Map>, Set>> rules) { final TreeAutomatonBU res = new TreeAutomatonBU<>(); res.extendAlphabet(mTreeAutomaton.getAlphabet()); for (final Entry>, Set>> letterMap : rules.entrySet()) { final LETTER letter = letterMap.getKey(); final Map>, Set> mp = rules.get(letter); for (final Entry>, Set> sSrcDest : mp.entrySet()) { final List> sSrc = sSrcDest.getKey(); final List src = new ArrayList<>(); for (final Set sub : sSrc) { final STATE state = reduceState(sub); src.add(state); for (final STATE s : sub) { if (mTreeAutomaton.isFinalState(s)) { res.addFinalState(state); } } } final Set sDest = sSrcDest.getValue(); final STATE dest = reduceState(sDest); res.addRule(new TreeAutomatonRule<>(letter, src, dest)); for (final STATE s : sDest) { if (mTreeAutomaton.isFinalState(s)) { res.addFinalState(dest); } } } } return res; } private Map>, Set>> transformToSingletonSets() { final Map> stateToSState = new HashMap<>(); final Map>, Set>> rules = new HashMap<>(); for (final List src : mTreeAutomaton.getSourceCombinations()) { for (final TreeAutomatonRule rule : mTreeAutomaton.getSuccessors(src)) { if (!rules.containsKey(rule.getLetter())) { rules.put(rule.getLetter(), new HashMap<>()); } final Map>, Set> mp = rules.get(rule.getLetter()); final List> source = new ArrayList<>(); for (final STATE sr : rule.getSource()) { if (!stateToSState.containsKey(sr)) { final Set nw = new HashSet<>(); nw.add(sr); stateToSState.put(sr, nw); } source.add(stateToSState.get(sr)); } final STATE sr = rule.getDest(); if (!stateToSState.containsKey(sr)) { final Set nw = new HashSet<>(); nw.add(sr); stateToSState.put(sr, nw); } if (!mp.containsKey(source)) { mp.put(source, new HashSet()); } mp.get(source).add(sr); } } return rules; } private void addAllSubsetStates(final Set state, final Map>, Set>>> newRules, final LETTER letter, final ArrayList> src, final Set dest, final int idx) { if (idx >= src.size()) { assert src.size() == letter.getRank(); if (!newRules.get(letter).containsKey(src)) { newRules.get(letter).put(src, new HashSet>()); } newRules.get(letter).get(src).add(dest); return; } if (state.containsAll(src.get(idx))) { @SuppressWarnings("unchecked") final ArrayList> toAdd = (ArrayList>) src.clone(); toAdd.set(idx, state); /* * if (!newRules.get(letter).containsKey(toAdd)) { * newRules.get(letter).put(toAdd, new HashSet>()); } * newRules.get(letter).get(toAdd).add(dest); */ addAllSubsetStates(state, newRules, letter, toAdd, dest, idx + 1); } addAllSubsetStates(state, newRules, letter, src, dest, idx + 1); } private List> determinizeOneStep(final Set state, final Map>, Set>> rules) { final Map>, Set>>> newRules = new HashMap<>(); for (final Entry>, Set>> letterRule : rules.entrySet()) { final LETTER letter = letterRule.getKey(); if (!newRules.containsKey(letter)) { newRules.put(letter, new HashMap<>()); } final Map>, Set> mp = letterRule.getValue(); for (final Entry>, Set> srcDest : mp.entrySet()) { final ArrayList> src = (ArrayList>) srcDest.getKey(); final Set dest = srcDest.getValue(); addAllSubsetStates(state, newRules, letter, src, dest, 0); } } final List> res = new ArrayList<>(); for (final Entry>, Set>>> letterMap : newRules.entrySet()) { final LETTER letter = letterMap.getKey(); final Map>, Set>> mp = newRules.get(letter); for (final Entry>, Set>> srcDestSet : mp.entrySet()) { final List> st = srcDestSet.getKey(); final Set> dest = mp.get(st); final Set uni = new HashSet<>(); for (final Set s : dest) { uni.addAll(s); } rules.get(letter).put(st, uni); if (mp.get(st).size() > 1) { res.add(uni); } } } return res; } private ITreeAutomatonBU computeDeterminize() { // Transform inital states to singleton sets final Map>, Set>> rules = transformToSingletonSets(); // Add non-deterministic rules final LinkedList> worklist = new LinkedList<>(); for (final Entry>, Set>> letterRule : rules.entrySet()) { final Map>, Set> mp = letterRule.getValue(); for (final Entry>, Set> st : mp.entrySet()) { if (st.getValue().size() > 1) { // Rules with multiple destination worklist.add(st.getValue()); } } } final Set> visited = new HashSet<>(); while (!worklist.isEmpty()) { final Set state = worklist.poll(); if (visited.contains(state)) { continue; } visited.add(state); worklist.addAll(determinizeOneStep(state, rules)); } return constructFromRules(rules); } @Override public ITreeAutomatonBU getResult() { assert new isDetereministic<>(mServices, mResult).getResult(); return mResult; } @Override public boolean checkResult(final IStateFactory stateFactory) throws AutomataLibraryException { return true; } }