diff --git a/learnlib/DumpRFSAEQOracle.java b/learnlib/DumpRFSAEQOracle.java index a9823fda9396632f03875e6dcc84caed9a1c00d4..f2856e8422b6d48904d1532c0d72d18fa08deaa2 100644 --- a/learnlib/DumpRFSAEQOracle.java +++ b/learnlib/DumpRFSAEQOracle.java @@ -33,6 +33,7 @@ public class DumpRFSAEQOracle<A extends FiniteStateAcceptor<?, I>, I, D> extends @Override public DefaultQuery<I, D> findCounterExample(A hypothesis, Collection<? extends I> inputs) { if (target.getStates().size() == hypothesis.getStates().size()) { + // TODO return null; } diff --git a/learnlib/GenRFSA.java b/learnlib/GenRFSA.java index 7816d27321058bca9e637bb6ccf2d2e2755fea60..050e4a9f467877572caf83cd0741a28386e900ba 100644 --- a/learnlib/GenRFSA.java +++ b/learnlib/GenRFSA.java @@ -1,26 +1,74 @@ +import com.google.common.collect.Sets; +import net.automatalib.automata.fsa.impl.compact.CompactDFA; import net.automatalib.automata.fsa.impl.compact.CompactNFA; import net.automatalib.util.automata.builders.FSABuilder; import net.automatalib.visualization.Visualization; +import java.util.*; + public class GenRFSA<A extends CompactNFA<I>, S, I> { - public CompactNFA<I> reverse(A source){ - FSABuilder<Integer, I, CompactNFA<I>> builder = new FSABuilder<>(new CompactNFA<>(source.getInputAlphabet(), source.getStates().size())); + public CompactNFA<I> reverse(A original){ + FSABuilder<Integer, I, CompactNFA<I>> builder = new FSABuilder<>(new CompactNFA<>(original.getInputAlphabet(), original.getStates().size())); - for (int a : source.getStates()) { - if (source.isAccepting(a)) + for (int a : original.getStates()) { + if (original.isAccepting(a)) builder.withInitial(a); - for (I i: source.getInputAlphabet()) - for (int c : source.getTransitions(a, i)) + for (I i: original.getInputAlphabet()) + for (int c : original.getTransitions(a, i)) builder.from(c).on(i).to(a); } - - builder.withAccepting(source.getInitialStates().toArray()); + original.getInitialStates().forEach(builder::withAccepting); return builder.create(); } - public + public CompactNFA<I> canonicalize(A original){ + FSABuilder<Integer, I, CompactNFA<I>> builder = new FSABuilder<>(new CompactNFA<>(original.getInputAlphabet())); + + Map<BitSet, Integer> outMap = new HashMap<>(); + + CompactDFA<I> out = new CompactDFA<I>(original.getInputAlphabet()); + NFAs.determinize(original, original.getInputAlphabet(), out, false, outMap); + + Set<BitSet> detStates = outMap.keySet(); + Set<BitSet> coverableStates = new HashSet<>(); + for (BitSet testState : detStates) { + Set<BitSet> usingStates = new HashSet<>(outMap.keySet()); + usingStates.remove(testState); + //kind a subset-sum + Set<Set<BitSet>> powersets = Sets.powerSet(usingStates); + for (Set<BitSet> set : powersets) { + BitSet sum = new BitSet(); + set.forEach(s -> sum.or(s)); + if (sum.equals(testState)){ + coverableStates.add(testState); + break; + } + } + } + detStates.removeAll(coverableStates); + + //initial states + BitSet initBs = new BitSet(); + original.getInitialStates().forEach(initBs::set); + + for (BitSet s : detStates) { + BitSet tmp = (BitSet) s.clone(); + //initial states + tmp.or(initBs); + if (s.equals(initBs)) + builder.withInitial(s); + + //final states + if () + builder.withAccepting(s); + } + + //TODO transitions + + return builder.create(); + } public static void main(String[] args) { RandomNFAGen c = new RandomNFAGen(10); @@ -29,5 +77,6 @@ public class GenRFSA<A extends CompactNFA<I>, S, I> { Visualization.visualize(nfa); Visualization.visualize(genRFSA.reverse(nfa)); + Visualization.visualize(genRFSA.canonicalize(nfa)); } } diff --git a/learnlib/NFAs.java b/learnlib/NFAs.java new file mode 100644 index 0000000000000000000000000000000000000000..c6031e67c783448307016c0bfbf1eb2702ffb36a --- /dev/null +++ b/learnlib/NFAs.java @@ -0,0 +1,160 @@ +/* Copyright (C) 2013-2019 TU Dortmund + * This file is part of AutomataLib, http://www.automatalib.net/. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collection; +import java.util.Deque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.automatalib.automata.concepts.InputAlphabetHolder; +import net.automatalib.automata.concepts.StateIDs; +import net.automatalib.automata.fsa.MutableDFA; +import net.automatalib.automata.fsa.NFA; +import net.automatalib.automata.fsa.impl.compact.CompactDFA; +import net.automatalib.automata.fsa.impl.compact.CompactNFA; +import net.automatalib.util.automata.Automata; +import net.automatalib.words.Alphabet; + +public final class NFAs { + + private NFAs() {} + + public static <I> CompactDFA<I> determinize(NFA<?, I> nfa, Alphabet<I> inputAlphabet) { + return determinize(nfa, inputAlphabet, false, true); + } + + public static <I> CompactDFA<I> determinize(NFA<?, I> nfa, + Alphabet<I> inputAlphabet, + boolean partial, + boolean minimize) { + CompactDFA<I> result = new CompactDFA<>(inputAlphabet); + determinize(nfa, inputAlphabet, result, partial, minimize); + return result; + } + + public static <I> void determinize(NFA<?, I> nfa, + Collection<? extends I> inputs, + MutableDFA<?, I> out, + boolean partial, + boolean minimize) { + doDeterminize(nfa, inputs, out, partial); + if (minimize) { + Automata.invasiveMinimize(out, inputs); + } + } + + public static <I, SO> void determinize(NFA<?, I> nfa, + Collection<? extends I> inputs, + MutableDFA<SO, I> out, + boolean partial, + Map<BitSet, SO> outMap) { + doDeterminize(nfa, inputs, out, partial, outMap); + } + + public static <I, A extends NFA<?, I> & InputAlphabetHolder<I>> CompactDFA<I> determinize(A nfa) { + return determinize(nfa, false, true); + } + + public static <I, A extends NFA<?, I> & InputAlphabetHolder<I>> CompactDFA<I> determinize(A nfa, + boolean partial, + boolean minimize) { + return determinize(nfa, nfa.getInputAlphabet(), partial, minimize); + } + + public static <I> void determinize(NFA<?, I> nfa, Collection<? extends I> inputs, MutableDFA<?, I> out) { + determinize(nfa, inputs, out, false, true); + } + + + private static <I, SI, SO> void doDeterminize(NFA<SI, I> nfa, + Collection<? extends I> inputs, + MutableDFA<SO, I> out, + boolean partial) { + Map<BitSet, SO> outStateMap = new HashMap<>(); + doDeterminize(nfa, inputs, out, partial, outStateMap); + } + + private static <I, SI, SO> void doDeterminize(NFA<SI, I> nfa, + Collection<? extends I> inputs, + MutableDFA<SO, I> out, + boolean partial, + Map<BitSet, SO> outStateMap) { + + StateIDs<SI> stateIds = nfa.stateIDs(); + + Deque<DeterminizeRecord<SI, SO>> stack = new ArrayDeque<>(); + + List<SI> initList = new ArrayList<>(nfa.getInitialStates()); + BitSet initBs = new BitSet(); + for (SI init : initList) { + initBs.set(stateIds.getStateId(init)); + } + + boolean initAcc = nfa.isAccepting(initList); + SO initOut = out.addInitialState(initAcc); + + outStateMap.put(initBs, initOut); + + stack.push(new DeterminizeRecord<>(initList, initOut)); + + while (!stack.isEmpty()) { + DeterminizeRecord<SI, SO> curr = stack.pop(); + + List<SI> inStates = curr.inputStates; + SO outState = curr.outputState; + + for (I sym : inputs) { + BitSet succBs = new BitSet(); + List<SI> succList = new ArrayList<>(); + + for (SI inState : inStates) { + for (SI succState : nfa.getSuccessors(inState, sym)) { + int succId = stateIds.getStateId(succState); + if (!succBs.get(succId)) { + succBs.set(succId); + succList.add(succState); + } + } + } + + if (!partial || !succList.isEmpty()) { + SO outSucc = outStateMap.get(succBs); + if (outSucc == null) { + outSucc = out.addState(nfa.isAccepting(succList)); + outStateMap.put(succBs, outSucc); + stack.push(new DeterminizeRecord<>(succList, outSucc)); + } + out.setTransition(outState, sym, outSucc); + } + } + } + + } + + private static final class DeterminizeRecord<SI, SO> { + + private final List<SI> inputStates; + private final SO outputState; + + DeterminizeRecord(List<SI> inputStates, SO outputState) { + this.inputStates = inputStates; + this.outputState = outputState; + } + } +}