From f26281c3f7941f345857ea3073e1a980b37befcd Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Wed, 13 Jul 2022 13:22:24 +0100 Subject: [PATCH 01/17] feat: Adaptive PTT --- .../mealy/tree/AdaptiveMealyTreeBuilder.java | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java new file mode 100644 index 0000000000..5bea349fc7 --- /dev/null +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java @@ -0,0 +1,86 @@ +/* Copyright (C) 2013-2022 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. + */ +package net.automatalib.incremental.mealy.tree; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.PriorityQueue; + +import net.automatalib.commons.util.Pair; +import net.automatalib.util.graphs.traversal.GraphTraversal; +import net.automatalib.words.Alphabet; +import net.automatalib.words.Word; + +public class AdaptiveMealyTreeBuilder extends IncrementalMealyTreeBuilder { + private final Map, Pair, Integer>> stateToQuery = new HashMap<>(); + private final PriorityQueue> queryStates = new PriorityQueue<>( + (Node a, Node b) -> Integer.compare(stateToQuery.get(a).getSecond(), + stateToQuery.get(b).getSecond())); + + public AdaptiveMealyTreeBuilder(Alphabet inputAlphabet) { + super(inputAlphabet); + } + + @Override + public void insert(Word input, Word outputWord) { + throw new UnsupportedOperationException("Use insert: (Word, Word, Intger) -> Boolean"); + } + + public Boolean insert(Word input, Word outputWord, Integer queryIndex) { + Boolean hasOverwritten = false; + Node curr = root; + + Iterator outputIt = outputWord.iterator(); + for (I sym : input) { + O out = outputIt.next(); + Edge, O> edge = getEdge(curr, sym); + if (edge == null) { + curr = insertNode(curr, sym, out); + } else { + if (!Objects.equals(out, edge.getOutput())) { + removeQueries(edge.getTarget()); + removeEdge(curr, sym); + curr = insertNode(curr, sym, out); + hasOverwritten = true; + } else { + curr = edge.getTarget(); + } + } + } + + stateToQuery.put(curr, Pair.of(input, queryIndex)); + queryStates.add(curr); + return hasOverwritten; + } + + private void removeQueries(Node node) { + GraphTraversal.bfIterator(this.asGraph(), Collections.singleton(node)).forEachRemaining(n -> { + queryStates.remove(n); + stateToQuery.remove(n); + }); + } + + private void removeEdge(Node node, I symbol) { + node.setEdge(inputAlphabet.getSymbolIndex(symbol), null); + } + + public Word getOldestQuery() { + return stateToQuery.get(queryStates.peek()).getFirst(); + } +} From 6b7847218c3f52f21736b46cdfd7742b6d806de1 Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Thu, 14 Jul 2022 11:49:53 +0100 Subject: [PATCH 02/17] feat: unit tests --- .../mealy/tree/AdaptiveMealyTreeBuilder.java | 26 +- .../mealy/AdaptiveMealyTreeBuilderTest.java | 252 ++++++++++++++++++ 2 files changed, 271 insertions(+), 7 deletions(-) create mode 100644 incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java index 5bea349fc7..f02c4a32b7 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java @@ -28,7 +28,7 @@ import net.automatalib.words.Word; public class AdaptiveMealyTreeBuilder extends IncrementalMealyTreeBuilder { - private final Map, Pair, Integer>> stateToQuery = new HashMap<>(); + private final Map, Pair, Integer>> stateToQuery = new HashMap<>(); private final PriorityQueue> queryStates = new PriorityQueue<>( (Node a, Node b) -> Integer.compare(stateToQuery.get(a).getSecond(), stateToQuery.get(b).getSecond())); @@ -39,15 +39,16 @@ public AdaptiveMealyTreeBuilder(Alphabet inputAlphabet) { @Override public void insert(Word input, Word outputWord) { - throw new UnsupportedOperationException("Use insert: (Word, Word, Intger) -> Boolean"); + this.insert(input, outputWord, 0); } - public Boolean insert(Word input, Word outputWord, Integer queryIndex) { + public Boolean insert(Word input, Word outputWord, Integer queryIndex) { Boolean hasOverwritten = false; Node curr = root; Iterator outputIt = outputWord.iterator(); - for (I sym : input) { + for (int i = 0; i < input.length(); i++) { + I sym = input.getSymbol(i); O out = outputIt.next(); Edge, O> edge = getEdge(curr, sym); if (edge == null) { @@ -64,23 +65,34 @@ public Boolean insert(Word input, Word outputWord, Integer queryIndex) { } } + assert curr != null; stateToQuery.put(curr, Pair.of(input, queryIndex)); + + // Make sure it uses the new ages. + queryStates.remove(curr); queryStates.add(curr); + + assert stateToQuery.size() == queryStates.size(); + return hasOverwritten; } private void removeQueries(Node node) { GraphTraversal.bfIterator(this.asGraph(), Collections.singleton(node)).forEachRemaining(n -> { - queryStates.remove(n); + if (queryStates.contains(n)) { + queryStates.remove(n); + } stateToQuery.remove(n); }); + + assert stateToQuery.size() == queryStates.size(); } private void removeEdge(Node node, I symbol) { - node.setEdge(inputAlphabet.getSymbolIndex(symbol), null); + node.setEdge(this.getInputAlphabet().getSymbolIndex(symbol), null); } - public Word getOldestQuery() { + public Word getOldestQuery() { return stateToQuery.get(queryStates.peek()).getFirst(); } } diff --git a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java new file mode 100644 index 0000000000..a53aa851bd --- /dev/null +++ b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java @@ -0,0 +1,252 @@ +/* Copyright (C) 2013-2022 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. + */ +package net.automatalib.incremental.mealy; + +import net.automatalib.automata.transducers.impl.compact.CompactMealy; +import net.automatalib.commons.util.Pair; +import net.automatalib.incremental.mealy.tree.AdaptiveMealyTreeBuilder; +import net.automatalib.ts.output.MealyTransitionSystem; +import net.automatalib.words.Alphabet; +import net.automatalib.words.GrowingAlphabet; +import net.automatalib.words.Word; +import net.automatalib.words.WordBuilder; +import net.automatalib.words.impl.Alphabets; +import net.automatalib.words.impl.GrowingMapAlphabet; + +import java.util.ArrayList; +import java.util.LinkedList; + +import org.testng.annotations.Test; + +import junit.framework.Assert; + +@Test +public class AdaptiveMealyTreeBuilderTest { + + private static final Alphabet TEST_ALPHABET = Alphabets.characters('a', 'c'); + private static final Word W_1 = Word.fromString("abc"); + private static final Word W_1_O = Word.fromString("xyz"); + private static final Word W_2 = Word.fromString("ac"); + private static final Word W_2_O = Word.fromString("xw"); + private static final Word W_3 = Word.fromString("acb"); + private static final Word W_3_O = Word.fromString("xwu"); + + // Confluence Bug + private static final Word W_B_1 = Word.fromString("aaa"); + private static final Word W_B_1_O = Word.fromString("xxx"); + private static final Word W_B_2 = Word.fromString("bba"); + private static final Word W_B_2_O = Word.fromString("xxx"); + private static final Word W_B_3 = Word.fromString("aabaa"); + private static final Word W_B_3_O = Word.fromString("xxxxx"); + + private AdaptiveMealyTreeBuilder adaptiveMealy = new AdaptiveMealyTreeBuilder<>( + TEST_ALPHABET); + + @Test + public void testConfluenceBug() { + adaptiveMealy.insert(W_B_1, W_B_1_O); + adaptiveMealy.insert(W_B_2, W_B_2_O); + adaptiveMealy.insert(W_B_3, W_B_3_O); + + Assert.assertFalse(adaptiveMealy.lookup(Word.fromString("aababaa"), new ArrayList<>())); + // reset for further tests + this.adaptiveMealy = new AdaptiveMealyTreeBuilder<>(TEST_ALPHABET); + } + + @Test(dependsOnMethods = "testConfluenceBug") + public void testLookup() { + Assert.assertFalse(adaptiveMealy.hasDefinitiveInformation(W_1)); + Assert.assertFalse(adaptiveMealy.hasDefinitiveInformation(W_2)); + Assert.assertFalse(adaptiveMealy.hasDefinitiveInformation(W_3)); + + adaptiveMealy.insert(W_1, W_1_O); + Assert.assertTrue(adaptiveMealy.hasDefinitiveInformation(W_1)); + Assert.assertTrue(adaptiveMealy.hasDefinitiveInformation(W_1.prefix(2))); + Assert.assertFalse(adaptiveMealy.hasDefinitiveInformation(W_1.append('a'))); + + WordBuilder wb = new WordBuilder<>(); + + Assert.assertTrue(adaptiveMealy.lookup(W_1, wb)); + Assert.assertEquals(wb.toWord(), W_1_O); + wb.clear(); + Assert.assertTrue(adaptiveMealy.lookup(W_1.prefix(2), wb)); + Assert.assertEquals(wb.toWord(), W_1_O.prefix(2)); + wb.clear(); + Assert.assertFalse(adaptiveMealy.hasDefinitiveInformation(W_2)); + Assert.assertFalse(adaptiveMealy.hasDefinitiveInformation(W_3)); + + adaptiveMealy.insert(W_2, W_2_O); + Assert.assertTrue(adaptiveMealy.hasDefinitiveInformation(W_1)); + Assert.assertTrue(adaptiveMealy.hasDefinitiveInformation(W_2)); + Assert.assertFalse(adaptiveMealy.hasDefinitiveInformation(W_3)); + + Assert.assertTrue(adaptiveMealy.lookup(W_2, wb)); + Assert.assertEquals(wb.toWord(), W_2_O); + wb.clear(); + Assert.assertTrue(adaptiveMealy.lookup(W_2.prefix(1), wb)); + Assert.assertEquals(wb.toWord(), W_2_O.prefix(1)); + wb.clear(); + Assert.assertTrue(adaptiveMealy.lookup(W_1, wb)); + Assert.assertEquals(wb.toWord(), W_1_O); + wb.clear(); + + adaptiveMealy.insert(W_3, W_3_O); + Assert.assertTrue(adaptiveMealy.hasDefinitiveInformation(W_1)); + Assert.assertTrue(adaptiveMealy.hasDefinitiveInformation(W_2)); + Assert.assertTrue(adaptiveMealy.hasDefinitiveInformation(W_3)); + + Assert.assertTrue(adaptiveMealy.lookup(W_3, wb)); + Assert.assertEquals(wb.toWord(), W_3_O); + wb.clear(); + Assert.assertTrue(adaptiveMealy.lookup(W_3.prefix(2), wb)); + Assert.assertEquals(wb.toWord(), W_3_O.prefix(2)); + wb.clear(); + Assert.assertTrue(adaptiveMealy.lookup(W_1, wb)); + Assert.assertEquals(wb.toWord(), W_1_O); + wb.clear(); + Assert.assertTrue(adaptiveMealy.lookup(W_2, wb)); + Assert.assertEquals(wb.toWord(), W_2_O); + wb.clear(); + } + + @Test(dependsOnMethods = "testLookup") + public void testInsertSame() { + adaptiveMealy.insert(W_1, W_1_O); + + } + + @Test(dependsOnMethods = "testFindSeparatingWord") + public void testConflict() { + adaptiveMealy.insert(W_1, W_1_O); + adaptiveMealy.insert(W_1, W_3_O); + LinkedList out = new LinkedList<>(); + adaptiveMealy.lookup(W_1, out); + Assert.assertEquals(W_3_O, Word.fromList(out)); + } + + @Test + public void testAges() { + LinkedList, Word>> words = new LinkedList<>(); + words.add(Pair.of(W_2, W_1_O)); + words.add(Pair.of(W_1, W_3_O)); + words.add(Pair.of(W_1, W_1_O)); + words.add(Pair.of(W_2, W_2_O)); + words.add(Pair.of(W_3, W_3_O)); + + for (int i = 0; i < words.size(); i++) { + adaptiveMealy.insert(words.get(i).getFirst(), words.get(i).getSecond(), i); + } + Assert.assertEquals(W_1, adaptiveMealy.getOldestQuery()); + adaptiveMealy.insert(W_1, W_1_O, 5); + Assert.assertEquals(W_2, adaptiveMealy.getOldestQuery()); + adaptiveMealy.insert(W_2, W_2_O, 6); + Assert.assertEquals(W_3, adaptiveMealy.getOldestQuery()); + adaptiveMealy.insert(W_3, W_3_O, 7); + Assert.assertEquals(W_1, adaptiveMealy.getOldestQuery()); + } + + @Test(dependsOnMethods = "testLookup") + public void testFindSeparatingWord() { + CompactMealy testMealy = new CompactMealy<>(TEST_ALPHABET); + + int s0 = testMealy.addInitialState(); + int s1 = testMealy.addState(); + int s2 = testMealy.addState(); + int s3 = testMealy.addState(); + int s4 = testMealy.addState(); + int s5 = testMealy.addState(); + + testMealy.addTransition(s0, 'a', s1, 'x'); + testMealy.addTransition(s0, 'b', s2, 'u'); + testMealy.addTransition(s1, 'b', s3, 'y'); + testMealy.addTransition(s3, 'c', s4, 'z'); + testMealy.addTransition(s1, 'c', s5, 'w'); + + Word sepWord; + sepWord = adaptiveMealy.findSeparatingWord(testMealy, TEST_ALPHABET, true); + Assert.assertNull(sepWord); + sepWord = adaptiveMealy.findSeparatingWord(testMealy, TEST_ALPHABET, false); + Assert.assertEquals(sepWord, Word.fromString("acb")); + + testMealy.addTransition(s5, 'b', s4, 'u'); + sepWord = adaptiveMealy.findSeparatingWord(testMealy, TEST_ALPHABET, true); + Assert.assertNull(sepWord); + sepWord = adaptiveMealy.findSeparatingWord(testMealy, TEST_ALPHABET, false); + Assert.assertNull(sepWord); + + testMealy.setTransition(s5, (Character) 'b', testMealy.getSuccessor(s5, (Character) 'b'), (Character) 'w'); + + sepWord = adaptiveMealy.findSeparatingWord(testMealy, TEST_ALPHABET, true); + Assert.assertEquals(sepWord, Word.fromString("acb")); + sepWord = adaptiveMealy.findSeparatingWord(testMealy, TEST_ALPHABET, false); + Assert.assertEquals(sepWord, Word.fromString("acb")); + } + + @Test(dependsOnMethods = "testLookup") + public void testTSView() { + final MealyTransitionSystem tsView = adaptiveMealy.asTransitionSystem(); + final WordBuilder wb = new WordBuilder<>(); + + tsView.trace(W_1, wb); + Assert.assertEquals(wb.toWord(), W_1_O); + wb.clear(); + + tsView.trace(W_2, wb); + Assert.assertEquals(wb.toWord(), W_2_O); + wb.clear(); + + tsView.trace(W_3, wb); + Assert.assertEquals(wb.toWord(), W_3_O); + } + + @Test + public void testCounterexampleOfLengthOne() { + final IncrementalMealyBuilder incMealy = new AdaptiveMealyTreeBuilder<>(TEST_ALPHABET); + incMealy.insert(Word.fromCharSequence("a"), Word.fromCharSequence("x")); + + final CompactMealy dfa = new CompactMealy<>(TEST_ALPHABET); + final Integer q0 = dfa.addInitialState(); + final Integer q1 = dfa.addState(); + + dfa.addTransition(q0, 'a', q1, 'y'); + + final Word ce = incMealy.findSeparatingWord(dfa, TEST_ALPHABET, false); + Assert.assertNotNull(ce); + } + + @Test(dependsOnMethods = "testLookup") + public void testNewInputSymbol() { + final GrowingAlphabet alphabet = new GrowingMapAlphabet<>(TEST_ALPHABET); + final IncrementalMealyBuilder growableBuilder = new AdaptiveMealyTreeBuilder<>( + alphabet); + + growableBuilder.addAlphabetSymbol('d'); + growableBuilder.addAlphabetSymbol('d'); + + final Word input1 = Word.fromCharSequence("dcba"); + final Word output1 = Word.fromCharSequence("1234"); + + growableBuilder.insert(input1, output1); + + Assert.assertTrue(growableBuilder.hasDefinitiveInformation(input1)); + Assert.assertEquals(growableBuilder.lookup(input1), output1); + + final Word input2 = Word.fromCharSequence("dddd"); + + Assert.assertFalse(growableBuilder.hasDefinitiveInformation(input2)); + Assert.assertEquals(growableBuilder.lookup(input2), Word.fromLetter('1')); + } +} From 64efc2bad6611a9d6d813e8459541e1a1df99d26 Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Thu, 21 Jul 2022 16:10:51 +0100 Subject: [PATCH 03/17] feat: conflicts() --- .../mealy/tree/AdaptiveMealyTreeBuilder.java | 22 +++++++++++++++++++ .../mealy/AdaptiveMealyTreeBuilderTest.java | 2 ++ 2 files changed, 24 insertions(+) diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java index f02c4a32b7..4fdd7c48df 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java @@ -42,6 +42,28 @@ public void insert(Word input, Word outputWord) { this.insert(input, outputWord, 0); } + public Boolean conflicts(Word input, Word outputWord) { + Node curr = root; + + Iterator outputIt = outputWord.iterator(); + for (int i = 0; i < input.length(); i++) { + I sym = input.getSymbol(i); + O out = outputIt.next(); + Edge, O> edge = getEdge(curr, sym); + if (edge == null) { + return false; + } else { + if (!Objects.equals(out, edge.getOutput())) { + return true; + } else { + curr = edge.getTarget(); + } + } + } + + return false; + } + public Boolean insert(Word input, Word outputWord, Integer queryIndex) { Boolean hasOverwritten = false; Node curr = root; diff --git a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java index a53aa851bd..44e855e31b 100644 --- a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java +++ b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java @@ -131,6 +131,8 @@ public void testInsertSame() { @Test(dependsOnMethods = "testFindSeparatingWord") public void testConflict() { adaptiveMealy.insert(W_1, W_1_O); + Assert.assertFalse("Expected no conflict but triggered one.", adaptiveMealy.conflicts(W_1, W_1_O)); + Assert.assertTrue("Expected conflict but found none.", adaptiveMealy.conflicts(W_1, W_3_O)); adaptiveMealy.insert(W_1, W_3_O); LinkedList out = new LinkedList<>(); adaptiveMealy.lookup(W_1, out); From 40e0e1175249fa336675be26ea546bcda62fd9f9 Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Fri, 19 Aug 2022 18:45:27 -0400 Subject: [PATCH 04/17] feat: LimitException --- .../incremental/LimitException.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 incremental/src/main/java/net/automatalib/incremental/LimitException.java diff --git a/incremental/src/main/java/net/automatalib/incremental/LimitException.java b/incremental/src/main/java/net/automatalib/incremental/LimitException.java new file mode 100644 index 0000000000..71129018f4 --- /dev/null +++ b/incremental/src/main/java/net/automatalib/incremental/LimitException.java @@ -0,0 +1,56 @@ +/* Copyright (C) 2013-2022 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. + */ +package net.automatalib.incremental; + +public class LimitException extends IllegalArgumentException { + + /** + * Default constructor. + * + * @see IllegalArgumentException#IllegalArgumentException() + */ + public LimitException() { + super(); + } + + /** + * Constructor. + * + * @see IllegalArgumentException#IllegalArgumentException(String, Throwable) + */ + public LimitException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor. + * + * @see IllegalArgumentException#IllegalArgumentException(String) + */ + public LimitException(String s) { + super(s); + } + + /** + * Constructor. + * + * @see IllegalArgumentException#IllegalArgumentException(Throwable) + */ + public LimitException(Throwable cause) { + super(cause); + } + +} From 903adf01a86c0356ce8c9de8812d31f257450aee Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Thu, 29 Sep 2022 16:28:06 +0100 Subject: [PATCH 05/17] chore: cleanup --- .../incremental/LimitException.java | 56 ------------------- 1 file changed, 56 deletions(-) delete mode 100644 incremental/src/main/java/net/automatalib/incremental/LimitException.java diff --git a/incremental/src/main/java/net/automatalib/incremental/LimitException.java b/incremental/src/main/java/net/automatalib/incremental/LimitException.java deleted file mode 100644 index 71129018f4..0000000000 --- a/incremental/src/main/java/net/automatalib/incremental/LimitException.java +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright (C) 2013-2022 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. - */ -package net.automatalib.incremental; - -public class LimitException extends IllegalArgumentException { - - /** - * Default constructor. - * - * @see IllegalArgumentException#IllegalArgumentException() - */ - public LimitException() { - super(); - } - - /** - * Constructor. - * - * @see IllegalArgumentException#IllegalArgumentException(String, Throwable) - */ - public LimitException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Constructor. - * - * @see IllegalArgumentException#IllegalArgumentException(String) - */ - public LimitException(String s) { - super(s); - } - - /** - * Constructor. - * - * @see IllegalArgumentException#IllegalArgumentException(Throwable) - */ - public LimitException(Throwable cause) { - super(cause); - } - -} From d2a6c658812342d6a9b04c6352b8039c964ac4fc Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Wed, 5 Oct 2022 20:29:08 +0100 Subject: [PATCH 06/17] feat: cleanup --- .../mealy/tree/AdaptiveMealyTreeBuilder.java | 56 +++++++++---------- .../mealy/AdaptiveMealyTreeBuilderTest.java | 8 +-- 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java index 4fdd7c48df..aa7706cb02 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java @@ -32,6 +32,7 @@ public class AdaptiveMealyTreeBuilder extends IncrementalMealyTreeBuilder< private final PriorityQueue> queryStates = new PriorityQueue<>( (Node a, Node b) -> Integer.compare(stateToQuery.get(a).getSecond(), stateToQuery.get(b).getSecond())); + private Integer insertCounter = 0; public AdaptiveMealyTreeBuilder(Alphabet inputAlphabet) { super(inputAlphabet); @@ -39,33 +40,6 @@ public AdaptiveMealyTreeBuilder(Alphabet inputAlphabet) { @Override public void insert(Word input, Word outputWord) { - this.insert(input, outputWord, 0); - } - - public Boolean conflicts(Word input, Word outputWord) { - Node curr = root; - - Iterator outputIt = outputWord.iterator(); - for (int i = 0; i < input.length(); i++) { - I sym = input.getSymbol(i); - O out = outputIt.next(); - Edge, O> edge = getEdge(curr, sym); - if (edge == null) { - return false; - } else { - if (!Objects.equals(out, edge.getOutput())) { - return true; - } else { - curr = edge.getTarget(); - } - } - } - - return false; - } - - public Boolean insert(Word input, Word outputWord, Integer queryIndex) { - Boolean hasOverwritten = false; Node curr = root; Iterator outputIt = outputWord.iterator(); @@ -80,7 +54,6 @@ public Boolean insert(Word input, Word outputWord, Int removeQueries(edge.getTarget()); removeEdge(curr, sym); curr = insertNode(curr, sym, out); - hasOverwritten = true; } else { curr = edge.getTarget(); } @@ -88,15 +61,14 @@ public Boolean insert(Word input, Word outputWord, Int } assert curr != null; - stateToQuery.put(curr, Pair.of(input, queryIndex)); + stateToQuery.put(curr, Pair.of(input, insertCounter)); + insertCounter += 1; // Make sure it uses the new ages. queryStates.remove(curr); queryStates.add(curr); assert stateToQuery.size() == queryStates.size(); - - return hasOverwritten; } private void removeQueries(Node node) { @@ -110,6 +82,28 @@ private void removeQueries(Node node) { assert stateToQuery.size() == queryStates.size(); } + public Boolean conflicts(Word input, Word outputWord) { + Node curr = root; + + Iterator outputIt = outputWord.iterator(); + for (int i = 0; i < input.length(); i++) { + I sym = input.getSymbol(i); + O out = outputIt.next(); + Edge, O> edge = getEdge(curr, sym); + if (edge == null) { + return false; + } else { + if (!Objects.equals(out, edge.getOutput())) { + return true; + } else { + curr = edge.getTarget(); + } + } + } + + return false; + } + private void removeEdge(Node node, I symbol) { node.setEdge(this.getInputAlphabet().getSymbolIndex(symbol), null); } diff --git a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java index 44e855e31b..194744234c 100644 --- a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java +++ b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java @@ -149,14 +149,14 @@ public void testAges() { words.add(Pair.of(W_3, W_3_O)); for (int i = 0; i < words.size(); i++) { - adaptiveMealy.insert(words.get(i).getFirst(), words.get(i).getSecond(), i); + adaptiveMealy.insert(words.get(i).getFirst(), words.get(i).getSecond()); } Assert.assertEquals(W_1, adaptiveMealy.getOldestQuery()); - adaptiveMealy.insert(W_1, W_1_O, 5); + adaptiveMealy.insert(W_1, W_1_O); Assert.assertEquals(W_2, adaptiveMealy.getOldestQuery()); - adaptiveMealy.insert(W_2, W_2_O, 6); + adaptiveMealy.insert(W_2, W_2_O); Assert.assertEquals(W_3, adaptiveMealy.getOldestQuery()); - adaptiveMealy.insert(W_3, W_3_O, 7); + adaptiveMealy.insert(W_3, W_3_O); Assert.assertEquals(W_1, adaptiveMealy.getOldestQuery()); } From 04dd4d4eda1102af5479abf61ccbb1b2d5502300 Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Wed, 5 Oct 2022 20:48:01 +0100 Subject: [PATCH 07/17] feat: return conflicted subtree in conflicts method --- .../incremental/mealy/tree/AdaptiveMealyTreeBuilder.java | 8 ++++---- .../incremental/mealy/AdaptiveMealyTreeBuilderTest.java | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java index aa7706cb02..be53c800f1 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java @@ -82,7 +82,7 @@ private void removeQueries(Node node) { assert stateToQuery.size() == queryStates.size(); } - public Boolean conflicts(Word input, Word outputWord) { + public Node conflicts(Word input, Word outputWord) { Node curr = root; Iterator outputIt = outputWord.iterator(); @@ -91,17 +91,17 @@ public Boolean conflicts(Word input, Word outputWord) O out = outputIt.next(); Edge, O> edge = getEdge(curr, sym); if (edge == null) { - return false; + return null; } else { if (!Objects.equals(out, edge.getOutput())) { - return true; + return edge.getTarget(); } else { curr = edge.getTarget(); } } } - return false; + return null; } private void removeEdge(Node node, I symbol) { diff --git a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java index 194744234c..d960a91e3d 100644 --- a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java +++ b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java @@ -131,8 +131,8 @@ public void testInsertSame() { @Test(dependsOnMethods = "testFindSeparatingWord") public void testConflict() { adaptiveMealy.insert(W_1, W_1_O); - Assert.assertFalse("Expected no conflict but triggered one.", adaptiveMealy.conflicts(W_1, W_1_O)); - Assert.assertTrue("Expected conflict but found none.", adaptiveMealy.conflicts(W_1, W_3_O)); + Assert.assertNull("Expected no conflict but triggered one.", adaptiveMealy.conflicts(W_1, W_1_O)); + Assert.assertNotNull("Expected conflict but found none.", adaptiveMealy.conflicts(W_1, W_3_O)); adaptiveMealy.insert(W_1, W_3_O); LinkedList out = new LinkedList<>(); adaptiveMealy.lookup(W_1, out); From 2ba1ae01194cea2707ad2dcacc8fabd4e7f003fe Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Wed, 5 Oct 2022 20:53:02 +0100 Subject: [PATCH 08/17] feat: make net.automatalib.incremental.mealy.tree.Node public --- .../main/java/net/automatalib/incremental/mealy/tree/Node.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/Node.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/Node.java index c50667a0bb..a1ec78628e 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/Node.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/Node.java @@ -18,7 +18,7 @@ import net.automatalib.commons.smartcollections.ResizingArrayStorage; import org.checkerframework.checker.nullness.qual.Nullable; -final class Node { +public final class Node { private final ResizingArrayStorage, O>> outEdges; From a9f613e33946149023409cee255fae9dd4afbe19 Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Thu, 6 Oct 2022 14:00:36 +0100 Subject: [PATCH 09/17] feat: cleanup --- .../incremental/mealy/tree/AdaptiveMealyTreeBuilder.java | 8 ++++---- .../incremental/mealy/AdaptiveMealyTreeBuilderTest.java | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java index be53c800f1..aa7706cb02 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java @@ -82,7 +82,7 @@ private void removeQueries(Node node) { assert stateToQuery.size() == queryStates.size(); } - public Node conflicts(Word input, Word outputWord) { + public Boolean conflicts(Word input, Word outputWord) { Node curr = root; Iterator outputIt = outputWord.iterator(); @@ -91,17 +91,17 @@ public Node conflicts(Word input, Word outputWord) O out = outputIt.next(); Edge, O> edge = getEdge(curr, sym); if (edge == null) { - return null; + return false; } else { if (!Objects.equals(out, edge.getOutput())) { - return edge.getTarget(); + return true; } else { curr = edge.getTarget(); } } } - return null; + return false; } private void removeEdge(Node node, I symbol) { diff --git a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java index d960a91e3d..194744234c 100644 --- a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java +++ b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java @@ -131,8 +131,8 @@ public void testInsertSame() { @Test(dependsOnMethods = "testFindSeparatingWord") public void testConflict() { adaptiveMealy.insert(W_1, W_1_O); - Assert.assertNull("Expected no conflict but triggered one.", adaptiveMealy.conflicts(W_1, W_1_O)); - Assert.assertNotNull("Expected conflict but found none.", adaptiveMealy.conflicts(W_1, W_3_O)); + Assert.assertFalse("Expected no conflict but triggered one.", adaptiveMealy.conflicts(W_1, W_1_O)); + Assert.assertTrue("Expected conflict but found none.", adaptiveMealy.conflicts(W_1, W_3_O)); adaptiveMealy.insert(W_1, W_3_O); LinkedList out = new LinkedList<>(); adaptiveMealy.lookup(W_1, out); From 2f36fdf1b46894a21f0449478c8dbc5e2fc1b846 Mon Sep 17 00:00:00 2001 From: Markus Frohme Date: Thu, 6 Oct 2022 16:17:09 +0200 Subject: [PATCH 10/17] add AdaptiveMealyBuilder interface and adjust incremental class hierarchy --- .../AbstractIncrementalMealyBuilder.java | 6 +- .../mealy/AdaptiveMealyBuilder.java | 63 +++++++++++++++ .../incremental/mealy/GraphView.java | 27 +++++++ .../mealy/IncrementalMealyBuilder.java | 9 --- .../mealy/dag/IncrementalMealyDAGBuilder.java | 3 +- .../AbstractIncrementalMealyTreeBuilder.java | 5 -- .../mealy/tree/AdaptiveMealyTreeBuilder.java | 76 ++++++++++++++++++- .../tree/IncrementalMealyTreeBuilder.java | 3 +- .../DynamicIncrementalMealyTreeBuilder.java | 4 +- 9 files changed, 174 insertions(+), 22 deletions(-) create mode 100644 incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java create mode 100644 incremental/src/main/java/net/automatalib/incremental/mealy/GraphView.java diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractIncrementalMealyBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractIncrementalMealyBuilder.java index 0520fe710f..d9f44928cb 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractIncrementalMealyBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractIncrementalMealyBuilder.java @@ -26,15 +26,15 @@ import net.automatalib.words.Word; import net.automatalib.words.WordBuilder; -public abstract class AbstractIncrementalMealyBuilder implements IncrementalMealyBuilder { +public abstract class AbstractIncrementalMealyBuilder { + + public abstract boolean lookup(Word inputWord, List output); - @Override public boolean hasDefinitiveInformation(Word word) { List unused = new ArrayList<>(word.length()); return lookup(word, unused); } - @Override public Word lookup(Word inputWord) { WordBuilder wb = new WordBuilder<>(inputWord.size()); lookup(inputWord, wb); diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java new file mode 100644 index 0000000000..cc5e8d69dc --- /dev/null +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java @@ -0,0 +1,63 @@ +/* Copyright (C) 2013-2022 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. + */ +package net.automatalib.incremental.mealy; + +import java.util.List; + +import net.automatalib.SupportsGrowingAlphabet; +import net.automatalib.automata.transducers.MealyMachine; +import net.automatalib.incremental.IncrementalConstruction; +import net.automatalib.ts.output.MealyTransitionSystem; +import net.automatalib.words.Word; + +public interface AdaptiveMealyBuilder + extends IncrementalConstruction, I>, SupportsGrowingAlphabet { + + Word lookup(Word inputWord); + + /** + * Retrieves the output word for the given input word. If no definitive information for the input word exists, the + * output for the longest known prefix will be returned. + * + * @param inputWord + * the input word + * @param output + * a consumer for constructing the output word + * + * @return {@code true} if the information contained was complete (in this case, {@code word.length() == + * output.size()} will hold), {@code false} otherwise. + */ + boolean lookup(Word inputWord, List output); + + /** + * Incorporates a pair of input/output words into the stored information. + * + * @param inputWord + * the input word + * @param outputWord + * the corresponding output word + * + * @return {@code true} if the inserted output word has overridden existing information, {@code false} otherwise. + */ + boolean insert(Word inputWord, Word outputWord); + + @Override + GraphView asGraph(); + + @Override + MealyTransitionSystem asTransitionSystem(); + +} diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/GraphView.java b/incremental/src/main/java/net/automatalib/incremental/mealy/GraphView.java new file mode 100644 index 0000000000..c0c6f2c6ed --- /dev/null +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/GraphView.java @@ -0,0 +1,27 @@ +/* Copyright (C) 2013-2022 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. + */ +package net.automatalib.incremental.mealy; + +import net.automatalib.graphs.Graph; + +interface GraphView extends Graph { + + I getInputSymbol(E edge); + + O getOutputSymbol(E edge); + + N getInitialNode(); +} diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/IncrementalMealyBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/IncrementalMealyBuilder.java index f1695ea353..cb695bf25d 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/IncrementalMealyBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/IncrementalMealyBuilder.java @@ -19,7 +19,6 @@ import net.automatalib.SupportsGrowingAlphabet; import net.automatalib.automata.transducers.MealyMachine; -import net.automatalib.graphs.Graph; import net.automatalib.incremental.ConflictException; import net.automatalib.incremental.IncrementalConstruction; import net.automatalib.ts.output.MealyTransitionSystem; @@ -63,12 +62,4 @@ public interface IncrementalMealyBuilder @Override MealyTransitionSystem asTransitionSystem(); - interface GraphView extends Graph { - - I getInputSymbol(E edge); - - O getOutputSymbol(E edge); - - N getInitialNode(); - } } diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/dag/IncrementalMealyDAGBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/dag/IncrementalMealyDAGBuilder.java index 7dad11bbaa..2515170bc6 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/dag/IncrementalMealyDAGBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/dag/IncrementalMealyDAGBuilder.java @@ -34,6 +34,7 @@ import net.automatalib.commons.util.UnionFind; import net.automatalib.incremental.ConflictException; import net.automatalib.incremental.mealy.AbstractIncrementalMealyBuilder; +import net.automatalib.incremental.mealy.IncrementalMealyBuilder; import net.automatalib.ts.output.MealyTransitionSystem; import net.automatalib.visualization.VisualizationHelper; import net.automatalib.visualization.helper.DelegateVisualizationHelper; @@ -54,7 +55,7 @@ * @author Malte Isberner */ public class IncrementalMealyDAGBuilder extends AbstractIncrementalMealyBuilder - implements InputAlphabetHolder { + implements IncrementalMealyBuilder, InputAlphabetHolder { private final Map<@Nullable StateSignature, State> register = new HashMap<>(); private final Alphabet inputAlphabet; diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractIncrementalMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractIncrementalMealyTreeBuilder.java index bc0fb4660d..df9bef5390 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractIncrementalMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractIncrementalMealyTreeBuilder.java @@ -46,7 +46,6 @@ public AbstractIncrementalMealyTreeBuilder(N root) { this.root = root; } - @Override public boolean lookup(Word word, List output) { N curr = root; @@ -62,7 +61,6 @@ public boolean lookup(Word word, List output) { return true; } - @Override public void insert(Word input, Word outputWord) { N curr = root; @@ -81,17 +79,14 @@ public void insert(Word input, Word outputWord) { } } - @Override public GraphView asGraph() { return new GraphView(); } - @Override public TransitionSystemView asTransitionSystem() { return new TransitionSystemView(); } - @Override public @Nullable Word findSeparatingWord(MealyMachine target, Collection inputs, boolean omitUndefined) { diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java index aa7706cb02..7d54d6d2eb 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java @@ -15,27 +15,99 @@ */ package net.automatalib.incremental.mealy.tree; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.PriorityQueue; +import net.automatalib.automata.concepts.InputAlphabetHolder; import net.automatalib.commons.util.Pair; +import net.automatalib.incremental.mealy.IncrementalMealyBuilder; import net.automatalib.util.graphs.traversal.GraphTraversal; import net.automatalib.words.Alphabet; import net.automatalib.words.Word; +import net.automatalib.words.impl.Alphabets; + +public class AdaptiveMealyTreeBuilder extends AbstractIncrementalMealyTreeBuilder, I, O> + implements IncrementalMealyBuilder, InputAlphabetHolder { -public class AdaptiveMealyTreeBuilder extends IncrementalMealyTreeBuilder { private final Map, Pair, Integer>> stateToQuery = new HashMap<>(); private final PriorityQueue> queryStates = new PriorityQueue<>( (Node a, Node b) -> Integer.compare(stateToQuery.get(a).getSecond(), stateToQuery.get(b).getSecond())); private Integer insertCounter = 0; + private final Alphabet inputAlphabet; + private int alphabetSize; + public AdaptiveMealyTreeBuilder(Alphabet inputAlphabet) { - super(inputAlphabet); + super(new Node<>(inputAlphabet.size())); + this.inputAlphabet = inputAlphabet; + this.alphabetSize = inputAlphabet.size(); + } + + @Override + public void addAlphabetSymbol(I symbol) { + if (!inputAlphabet.containsSymbol(symbol)) { + Alphabets.toGrowingAlphabetOrThrowException(inputAlphabet).addSymbol(symbol); + } + + final int newAlphabetSize = inputAlphabet.size(); + // even if the symbol was already in the alphabet, we need to make sure to be able to store the new symbol + if (alphabetSize < newAlphabetSize) { + ensureInputCapacity(root, alphabetSize, newAlphabetSize); + alphabetSize = newAlphabetSize; + } + } + + private void ensureInputCapacity(Node node, int oldAlphabetSize, int newAlphabetSize) { + node.ensureInputCapacity(newAlphabetSize); + for (int i = 0; i < oldAlphabetSize; i++) { + final Node child = node.getSuccessor(i); + if (child != null) { + ensureInputCapacity(child, oldAlphabetSize, newAlphabetSize); + } + } + } + + @Override + protected Edge, O> getEdge(Node node, I symbol) { + return node.getEdge(inputAlphabet.getSymbolIndex(symbol)); + } + + @Override + protected Node createNode() { + return new Node<>(alphabetSize); + } + + @Override + protected Node insertNode(Node parent, I symIdx, O output) { + Node succ = createNode(); + Edge, O> edge = new Edge<>(output, succ); + parent.setEdge(inputAlphabet.getSymbolIndex(symIdx), edge); + return succ; + } + + @Override + protected Collection, I, O>> getOutgoingEdges(Node node) { + List, I, O>> result = new ArrayList<>(alphabetSize); + for (int i = 0; i < alphabetSize; i++) { + Edge, O> edge = node.getEdge(i); + if (edge != null) { + result.add(new AnnotatedEdge<>(edge, inputAlphabet.getSymbol(i))); + } + } + return result; + } + + @Override + public Alphabet getInputAlphabet() { + return inputAlphabet; } @Override diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/IncrementalMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/IncrementalMealyTreeBuilder.java index 4486d6afd2..228d366d51 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/IncrementalMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/IncrementalMealyTreeBuilder.java @@ -20,11 +20,12 @@ import java.util.List; import net.automatalib.automata.concepts.InputAlphabetHolder; +import net.automatalib.incremental.mealy.IncrementalMealyBuilder; import net.automatalib.words.Alphabet; import net.automatalib.words.impl.Alphabets; public class IncrementalMealyTreeBuilder extends AbstractIncrementalMealyTreeBuilder, I, O> - implements InputAlphabetHolder { + implements IncrementalMealyBuilder, InputAlphabetHolder { private final Alphabet inputAlphabet; private int alphabetSize; diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/dynamic/DynamicIncrementalMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/dynamic/DynamicIncrementalMealyTreeBuilder.java index 0fc8344438..8535ba2249 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/dynamic/DynamicIncrementalMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/dynamic/DynamicIncrementalMealyTreeBuilder.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; +import net.automatalib.incremental.mealy.IncrementalMealyBuilder; import net.automatalib.incremental.mealy.tree.AbstractIncrementalMealyTreeBuilder; import net.automatalib.incremental.mealy.tree.AnnotatedEdge; import net.automatalib.incremental.mealy.tree.Edge; @@ -41,7 +42,8 @@ * * @author frohme */ -public class DynamicIncrementalMealyTreeBuilder extends AbstractIncrementalMealyTreeBuilder, I, O> { +public class DynamicIncrementalMealyTreeBuilder extends AbstractIncrementalMealyTreeBuilder, I, O> + implements IncrementalMealyBuilder { public DynamicIncrementalMealyTreeBuilder() { this(new Node<>()); From 67d1959a4dc9fdd9702e8494a1c7dd722097dc49 Mon Sep 17 00:00:00 2001 From: Markus Frohme Date: Thu, 6 Oct 2022 22:07:56 +0200 Subject: [PATCH 11/17] add intermediate abstraction layer to separate incremental and adaptive behavior --- ...Builder.java => AbstractMealyBuilder.java} | 5 +- .../mealy/AdaptiveMealyBuilder.java | 25 +- .../incremental/mealy/GraphView.java | 27 --- .../mealy/IncrementalMealyBuilder.java | 25 +- .../incremental/mealy/MealyBuilder.java | 59 +++++ .../mealy/dag/IncrementalMealyDAGBuilder.java | 4 +- .../AbstractIncrementalMealyTreeBuilder.java | 201 +-------------- .../mealy/tree/AbstractMealyTreeBuilder.java | 229 ++++++++++++++++++ .../mealy/tree/AdaptiveMealyTreeBuilder.java | 2 +- 9 files changed, 297 insertions(+), 280 deletions(-) rename incremental/src/main/java/net/automatalib/incremental/mealy/{AbstractIncrementalMealyBuilder.java => AbstractMealyBuilder.java} (93%) delete mode 100644 incremental/src/main/java/net/automatalib/incremental/mealy/GraphView.java create mode 100644 incremental/src/main/java/net/automatalib/incremental/mealy/MealyBuilder.java create mode 100644 incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractMealyTreeBuilder.java diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractIncrementalMealyBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractMealyBuilder.java similarity index 93% rename from incremental/src/main/java/net/automatalib/incremental/mealy/AbstractIncrementalMealyBuilder.java rename to incremental/src/main/java/net/automatalib/incremental/mealy/AbstractMealyBuilder.java index d9f44928cb..c1a4da61a0 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractIncrementalMealyBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractMealyBuilder.java @@ -26,15 +26,14 @@ import net.automatalib.words.Word; import net.automatalib.words.WordBuilder; -public abstract class AbstractIncrementalMealyBuilder { - - public abstract boolean lookup(Word inputWord, List output); +public abstract class AbstractMealyBuilder implements MealyBuilder { public boolean hasDefinitiveInformation(Word word) { List unused = new ArrayList<>(word.length()); return lookup(word, unused); } + @Override public Word lookup(Word inputWord) { WordBuilder wb = new WordBuilder<>(inputWord.size()); lookup(inputWord, wb); diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java index cc5e8d69dc..5c2999dd94 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java @@ -23,24 +23,7 @@ import net.automatalib.ts.output.MealyTransitionSystem; import net.automatalib.words.Word; -public interface AdaptiveMealyBuilder - extends IncrementalConstruction, I>, SupportsGrowingAlphabet { - - Word lookup(Word inputWord); - - /** - * Retrieves the output word for the given input word. If no definitive information for the input word exists, the - * output for the longest known prefix will be returned. - * - * @param inputWord - * the input word - * @param output - * a consumer for constructing the output word - * - * @return {@code true} if the information contained was complete (in this case, {@code word.length() == - * output.size()} will hold), {@code false} otherwise. - */ - boolean lookup(Word inputWord, List output); +public interface AdaptiveMealyBuilder extends MealyBuilder { /** * Incorporates a pair of input/output words into the stored information. @@ -54,10 +37,4 @@ public interface AdaptiveMealyBuilder */ boolean insert(Word inputWord, Word outputWord); - @Override - GraphView asGraph(); - - @Override - MealyTransitionSystem asTransitionSystem(); - } diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/GraphView.java b/incremental/src/main/java/net/automatalib/incremental/mealy/GraphView.java deleted file mode 100644 index c0c6f2c6ed..0000000000 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/GraphView.java +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (C) 2013-2022 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. - */ -package net.automatalib.incremental.mealy; - -import net.automatalib.graphs.Graph; - -interface GraphView extends Graph { - - I getInputSymbol(E edge); - - O getOutputSymbol(E edge); - - N getInitialNode(); -} diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/IncrementalMealyBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/IncrementalMealyBuilder.java index cb695bf25d..b2ae0c5344 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/IncrementalMealyBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/IncrementalMealyBuilder.java @@ -24,24 +24,7 @@ import net.automatalib.ts.output.MealyTransitionSystem; import net.automatalib.words.Word; -public interface IncrementalMealyBuilder - extends IncrementalConstruction, I>, SupportsGrowingAlphabet { - - Word lookup(Word inputWord); - - /** - * Retrieves the output word for the given input word. If no definitive information for the input word exists, the - * output for the longest known prefix will be returned. - * - * @param inputWord - * the input word - * @param output - * a consumer for constructing the output word - * - * @return {@code true} if the information contained was complete (in this case, {@code word.length() == - * output.size()} will hold), {@code false} otherwise. - */ - boolean lookup(Word inputWord, List output); +public interface IncrementalMealyBuilder extends MealyBuilder { /** * Incorporates a pair of input/output words into the stored information. @@ -56,10 +39,4 @@ public interface IncrementalMealyBuilder */ void insert(Word inputWord, Word outputWord); - @Override - GraphView asGraph(); - - @Override - MealyTransitionSystem asTransitionSystem(); - } diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/MealyBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/MealyBuilder.java new file mode 100644 index 0000000000..11a692c00b --- /dev/null +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/MealyBuilder.java @@ -0,0 +1,59 @@ +/* Copyright (C) 2013-2022 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. + */ +package net.automatalib.incremental.mealy; + +import java.util.List; + +import net.automatalib.SupportsGrowingAlphabet; +import net.automatalib.automata.transducers.MealyMachine; +import net.automatalib.graphs.Graph; +import net.automatalib.incremental.IncrementalConstruction; +import net.automatalib.ts.output.MealyTransitionSystem; +import net.automatalib.words.Word; + +interface MealyBuilder extends IncrementalConstruction, I>, SupportsGrowingAlphabet { + + Word lookup(Word inputWord); + + /** + * Retrieves the output word for the given input word. If no definitive information for the input word exists, the + * output for the longest known prefix will be returned. + * + * @param inputWord + * the input word + * @param output + * a consumer for constructing the output word + * + * @return {@code true} if the information contained was complete (in this case, + * {@code word.length() == output.size()} will hold), {@code false} otherwise. + */ + boolean lookup(Word inputWord, List output); + + @Override + GraphView asGraph(); + + @Override + MealyTransitionSystem asTransitionSystem(); + + interface GraphView extends Graph { + + I getInputSymbol(E edge); + + O getOutputSymbol(E edge); + + N getInitialNode(); + } +} diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/dag/IncrementalMealyDAGBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/dag/IncrementalMealyDAGBuilder.java index 2515170bc6..e4efa94201 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/dag/IncrementalMealyDAGBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/dag/IncrementalMealyDAGBuilder.java @@ -33,7 +33,7 @@ import net.automatalib.commons.util.IntDisjointSets; import net.automatalib.commons.util.UnionFind; import net.automatalib.incremental.ConflictException; -import net.automatalib.incremental.mealy.AbstractIncrementalMealyBuilder; +import net.automatalib.incremental.mealy.AbstractMealyBuilder; import net.automatalib.incremental.mealy.IncrementalMealyBuilder; import net.automatalib.ts.output.MealyTransitionSystem; import net.automatalib.visualization.VisualizationHelper; @@ -54,7 +54,7 @@ * * @author Malte Isberner */ -public class IncrementalMealyDAGBuilder extends AbstractIncrementalMealyBuilder +public class IncrementalMealyDAGBuilder extends AbstractMealyBuilder implements IncrementalMealyBuilder, InputAlphabetHolder { private final Map<@Nullable StateSignature, State> register = new HashMap<>(); diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractIncrementalMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractIncrementalMealyTreeBuilder.java index df9bef5390..38795e4170 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractIncrementalMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractIncrementalMealyTreeBuilder.java @@ -15,50 +15,16 @@ */ package net.automatalib.incremental.mealy.tree; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Deque; import java.util.Iterator; -import java.util.List; -import java.util.Map; import java.util.Objects; -import com.google.common.collect.Iterators; -import net.automatalib.automata.transducers.MealyMachine; import net.automatalib.incremental.ConflictException; -import net.automatalib.incremental.mealy.AbstractIncrementalMealyBuilder; -import net.automatalib.ts.output.MealyTransitionSystem; -import net.automatalib.util.graphs.traversal.GraphTraversal; -import net.automatalib.visualization.VisualizationHelper; -import net.automatalib.visualization.helper.DelegateVisualizationHelper; import net.automatalib.words.Word; -import net.automatalib.words.WordBuilder; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -public abstract class AbstractIncrementalMealyTreeBuilder extends AbstractIncrementalMealyBuilder { - - protected final N root; +public abstract class AbstractIncrementalMealyTreeBuilder extends AbstractMealyTreeBuilder { public AbstractIncrementalMealyTreeBuilder(N root) { - this.root = root; - } - - public boolean lookup(Word word, List output) { - N curr = root; - - for (I sym : word) { - Edge edge = getEdge(curr, sym); - if (edge == null) { - return false; - } - output.add(edge.getOutput()); - curr = edge.getTarget(); - } - - return true; + super(root); } public void insert(Word input, Word outputWord) { @@ -78,167 +44,4 @@ public void insert(Word input, Word outputWord) { } } } - - public GraphView asGraph() { - return new GraphView(); - } - - public TransitionSystemView asTransitionSystem() { - return new TransitionSystemView(); - } - - public @Nullable Word findSeparatingWord(MealyMachine target, - Collection inputs, - boolean omitUndefined) { - return doFindSeparatingWord(target, inputs, omitUndefined); - } - - private @Nullable Word doFindSeparatingWord(MealyMachine target, - Collection inputs, - boolean omitUndefined) { - Deque> dfsStack = new ArrayDeque<>(); - - // reachedFrom can be null here, because we will always skip the bottom stack element below - @SuppressWarnings("nullness") - final Record<@Nullable S, N, I> init = new Record<>(target.getInitialState(), root, null, inputs.iterator()); - - dfsStack.push(init); - - while (!dfsStack.isEmpty()) { - @SuppressWarnings("nullness") // false positive https://github.com/typetools/checker-framework/issues/399 - @NonNull Record<@Nullable S, N, I> rec = dfsStack.peek(); - if (!rec.inputIt.hasNext()) { - dfsStack.pop(); - continue; - } - I input = rec.inputIt.next(); - Edge edge = getEdge(rec.treeNode, input); - if (edge == null) { - continue; - } - - S state = rec.automatonState; - T trans = state == null ? null : target.getTransition(state, input); - if (omitUndefined && trans == null) { - continue; - } - if (trans == null || !Objects.equals(target.getTransitionOutput(trans), edge.getOutput())) { - - WordBuilder wb = new WordBuilder<>(dfsStack.size()); - wb.append(input); - dfsStack.pop(); - - while (!dfsStack.isEmpty()) { - wb.append(rec.incomingInput); - rec = dfsStack.pop(); - } - return wb.reverse().toWord(); - } - - final Record<@Nullable S, N, I> nextRecord = - new Record<>(target.getSuccessor(trans), edge.getTarget(), input, inputs.iterator()); - dfsStack.push(nextRecord); - } - - return null; - } - - protected abstract @Nullable Edge getEdge(N node, I symbol); - - protected abstract N createNode(); - - protected abstract N insertNode(N parent, I symIdx, O output); - - protected abstract Collection> getOutgoingEdges(N node); - - private static final class Record { - - private final S automatonState; - private final N treeNode; - private final I incomingInput; - private final Iterator inputIt; - - Record(S automatonState, N treeNode, I incomingInput, Iterator inputIt) { - this.automatonState = automatonState; - this.treeNode = treeNode; - this.inputIt = inputIt; - this.incomingInput = incomingInput; - } - } - - public class GraphView extends AbstractGraphView> { - - @Override - public Collection getNodes() { - List result = new ArrayList<>(); - Iterators.addAll(result, GraphTraversal.dfIterator(this, Collections.singleton(root))); - return result; - } - - @Override - public Collection> getOutgoingEdges(N node) { - return AbstractIncrementalMealyTreeBuilder.this.getOutgoingEdges(node); - } - - @Override - public N getTarget(AnnotatedEdge edge) { - return edge.getTarget(); - } - - @Override - public I getInputSymbol(AnnotatedEdge edge) { - return edge.getInput(); - } - - @Override - public O getOutputSymbol(AnnotatedEdge edge) { - return edge.getOutput(); - } - - @Override - public N getInitialNode() { - return root; - } - - @Override - public VisualizationHelper> getVisualizationHelper() { - return new DelegateVisualizationHelper>(super.getVisualizationHelper()) { - - private int id; - - @Override - public boolean getNodeProperties(N node, Map properties) { - if (!super.getNodeProperties(node, properties)) { - return false; - } - properties.put(NodeAttrs.LABEL, "n" + (id++)); - return true; - } - }; - } - - } - - public class TransitionSystemView implements MealyTransitionSystem, O> { - - @Override - public @Nullable Edge getTransition(N state, I input) { - return getEdge(state, input); - } - - @Override - public N getSuccessor(Edge transition) { - return transition.getTarget(); - } - - @Override - public N getInitialState() { - return root; - } - - @Override - public O getTransitionOutput(Edge transition) { - return transition.getOutput(); - } - } } diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractMealyTreeBuilder.java new file mode 100644 index 0000000000..e838419d88 --- /dev/null +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractMealyTreeBuilder.java @@ -0,0 +1,229 @@ +/* Copyright (C) 2013-2022 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. + */ +package net.automatalib.incremental.mealy.tree; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.google.common.collect.Iterators; +import net.automatalib.automata.transducers.MealyMachine; +import net.automatalib.incremental.mealy.AbstractMealyBuilder; +import net.automatalib.ts.output.MealyTransitionSystem; +import net.automatalib.util.graphs.traversal.GraphTraversal; +import net.automatalib.visualization.VisualizationHelper; +import net.automatalib.visualization.helper.DelegateVisualizationHelper; +import net.automatalib.words.Word; +import net.automatalib.words.WordBuilder; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +public abstract class AbstractMealyTreeBuilder extends AbstractMealyBuilder { + + protected final N root; + + public AbstractMealyTreeBuilder(N root) { + this.root = root; + } + + @Override + public boolean lookup(Word word, List output) { + N curr = root; + + for (I sym : word) { + Edge edge = getEdge(curr, sym); + if (edge == null) { + return false; + } + output.add(edge.getOutput()); + curr = edge.getTarget(); + } + + return true; + } + + @Override + public GraphView asGraph() { + return new GraphView(); + } + + @Override + public TransitionSystemView asTransitionSystem() { + return new TransitionSystemView(); + } + + @Override + public @Nullable Word findSeparatingWord(MealyMachine target, + Collection inputs, + boolean omitUndefined) { + return doFindSeparatingWord(target, inputs, omitUndefined); + } + + private @Nullable Word doFindSeparatingWord(MealyMachine target, + Collection inputs, + boolean omitUndefined) { + Deque> dfsStack = new ArrayDeque<>(); + + // reachedFrom can be null here, because we will always skip the bottom stack element below + @SuppressWarnings("nullness") + final Record<@Nullable S, N, I> init = new Record<>(target.getInitialState(), root, null, inputs.iterator()); + + dfsStack.push(init); + + while (!dfsStack.isEmpty()) { + @SuppressWarnings("nullness") // false positive https://github.com/typetools/checker-framework/issues/399 + @NonNull Record<@Nullable S, N, I> rec = dfsStack.peek(); + if (!rec.inputIt.hasNext()) { + dfsStack.pop(); + continue; + } + I input = rec.inputIt.next(); + Edge edge = getEdge(rec.treeNode, input); + if (edge == null) { + continue; + } + + S state = rec.automatonState; + T trans = state == null ? null : target.getTransition(state, input); + if (omitUndefined && trans == null) { + continue; + } + if (trans == null || !Objects.equals(target.getTransitionOutput(trans), edge.getOutput())) { + + WordBuilder wb = new WordBuilder<>(dfsStack.size()); + wb.append(input); + dfsStack.pop(); + + while (!dfsStack.isEmpty()) { + wb.append(rec.incomingInput); + rec = dfsStack.pop(); + } + return wb.reverse().toWord(); + } + + final Record<@Nullable S, N, I> nextRecord = + new Record<>(target.getSuccessor(trans), edge.getTarget(), input, inputs.iterator()); + dfsStack.push(nextRecord); + } + + return null; + } + + protected abstract @Nullable Edge getEdge(N node, I symbol); + + protected abstract N createNode(); + + protected abstract N insertNode(N parent, I symIdx, O output); + + protected abstract Collection> getOutgoingEdges(N node); + + private static final class Record { + + private final S automatonState; + private final N treeNode; + private final I incomingInput; + private final Iterator inputIt; + + Record(S automatonState, N treeNode, I incomingInput, Iterator inputIt) { + this.automatonState = automatonState; + this.treeNode = treeNode; + this.inputIt = inputIt; + this.incomingInput = incomingInput; + } + } + + public class GraphView extends AbstractGraphView> { + + @Override + public Collection getNodes() { + List result = new ArrayList<>(); + Iterators.addAll(result, GraphTraversal.dfIterator(this, Collections.singleton(root))); + return result; + } + + @Override + public Collection> getOutgoingEdges(N node) { + return AbstractMealyTreeBuilder.this.getOutgoingEdges(node); + } + + @Override + public N getTarget(AnnotatedEdge edge) { + return edge.getTarget(); + } + + @Override + public I getInputSymbol(AnnotatedEdge edge) { + return edge.getInput(); + } + + @Override + public O getOutputSymbol(AnnotatedEdge edge) { + return edge.getOutput(); + } + + @Override + public N getInitialNode() { + return root; + } + + @Override + public VisualizationHelper> getVisualizationHelper() { + return new DelegateVisualizationHelper>(super.getVisualizationHelper()) { + + private int id; + + @Override + public boolean getNodeProperties(N node, Map properties) { + if (!super.getNodeProperties(node, properties)) { + return false; + } + properties.put(NodeAttrs.LABEL, "n" + (id++)); + return true; + } + }; + } + + } + + public class TransitionSystemView implements MealyTransitionSystem, O> { + + @Override + public @Nullable Edge getTransition(N state, I input) { + return getEdge(state, input); + } + + @Override + public N getSuccessor(Edge transition) { + return transition.getTarget(); + } + + @Override + public N getInitialState() { + return root; + } + + @Override + public O getTransitionOutput(Edge transition) { + return transition.getOutput(); + } + } +} diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java index 7d54d6d2eb..d0a53103c6 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java @@ -33,7 +33,7 @@ import net.automatalib.words.Word; import net.automatalib.words.impl.Alphabets; -public class AdaptiveMealyTreeBuilder extends AbstractIncrementalMealyTreeBuilder, I, O> +public class AdaptiveMealyTreeBuilder extends AbstractMealyTreeBuilder, I, O> implements IncrementalMealyBuilder, InputAlphabetHolder { private final Map, Pair, Integer>> stateToQuery = new HashMap<>(); From 60f5b26258b9d9be289f005ee8ca2f5c50bb32cf Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Thu, 6 Oct 2022 21:23:19 +0100 Subject: [PATCH 12/17] feat: implement new interfcae --- .../mealy/tree/AdaptiveMealyTreeBuilder.java | 33 ++++--------------- .../mealy/AdaptiveMealyTreeBuilderTest.java | 15 ++------- 2 files changed, 9 insertions(+), 39 deletions(-) diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java index d0a53103c6..7b05a2b07b 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java @@ -27,14 +27,14 @@ import net.automatalib.automata.concepts.InputAlphabetHolder; import net.automatalib.commons.util.Pair; -import net.automatalib.incremental.mealy.IncrementalMealyBuilder; +import net.automatalib.incremental.mealy.AdaptiveMealyBuilder; import net.automatalib.util.graphs.traversal.GraphTraversal; import net.automatalib.words.Alphabet; import net.automatalib.words.Word; import net.automatalib.words.impl.Alphabets; public class AdaptiveMealyTreeBuilder extends AbstractMealyTreeBuilder, I, O> - implements IncrementalMealyBuilder, InputAlphabetHolder { + implements AdaptiveMealyBuilder, InputAlphabetHolder { private final Map, Pair, Integer>> stateToQuery = new HashMap<>(); private final PriorityQueue> queryStates = new PriorityQueue<>( @@ -110,9 +110,9 @@ public Alphabet getInputAlphabet() { return inputAlphabet; } - @Override - public void insert(Word input, Word outputWord) { + public boolean insert(Word input, Word outputWord) { Node curr = root; + boolean hasOverwritten = false; Iterator outputIt = outputWord.iterator(); for (int i = 0; i < input.length(); i++) { @@ -123,6 +123,7 @@ public void insert(Word input, Word outputWord) { curr = insertNode(curr, sym, out); } else { if (!Objects.equals(out, edge.getOutput())) { + hasOverwritten = true; removeQueries(edge.getTarget()); removeEdge(curr, sym); curr = insertNode(curr, sym, out); @@ -141,6 +142,8 @@ public void insert(Word input, Word outputWord) { queryStates.add(curr); assert stateToQuery.size() == queryStates.size(); + + return hasOverwritten; } private void removeQueries(Node node) { @@ -154,28 +157,6 @@ private void removeQueries(Node node) { assert stateToQuery.size() == queryStates.size(); } - public Boolean conflicts(Word input, Word outputWord) { - Node curr = root; - - Iterator outputIt = outputWord.iterator(); - for (int i = 0; i < input.length(); i++) { - I sym = input.getSymbol(i); - O out = outputIt.next(); - Edge, O> edge = getEdge(curr, sym); - if (edge == null) { - return false; - } else { - if (!Objects.equals(out, edge.getOutput())) { - return true; - } else { - curr = edge.getTarget(); - } - } - } - - return false; - } - private void removeEdge(Node node, I symbol) { node.setEdge(this.getInputAlphabet().getSymbolIndex(symbol), null); } diff --git a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java index 194744234c..0040b71a0b 100644 --- a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java +++ b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java @@ -128,17 +128,6 @@ public void testInsertSame() { } - @Test(dependsOnMethods = "testFindSeparatingWord") - public void testConflict() { - adaptiveMealy.insert(W_1, W_1_O); - Assert.assertFalse("Expected no conflict but triggered one.", adaptiveMealy.conflicts(W_1, W_1_O)); - Assert.assertTrue("Expected conflict but found none.", adaptiveMealy.conflicts(W_1, W_3_O)); - adaptiveMealy.insert(W_1, W_3_O); - LinkedList out = new LinkedList<>(); - adaptiveMealy.lookup(W_1, out); - Assert.assertEquals(W_3_O, Word.fromList(out)); - } - @Test public void testAges() { LinkedList, Word>> words = new LinkedList<>(); @@ -216,7 +205,7 @@ public void testTSView() { @Test public void testCounterexampleOfLengthOne() { - final IncrementalMealyBuilder incMealy = new AdaptiveMealyTreeBuilder<>(TEST_ALPHABET); + final AdaptiveMealyBuilder incMealy = new AdaptiveMealyTreeBuilder<>(TEST_ALPHABET); incMealy.insert(Word.fromCharSequence("a"), Word.fromCharSequence("x")); final CompactMealy dfa = new CompactMealy<>(TEST_ALPHABET); @@ -232,7 +221,7 @@ public void testCounterexampleOfLengthOne() { @Test(dependsOnMethods = "testLookup") public void testNewInputSymbol() { final GrowingAlphabet alphabet = new GrowingMapAlphabet<>(TEST_ALPHABET); - final IncrementalMealyBuilder growableBuilder = new AdaptiveMealyTreeBuilder<>( + final AdaptiveMealyBuilder growableBuilder = new AdaptiveMealyTreeBuilder<>( alphabet); growableBuilder.addAlphabetSymbol('d'); From 162cae487a31f381475d9c82630928c8b8ef7956 Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Thu, 6 Oct 2022 21:27:04 +0100 Subject: [PATCH 13/17] Revert "feat: make net.automatalib.incremental.mealy.tree.Node public" This reverts commit 2ba1ae01194cea2707ad2dcacc8fabd4e7f003fe. --- .../main/java/net/automatalib/incremental/mealy/tree/Node.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/Node.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/Node.java index a1ec78628e..c50667a0bb 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/Node.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/Node.java @@ -18,7 +18,7 @@ import net.automatalib.commons.smartcollections.ResizingArrayStorage; import org.checkerframework.checker.nullness.qual.Nullable; -public final class Node { +final class Node { private final ResizingArrayStorage, O>> outEdges; From 9d37c401d8fe7dcdfb958920bbee92cc216f37ed Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Fri, 7 Oct 2022 14:41:00 +0100 Subject: [PATCH 14/17] feat: extend interface --- .../incremental/mealy/AdaptiveMealyBuilder.java | 13 +++++++------ .../mealy/tree/AdaptiveMealyTreeBuilder.java | 2 +- .../mealy/AdaptiveMealyTreeBuilderTest.java | 8 ++++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java index 5c2999dd94..9efd15f054 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java @@ -15,12 +15,6 @@ */ package net.automatalib.incremental.mealy; -import java.util.List; - -import net.automatalib.SupportsGrowingAlphabet; -import net.automatalib.automata.transducers.MealyMachine; -import net.automatalib.incremental.IncrementalConstruction; -import net.automatalib.ts.output.MealyTransitionSystem; import net.automatalib.words.Word; public interface AdaptiveMealyBuilder extends MealyBuilder { @@ -37,4 +31,11 @@ public interface AdaptiveMealyBuilder extends MealyBuilder { */ boolean insert(Word inputWord, Word outputWord); + /** + * Returns the oldest input that has been introduced, and persisted. + * + * @return the {@code Word} representing the oldest stored input. + */ + Word getOldestInput(); + } diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java index 7b05a2b07b..6aa86ccf05 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java @@ -161,7 +161,7 @@ private void removeEdge(Node node, I symbol) { node.setEdge(this.getInputAlphabet().getSymbolIndex(symbol), null); } - public Word getOldestQuery() { + public Word getOldestInput() { return stateToQuery.get(queryStates.peek()).getFirst(); } } diff --git a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java index 0040b71a0b..e075fffd2b 100644 --- a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java +++ b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java @@ -140,13 +140,13 @@ public void testAges() { for (int i = 0; i < words.size(); i++) { adaptiveMealy.insert(words.get(i).getFirst(), words.get(i).getSecond()); } - Assert.assertEquals(W_1, adaptiveMealy.getOldestQuery()); + Assert.assertEquals(W_1, adaptiveMealy.getOldestInput()); adaptiveMealy.insert(W_1, W_1_O); - Assert.assertEquals(W_2, adaptiveMealy.getOldestQuery()); + Assert.assertEquals(W_2, adaptiveMealy.getOldestInput()); adaptiveMealy.insert(W_2, W_2_O); - Assert.assertEquals(W_3, adaptiveMealy.getOldestQuery()); + Assert.assertEquals(W_3, adaptiveMealy.getOldestInput()); adaptiveMealy.insert(W_3, W_3_O); - Assert.assertEquals(W_1, adaptiveMealy.getOldestQuery()); + Assert.assertEquals(W_1, adaptiveMealy.getOldestInput()); } @Test(dependsOnMethods = "testLookup") From 688b5bf722f39de13bb425f46fe68a0293132f99 Mon Sep 17 00:00:00 2001 From: Markus Frohme Date: Thu, 13 Oct 2022 01:46:22 +0200 Subject: [PATCH 15/17] cleanup and fix validations --- .../incremental/mealy/AbstractGraphView.java | 50 ++++++++++ .../mealy/AbstractMealyBuilder.java | 69 ------------- .../mealy/AdaptiveMealyBuilder.java | 16 ++- .../mealy/IncrementalMealyBuilder.java | 6 -- .../incremental/mealy/MealyBuilder.java | 27 +++++- .../mealy/dag/IncrementalMealyDAGBuilder.java | 5 +- ...AbstractAlphabetBasedMealyTreeBuilder.java | 97 +++++++++++++++++++ .../AbstractIncrementalMealyTreeBuilder.java | 47 --------- .../mealy/tree/AbstractMealyTreeBuilder.java | 5 +- .../mealy/tree/AdaptiveMealyTreeBuilder.java | 81 ++-------------- .../tree/IncrementalMealyTreeBuilder.java | 88 ++++------------- .../incremental/mealy/tree/Node.java | 6 +- .../DynamicIncrementalMealyTreeBuilder.java | 29 +++++- .../mealy/AdaptiveMealyTreeBuilderTest.java | 13 ++- 14 files changed, 251 insertions(+), 288 deletions(-) create mode 100644 incremental/src/main/java/net/automatalib/incremental/mealy/AbstractGraphView.java delete mode 100644 incremental/src/main/java/net/automatalib/incremental/mealy/AbstractMealyBuilder.java create mode 100644 incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractAlphabetBasedMealyTreeBuilder.java delete mode 100644 incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractIncrementalMealyTreeBuilder.java diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractGraphView.java b/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractGraphView.java new file mode 100644 index 0000000000..4e8e2f7e96 --- /dev/null +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractGraphView.java @@ -0,0 +1,50 @@ +/* Copyright (C) 2013-2022 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. + */ +package net.automatalib.incremental.mealy; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import net.automatalib.incremental.mealy.MealyBuilder.GraphView; +import net.automatalib.visualization.DefaultVisualizationHelper; +import net.automatalib.visualization.VisualizationHelper; + +public abstract class AbstractGraphView implements GraphView { + + @Override + public VisualizationHelper getVisualizationHelper() { + return new DefaultVisualizationHelper() { + + @Override + public Collection initialNodes() { + return Collections.singleton(getInitialNode()); + } + + @Override + public boolean getEdgeProperties(N src, E edge, N tgt, Map properties) { + if (!super.getEdgeProperties(src, edge, tgt, properties)) { + return false; + } + I input = getInputSymbol(edge); + O output = getOutputSymbol(edge); + properties.put(EdgeAttrs.LABEL, input + " / " + output); + return true; + } + + }; + } +} diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractMealyBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractMealyBuilder.java deleted file mode 100644 index c1a4da61a0..0000000000 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractMealyBuilder.java +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright (C) 2013-2022 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. - */ -package net.automatalib.incremental.mealy; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import net.automatalib.visualization.DefaultVisualizationHelper; -import net.automatalib.visualization.VisualizationHelper; -import net.automatalib.words.Word; -import net.automatalib.words.WordBuilder; - -public abstract class AbstractMealyBuilder implements MealyBuilder { - - public boolean hasDefinitiveInformation(Word word) { - List unused = new ArrayList<>(word.length()); - return lookup(word, unused); - } - - @Override - public Word lookup(Word inputWord) { - WordBuilder wb = new WordBuilder<>(inputWord.size()); - lookup(inputWord, wb); - return wb.toWord(); - } - - public abstract static class AbstractGraphView implements GraphView { - - @Override - public VisualizationHelper getVisualizationHelper() { - return new DefaultVisualizationHelper() { - - @Override - public Collection initialNodes() { - return Collections.singleton(getInitialNode()); - } - - @Override - public boolean getEdgeProperties(N src, E edge, N tgt, Map properties) { - if (!super.getEdgeProperties(src, edge, tgt, properties)) { - return false; - } - I input = getInputSymbol(edge); - O output = getOutputSymbol(edge); - properties.put(EdgeAttrs.LABEL, input + " / " + output); - return true; - } - - }; - } - } - -} diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java index 9efd15f054..4e336cbdf1 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java @@ -17,6 +17,18 @@ import net.automatalib.words.Word; +/** + * A variation of the {@link IncrementalMealyBuilder} interface that allows one to override previously inserted + * traces. + * + * @param + * input symbol type + * @param + * output symbol type + * + * @author ferreira + * @author frohme + */ public interface AdaptiveMealyBuilder extends MealyBuilder { /** @@ -32,8 +44,8 @@ public interface AdaptiveMealyBuilder extends MealyBuilder { boolean insert(Word inputWord, Word outputWord); /** - * Returns the oldest input that has been introduced, and persisted. - * + * Returns the oldest, non-overridden input that has been introduced and persisted. + * * @return the {@code Word} representing the oldest stored input. */ Word getOldestInput(); diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/IncrementalMealyBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/IncrementalMealyBuilder.java index b2ae0c5344..0bc4f997c9 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/IncrementalMealyBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/IncrementalMealyBuilder.java @@ -15,13 +15,7 @@ */ package net.automatalib.incremental.mealy; -import java.util.List; - -import net.automatalib.SupportsGrowingAlphabet; -import net.automatalib.automata.transducers.MealyMachine; import net.automatalib.incremental.ConflictException; -import net.automatalib.incremental.IncrementalConstruction; -import net.automatalib.ts.output.MealyTransitionSystem; import net.automatalib.words.Word; public interface IncrementalMealyBuilder extends MealyBuilder { diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/MealyBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/MealyBuilder.java index 11a692c00b..ba280a9352 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/MealyBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/MealyBuilder.java @@ -15,6 +15,7 @@ */ package net.automatalib.incremental.mealy; +import java.util.ArrayList; import java.util.List; import net.automatalib.SupportsGrowingAlphabet; @@ -23,10 +24,19 @@ import net.automatalib.incremental.IncrementalConstruction; import net.automatalib.ts.output.MealyTransitionSystem; import net.automatalib.words.Word; +import net.automatalib.words.WordBuilder; -interface MealyBuilder extends IncrementalConstruction, I>, SupportsGrowingAlphabet { - - Word lookup(Word inputWord); +/** + * A utility interface to share functionality between {@link IncrementalMealyBuilder}s and + * {@link AdaptiveMealyBuilder}s. + * + * @param + * input symbol type + * @param + * output symbol type + */ +public interface MealyBuilder + extends IncrementalConstruction, I>, SupportsGrowingAlphabet { /** * Retrieves the output word for the given input word. If no definitive information for the input word exists, the @@ -42,6 +52,17 @@ interface MealyBuilder extends IncrementalConstruction inputWord, List output); + default Word lookup(Word inputWord) { + WordBuilder wb = new WordBuilder<>(inputWord.size()); + lookup(inputWord, wb); + return wb.toWord(); + } + + @Override + default boolean hasDefinitiveInformation(Word word) { + return lookup(word, new ArrayList<>(word.length())); + } + @Override GraphView asGraph(); diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/dag/IncrementalMealyDAGBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/dag/IncrementalMealyDAGBuilder.java index e4efa94201..d7e0d83019 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/dag/IncrementalMealyDAGBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/dag/IncrementalMealyDAGBuilder.java @@ -33,7 +33,7 @@ import net.automatalib.commons.util.IntDisjointSets; import net.automatalib.commons.util.UnionFind; import net.automatalib.incremental.ConflictException; -import net.automatalib.incremental.mealy.AbstractMealyBuilder; +import net.automatalib.incremental.mealy.AbstractGraphView; import net.automatalib.incremental.mealy.IncrementalMealyBuilder; import net.automatalib.ts.output.MealyTransitionSystem; import net.automatalib.visualization.VisualizationHelper; @@ -54,8 +54,7 @@ * * @author Malte Isberner */ -public class IncrementalMealyDAGBuilder extends AbstractMealyBuilder - implements IncrementalMealyBuilder, InputAlphabetHolder { +public class IncrementalMealyDAGBuilder implements IncrementalMealyBuilder, InputAlphabetHolder { private final Map<@Nullable StateSignature, State> register = new HashMap<>(); private final Alphabet inputAlphabet; diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractAlphabetBasedMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractAlphabetBasedMealyTreeBuilder.java new file mode 100644 index 0000000000..b3bddda804 --- /dev/null +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractAlphabetBasedMealyTreeBuilder.java @@ -0,0 +1,97 @@ +/* Copyright (C) 2013-2022 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. + */ +package net.automatalib.incremental.mealy.tree; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import net.automatalib.automata.concepts.InputAlphabetHolder; +import net.automatalib.words.Alphabet; +import net.automatalib.words.impl.Alphabets; +import org.checkerframework.checker.nullness.qual.Nullable; + +abstract class AbstractAlphabetBasedMealyTreeBuilder extends AbstractMealyTreeBuilder, I, O> + implements InputAlphabetHolder { + + private final Alphabet inputAlphabet; + private int alphabetSize; + + AbstractAlphabetBasedMealyTreeBuilder(Alphabet inputAlphabet) { + super(new Node<>(inputAlphabet.size())); + this.inputAlphabet = inputAlphabet; + this.alphabetSize = inputAlphabet.size(); + } + + @Override + public void addAlphabetSymbol(I symbol) { + if (!inputAlphabet.containsSymbol(symbol)) { + Alphabets.toGrowingAlphabetOrThrowException(inputAlphabet).addSymbol(symbol); + } + + final int newAlphabetSize = inputAlphabet.size(); + // even if the symbol was already in the alphabet, we need to make sure to be able to store the new symbol + if (alphabetSize < newAlphabetSize) { + ensureInputCapacity(root, alphabetSize, newAlphabetSize); + alphabetSize = newAlphabetSize; + } + } + + private void ensureInputCapacity(Node node, int oldAlphabetSize, int newAlphabetSize) { + node.ensureInputCapacity(newAlphabetSize); + for (int i = 0; i < oldAlphabetSize; i++) { + final Node child = node.getSuccessor(i); + if (child != null) { + ensureInputCapacity(child, oldAlphabetSize, newAlphabetSize); + } + } + } + + @Override + protected @Nullable Edge, O> getEdge(Node node, I symbol) { + return node.getEdge(inputAlphabet.getSymbolIndex(symbol)); + } + + @Override + protected Node createNode() { + return new Node<>(alphabetSize); + } + + @Override + protected Node insertNode(Node parent, I symIdx, O output) { + Node succ = createNode(); + Edge, O> edge = new Edge<>(output, succ); + parent.setEdge(inputAlphabet.getSymbolIndex(symIdx), edge); + return succ; + } + + @Override + protected Collection, I, O>> getOutgoingEdges(Node node) { + List, I, O>> result = new ArrayList<>(alphabetSize); + for (int i = 0; i < alphabetSize; i++) { + Edge, O> edge = node.getEdge(i); + if (edge != null) { + result.add(new AnnotatedEdge<>(edge, inputAlphabet.getSymbol(i))); + } + } + return result; + } + + @Override + public Alphabet getInputAlphabet() { + return inputAlphabet; + } +} diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractIncrementalMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractIncrementalMealyTreeBuilder.java deleted file mode 100644 index 38795e4170..0000000000 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractIncrementalMealyTreeBuilder.java +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright (C) 2013-2022 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. - */ -package net.automatalib.incremental.mealy.tree; - -import java.util.Iterator; -import java.util.Objects; - -import net.automatalib.incremental.ConflictException; -import net.automatalib.words.Word; - -public abstract class AbstractIncrementalMealyTreeBuilder extends AbstractMealyTreeBuilder { - - public AbstractIncrementalMealyTreeBuilder(N root) { - super(root); - } - - public void insert(Word input, Word outputWord) { - N curr = root; - - Iterator outputIt = outputWord.iterator(); - for (I sym : input) { - O out = outputIt.next(); - Edge edge = getEdge(curr, sym); - if (edge == null) { - curr = insertNode(curr, sym, out); - } else { - if (!Objects.equals(out, edge.getOutput())) { - throw new ConflictException(); - } - curr = edge.getTarget(); - } - } - } -} diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractMealyTreeBuilder.java index e838419d88..d7907f6c02 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AbstractMealyTreeBuilder.java @@ -27,7 +27,8 @@ import com.google.common.collect.Iterators; import net.automatalib.automata.transducers.MealyMachine; -import net.automatalib.incremental.mealy.AbstractMealyBuilder; +import net.automatalib.incremental.mealy.AbstractGraphView; +import net.automatalib.incremental.mealy.MealyBuilder; import net.automatalib.ts.output.MealyTransitionSystem; import net.automatalib.util.graphs.traversal.GraphTraversal; import net.automatalib.visualization.VisualizationHelper; @@ -37,7 +38,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -public abstract class AbstractMealyTreeBuilder extends AbstractMealyBuilder { +public abstract class AbstractMealyTreeBuilder implements MealyBuilder { protected final N root; diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java index 6aa86ccf05..258eb9046a 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java @@ -15,101 +15,33 @@ */ package net.automatalib.incremental.mealy.tree; -import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.PriorityQueue; -import net.automatalib.automata.concepts.InputAlphabetHolder; import net.automatalib.commons.util.Pair; import net.automatalib.incremental.mealy.AdaptiveMealyBuilder; import net.automatalib.util.graphs.traversal.GraphTraversal; import net.automatalib.words.Alphabet; import net.automatalib.words.Word; -import net.automatalib.words.impl.Alphabets; -public class AdaptiveMealyTreeBuilder extends AbstractMealyTreeBuilder, I, O> - implements AdaptiveMealyBuilder, InputAlphabetHolder { +public class AdaptiveMealyTreeBuilder extends AbstractAlphabetBasedMealyTreeBuilder + implements AdaptiveMealyBuilder { private final Map, Pair, Integer>> stateToQuery = new HashMap<>(); - private final PriorityQueue> queryStates = new PriorityQueue<>( - (Node a, Node b) -> Integer.compare(stateToQuery.get(a).getSecond(), - stateToQuery.get(b).getSecond())); + private final PriorityQueue> queryStates = new PriorityQueue<>((Node a, Node b) -> Integer.compare( + stateToQuery.get(a).getSecond(), + stateToQuery.get(b).getSecond())); private Integer insertCounter = 0; - private final Alphabet inputAlphabet; - private int alphabetSize; - public AdaptiveMealyTreeBuilder(Alphabet inputAlphabet) { - super(new Node<>(inputAlphabet.size())); - this.inputAlphabet = inputAlphabet; - this.alphabetSize = inputAlphabet.size(); - } - - @Override - public void addAlphabetSymbol(I symbol) { - if (!inputAlphabet.containsSymbol(symbol)) { - Alphabets.toGrowingAlphabetOrThrowException(inputAlphabet).addSymbol(symbol); - } - - final int newAlphabetSize = inputAlphabet.size(); - // even if the symbol was already in the alphabet, we need to make sure to be able to store the new symbol - if (alphabetSize < newAlphabetSize) { - ensureInputCapacity(root, alphabetSize, newAlphabetSize); - alphabetSize = newAlphabetSize; - } - } - - private void ensureInputCapacity(Node node, int oldAlphabetSize, int newAlphabetSize) { - node.ensureInputCapacity(newAlphabetSize); - for (int i = 0; i < oldAlphabetSize; i++) { - final Node child = node.getSuccessor(i); - if (child != null) { - ensureInputCapacity(child, oldAlphabetSize, newAlphabetSize); - } - } - } - - @Override - protected Edge, O> getEdge(Node node, I symbol) { - return node.getEdge(inputAlphabet.getSymbolIndex(symbol)); + super(inputAlphabet); } @Override - protected Node createNode() { - return new Node<>(alphabetSize); - } - - @Override - protected Node insertNode(Node parent, I symIdx, O output) { - Node succ = createNode(); - Edge, O> edge = new Edge<>(output, succ); - parent.setEdge(inputAlphabet.getSymbolIndex(symIdx), edge); - return succ; - } - - @Override - protected Collection, I, O>> getOutgoingEdges(Node node) { - List, I, O>> result = new ArrayList<>(alphabetSize); - for (int i = 0; i < alphabetSize; i++) { - Edge, O> edge = node.getEdge(i); - if (edge != null) { - result.add(new AnnotatedEdge<>(edge, inputAlphabet.getSymbol(i))); - } - } - return result; - } - - @Override - public Alphabet getInputAlphabet() { - return inputAlphabet; - } - public boolean insert(Word input, Word outputWord) { Node curr = root; boolean hasOverwritten = false; @@ -161,6 +93,7 @@ private void removeEdge(Node node, I symbol) { node.setEdge(this.getInputAlphabet().getSymbolIndex(symbol), null); } + @Override public Word getOldestInput() { return stateToQuery.get(queryStates.peek()).getFirst(); } diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/IncrementalMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/IncrementalMealyTreeBuilder.java index 228d366d51..2090943a45 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/IncrementalMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/IncrementalMealyTreeBuilder.java @@ -15,83 +15,37 @@ */ package net.automatalib.incremental.mealy.tree; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +import java.util.Iterator; +import java.util.Objects; -import net.automatalib.automata.concepts.InputAlphabetHolder; +import net.automatalib.incremental.ConflictException; import net.automatalib.incremental.mealy.IncrementalMealyBuilder; import net.automatalib.words.Alphabet; -import net.automatalib.words.impl.Alphabets; +import net.automatalib.words.Word; -public class IncrementalMealyTreeBuilder extends AbstractIncrementalMealyTreeBuilder, I, O> - implements IncrementalMealyBuilder, InputAlphabetHolder { - - private final Alphabet inputAlphabet; - private int alphabetSize; +public class IncrementalMealyTreeBuilder extends AbstractAlphabetBasedMealyTreeBuilder + implements IncrementalMealyBuilder { public IncrementalMealyTreeBuilder(Alphabet inputAlphabet) { - super(new Node<>(inputAlphabet.size())); - this.inputAlphabet = inputAlphabet; - this.alphabetSize = inputAlphabet.size(); + super(inputAlphabet); } @Override - public void addAlphabetSymbol(I symbol) { - if (!inputAlphabet.containsSymbol(symbol)) { - Alphabets.toGrowingAlphabetOrThrowException(inputAlphabet).addSymbol(symbol); - } - - final int newAlphabetSize = inputAlphabet.size(); - // even if the symbol was already in the alphabet, we need to make sure to be able to store the new symbol - if (alphabetSize < newAlphabetSize) { - ensureInputCapacity(root, alphabetSize, newAlphabetSize); - alphabetSize = newAlphabetSize; - } - } - - private void ensureInputCapacity(Node node, int oldAlphabetSize, int newAlphabetSize) { - node.ensureInputCapacity(newAlphabetSize); - for (int i = 0; i < oldAlphabetSize; i++) { - final Node child = node.getSuccessor(i); - if (child != null) { - ensureInputCapacity(child, oldAlphabetSize, newAlphabetSize); + public void insert(Word input, Word outputWord) { + Node curr = root; + + Iterator outputIt = outputWord.iterator(); + for (I sym : input) { + O out = outputIt.next(); + Edge, O> edge = getEdge(curr, sym); + if (edge == null) { + curr = insertNode(curr, sym, out); + } else { + if (!Objects.equals(out, edge.getOutput())) { + throw new ConflictException(); + } + curr = edge.getTarget(); } } } - - @Override - protected Edge, O> getEdge(Node node, I symbol) { - return node.getEdge(inputAlphabet.getSymbolIndex(symbol)); - } - - @Override - protected Node createNode() { - return new Node<>(alphabetSize); - } - - @Override - protected Node insertNode(Node parent, I symIdx, O output) { - Node succ = createNode(); - Edge, O> edge = new Edge<>(output, succ); - parent.setEdge(inputAlphabet.getSymbolIndex(symIdx), edge); - return succ; - } - - @Override - protected Collection, I, O>> getOutgoingEdges(Node node) { - List, I, O>> result = new ArrayList<>(alphabetSize); - for (int i = 0; i < alphabetSize; i++) { - Edge, O> edge = node.getEdge(i); - if (edge != null) { - result.add(new AnnotatedEdge<>(edge, inputAlphabet.getSymbol(i))); - } - } - return result; - } - - @Override - public Alphabet getInputAlphabet() { - return inputAlphabet; - } } diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/Node.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/Node.java index c50667a0bb..97b24eca42 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/Node.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/Node.java @@ -20,17 +20,17 @@ final class Node { - private final ResizingArrayStorage, O>> outEdges; + private final ResizingArrayStorage<@Nullable Edge, O>> outEdges; Node(int alphabetSize) { this.outEdges = new ResizingArrayStorage<>(Edge.class, alphabetSize); } - Edge, O> getEdge(int idx) { + @Nullable Edge, O> getEdge(int idx) { return outEdges.array[idx]; } - void setEdge(int idx, Edge, O> edge) { + void setEdge(int idx, @Nullable Edge, O> edge) { outEdges.array[idx] = edge; } diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/dynamic/DynamicIncrementalMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/dynamic/DynamicIncrementalMealyTreeBuilder.java index 8535ba2249..8e9335509c 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/dynamic/DynamicIncrementalMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/dynamic/DynamicIncrementalMealyTreeBuilder.java @@ -17,14 +17,18 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; +import net.automatalib.incremental.ConflictException; import net.automatalib.incremental.mealy.IncrementalMealyBuilder; -import net.automatalib.incremental.mealy.tree.AbstractIncrementalMealyTreeBuilder; +import net.automatalib.incremental.mealy.tree.AbstractMealyTreeBuilder; import net.automatalib.incremental.mealy.tree.AnnotatedEdge; import net.automatalib.incremental.mealy.tree.Edge; import net.automatalib.incremental.mealy.tree.IncrementalMealyTreeBuilder; +import net.automatalib.words.Word; import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -42,15 +46,30 @@ * * @author frohme */ -public class DynamicIncrementalMealyTreeBuilder extends AbstractIncrementalMealyTreeBuilder, I, O> +public class DynamicIncrementalMealyTreeBuilder extends AbstractMealyTreeBuilder, I, O> implements IncrementalMealyBuilder { public DynamicIncrementalMealyTreeBuilder() { - this(new Node<>()); + super(new Node<>()); } - DynamicIncrementalMealyTreeBuilder(Node root) { - super(root); + @Override + public void insert(Word input, Word outputWord) { + Node curr = root; + + Iterator outputIt = outputWord.iterator(); + for (I sym : input) { + O out = outputIt.next(); + Edge, O> edge = getEdge(curr, sym); + if (edge == null) { + curr = insertNode(curr, sym, out); + } else { + if (!Objects.equals(out, edge.getOutput())) { + throw new ConflictException(); + } + curr = edge.getTarget(); + } + } } @Override diff --git a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java index e075fffd2b..8871ef3d58 100644 --- a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java +++ b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java @@ -15,6 +15,9 @@ */ package net.automatalib.incremental.mealy; +import java.util.ArrayList; +import java.util.LinkedList; + import net.automatalib.automata.transducers.impl.compact.CompactMealy; import net.automatalib.commons.util.Pair; import net.automatalib.incremental.mealy.tree.AdaptiveMealyTreeBuilder; @@ -25,13 +28,9 @@ import net.automatalib.words.WordBuilder; import net.automatalib.words.impl.Alphabets; import net.automatalib.words.impl.GrowingMapAlphabet; - -import java.util.ArrayList; -import java.util.LinkedList; - +import org.testng.Assert; import org.testng.annotations.Test; -import junit.framework.Assert; @Test public class AdaptiveMealyTreeBuilderTest { @@ -137,8 +136,8 @@ public void testAges() { words.add(Pair.of(W_2, W_2_O)); words.add(Pair.of(W_3, W_3_O)); - for (int i = 0; i < words.size(); i++) { - adaptiveMealy.insert(words.get(i).getFirst(), words.get(i).getSecond()); + for (Pair, Word> word : words) { + adaptiveMealy.insert(word.getFirst(), word.getSecond()); } Assert.assertEquals(W_1, adaptiveMealy.getOldestInput()); adaptiveMealy.insert(W_1, W_1_O); From a6d4f761d807b3b49d7ef24bb0e86e6e6be8d65a Mon Sep 17 00:00:00 2001 From: Markus Frohme Date: Fri, 14 Oct 2022 00:05:42 +0200 Subject: [PATCH 16/17] refactor adaptive impl --- .../mealy/AdaptiveMealyBuilder.java | 8 ++--- .../mealy/tree/AdaptiveMealyTreeBuilder.java | 35 ++++++------------- .../mealy/AdaptiveMealyTreeBuilderTest.java | 17 +++++---- 3 files changed, 25 insertions(+), 35 deletions(-) diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java index 4e336cbdf1..9e1025dddf 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/AdaptiveMealyBuilder.java @@ -16,10 +16,10 @@ package net.automatalib.incremental.mealy; import net.automatalib.words.Word; +import org.checkerframework.checker.nullness.qual.Nullable; /** - * A variation of the {@link IncrementalMealyBuilder} interface that allows one to override previously inserted - * traces. + * A variation of the {@link IncrementalMealyBuilder} interface that allows one to override previously inserted traces. * * @param * input symbol type @@ -46,8 +46,8 @@ public interface AdaptiveMealyBuilder extends MealyBuilder { /** * Returns the oldest, non-overridden input that has been introduced and persisted. * - * @return the {@code Word} representing the oldest stored input. + * @return the {@code Word} representing the oldest stored input, {@code null} if the cache is empty. */ - Word getOldestInput(); + @Nullable Word getOldestInput(); } diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java index 258eb9046a..d0a1ade493 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java @@ -16,29 +16,25 @@ package net.automatalib.incremental.mealy.tree; import java.util.Collections; -import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; -import java.util.PriorityQueue; -import net.automatalib.commons.util.Pair; import net.automatalib.incremental.mealy.AdaptiveMealyBuilder; import net.automatalib.util.graphs.traversal.GraphTraversal; import net.automatalib.words.Alphabet; import net.automatalib.words.Word; +import org.checkerframework.checker.nullness.qual.Nullable; public class AdaptiveMealyTreeBuilder extends AbstractAlphabetBasedMealyTreeBuilder implements AdaptiveMealyBuilder { - private final Map, Pair, Integer>> stateToQuery = new HashMap<>(); - private final PriorityQueue> queryStates = new PriorityQueue<>((Node a, Node b) -> Integer.compare( - stateToQuery.get(a).getSecond(), - stateToQuery.get(b).getSecond())); - private Integer insertCounter = 0; + private final Map, Word> stateToQuery; public AdaptiveMealyTreeBuilder(Alphabet inputAlphabet) { super(inputAlphabet); + this.stateToQuery = new LinkedHashMap<>(); } @Override @@ -66,27 +62,15 @@ public boolean insert(Word input, Word outputWord) { } assert curr != null; - stateToQuery.put(curr, Pair.of(input, insertCounter)); - insertCounter += 1; - // Make sure it uses the new ages. - queryStates.remove(curr); - queryStates.add(curr); - - assert stateToQuery.size() == queryStates.size(); + stateToQuery.remove(curr); + stateToQuery.put(curr, Word.upcast(input)); return hasOverwritten; } private void removeQueries(Node node) { - GraphTraversal.bfIterator(this.asGraph(), Collections.singleton(node)).forEachRemaining(n -> { - if (queryStates.contains(n)) { - queryStates.remove(n); - } - stateToQuery.remove(n); - }); - - assert stateToQuery.size() == queryStates.size(); + GraphTraversal.bfIterator(this.asGraph(), Collections.singleton(node)).forEachRemaining(stateToQuery::remove); } private void removeEdge(Node node, I symbol) { @@ -94,7 +78,8 @@ private void removeEdge(Node node, I symbol) { } @Override - public Word getOldestInput() { - return stateToQuery.get(queryStates.peek()).getFirst(); + public @Nullable Word getOldestInput() { + final Iterator> iter = stateToQuery.values().iterator(); + return iter.hasNext() ? iter.next() : null; } } diff --git a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java index 8871ef3d58..85a77e6c55 100644 --- a/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java +++ b/incremental/src/test/java/net/automatalib/incremental/mealy/AdaptiveMealyTreeBuilderTest.java @@ -31,7 +31,6 @@ import org.testng.Assert; import org.testng.annotations.Test; - @Test public class AdaptiveMealyTreeBuilderTest { @@ -51,8 +50,8 @@ public class AdaptiveMealyTreeBuilderTest { private static final Word W_B_3 = Word.fromString("aabaa"); private static final Word W_B_3_O = Word.fromString("xxxxx"); - private AdaptiveMealyTreeBuilder adaptiveMealy = new AdaptiveMealyTreeBuilder<>( - TEST_ALPHABET); + private AdaptiveMealyTreeBuilder adaptiveMealy = + new AdaptiveMealyTreeBuilder<>(TEST_ALPHABET); @Test public void testConfluenceBug() { @@ -124,7 +123,6 @@ public void testLookup() { @Test(dependsOnMethods = "testLookup") public void testInsertSame() { adaptiveMealy.insert(W_1, W_1_O); - } @Test @@ -220,8 +218,7 @@ public void testCounterexampleOfLengthOne() { @Test(dependsOnMethods = "testLookup") public void testNewInputSymbol() { final GrowingAlphabet alphabet = new GrowingMapAlphabet<>(TEST_ALPHABET); - final AdaptiveMealyBuilder growableBuilder = new AdaptiveMealyTreeBuilder<>( - alphabet); + final AdaptiveMealyBuilder growableBuilder = new AdaptiveMealyTreeBuilder<>(alphabet); growableBuilder.addAlphabetSymbol('d'); growableBuilder.addAlphabetSymbol('d'); @@ -239,4 +236,12 @@ public void testNewInputSymbol() { Assert.assertFalse(growableBuilder.hasDefinitiveInformation(input2)); Assert.assertEquals(growableBuilder.lookup(input2), Word.fromLetter('1')); } + + @Test + public void testEmpty() { + final AdaptiveMealyBuilder incMealy = new AdaptiveMealyTreeBuilder<>(TEST_ALPHABET); + + Assert.assertTrue(incMealy.lookup(W_B_1).isEmpty()); + Assert.assertNull(incMealy.getOldestInput()); + } } From 4a718a13278976cc3cc40211149c48519575f6eb Mon Sep 17 00:00:00 2001 From: Markus Frohme Date: Fri, 14 Oct 2022 00:27:26 +0200 Subject: [PATCH 17/17] minor cleanups + renamings --- .../incremental/mealy/AbstractGraphView.java | 1 - .../mealy/tree/AdaptiveMealyTreeBuilder.java | 15 +++++++-------- .../mealy/tree/IncrementalMealyTreeBuilder.java | 7 +++---- .../DynamicIncrementalMealyTreeBuilder.java | 7 +++---- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractGraphView.java b/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractGraphView.java index 4e8e2f7e96..80307fca79 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractGraphView.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/AbstractGraphView.java @@ -44,7 +44,6 @@ public boolean getEdgeProperties(N src, E edge, N tgt, Map prope properties.put(EdgeAttrs.LABEL, input + " / " + output); return true; } - }; } } diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java index d0a1ade493..a4cc3425d9 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/AdaptiveMealyTreeBuilder.java @@ -30,11 +30,11 @@ public class AdaptiveMealyTreeBuilder extends AbstractAlphabetBasedMealyTreeBuilder implements AdaptiveMealyBuilder { - private final Map, Word> stateToQuery; + private final Map, Word> nodeToQuery; public AdaptiveMealyTreeBuilder(Alphabet inputAlphabet) { super(inputAlphabet); - this.stateToQuery = new LinkedHashMap<>(); + this.nodeToQuery = new LinkedHashMap<>(); } @Override @@ -42,10 +42,9 @@ public boolean insert(Word input, Word outputWord) { Node curr = root; boolean hasOverwritten = false; - Iterator outputIt = outputWord.iterator(); for (int i = 0; i < input.length(); i++) { I sym = input.getSymbol(i); - O out = outputIt.next(); + O out = outputWord.getSymbol(i); Edge, O> edge = getEdge(curr, sym); if (edge == null) { curr = insertNode(curr, sym, out); @@ -63,14 +62,14 @@ public boolean insert(Word input, Word outputWord) { assert curr != null; // Make sure it uses the new ages. - stateToQuery.remove(curr); - stateToQuery.put(curr, Word.upcast(input)); + nodeToQuery.remove(curr); + nodeToQuery.put(curr, Word.upcast(input)); return hasOverwritten; } private void removeQueries(Node node) { - GraphTraversal.bfIterator(this.asGraph(), Collections.singleton(node)).forEachRemaining(stateToQuery::remove); + GraphTraversal.bfIterator(this.asGraph(), Collections.singleton(node)).forEachRemaining(nodeToQuery::remove); } private void removeEdge(Node node, I symbol) { @@ -79,7 +78,7 @@ private void removeEdge(Node node, I symbol) { @Override public @Nullable Word getOldestInput() { - final Iterator> iter = stateToQuery.values().iterator(); + final Iterator> iter = nodeToQuery.values().iterator(); return iter.hasNext() ? iter.next() : null; } } diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/IncrementalMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/IncrementalMealyTreeBuilder.java index 2090943a45..78d2c69dff 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/IncrementalMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/IncrementalMealyTreeBuilder.java @@ -15,7 +15,6 @@ */ package net.automatalib.incremental.mealy.tree; -import java.util.Iterator; import java.util.Objects; import net.automatalib.incremental.ConflictException; @@ -34,9 +33,9 @@ public IncrementalMealyTreeBuilder(Alphabet inputAlphabet) { public void insert(Word input, Word outputWord) { Node curr = root; - Iterator outputIt = outputWord.iterator(); - for (I sym : input) { - O out = outputIt.next(); + for (int i = 0; i < input.length(); i++) { + I sym = input.getSymbol(i); + O out = outputWord.getSymbol(i); Edge, O> edge = getEdge(curr, sym); if (edge == null) { curr = insertNode(curr, sym, out); diff --git a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/dynamic/DynamicIncrementalMealyTreeBuilder.java b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/dynamic/DynamicIncrementalMealyTreeBuilder.java index 8e9335509c..a784136aaf 100644 --- a/incremental/src/main/java/net/automatalib/incremental/mealy/tree/dynamic/DynamicIncrementalMealyTreeBuilder.java +++ b/incremental/src/main/java/net/automatalib/incremental/mealy/tree/dynamic/DynamicIncrementalMealyTreeBuilder.java @@ -17,7 +17,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -57,9 +56,9 @@ public DynamicIncrementalMealyTreeBuilder() { public void insert(Word input, Word outputWord) { Node curr = root; - Iterator outputIt = outputWord.iterator(); - for (I sym : input) { - O out = outputIt.next(); + for (int i = 0; i < input.length(); i++) { + I sym = input.getSymbol(i); + O out = outputWord.getSymbol(i); Edge, O> edge = getEdge(curr, sym); if (edge == null) { curr = insertNode(curr, sym, out);