All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.learnlib.algorithm.lstar.AbstractLStar Maven / Gradle / Ivy

Go to download

This artifact provides the implementation of the L* learning algorithm described in the paper "Learning Regular Sets from Queries and Counterexamples" (https://doi.org/10.1016/0890-5401(87)90052-6) by Dana Angluin including variations and optimizations thereof such as the versions based on "On the Learnability of Infinitary Regular Sets" (https://dx.doi.org/10.1006/inco.1995.1070) by Oded Maler and Amir Pnueli or "Inference of finite automata using homing sequences" (http://dx.doi.org/10.1006/inco.1993.1021) by Ronald L. Rivest and Robert E. Schapire.

The newest version!
/* Copyright (C) 2013-2023 TU Dortmund
 * This file is part of LearnLib, http://www.learnlib.de/.
 *
 * 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 de.learnlib.algorithm.lstar;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import de.learnlib.algorithm.GlobalSuffixLearner;
import de.learnlib.algorithm.lstar.ce.ObservationTableCEXHandlers;
import de.learnlib.datastructure.observationtable.GenericObservationTable;
import de.learnlib.datastructure.observationtable.Inconsistency;
import de.learnlib.datastructure.observationtable.OTLearner;
import de.learnlib.datastructure.observationtable.ObservationTable;
import de.learnlib.datastructure.observationtable.Row;
import de.learnlib.oracle.MembershipOracle;
import de.learnlib.query.DefaultQuery;
import de.learnlib.util.MQUtil;
import net.automatalib.alphabet.Alphabet;
import net.automatalib.alphabet.Alphabets;
import net.automatalib.alphabet.SupportsGrowingAlphabet;
import net.automatalib.automaton.concept.SuffixOutput;
import net.automatalib.word.Word;

/**
 * An abstract base class for L*-style algorithms.
 * 

* This class implements basic management features (table, alphabet, oracle) and the main loop of alternating * completeness and consistency checks. It does not take care of choosing how to initialize the table and hypothesis * construction. * * @param * automaton type * @param * input symbol type * @param * output domain type */ public abstract class AbstractLStar implements OTLearner, GlobalSuffixLearner, SupportsGrowingAlphabet { protected final Alphabet alphabet; protected final MembershipOracle oracle; protected GenericObservationTable table; /** * Constructor. * * @param alphabet * the learning alphabet. * @param oracle * the membership oracle. */ protected AbstractLStar(Alphabet alphabet, MembershipOracle oracle) { this.alphabet = alphabet; this.oracle = oracle; this.table = new GenericObservationTable<>(alphabet); } @Override public void startLearning() { List> prefixes = initialPrefixes(); List> suffixes = initialSuffixes(); List>> initialUnclosed = table.initialize(prefixes, suffixes, oracle); completeConsistentTable(initialUnclosed, table.isInitialConsistencyCheckRequired()); } @Override public final boolean refineHypothesis(DefaultQuery ceQuery) { if (!MQUtil.isCounterexample(ceQuery, hypothesisOutput())) { return false; } int oldDistinctRows = table.numberOfDistinctRows(); doRefineHypothesis(ceQuery); assert table.numberOfDistinctRows() > oldDistinctRows; return true; } protected abstract SuffixOutput hypothesisOutput(); protected void doRefineHypothesis(DefaultQuery ceQuery) { List>> unclosed = incorporateCounterExample(ceQuery); completeConsistentTable(unclosed, true); } /** * Incorporates the information provided by a counterexample into the observation data structure. * * @param ce * the query which contradicts the hypothesis * * @return the rows (equivalence classes) which became unclosed by adding the information. */ protected List>> incorporateCounterExample(DefaultQuery ce) { return ObservationTableCEXHandlers.handleClassicLStar(ce, table, oracle); } protected List> initialPrefixes() { return Collections.singletonList(Word.epsilon()); } /** * Returns the list of initial suffixes which are used to initialize the table. * * @return the list of initial suffixes. */ protected abstract List> initialSuffixes(); /** * Iteratedly checks for unclosedness and inconsistencies in the table, and fixes any occurrences thereof. This * process is repeated until the observation table is both closed and consistent. * * @param unclosed * the unclosed rows (equivalence classes) to start with. */ protected boolean completeConsistentTable(List>> unclosed, boolean checkConsistency) { boolean refined = false; List>> unclosedIter = unclosed; do { while (!unclosedIter.isEmpty()) { List> closingRows = selectClosingRows(unclosedIter); unclosedIter = table.toShortPrefixes(closingRows, oracle); refined = true; } if (checkConsistency) { Inconsistency incons; do { incons = table.findInconsistency(); if (incons != null) { Word newSuffix = analyzeInconsistency(incons); unclosedIter = table.addSuffix(newSuffix, oracle); } } while (unclosedIter.isEmpty() && incons != null); } } while (!unclosedIter.isEmpty()); return refined; } /** * This method selects a set of rows to use for closing the table. It receives as input a list of row lists, such * that each (inner) list contains long prefix rows with (currently) identical contents, which have no matching * short prefix row. The outer list is the list of all those equivalence classes. * * @param unclosed * a list of equivalence classes of unclosed rows. * * @return a list containing a representative row from each class to move to the short prefix part. */ protected List> selectClosingRows(List>> unclosed) { List> closingRows = new ArrayList<>(unclosed.size()); for (List> rowList : unclosed) { closingRows.add(rowList.get(0)); } return closingRows; } /** * Analyzes an inconsistency. This analysis consists in determining the column in which the two successor rows * differ. * * @param incons * the inconsistency description * * @return the suffix to add in order to fix the inconsistency */ protected Word analyzeInconsistency(Inconsistency incons) { int inputIdx = alphabet.getSymbolIndex(incons.getSymbol()); Row succRow1 = incons.getFirstRow().getSuccessor(inputIdx); Row succRow2 = incons.getSecondRow().getSuccessor(inputIdx); int numSuffixes = table.getSuffixes().size(); for (int i = 0; i < numSuffixes; i++) { D val1 = table.cellContents(succRow1, i), val2 = table.cellContents(succRow2, i); if (!Objects.equals(val1, val2)) { I sym = alphabet.getSymbol(inputIdx); Word suffix = table.getSuffixes().get(i); return suffix.prepend(sym); } } throw new IllegalArgumentException("Bogus inconsistency"); } @Override public Collection> getGlobalSuffixes() { return Collections.unmodifiableCollection(table.getSuffixes()); } @Override public boolean addGlobalSuffixes(Collection> newGlobalSuffixes) { List>> unclosed = table.addSuffixes(newGlobalSuffixes, oracle); if (unclosed.isEmpty()) { return false; } return completeConsistentTable(unclosed, false); } @Override public ObservationTable getObservationTable() { return table; } @Override public void addAlphabetSymbol(I symbol) { if (!this.alphabet.containsSymbol(symbol)) { Alphabets.toGrowingAlphabetOrThrowException(this.alphabet).addSymbol(symbol); } final List>> unclosed = this.table.addAlphabetSymbol(symbol, oracle); completeConsistentTable(unclosed, true); } }