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

de.learnlib.algorithms.nlstar.ObservationTable Maven / Gradle / Ivy

Go to download

This artifact provides the implementation of the NL* learning algorithm as described in the paper "Angluin-Style Learning of NFA" (http://ijcai.org/Proceedings/09/Papers/170.pdf) by Benedikt Bollig, Peter Habermehl, Carsten Kern, and Martin Leucker.

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

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import de.learnlib.api.oracle.MembershipOracle;
import de.learnlib.api.query.DefaultQuery;
import net.automatalib.words.Alphabet;
import net.automatalib.words.Word;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * The observation table implementation for the {@link NLStarLearner NL* algorithm}.
 *
 * @param 
 *         input symbol type
 *
 * @author Malte Isberner
 */
public class ObservationTable {

    private final Alphabet alphabet;
    private final MembershipOracle oracle;

    private final List> upperRows = new ArrayList<>();
    private final List> allRows = new ArrayList<>();

    private final List> newUppers = new ArrayList<>();
    private final List> newRows = new ArrayList<>();

    private final List> suffixes = new ArrayList<>();
    private final Set> suffixSet = new HashSet<>();

    private final List> upperPrimes = new ArrayList<>();

    public ObservationTable(Alphabet alphabet, MembershipOracle oracle) {
        this.alphabet = alphabet;
        this.oracle = oracle;
    }

    public List>> initialize() {
        if (!suffixes.isEmpty()) {
            throw new IllegalStateException();
        }

        Row row = createRow(Word.epsilon());

        makeUpper(row);

        return addSuffix(Word.epsilon());
    }

    private Row createRow(Word prefix) {
        Row row = new Row<>(prefix);
        allRows.add(row);
        newRows.add(row);
        return row;
    }

    public List>> addSuffix(Word suffixToAdd) {
        return addSuffixes(Collections.singletonList(suffixToAdd));
    }

    private void makeUpper(Row row) {
        makeUpper(Collections.singletonList(row));
    }

    public List>> makeUpper(List> rows) {
        List> newRows = new ArrayList<>(rows.size() * alphabet.size());
        for (Row row : rows) {
            makeShort(row);
            Word prefix = row.getPrefix();

            for (int i = 0; i < alphabet.size(); i++) {
                I sym = alphabet.getSymbol(i);
                Word newPrefix = prefix.append(sym);
                Row newRow = createRow(newPrefix);
                row.setSuccessorRow(i, newRow);
                newRows.add(newRow);
            }
        }

        if (suffixes.isEmpty()) {
            return Collections.emptyList();
        }

        int numSuffixes = suffixes.size();

        List> queries = new ArrayList<>(newRows.size() * numSuffixes);

        for (Row newRow : newRows) {
            for (Word suffix : suffixes) {
                queries.add(new DefaultQuery<>(newRow.getPrefix(), suffix));
            }
        }

        oracle.processQueries(queries);

        Iterator> queryIt = queries.iterator();

        for (Row newRow : newRows) {
            newRow.fetchContents(queryIt, 0, numSuffixes);
        }

        return updateMetadata();

    }

    public List>> addSuffixes(List> suffixesToAdd) {
        List> newSuffixes = new ArrayList<>();

        int oldNumSuffixes = suffixes.size();

        for (Word suffix : suffixesToAdd) {
            if (suffixSet.add(suffix)) {
                suffixes.add(suffix);
                newSuffixes.add(suffix);
            }
        }

        if (newSuffixes.isEmpty()) {
            return Collections.emptyList();
        }

        int numNewSuffixes = newSuffixes.size();

        List> queries = new ArrayList<>(allRows.size() * numNewSuffixes);

        for (Row row : allRows) {
            Word prefix = row.getPrefix();
            for (Word suffix : newSuffixes) {
                queries.add(new DefaultQuery<>(prefix, suffix));
            }
        }

        oracle.processQueries(queries);

        Iterator> queryIt = queries.iterator();

        for (Row row : allRows) {
            row.fetchContents(queryIt, oldNumSuffixes, numNewSuffixes);
        }

        return updateMetadata();
    }

    private void makeShort(Row row) {
        row.makeShort(upperRows.size(), alphabet.size());
        upperRows.add(row);
        newUppers.add(row);
    }

    private List>> updateMetadata() {

        // Update coverage information
        for (Row row : allRows) {
            if (row.isShortPrefixRow()) {
                if (row.isNew()) {
                    row.updateCovered(upperRows);
                } else {
                    row.updateCovered(newUppers);
                }
            } else {
                if (row.isNew()) {
                    row.updateCovered(allRows);
                } else {
                    row.updateCovered(newRows);
                }
            }
        }

        newRows.clear();
        newUppers.clear();

        upperPrimes.clear();

        Map>> primeContents = new HashMap<>();
        List>> allUnclosed = new ArrayList<>();

        for (Row row : allRows) {
            boolean prime = row.checkPrime();

            if (prime) {
                if (row.isShortPrefixRow()) {
                    upperPrimes.add(row);
                } else {
                    List> unclosedClass = primeContents.get(row.getContents());
                    if (unclosedClass == null) {
                        unclosedClass = new ArrayList<>();
                        allUnclosed.add(unclosedClass);
                        primeContents.put(row.getContents(), unclosedClass);
                    }
                    unclosedClass.add(row);
                }
            }
        }

        return allUnclosed;
    }

    public Word getSuffix(int suffixIdx) {
        return suffixes.get(suffixIdx);
    }

    public List> getCoveredRows(Row coveringRow) {
        return coveringRow.getCoveredRows();
    }

    public Row getUpperRow(int index) {
        return upperRows.get(index);
    }

    public List> getUpperRows() {
        return upperRows;
    }

    public List> getUpperPrimes() {
        return upperPrimes;
    }

    public int getNumUpperRows() {
        return upperRows.size();
    }

    public @Nullable Inconsistency findInconsistency() {
        for (Row row1 : upperRows) {
            for (Row row2 : row1.getCoveredRows()) {
                assert row2.isShortPrefixRow();

                for (int i = 0; i < alphabet.size(); i++) {
                    Row row1succ = row1.getSuccessorRow(i);
                    Row row2succ = row2.getSuccessorRow(i);

                    for (int j = 0; j < suffixes.size(); j++) {
                        if (!row1succ.getContent(j) && row2succ.getContent(j)) {
                            return new Inconsistency<>(row1, row2, i, j);
                        }
                    }
                }
            }
        }

        return null;
    }
}