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

de.learnlib.algorithms.lstar.AbstractAutomatonLStar 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.

There is a newer version: 0.17.0
Show newest version
/* Copyright (C) 2013-2018 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.algorithms.lstar;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import de.learnlib.api.algorithm.feature.ResumableLearner;
import de.learnlib.api.oracle.MembershipOracle;
import de.learnlib.api.query.DefaultQuery;
import de.learnlib.datastructure.observationtable.ObservationTable;
import de.learnlib.datastructure.observationtable.Row;
import net.automatalib.automata.GrowableAlphabetAutomaton;
import net.automatalib.automata.MutableDeterministic;
import net.automatalib.commons.util.collections.CollectionsUtil;
import net.automatalib.words.Alphabet;
import net.automatalib.words.impl.SymbolHidingAlphabet;

/**
 * Abstract base class for algorithms that produce (subclasses of) {@link MutableDeterministic} automata.
 * 

* This class provides the L*-style hypothesis construction. Implementing classes solely have to specify how state and * transition properties should be derived. * * @param * automaton type, must be a subclass of {@link MutableDeterministic} * @param * input symbol type * @param * output domain type * @param * state property type * @param * transition property type * * @author Malte Isberner */ public abstract class AbstractAutomatonLStar & GrowableAlphabetAutomaton> extends AbstractLStar implements ResumableLearner> { protected AI internalHyp; protected List> stateInfos = new ArrayList<>(); /** * Constructor. * * @param alphabet * the learning alphabet * @param oracle * the learning oracle */ protected AbstractAutomatonLStar(Alphabet alphabet, MembershipOracle oracle, AI internalHyp) { super(SymbolHidingAlphabet.wrapIfMutable(alphabet), oracle); this.internalHyp = internalHyp; internalHyp.clear(); } @Override public A getHypothesisModel() { return exposeInternalHypothesis(); } protected abstract A exposeInternalHypothesis(); @Override public final void startLearning() { super.startLearning(); updateInternalHypothesis(); } /** * Performs the L*-style hypothesis construction. For creating states and transitions, the {@link * #stateProperty(ObservationTable, Row)} and {@link #transitionProperty(ObservationTable, Row, int)} methods are * used to derive the respective properties. */ protected void updateInternalHypothesis() { if (!table.isInitialized()) { throw new IllegalStateException("Cannot update internal hypothesis: not initialized"); } int oldStates = internalHyp.size(); int numDistinct = table.numberOfDistinctRows(); int newStates = numDistinct - oldStates; stateInfos.addAll(CollectionsUtil.nullList(newStates)); // TODO: Is there a quicker way than iterating over *all* rows? // FIRST PASS: Create new hypothesis states for (Row sp : table.getShortPrefixRows()) { int id = sp.getRowContentId(); StateInfo info = stateInfos.get(id); if (info != null) { // State from previous hypothesis, property might have changed if (info.getRow() == sp) { internalHyp.setStateProperty(info.getState(), stateProperty(table, sp)); } continue; } S state = createState((id == 0), sp); stateInfos.set(id, new StateInfo<>(sp, state)); } // SECOND PASS: Create hypothesis transitions for (StateInfo info : stateInfos) { Row sp = info.getRow(); S state = info.getState(); for (int i = 0; i < alphabet.size(); i++) { I input = alphabet.getSymbol(i); Row succ = sp.getSuccessor(i); int succId = succ.getRowContentId(); S succState = stateInfos.get(succId).getState(); setTransition(state, input, succState, sp, i); } } } /** * Derives a state property from the corresponding row. * * @param table * the current observation table * @param stateRow * the row for which the state is created * * @return the state property of the corresponding state */ protected abstract SP stateProperty(ObservationTable table, Row stateRow); protected S createState(boolean initial, Row row) { SP prop = stateProperty(table, row); if (initial) { return internalHyp.addInitialState(prop); } return internalHyp.addState(prop); } protected void setTransition(S from, I input, S to, Row fromRow, int inputIdx) { TP prop = transitionProperty(table, fromRow, inputIdx); internalHyp.setTransition(from, input, to, prop); } /** * Derives a transition property from the corresponding transition. *

* N.B.: Not the transition row is passed to this method, but the row for the outgoing state. The transition row can * be retrieved using {@link Row#getSuccessor(int)}. * * @param stateRow * the row for the source state * @param inputIdx * the index of the input symbol to consider * * @return the transition property of the corresponding transition */ protected abstract TP transitionProperty(ObservationTable table, Row stateRow, int inputIdx); @Override protected final void doRefineHypothesis(DefaultQuery ceQuery) { refineHypothesisInternal(ceQuery); updateInternalHypothesis(); } protected void refineHypothesisInternal(DefaultQuery ceQuery) { super.doRefineHypothesis(ceQuery); } @Override public void addAlphabetSymbol(I symbol) { if (alphabet.containsSymbol(symbol)) { return; } this.internalHyp.addAlphabetSymbol(symbol); SymbolHidingAlphabet.runWhileHiding(alphabet, symbol, () -> super.addAlphabetSymbol(symbol)); this.updateInternalHypothesis(); } @Override public AutomatonLStarState suspend() { return new AutomatonLStarState<>(table, internalHyp, stateInfos); } @Override public void resume(final AutomatonLStarState state) { this.table = state.getObservationTable(); this.table.setInputAlphabet(alphabet); this.internalHyp = state.getHypothesis(); this.stateInfos = state.getStateInfos(); } static final class StateInfo implements Serializable { private final Row row; private final S state; StateInfo(Row row, S state) { this.row = row; this.state = state; } public Row getRow() { return row; } public S getState() { return state; } // IDENTITY SEMANTICS! } }