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.algorithms.lstargeneric; 018 019import java.util.ArrayList; 020import java.util.List; 021import java.util.Objects; 022 023import net.automatalib.words.Alphabet; 024import net.automatalib.words.Word; 025import de.learnlib.algorithms.lstargeneric.ce.ObservationTableCEXHandlers; 026import de.learnlib.algorithms.lstargeneric.table.Inconsistency; 027import de.learnlib.algorithms.lstargeneric.table.ObservationTable; 028import de.learnlib.algorithms.lstargeneric.table.Row; 029import de.learnlib.api.LearningAlgorithm; 030import de.learnlib.api.MembershipOracle; 031import de.learnlib.oracles.DefaultQuery; 032 033/** 034 * An abstract base class for L*-style algorithms. 035 * 036 * This class implements basic management features (table, alphabet, oracle) and 037 * the main loop of alternating completeness and consistency checks. It does not take 038 * care of choosing how to initialize the table and hypothesis construction. 039 * 040 * @author Malte Isberner <malte.isberner@gmail.com> 041 * 042 * @param <A> automaton class. 043 * @param <I> input symbol class. 044 * @param <O> output class. 045 */ 046public abstract class AbstractLStar<A, I, O> implements LearningAlgorithm<A, I, O> { 047 048 protected final Alphabet<? extends I> alphabet; 049 protected final MembershipOracle<I, O> oracle; 050 protected final ObservationTable<I, O> table; 051 052 /** 053 * Constructor. 054 * @param alphabet the learning alphabet. 055 * @param oracle the membership oracle. 056 * @param outputMapping a mapping that translates between oracle outputs and data entries stored 057 * in the observation table. 058 */ 059 public AbstractLStar(Alphabet<I> alphabet, MembershipOracle<I,O> oracle) { 060 this.alphabet = alphabet; 061 this.oracle = oracle; 062 063 this.table = new ObservationTable<>(alphabet); 064 } 065 066 067 /* 068 * (non-Javadoc) 069 * @see de.learnlib.api.LearningAlgorithm#start() 070 */ 071 @Override 072 public void startLearning() { 073 List<Word<I>> suffixes = initialSuffixes(); 074 List<List<Row<I>>> initialUnclosed = table.initialize(suffixes, oracle); 075 076 completeConsistentTable(initialUnclosed, false); 077 } 078 079 /* 080 * (non-Javadoc) 081 * @see de.learnlib.api.LearningAlgorithm#refineHypothesis(de.learnlib.api.Query) 082 */ 083 @Override 084 public final boolean refineHypothesis(DefaultQuery<I, O> ceQuery) { 085 int oldDistinctRows = table.numDistinctRows(); 086 doRefineHypothesis(ceQuery); 087 return (table.numDistinctRows() > oldDistinctRows); 088 } 089 090 protected void doRefineHypothesis(DefaultQuery<I,O> ceQuery) { 091 List<List<Row<I>>> unclosed = incorporateCounterExample(ceQuery); 092 completeConsistentTable(unclosed, true); 093 } 094 095 /** 096 * Iteratedly checks for unclosedness and inconsistencies in the table, 097 * and fixes any occurrences thereof. This process is repeated until the 098 * observation table is both closed and consistent. 099 * @param unclosed the unclosed rows (equivalence classes) to start with. 100 */ 101 protected void completeConsistentTable(List<List<Row<I>>> unclosed, boolean checkConsistency) { 102 do { 103 while(!unclosed.isEmpty()) { 104 List<Row<I>> closingRows = selectClosingRows(unclosed); 105 unclosed = table.toShortPrefixes(closingRows, oracle); 106 } 107 108 109 if(checkConsistency) { 110 Inconsistency<I,O> incons = null; 111 112 do { 113 incons = table.findInconsistency(); 114 if(incons != null) { 115 Word<I> newSuffix = analyzeInconsistency(incons); 116 unclosed = table.addSuffix(newSuffix, oracle); 117 } 118 } while(unclosed.isEmpty() && (incons != null)); 119 } 120 } while(!unclosed.isEmpty()); 121 } 122 123 124 /** 125 * Analyzes an inconsistency. This analysis consists in determining 126 * the column in which the two successor rows differ. 127 * @param incons the inconsistency description 128 * @return the suffix to add in order to fix the inconsistency 129 */ 130 protected Word<I> analyzeInconsistency(Inconsistency<I,O> incons) { 131 int inputIdx = incons.getInputIndex(); 132 133 Row<I> succRow1 = incons.getFirstRow().getSuccessor(inputIdx); 134 Row<I> succRow2 = incons.getSecondRow().getSuccessor(inputIdx); 135 136 int numSuffixes = table.numSuffixes(); 137 138 List<O> contents1 = table.rowContents(succRow1); 139 List<O> contents2 = table.rowContents(succRow2); 140 141 for(int i = 0; i < numSuffixes; i++) { 142 O val1 = contents1.get(i), val2 = contents2.get(i); 143 if(!Objects.equals(val1, val2)) { 144 I sym = alphabet.getSymbol(inputIdx); 145 Word<I> suffix = table.getSuffixes().get(i); 146 return suffix.prepend(sym); 147 } 148 } 149 150 throw new IllegalArgumentException("Bogus inconsistency"); 151 } 152 153 154 /** 155 * Incorporates the information provided by a counterexample into 156 * the observation data structure. 157 * @param ce the query which contradicts the hypothesis 158 * @return the rows (equivalence classes) which became unclosed by 159 * adding the information. 160 */ 161 protected List<List<Row<I>>> incorporateCounterExample(DefaultQuery<I,O> ce) { 162 return ObservationTableCEXHandlers.handleClassicLStar(ce, table, oracle); 163 } 164 165 /** 166 * This method selects a set of rows to use for closing the table. 167 * It receives as input a list of row lists, such that each (inner) list contains 168 * long prefix rows with (currently) identical contents, which have no matching 169 * short prefix row. The outer list is the list of all those equivalence classes. 170 * 171 * @param unclosed a list of equivalence classes of unclosed rows. 172 * @return a list containing a representative row from each class to move 173 * to the short prefix part. 174 */ 175 protected List<Row<I>> selectClosingRows(List<List<Row<I>>> unclosed) { 176 List<Row<I>> closingRows = new ArrayList<Row<I>>(unclosed.size()); 177 178 for(List<Row<I>> rowList : unclosed) 179 closingRows.add(rowList.get(0)); 180 181 return closingRows; 182 } 183 184 185 /** 186 * Returns the list of initial suffixes which are used to initialize the table. 187 * @return the list of initial suffixes. 188 */ 189 protected abstract List<Word<I>> initialSuffixes(); 190 191}