/*
* 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;
}
}