package yannakakis; import com.google.common.base.Supplier; import com.google.common.collect.Lists; import de.learnlib.acex.analyzers.AcexAnalyzers; import de.learnlib.algorithms.kv.mealy.KearnsVaziraniMealy; import de.learnlib.algorithms.lstargeneric.ce.ObservationTableCEXHandlers; import de.learnlib.algorithms.lstargeneric.closing.ClosingStrategies; import de.learnlib.algorithms.lstargeneric.mealy.ExtensibleLStarMealy; import de.learnlib.algorithms.ttt.mealy.TTTLearnerMealy; import de.learnlib.api.EquivalenceOracle; import de.learnlib.api.LearningAlgorithm; import de.learnlib.api.MembershipOracle; import de.learnlib.api.Query; import de.learnlib.counterexamples.LocalSuffixFinders; import de.learnlib.eqtests.basic.WMethodEQOracle; import de.learnlib.eqtests.basic.WpMethodEQOracle; import de.learnlib.oracles.DefaultQuery; import de.learnlib.oracles.SimulatorOracle; import de.learnlib.parallelism.ParallelOracle; import de.learnlib.parallelism.ParallelOracleBuilders; import net.automatalib.automata.transout.MealyMachine; import net.automatalib.automata.transout.impl.compact.CompactMealy; import net.automatalib.util.graphs.dot.GraphDOT; import net.automatalib.words.Alphabet; import net.automatalib.words.Word; import net.automatalib.words.impl.Alphabets; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.nio.file.Paths; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.List; /** * Test for the Lee and Yannakakis implementation. */ public class Main { public static enum Method { LeeYannakakis, WMethod, WpMethod } public static enum Learner { LStar, RivestSchapire, TTT, KearnsVazirani } public static void main(String[] args) throws IOException { // Some settings boolean learnRealESM = false; boolean saveAllHypotheses = false; int threadsForMembershipQueries = 4; Method testMethod = Method.LeeYannakakis; int maxDepthForWMethod = 2; Learner learnAlgorithm = Learner.LStar; System.out.println("Setting up SUL"); Supplier> supplier = null; Alphabet alphabet = null; List> alphabets = null; if(learnRealESM){ // Real ESM // I did not have this class, so I commented this out. // long seed = 78465782; // Random random = new Random(seed); // supplier = new ESMOracleSupplier(random); // // alphabet = esmSupplier.getInputAlphabet(); // Alphabet realSubAlphabet = esmSupplier.getSubAlphabet(); // alphabets = Arrays.asList(realSubAlphabet, realAlphabet); } else { // A simulated ESM (by means of a mealy machine) String filename = "esm-manual-controller.dot"; System.out.println(" Reading dot file: " + filename); GraphvizParser p = new GraphvizParser(Paths.get(filename)); CompactMealy fm = p.createMachine(); alphabet = fm.getInputAlphabet(); Alphabet simSubAlphabet = Alphabets.fromArray("21.1", "21.0", "22", "53.4", "52.5"); alphabets = Arrays.asList(simSubAlphabet, alphabet); System.out.println(" created machine with " + fm.size() + " states and " + alphabet.size() + " inputs"); supplier = () -> { return new SimulatorOracle.MealySimulatorOracle<>(fm); }; } assert supplier != null; assert alphabet != null; assert alphabets != null; System.out.println("Setting up membership oracle"); MembershipOracle> mParallelOracle = ParallelOracleBuilders .newDynamicParallelOracle(supplier) .withBatchSize(5) .withPoolSize(threadsForMembershipQueries) .withPoolPolicy(ParallelOracle.PoolPolicy.FIXED) .create(); // We will have the simulating membership oracle. Since the equivalence oracles use a membership oracle to // test equality, we make membership oracle which maintain a count. E.g. This way we can count the number of // queries needed to find a counterexample. CountingMembershipOracle> mOracleForLearning = new CountingMembershipOracle<>(mParallelOracle, "learning"); CountingMembershipOracle> mOracleForTesting = new CountingMembershipOracle<>(mParallelOracle, "testing"); System.out.println("Setting up equivalence oracle"); EquivalenceOracle, String, Word> eqOracle = null; switch (testMethod){ case LeeYannakakis: eqOracle = new YannakakisEQOracle<>(alphabets, mOracleForTesting); break; case WMethod: eqOracle = new WMethodEQOracle.MealyWMethodEQOracle<>(maxDepthForWMethod, mOracleForTesting); break; case WpMethod: eqOracle = new WpMethodEQOracle.MealyWpMethodEQOracle<>(maxDepthForWMethod, mOracleForTesting); break; } assert eqOracle != null; System.out.println("Setting up learner"); LearningAlgorithm, String, Word> learner = null; switch (learnAlgorithm){ case LStar: learner = new ExtensibleLStarMealy<>(alphabet, mOracleForLearning, Lists.>newArrayList(), ObservationTableCEXHandlers.CLASSIC_LSTAR, ClosingStrategies.CLOSE_SHORTEST); break; case RivestSchapire: learner = new ExtensibleLStarMealy<>(alphabet, mOracleForLearning, Lists.>newArrayList(), ObservationTableCEXHandlers.RIVEST_SCHAPIRE, ClosingStrategies.CLOSE_SHORTEST); break; case TTT: learner = new TTTLearnerMealy<>(alphabet, mOracleForLearning, LocalSuffixFinders.FIND_LINEAR); break; case KearnsVazirani: learner = new KearnsVaziraniMealy<>(alphabet, mOracleForLearning, false, AcexAnalyzers.BINARY_SEARCH); break; } assert learner != null; // Here we will perform our experiment. I did not use the Experiment class from LearnLib, as I wanted some // more control (for example, I want to reset the counters in the membership oracles). This control flow // is suggested by LearnLib (on their wiki). System.out.println("Starting experiment\n"); int stage = 0; learner.startLearning(); while(true) { if(saveAllHypotheses) { String dir = "/Users/joshua/Documents/PhD/Machines/Mealy/esms3/"; String filename = dir + "hyp." + stage + ".obf.dot"; PrintWriter output = new PrintWriter(filename); GraphDOT.write(learner.getHypothesisModel(), alphabet, output); output.close(); } System.out.println(stage++ + ": " + Calendar.getInstance().getTime()); System.out.println("Hypothesis: " + learner.getHypothesisModel().getStates().size()); mOracleForLearning.logAndReset(System.out); mOracleForTesting.logAndReset(System.out); System.out.println(); DefaultQuery> ce = eqOracle.findCounterExample(learner.getHypothesisModel(), alphabet); if(ce == null) break; learner.refineHypothesis(ce); } System.out.println("Done with learning, no counter example found after:"); mOracleForLearning.logAndReset(System.out); mOracleForTesting.logAndReset(System.out); System.out.println(); PrintWriter output = new PrintWriter("last_hypothesis.dot"); GraphDOT.write(learner.getHypothesisModel(), alphabet, output); output.close(); } /** * An membership oracle which maintains a count of the number of queries (and symbols). Usefull for testing * performance of equivalence oracles (which use membership oracles). It simply delegates the queries to * a delegate. * @param Input alphabet * @param Output domain (should be Word for mealy machines) */ static public class CountingMembershipOracle implements MembershipOracle { private final MembershipOracle delegate; private final String name; private long queries = 0; private long symbols = 0; public CountingMembershipOracle(MembershipOracle delegate, String name){ this.delegate = delegate; this.name = name; } public void reset(){ queries = 0; symbols = 0; } public void log(PrintStream output){ output.println(name + ": queries: " + queries); output.println(name + ": symbols: " + symbols); } public void logAndReset(PrintStream output){ log(output); reset(); } @Override public void processQueries(Collection> collection) { queries += collection.size(); collection.parallelStream().forEach((Query idQuery) -> { symbols += idQuery.getInput().size(); }); delegate.processQueries(collection); } } }