001/* Copyright (C) 2013 TU Dortmund
002 * This file is part of LearnLib, http://www.learnlib.de/.
003 * 
004 * LearnLib is free software; you can redistribute it and/or
005 * modify it under the terms of the GNU Lesser General Public
006 * License version 3.0 as published by the Free Software Foundation.
007 * 
008 * LearnLib is distributed in the hope that it will be useful,
009 * but WITHOUT ANY WARRANTY; without even the implied warranty of
010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
011 * Lesser General Public License for more details.
012 * 
013 * You should have received a copy of the GNU Lesser General Public
014 * License along with LearnLib; if not, see
015 * <http://www.gnu.de/documents/lgpl.en.html>.
016 */
017package de.learnlib.experiments;
018
019import net.automatalib.automata.fsa.DFA;
020import net.automatalib.automata.transout.MealyMachine;
021import net.automatalib.words.Alphabet;
022import net.automatalib.words.Word;
023import de.learnlib.api.EquivalenceOracle;
024import de.learnlib.api.LearningAlgorithm;
025import de.learnlib.logging.LearnLogger;
026import de.learnlib.oracles.DefaultQuery;
027import de.learnlib.statistics.Counter;
028import de.learnlib.statistics.SimpleProfiler;
029
030/**
031 * runs a learning experiment. 
032 * 
033 * @author falkhowar
034 * @param <A>
035 * @param <I>
036 * @param <O> 
037 */
038public class Experiment<A> {
039        
040        public static class DFAExperiment<I> extends Experiment<DFA<?,I>> {
041                public DFAExperiment(
042                                LearningAlgorithm<? extends DFA<?, I>, I, Boolean> learningAlgorithm,
043                                EquivalenceOracle<? super DFA<?, I>, I, Boolean> equivalenceAlgorithm,
044                                Alphabet<I> inputs) {
045                        super(learningAlgorithm, equivalenceAlgorithm, inputs);
046                }
047        }
048        
049        public static class MealyExperiment<I,O> extends Experiment<MealyMachine<?,I,?,O>> {
050                public MealyExperiment(
051                                LearningAlgorithm<? extends MealyMachine<?, I, ?, O>, I, Word<O>> learningAlgorithm,
052                                EquivalenceOracle<? super MealyMachine<?, I, ?, O>, I, Word<O>> equivalenceAlgorithm,
053                                Alphabet<I> inputs) {
054                        super(learningAlgorithm, equivalenceAlgorithm, inputs);
055                }
056                
057        }
058        
059        private final class ExperimentImpl<I,O> {
060                private final LearningAlgorithm<? extends A, I, O> learningAlgorithm;
061            private final EquivalenceOracle<? super A, I, O> equivalenceAlgorithm;
062            private final Alphabet<I> inputs;
063            
064            public ExperimentImpl(LearningAlgorithm<? extends A, I, O> learningAlgorithm, EquivalenceOracle<? super A, I, O> equivalenceAlgorithm, Alphabet<I> inputs) {
065                this.learningAlgorithm = learningAlgorithm;
066                this.equivalenceAlgorithm = equivalenceAlgorithm;
067                this.inputs = inputs;
068            }
069            
070            public A run() {
071                rounds.increment();
072                logger.logPhase("Starting round " + rounds.getCount());
073                logger.logPhase("Learning");
074                profileStart("Learning");
075                learningAlgorithm.startLearning();
076                profileStop("Learning");
077
078                boolean done = false;
079                A hyp = null;
080                while (!done) {
081                        hyp = learningAlgorithm.getHypothesisModel();
082                    if (logModels) {
083                        logger.logModel(hyp);
084                    }
085
086                    logger.logPhase("Searching for counterexample");
087                    profileStart("Searching for counterexample");
088                    DefaultQuery<I, O> ce = equivalenceAlgorithm.findCounterExample(hyp, inputs);
089                    if (ce == null) {
090                        done = true;
091                        continue;
092                    }
093                    profileStop("Searching for counterexample");
094                    
095                    logger.logCounterexample(ce.getInput().toString());
096
097                    // next round ...
098                    rounds.increment();
099                    logger.logPhase("Starting round " + rounds.getCount());
100                    logger.logPhase("Learning");
101                    profileStart("Learning");
102                    learningAlgorithm.refineHypothesis(ce);
103                    profileStop("Learning");
104                }
105
106                return hyp;
107            }
108        }
109
110    private static LearnLogger logger = LearnLogger.getLogger(Experiment.class);
111    
112    private boolean logModels = false;
113    private boolean profile = false;
114    private Counter rounds = new Counter("rounds", "#");
115    private A finalHypothesis = null;
116    private final ExperimentImpl<?,?> impl;
117
118    public <I,O> Experiment(LearningAlgorithm<? extends A, I, O> learningAlgorithm, EquivalenceOracle<? super A, I, O> equivalenceAlgorithm, Alphabet<I> inputs) {
119        this.impl = new ExperimentImpl<>(learningAlgorithm, equivalenceAlgorithm, inputs);
120    }
121    
122
123    
124    /**
125     * 
126     */
127    public A run() {
128        finalHypothesis = impl.run();
129        return finalHypothesis;
130    }
131    
132    public A getFinalHypothesis() {
133        return finalHypothesis;
134    }
135
136    
137    
138    private void profileStart(String taskname) {
139        if (profile) {
140            SimpleProfiler.start(taskname);
141        }
142    }
143
144    private void profileStop(String taskname) {
145        if (profile) {
146            SimpleProfiler.stop(taskname);
147        }
148    }
149
150    /**
151     * @param logModels the logModels to set
152     */
153    public void setLogModels(boolean logModels) {
154        this.logModels = logModels;
155    }
156
157    /**
158     * @param profile the profile to set
159     */
160    public void setProfile(boolean profile) {
161        this.profile = profile;
162    }
163
164    /**
165     * @return the rounds
166     */
167    public Counter getRounds() {
168        return rounds;
169    }
170}