diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1908d29c57..13d1ca2e4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,5 @@ name: CI +permissions: read-all on: [ push, pull_request ] @@ -11,7 +12,7 @@ jobs: name: "Tests and Analysis (JDK: ${{ matrix.jdk }})" strategy: matrix: - jdk: [ 8, 11, 17, 21 ] + jdk: [ 11, 17, 21 ] runs-on: ubuntu-latest steps: - name: Checkout @@ -49,13 +50,13 @@ jobs: mvn -B install -DskipTests cd $GITHUB_WORKSPACE - name: Run Maven - run: mvn -B install site -Pintegration-tests,code-analysis,bundles + run: mvn -B install site -Pintegration-tests,code-analysis,bundles,jlink platform-integration: name: "Platform Integration (JDK: ${{ matrix.jdk }}, OS: ${{ matrix.os }})" needs: [ tests-and-analysis ] strategy: matrix: - jdk: [ 8, 11, 17, 21 ] + jdk: [ 11, 17, 21 ] os: [ ubuntu-latest, windows-latest, macOS-latest ] runs-on: ${{ matrix.os }} steps: @@ -83,10 +84,10 @@ jobs: run: | git clone -b ${AUTOMATALIB_BRANCH:-develop} --single-branch https://github.com/${AUTOMATALIB_FORK:-LearnLib}/automatalib.git ${HOME}/automatalib-git cd ${HOME}/automatalib-git - mvn -B '-Dmaven.compiler.source=${{ matrix.jdk }}' '-Dmaven.compiler.target=${{ matrix.jdk }}' '-Dautomatalib.targetVersion=${{ matrix.jdk }}' install -DskipTests + mvn -B install -DskipTests cd $GITHUB_WORKSPACE - name: Run Maven - run: mvn -B '-Dmaven.compiler.source=${{ matrix.jdk }}' '-Dmaven.compiler.target=${{ matrix.jdk }}' '-Dlearnlib.targetVersion=${{ matrix.jdk }}' install + run: mvn -B install -Pjlink coverage: name: "Coverage" needs: [ platform-integration ] @@ -98,7 +99,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 8 + java-version: 11 - name: Set up cache uses: actions/cache@v3 with: @@ -149,7 +150,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 8 + java-version: 11 - name: Set up cache uses: actions/cache@v3 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fe3a76279..d1874e4962 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,55 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## [0.17.0-SNAPSHOT] - Unreleased +## [0.18.0-SNAPSHOT] - Unreleased -[Full changelog](https://github.com/LearnLib/learnlib/compare/learnlib-0.16.0...HEAD) +[Full changelog](https://github.com/LearnLib/learnlib/compare/learnlib-0.17.0...HEAD) + +### Added + +* LearnLib now supports JPMS modules. All artifacts now provide a `module-info` descriptor except of the distribution artifacts (for Maven-less environments) which only provide an `Automatic-Module-Name` due to non-modular dependencies. Note that while this is a Java 9+ feature, LearnLib still supports Java 8 byte code for the remaining class files. +* Added the L# active learning algorithm (thanks to [Tiago Ferreira](https://github.com/tiferrei)). +* The `ADTLearner` has been refactored to no longer use the (now-removed) `SymbolQueryOracle` but a new `AdaptiveMembershipOracle` instead which supports answering queries in parallel (thanks to [Leon Vitorovic](https://github.com/leonthalee)). +* The `ADTLearner` can now be parameterized in its counterexample analysis method. +* Added an `InterningMembershipOracle` (including refinements) to the `learnlib-cache` artifact that interns query responses to reduce memory consumption of large data structures. This exports the internal concepts of the DHC learner (which no longer interns query responses automatically). +* `StaticParallelOracleBuilder` now supports custom executor services. + +### Changed + +* The JPMS support introduces several changes: + * You now require at least a JDK 11 to build LearnLib. + * We use modules to better structure the aggregated JavaDoc. Since there exist breaking changes between Java 8 and Java 9 regarding documentation (see package-list vs. element-list), you can no longer link against the LearnLib documentation on JDK 8 builds. + * Split packages had to be refactored. + * The `de.learnlib.oracle.parallelism` interfaces in the `learnlib-api` artifact have been moved to the `de.learnlib.oracle` package. + * The `ThreadSafe` caches have been moved from the `learnlib-parallelism` artifact to the `learnlib-cache` artifact. + * The `GrowingAlphabet` and `Resumable` tests from the `learnlib-learner-it-support` artifact been moved to the `learnlib-test-support` artifact. + * The `OTUtils` class no longer provides the `displayHTMLInBrowser` methods in order to not depend on `java.desktop`. If you relied on this functionality, use the `writeHTMLToFile` methods instead and call `Desktop.getDesktop().open(file.toURI())` yourself. + * The classes in the `learnlib-learning-examples` artifact have their package renamed to `de.learnlib.testsupport.example`. +* The `AbstractVisualizationTest` has been refactored into the `VisualizationUtils` factory. +* Various counters (especially `*Counter*SUL`s) have been streamlined. In most cases there now exists a single counter that tracks multiple properties. +* The `ReuseOracleBuilder` and `ReuseTreeBuilder` classes are now auto-generated and therefore reside in the respective packages of their previously enclosing classes. +* The `TTTLearnerMealy#createTransition` method no longer queries for its transition output directly, but instead requires a call to `initTransitions` now. +* With the removal of the `learnlib-annotation-processor` artifact (see below), the `learnlib-build-config` artifact is now part of the `de.learnlib` group again. +* The `learnlib-datastructure-ot`, `learnlib-datastructure-dt`, `learnlib-datastructure-list`, and `learnlib-datastructure-pta` artifacts have been merged into a new `learnlib-datastructures` artifact. +* The `learnlib-oml` artifact (including its packages and class names) has been renamed to `learnlib-lambda`. +* Switched to [AutomataLib 0.12.0](https://github.com/LearnLib/automatalib/releases/tag/automatalib-0.12.0). + +### Removed + +* The `de.learnlib.tooling:learnlib-annotation-processor` artifact has been dropped. The functionality has been moved to a [standalone project](https://github.com/LearnLib/build-tools). +* The `de.learnlib:learnlib-rpni-edsm` and `de.learnlib:learnlib-rpni-mdl` artifacts have been dropped. The code has been merged with the `de.learnlib:learnlib-rpni` artifact. +* `MQUtil` has been stripped of unused methods. Especially the `query` method can be simulated by the respective oracles themselves. +* `PropertyOracle`s can no longer set a property. This value is now immutable and must be provided during instantiation. Previously, the internal state wasn't updated accordingly if a property was overridden. +* `SymbolQueryOracle`s (and related code such as the respective caches, counters, etc.) have been removed without replacement. Equivalent functionality on the basis of the new `AdaptiveMembershipOracle`s is available instead. + +### Fixed + +* Improved query batching of `TTT` learner (both the regular and visibly push-down version). + + +## [0.17.0] - 2023-11-15 + +[Full changelog](https://github.com/LearnLib/learnlib/compare/learnlib-0.16.0...learnlib-0.17.0) ### Added @@ -29,11 +75,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Some actual re-namings concern * All code concerning visibly push-down automata now uses the "vpa" acronym (previously "vpda"). This includes package names, class names and (Maven) module names. * The "discrimination-tree" learner has been renamed to "observation-pack". This includes classes (`DTLearnerDFA` -> `OPLearnerDFA`, etc.), package names, and Maven modules. The same refactoring happened for the VPA-based version of the learner. - * The `learnlib-acex` Maven module has been merged with the `learnlib-counterexamples` module and the respective interfaces have been move to the `learnlib-api` module. + * The `learnlib-acex` Maven module has been merged with the `learnlib-counterexamples` module. * Classes in the `learnlib-api` have been moved from `de.learnlib.api` to `de.learnlib`. * Refactored the package `de.learnlib.datastructure.pta.pta.*` to `de.learnlib.datastructure.pta.*`. * Refactored the package `de.learnlib.driver.util.*` to `de.learnlib.driver.simulator.*`. - * Moved classes from the package `de.learnlib.mapper.api.*` to `de.learnlib.api.*`. + * Moved classes from the package `de.learnlib.mapper.api.*` to `de.learnlib.sul.*`. * Renamed `PassiveLearnerVariantTICase` to `PassiveLearnerVariantITCase`. * `AbstractTTTHypothesis` has received an additional type parameter for its state type. * `AutomatonOracle#accepts` no longer has a `length` parameter. Provide a correctly sized `input` iterable instead. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..027d4b11cf --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +info(at)learnlib.de. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..2ab8b50da6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# How to contribute to LearnLib + + +### **Did you find a bug?** + +* **If the bug is a security vulnerability** do not open up a GitHub issue but instead refer to our [security policy](https://github.com/LearnLib/learnlib/blob/develop/SECURITY.md). + +* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/LearnLib/learnlib/issues). + +* If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/LearnLib/learnlib/issues/new?template=bug_report.md). Please provide as much relevant information as possible including a precise title. + + +### **Did you write a patch that fixes a bug?** + +* Open a new GitHub pull request with the patch. + +* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. + +* Before submitting, you can test locally whether the integration tests will (likely) pass. Simply enable the same Maven profiles as our [CI pipelines](https://github.com/LearnLib/learnlib/blob/develop/.github/workflows/ci.yml). + + +### **Do you intend to add a new feature or change an existing one?** + +* Suggest your change in the [discussions](https://github.com/LearnLib/learnlib/discussions/categories/ideas) and start writing code. + +* Do not open an issue on GitHub until you have collected positive feedback about the change. GitHub issues are primarily intended for bug reports and confirmed feature requests. + + +### **Do you have questions about the source code?** + +* Ask any questions about how to use LearnLib in the [discussions](https://github.com/LearnLib/learnlib/discussions/categories/q-a). diff --git a/README.md b/README.md index 690a48a8a5..dd2610ed6a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Coverage](https://coveralls.io/repos/github/LearnLib/learnlib/badge.svg?branch=develop)](https://coveralls.io/github/LearnLib/learnlib?branch=develop) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/de.learnlib/learnlib-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/de.learnlib/learnlib-parent) -LearnLib is a free, open source ([Apache License, v2.0][1]) Java library for automata learning algorithms. +LearnLib is a free, open-source ([Apache License, v2.0][1]) Java library for automata learning algorithms. ## About @@ -17,23 +17,23 @@ While certain features have been stripped for improved modularity, development h Currently, the following learning algorithms with respective target models are supported: -Algorithm (active) | Target models || Algorithm (passive) | Models ---- | --- | --- | --- | --- -AAAR | `DFA` `Mealy` `Moore` || OSTIA | `SST` -ADT | `Mealy` || RPNI | `DFA` `Mealy` `Moore` -DHC | `Mealy` || RPNI (EDSM) | `DFA` -Kearns & Vazirani | `DFA` `Mealy` || RPNI (MDL) | `DFA` -L* (incl. variants) | `DFA` `Mealy` `Moore` -NL* | `NFA` -Observation Pack | `DFA` `Mealy` `Moore` `VPA` -OML | `DFA` `Mealy` -Procedural | `SPA` `SBA` `SPMM` -TTT | `DFA` `Mealy` `Moore` `VPA` - +| Algorithm (active) | Target models | | Algorithm (passive) | Models | +|---------------------|-----------------------------|-----|-----------------------|-----------------------| +| AAAR | `DFA` `Mealy` `Moore` | | OSTIA | `SST` | +| ADT | `Mealy` | | RPNI (incl. variants) | `DFA` `Mealy` `Moore` | +| DHC | `Mealy` | | | | +| Kearns & Vazirani | `DFA` `Mealy` | | | | +| Lambda | `DFA` `Mealy` | | | | +| L# | `Mealy` | | | | +| L* (incl. variants) | `DFA` `Mealy` `Moore` | | | | +| NL* | `NFA` | | | | +| Observation Pack | `DFA` `Mealy` `Moore` `VPA` | | | | +| Procedural | `SPA` `SBA` `SPMM` | | | | +| TTT | `DFA` `Mealy` `Moore` `VPA` | | | | Additionally, LearnLib offers a variety of tools to ease the practical application of automata learning on real-world systems. This includes drivers and mappers for interfacing software systems with the LearnLib API as well as caches and parallelization for improving the overall performance of the learning setup. -Also, more nuanced setups such as Black-Box-Checking (via [LTSMin][ltsmin]) or inferring partial machines are possible. +Also, more nuanced setups such as Black-Box-Checking (via [LTSmin][ltsmin]) or inferring partial machines are possible. While we strive to deliver code at a high quality, please note that there exist parts of the library that still need thorough testing. Contributions -- whether it is in the form of new features, better documentation or tests -- are welcome. @@ -42,7 +42,7 @@ Contributions -- whether it is in the form of new features, better documentation For simply using LearnLib you may use the Maven artifacts which are available in the [Maven Central repository][maven-central]. It is also possible to download a bundled [distribution artifact][maven-central-distr] if you want to use LearnLib without Maven support. -Note that LearnLib requires Java 8 or newer. +Note that LearnLib requires Java 11 (or newer) to build but still supports Java 8 at runtime. #### Building development versions @@ -68,14 +68,13 @@ For developing the code base of LearnLib it is suggested to use one of the major * For [IntelliJ IDEA][intellij]: 1. Select `File` -> `New` -> `Project from existing sources` and select the folder containing the development checkout. - 1. Choose "Import Project from external model", select "Maven" and click `Next`. - 1. Configure the project to your liking but make sure to check "Import Maven projects automatically" and have "Generated sources folders" set to "Detect automatically". - 1. Click `Next` until the project is imported (no Maven profile needs to be selected). + 1. Choose "Import Project from external model", select "Maven" and click `Create`. 1. In order to have both development versions of AutomataLib and LearnLib available at once, continue to import AutomataLib as documented in the project's README, but choose `File` -> `New` -> `Module from existing sources` as the first step. * For [Eclipse][eclipse]: 1. **Note**: LearnLib uses annotation processing on several occasions throughout the build process. - This is usually handled correctly by Maven, however, for Eclipse you need to install the [m2e-apt-plugin](https://marketplace.eclipse.org/content/m2e-apt) and activate annotation processing afterward (see the [issue #32](https://github.com/LearnLib/learnlib/issues/32)). + This is usually handled correctly by Maven. + However, for Eclipse, you may need to manually enable annotation processing under `Preferences` -> `Maven` -> `Annotation Processing`. 1. Select `File` -> `Import...` and select "Existing Maven Projects". 1. Select the folder containing the development checkout as the root directory and click `Finish`. 1. In order to have both development versions of AutomataLib and LearnLib available at once, continue to import AutomataLib as documented in the project's README. @@ -83,8 +82,8 @@ For developing the code base of LearnLib it is suggested to use one of the major ## Documentation -* **Maven Project Site:** [latest release](http://learnlib.github.io/learnlib/maven-site/latest/) | [older versions](http://learnlib.github.io/learnlib/maven-site/) -* **API Documentation:** [latest release](http://learnlib.github.io/learnlib/maven-site/latest/apidocs/) | [older versions](http://learnlib.github.io/learnlib/maven-site/) +* **Maven Project Site:** [latest release](https://learnlib.github.io/learnlib/maven-site/latest/) | [older versions](https://learnlib.github.io/learnlib/maven-site/) +* **API Documentation:** [latest release](https://learnlib.github.io/learnlib/maven-site/latest/apidocs/) | [older versions](https://learnlib.github.io/learnlib/maven-site/) ## Questions? @@ -100,19 +99,15 @@ If you have any questions regarding the usage of LearnLib or if you want to disc [1]: http://www.apache.org/licenses/LICENSE-2.0 -[2]: http://www.cs.tu-dortmund.de -[3]: http://www.learnlib.de +[2]: https://cs.tu-dortmund.de +[3]: https://learnlib.de [4]: https://github.com/misberner [5]: https://github.com/fhowar [6]: https://github.com/mtf90 [7]: https://github.com/LearnLib/automatalib -[learnlib-qa]: https://groups.google.com/d/forum/learnlib-qa -[learnlib-discussion]: https://groups.google.com/d/forum/learnlib-discussion -[learnlib-internal]: https://groups.google.com/d/forum/learnlib-internal - -[maven-central]: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.learnlib%22 -[maven-central-distr]: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.learnlib.distribution%22 +[maven-central]: https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.learnlib%22 +[maven-central-distr]: https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.learnlib.distribution%22 [intellij]: https://www.jetbrains.com/idea/ [eclipse]: https://www.eclipse.org/ [ltsmin]: https://ltsmin.utwente.nl/ diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..5e4b442340 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +# Reporting Security Issues + +To report a security issue, please use the GitHub ["Security Advisory"](https://github.com/learnlib/learnlib/security/advisories/new) function. + +The LearnLib team will send a response indicating the next steps in handling your report. After the initial reply to your report, we will keep you informed of the progress towards a fix and may ask for additional information or guidance. diff --git a/algorithms/active/aaar/pom.xml b/algorithms/active/aaar/pom.xml index 24f0d78f65..d0b7dcb4f8 100644 --- a/algorithms/active/aaar/pom.xml +++ b/algorithms/active/aaar/pom.xml @@ -1,7 +1,7 @@ - - com.google.guava - guava - - net.automatalib automata-api @@ -83,22 +78,22 @@ limitations under the License. de.learnlib - learnlib-lstar + learnlib-lambda test de.learnlib - learnlib-membership-oracles + learnlib-lstar test de.learnlib - learnlib-observation-pack + learnlib-membership-oracles test de.learnlib - learnlib-oml + learnlib-observation-pack test diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/AbstractAAARLearner.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/AbstractAAARLearner.java index 06527f7502..6a03993e76 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/AbstractAAARLearner.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/AbstractAAARLearner.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,8 +28,8 @@ import de.learnlib.query.DefaultQuery; import net.automatalib.alphabet.Alphabet; import net.automatalib.alphabet.GrowingAlphabet; -import net.automatalib.alphabet.GrowingMapAlphabet; import net.automatalib.alphabet.SupportsGrowingAlphabet; +import net.automatalib.alphabet.impl.GrowingMapAlphabet; import net.automatalib.automaton.MutableDeterministic; import net.automatalib.automaton.UniversalDeterministicAutomaton; import net.automatalib.word.Word; @@ -116,15 +116,15 @@ public boolean refineHypothesis(DefaultQuery query) { final D outOld = oracle.answerQuery(testOld); final D outNew = oracle.answerQuery(testNew); - if (!Objects.equals(outOld, outNew)) { // add new abstraction + if (Objects.equals(outOld, outNew)) { + prefix = prefix.append(r); + wb.append(r); + } else { // add new abstraction final AI newA = tree.splitLeaf(r, cur, prefix, suffix, outOld); abs.addSymbol(newA); rep.addSymbol(cur); learner.addAlphabetSymbol(cur); return true; - } else { - prefix = prefix.append(r); - wb.append(r); } } @@ -182,8 +182,8 @@ public L getLearner() { return this.learner; } - protected void copyAbstract(UniversalDeterministicAutomaton src, - MutableDeterministic tgt) { + protected void copyAbstract(UniversalDeterministicAutomaton src, + MutableDeterministic tgt) { // states final Map states = new HashMap<>(); final Map statesRev = new HashMap<>(); @@ -200,12 +200,15 @@ protected void copyAbstract(UniversalDeterministicAutomaton e : states.entrySet()) { for (CI r : rep) { - final AbstractAbstractionTree tree = getTreeForRepresentative(r); - final AI a = tree.getAbstractSymbol(r); - tgt.setTransition(e.getKey(), - a, - statesRev.get(src.getSuccessor(e.getValue(), r)), - src.getTransitionProperty(e.getValue(), r)); + final T trans = src.getTransition(e.getValue(), r); + if (trans != null) { + final AbstractAbstractionTree tree = getTreeForRepresentative(r); + final AI a = tree.getAbstractSymbol(r); + tgt.setTransition(e.getKey(), + a, + statesRev.get(src.getSuccessor(trans)), + src.getTransitionProperty(trans)); + } } } } diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/Abstraction.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/Abstraction.java index db295959d8..6db5db6ab6 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/Abstraction.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/Abstraction.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/ExplicitInitialAbstraction.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/ExplicitInitialAbstraction.java index e098173859..fe84baee5a 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/ExplicitInitialAbstraction.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/ExplicitInitialAbstraction.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/TranslatingDFA.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/TranslatingDFA.java index 2344c65da4..88db879495 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/TranslatingDFA.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/TranslatingDFA.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/TranslatingMealyMachine.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/TranslatingMealyMachine.java index 60076126f2..88ee5edd31 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/TranslatingMealyMachine.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/TranslatingMealyMachine.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/TranslatingMooreMachine.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/TranslatingMooreMachine.java index 25f2a3366c..a330c73334 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/TranslatingMooreMachine.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/TranslatingMooreMachine.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/AbstractAbstractionTree.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/AbstractAbstractionTree.java index 354d62ebbb..736144f3f1 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/AbstractAbstractionTree.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/AbstractAbstractionTree.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/ExplicitAbstractionTree.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/ExplicitAbstractionTree.java index fb47397c93..f66941ebd6 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/ExplicitAbstractionTree.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/ExplicitAbstractionTree.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/GenericAbstractionTree.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/GenericAbstractionTree.java index 827ce9d149..dcb6ff4716 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/GenericAbstractionTree.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/GenericAbstractionTree.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/Node.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/Node.java index 33229e55b1..174a6acc37 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/Node.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/abstraction/Node.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/AbstractExplicitAAARLearner.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/AbstractExplicitAAARLearner.java index 1033712c60..7baad77f37 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/AbstractExplicitAAARLearner.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/AbstractExplicitAAARLearner.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,12 +17,12 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.Function; -import com.google.common.collect.Maps; import de.learnlib.algorithm.LearnerConstructor; import de.learnlib.algorithm.LearningAlgorithm; import de.learnlib.algorithm.aaar.AbstractAAARLearner; @@ -32,8 +32,9 @@ import de.learnlib.algorithm.aaar.generic.AbstractGenericAAARLearner; import de.learnlib.oracle.MembershipOracle; import net.automatalib.alphabet.Alphabet; -import net.automatalib.alphabet.Alphabets; import net.automatalib.alphabet.SupportsGrowingAlphabet; +import net.automatalib.alphabet.impl.Alphabets; +import net.automatalib.common.util.HashUtil; /** * An "explicit" refinement of the {@link AbstractAAARLearner}. This implementation requires a prior partition of (all) @@ -83,7 +84,7 @@ public AbstractExplicitAAARLearner(LearnerConstructor learnerConstruct super(learnerConstructor, oracle); this.explicitInitialAbstraction = explicitInitialAbstraction; - this.trees = Maps.newHashMapWithExpectedSize(explicitInitialAbstraction.getInitialAbstracts().size()); + this.trees = new HashMap<>(HashUtil.capacity(explicitInitialAbstraction.getInitialAbstracts().size())); for (AI a : explicitInitialAbstraction.getInitialAbstracts()) { final CI rep = explicitInitialAbstraction.getRepresentative(a); diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerDFA.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerDFA.java index f63ee418a8..bed63410fa 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerDFA.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerDFA.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,8 @@ import de.learnlib.algorithm.aaar.TranslatingDFA; import de.learnlib.oracle.MembershipOracle; import net.automatalib.alphabet.SupportsGrowingAlphabet; -import net.automatalib.automaton.fsa.CompactDFA; import net.automatalib.automaton.fsa.DFA; +import net.automatalib.automaton.fsa.impl.CompactDFA; /** * A {@link DFA}-specific refinement of {@link AbstractExplicitAAARLearner}. diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMealy.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMealy.java index 7a2f447145..f487c36fc2 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMealy.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMealy.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,8 @@ import de.learnlib.algorithm.aaar.TranslatingMealyMachine; import de.learnlib.oracle.MembershipOracle; import net.automatalib.alphabet.SupportsGrowingAlphabet; -import net.automatalib.automaton.transducer.CompactMealy; import net.automatalib.automaton.transducer.MealyMachine; +import net.automatalib.automaton.transducer.impl.CompactMealy; import net.automatalib.word.Word; /** diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMoore.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMoore.java index a66ee4742f..5f29f67b53 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMoore.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMoore.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,8 @@ import de.learnlib.algorithm.aaar.TranslatingMooreMachine; import de.learnlib.oracle.MembershipOracle; import net.automatalib.alphabet.SupportsGrowingAlphabet; -import net.automatalib.automaton.transducer.CompactMoore; import net.automatalib.automaton.transducer.MooreMachine; +import net.automatalib.automaton.transducer.impl.CompactMoore; import net.automatalib.word.Word; /** diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/AbstractGenericAAARLearner.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/AbstractGenericAAARLearner.java index c943c3fd79..a5dfb6bc4b 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/AbstractGenericAAARLearner.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/AbstractGenericAAARLearner.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,8 @@ import de.learnlib.algorithm.aaar.explicit.AbstractExplicitAAARLearner; import de.learnlib.oracle.MembershipOracle; import net.automatalib.alphabet.Alphabet; -import net.automatalib.alphabet.Alphabets; import net.automatalib.alphabet.SupportsGrowingAlphabet; +import net.automatalib.alphabet.impl.Alphabets; /** * A "generic" refinement of the {@link AbstractAAARLearner}. This implementation uses a single diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerDFA.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerDFA.java index 5a41e50015..4024bd5f38 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerDFA.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerDFA.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ import de.learnlib.algorithm.aaar.TranslatingDFA; import de.learnlib.oracle.MembershipOracle; import net.automatalib.alphabet.SupportsGrowingAlphabet; -import net.automatalib.automaton.fsa.CompactDFA; import net.automatalib.automaton.fsa.DFA; +import net.automatalib.automaton.fsa.impl.CompactDFA; /** * A {@link DFA}-specific refinement of {@link AbstractGenericAAARLearner}. diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMealy.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMealy.java index f70e767fc9..10fac4be70 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMealy.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMealy.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ import de.learnlib.algorithm.aaar.TranslatingMealyMachine; import de.learnlib.oracle.MembershipOracle; import net.automatalib.alphabet.SupportsGrowingAlphabet; -import net.automatalib.automaton.transducer.CompactMealy; import net.automatalib.automaton.transducer.MealyMachine; +import net.automatalib.automaton.transducer.impl.CompactMealy; import net.automatalib.word.Word; /** diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMoore.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMoore.java index 11ed0d92af..76999d145f 100644 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMoore.java +++ b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMoore.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ import de.learnlib.algorithm.aaar.TranslatingMooreMachine; import de.learnlib.oracle.MembershipOracle; import net.automatalib.alphabet.SupportsGrowingAlphabet; -import net.automatalib.automaton.transducer.CompactMoore; import net.automatalib.automaton.transducer.MooreMachine; +import net.automatalib.automaton.transducer.impl.CompactMoore; import net.automatalib.word.Word; /** diff --git a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/package-info.java b/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/package-info.java deleted file mode 100644 index 5f67cd7674..0000000000 --- a/algorithms/active/aaar/src/main/java/de/learnlib/algorithm/aaar/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* 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. - */ - -/** - * This package (and sub-packages) provides the implementation of the AAAR learning algorithm as described in the paper - * Automata Learning with Automated Alphabet Abstraction - * Refinement by Falk Howar, Bernhard Steffen, and Maik Merten. - */ -package de.learnlib.algorithm.aaar; diff --git a/algorithms/active/aaar/src/main/java/module-info.java b/algorithms/active/aaar/src/main/java/module-info.java new file mode 100644 index 0000000000..4a83a3bf73 --- /dev/null +++ b/algorithms/active/aaar/src/main/java/module-info.java @@ -0,0 +1,45 @@ +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . + * + * 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. + */ + +/** + * This module provides the implementation of the AAAR learning algorithm as described in the paper + * Automata Learning with Automated Alphabet Abstraction + * Refinement by Falk Howar, Bernhard Steffen, and Maik Merten. + *

+ * This module is provided by the following Maven dependency: + *

+ * <dependency>
+ *   <groupId>de.learnlib</groupId>
+ *   <artifactId>learnlib-aaar</artifactId>
+ *   <version>${version}</version>
+ * </dependency>
+ * 
+ */ +open module de.learnlib.algorithm.aaar { + + requires de.learnlib.api; + requires net.automatalib.api; + requires net.automatalib.common.util; + requires net.automatalib.core; + + // annotations are 'provided'-scoped and do not need to be loaded at runtime + requires static org.checkerframework.checker.qual; + + exports de.learnlib.algorithm.aaar; + exports de.learnlib.algorithm.aaar.abstraction; + exports de.learnlib.algorithm.aaar.explicit; + exports de.learnlib.algorithm.aaar.generic; +} diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/AAARTestUtil.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/AAARTestUtil.java index fdcb4fcee1..9c0a3eae4a 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/AAARTestUtil.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/AAARTestUtil.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,8 @@ import de.learnlib.algorithm.LearningAlgorithm.MooreLearner; import de.learnlib.algorithm.kv.dfa.KearnsVaziraniDFA; import de.learnlib.algorithm.kv.mealy.KearnsVaziraniMealy; +import de.learnlib.algorithm.lambda.ttt.dfa.TTTLambdaDFA; +import de.learnlib.algorithm.lambda.ttt.mealy.TTTLambdaMealy; import de.learnlib.algorithm.lstar.ce.ObservationTableCEXHandlers; import de.learnlib.algorithm.lstar.closing.ClosingStrategies; import de.learnlib.algorithm.lstar.dfa.ClassicLStarDFA; @@ -33,8 +35,6 @@ import de.learnlib.algorithm.observationpack.dfa.OPLearnerDFA; import de.learnlib.algorithm.observationpack.mealy.OPLearnerMealy; import de.learnlib.algorithm.observationpack.moore.OPLearnerMoore; -import de.learnlib.algorithm.oml.ttt.dfa.OptimalTTTDFA; -import de.learnlib.algorithm.oml.ttt.mealy.OptimalTTTMealy; import de.learnlib.algorithm.rivestschapire.RivestSchapireDFA; import de.learnlib.algorithm.rivestschapire.RivestSchapireMealy; import de.learnlib.algorithm.rivestschapire.RivestSchapireMoore; @@ -61,14 +61,14 @@ public static List, I, (alph, mqo) -> new OPLearnerDFA<>(alph, mqo, LocalSuffixFinders.RIVEST_SCHAPIRE, true, true); final ComboConstructor, I, Boolean> ttt = (alph, mqo) -> new TTTLearnerDFA<>(alph, mqo, AcexAnalyzers.BINARY_SEARCH_FWD); - final ComboConstructor, I, Boolean> oml = (alph, mqo) -> new OptimalTTTDFA<>(alph, mqo, mqo); + final ComboConstructor, I, Boolean> lambda = (alph, mqo) -> new TTTLambdaDFA<>(alph, mqo, mqo); return Arrays.asList(Pair.of("L*", lstar), Pair.of("RS", rs), Pair.of("KV", kv), Pair.of("DT", dt), Pair.of("TTT", ttt), - Pair.of("OML", oml)); + Pair.of("TTTLambda", lambda)); } public static List, I, Word>>> getMealyLearners() { @@ -86,15 +86,15 @@ public static List new OPLearnerMealy<>(alph, mqo, LocalSuffixFinders.RIVEST_SCHAPIRE, true); final ComboConstructor, I, Word> ttt = (alph, mqo) -> new TTTLearnerMealy<>(alph, mqo, AcexAnalyzers.BINARY_SEARCH_FWD); - final ComboConstructor, I, Word> oml = - (alph, mqo) -> new OptimalTTTMealy<>(alph, mqo, mqo); + final ComboConstructor, I, Word> lambda = + (alph, mqo) -> new TTTLambdaMealy<>(alph, mqo, mqo); return Arrays.asList(Pair.of("L*", lstar), Pair.of("RS", rs), Pair.of("KV", kv), Pair.of("DT", dt), Pair.of("TTT", ttt), - Pair.of("OML", oml)); + Pair.of("TTTLambda", lambda)); } public static List, I, Word>>> getMooreLearners() { diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/AbstractAAARTest.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/AbstractAAARTest.java index eaea096e20..36c5603c63 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/AbstractAAARTest.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/AbstractAAARTest.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,19 +15,19 @@ */ package de.learnlib.algorithm.aaar; -import java.util.ArrayList; import java.util.HashSet; +import java.util.List; -import com.google.common.collect.Lists; import de.learnlib.algorithm.LearningAlgorithm; -import de.learnlib.example.LearningExample; import de.learnlib.oracle.MembershipOracle; import de.learnlib.oracle.equivalence.SampleSetEQOracle; import de.learnlib.oracle.membership.SimulatorOracle; +import de.learnlib.testsupport.example.LearningExample; import de.learnlib.util.Experiment; import net.automatalib.alphabet.Alphabet; import net.automatalib.automaton.UniversalDeterministicAutomaton; import net.automatalib.automaton.concept.SuffixOutput; +import net.automatalib.common.util.collection.IteratorUtil; import net.automatalib.util.automaton.Automata; import net.automatalib.util.automaton.conformance.WpMethodTestsIterator; import net.automatalib.word.Word; @@ -50,9 +50,9 @@ public AbstractAAARTest(LearningExample learningExample) { public void testAbstractHypothesisEquivalence() { final WpMethodTestsIterator iter = new WpMethodTestsIterator<>(automaton, alphabet); - final ArrayList> testCases = Lists.newArrayList(iter); + final List> testCases = IteratorUtil.list(iter); - final SampleSetEQOracle eqo = new SampleSetEQOracle<>(false); + final SampleSetEQOracle eqo = new SampleSetEQOracle<>(); eqo.addAll(new SimulatorOracle<>(automaton), testCases); final LearningAlgorithm learner = diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/ComboConstructor.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/ComboConstructor.java index 3c0e1cfb31..feaee17135 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/ComboConstructor.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/ComboConstructor.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/TranslatingLearnerWrapper.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/TranslatingLearnerWrapper.java index 61c5f593ab..5872da113f 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/TranslatingLearnerWrapper.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/TranslatingLearnerWrapper.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerDFATest.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerDFATest.java index d7b57396b2..febf4ad153 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerDFATest.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerDFATest.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,8 @@ import de.learnlib.algorithm.aaar.AAARTestUtil; import de.learnlib.algorithm.aaar.AbstractAAARTest; -import de.learnlib.example.dfa.ExamplePaulAndMary; import de.learnlib.oracle.MembershipOracle; +import de.learnlib.testsupport.example.dfa.ExamplePaulAndMary; import net.automatalib.alphabet.Alphabet; import net.automatalib.automaton.fsa.DFA; diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMealyTest.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMealyTest.java index d00f1a1122..7321972eed 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMealyTest.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMealyTest.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,9 +17,9 @@ import de.learnlib.algorithm.aaar.AAARTestUtil; import de.learnlib.algorithm.aaar.AbstractAAARTest; -import de.learnlib.example.mealy.ExampleCoffeeMachine; -import de.learnlib.example.mealy.ExampleCoffeeMachine.Input; import de.learnlib.oracle.MembershipOracle; +import de.learnlib.testsupport.example.mealy.ExampleCoffeeMachine; +import de.learnlib.testsupport.example.mealy.ExampleCoffeeMachine.Input; import net.automatalib.alphabet.Alphabet; import net.automatalib.automaton.transducer.MealyMachine; import net.automatalib.word.Word; diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMooreTest.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMooreTest.java index a5a594b3db..0852ac11bd 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMooreTest.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ExplicitAAARLearnerMooreTest.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,10 +21,10 @@ import de.learnlib.algorithm.aaar.AAARTestUtil; import de.learnlib.algorithm.aaar.AbstractAAARTest; import de.learnlib.algorithm.aaar.ComboConstructor; -import de.learnlib.example.moore.ExampleRandomMoore; import de.learnlib.oracle.MembershipOracle; +import de.learnlib.testsupport.example.moore.ExampleRandomMoore; import net.automatalib.alphabet.Alphabet; -import net.automatalib.alphabet.Alphabets; +import net.automatalib.alphabet.impl.Alphabets; import net.automatalib.automaton.transducer.MooreMachine; import net.automatalib.common.util.Pair; import net.automatalib.word.Word; diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/IdentityInitialAbstraction.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/IdentityInitialAbstraction.java index 4ac1a11d93..8b7d5ec541 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/IdentityInitialAbstraction.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/IdentityInitialAbstraction.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/Incrementor.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/Incrementor.java index d6fe86c9b4..8bed37af76 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/Incrementor.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/Incrementor.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ModuloInitialAbstraction.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ModuloInitialAbstraction.java index 69159af573..428b9ac466 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ModuloInitialAbstraction.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/ModuloInitialAbstraction.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/NoopIncrementor.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/NoopIncrementor.java index 78bef51fca..eb89e459f9 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/NoopIncrementor.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/NoopIncrementor.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerIdentityDFAIT.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerIdentityDFAIT.java index ccd3e75a3a..a453130886 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerIdentityDFAIT.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerIdentityDFAIT.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerIdentityMealyIT.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerIdentityMealyIT.java index 8aa4c6ba70..ad22940c72 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerIdentityMealyIT.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerIdentityMealyIT.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerIdentityMooreIT.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerIdentityMooreIT.java index d27fb25e62..213b4ae843 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerIdentityMooreIT.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerIdentityMooreIT.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerModuloDFAIT.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerModuloDFAIT.java index d97d26a6dc..7054acefdc 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerModuloDFAIT.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerModuloDFAIT.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerModuloMealyIT.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerModuloMealyIT.java index d9d23091ef..e4f04c4db7 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerModuloMealyIT.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerModuloMealyIT.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerModuloMooreIT.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerModuloMooreIT.java index 41d45b365c..84bda9298a 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerModuloMooreIT.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/explicit/it/ExplicitAAARLearnerModuloMooreIT.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerDFATest.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerDFATest.java index b5928cc89e..64b61c06aa 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerDFATest.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerDFATest.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,12 +21,11 @@ import java.io.Writer; import java.util.function.Function; -import com.google.common.io.CharStreams; import de.learnlib.algorithm.aaar.AAARTestUtil; import de.learnlib.algorithm.aaar.AbstractAAARTest; import de.learnlib.algorithm.aaar.abstraction.AbstractAbstractionTree; -import de.learnlib.example.dfa.ExamplePaulAndMary; import de.learnlib.oracle.MembershipOracle; +import de.learnlib.testsupport.example.dfa.ExamplePaulAndMary; import net.automatalib.alphabet.Alphabet; import net.automatalib.automaton.fsa.DFA; import net.automatalib.common.util.IOUtil; @@ -59,7 +58,7 @@ public void testTreeSerialization() throws IOException { try (Reader r = IOUtil.asBufferedUTF8Reader(GenericAAARLearnerDFATest.class.getResourceAsStream("/tree_dfa.dot")); Writer w = new StringWriter()) { - final String expected = CharStreams.toString(r); + final String expected = IOUtil.toString(r); GraphDOT.write((GraphViewable) tree, w); Assert.assertEquals(w.toString(), expected); diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMealyTest.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMealyTest.java index cccfac0f8e..d1f4bfe398 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMealyTest.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMealyTest.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,12 +21,11 @@ import java.io.Writer; import java.util.function.Function; -import com.google.common.io.CharStreams; import de.learnlib.algorithm.aaar.AAARTestUtil; import de.learnlib.algorithm.aaar.AbstractAAARTest; import de.learnlib.algorithm.aaar.abstraction.AbstractAbstractionTree; -import de.learnlib.example.mealy.ExampleGrid; import de.learnlib.oracle.MembershipOracle; +import de.learnlib.testsupport.example.mealy.ExampleGrid; import net.automatalib.alphabet.Alphabet; import net.automatalib.automaton.transducer.MealyMachine; import net.automatalib.common.util.IOUtil; @@ -63,7 +62,7 @@ public void testTreeSerialization() throws IOException { "/tree_mealy.dot")); Writer w = new StringWriter()) { - final String expected = CharStreams.toString(r); + final String expected = IOUtil.toString(r); GraphDOT.write((GraphViewable) tree, w); Assert.assertEquals(w.toString(), expected); diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMooreTest.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMooreTest.java index 1cbd9067ff..c9c45c3a51 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMooreTest.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/GenericAAARLearnerMooreTest.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,16 +22,15 @@ import java.util.Random; import java.util.function.Function; -import com.google.common.io.CharStreams; import de.learnlib.algorithm.LearningAlgorithm.MooreLearner; import de.learnlib.algorithm.aaar.AAARTestUtil; import de.learnlib.algorithm.aaar.AbstractAAARTest; import de.learnlib.algorithm.aaar.ComboConstructor; import de.learnlib.algorithm.aaar.abstraction.AbstractAbstractionTree; -import de.learnlib.example.moore.ExampleRandomMoore; import de.learnlib.oracle.MembershipOracle; +import de.learnlib.testsupport.example.moore.ExampleRandomMoore; import net.automatalib.alphabet.Alphabet; -import net.automatalib.alphabet.Alphabets; +import net.automatalib.alphabet.impl.Alphabets; import net.automatalib.automaton.transducer.MooreMachine; import net.automatalib.common.util.IOUtil; import net.automatalib.common.util.Pair; @@ -76,7 +75,7 @@ public void testTreeSerialization() throws IOException { "/tree_moore.dot")); Writer w = new StringWriter()) { - final String expected = CharStreams.toString(r); + final String expected = IOUtil.toString(r); GraphDOT.write((GraphViewable) tree, w); Assert.assertEquals(w.toString(), expected); diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/it/GenericAAARLearnerDFAIT.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/it/GenericAAARLearnerDFAIT.java index 3030c7c299..b02ede9624 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/it/GenericAAARLearnerDFAIT.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/it/GenericAAARLearnerDFAIT.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/it/GenericAAARLearnerMealyIT.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/it/GenericAAARLearnerMealyIT.java index 209da7c58a..6fe93c698b 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/it/GenericAAARLearnerMealyIT.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/it/GenericAAARLearnerMealyIT.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/it/GenericAAARLearnerMooreIT.java b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/it/GenericAAARLearnerMooreIT.java index b202f060e1..389f648abc 100644 --- a/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/it/GenericAAARLearnerMooreIT.java +++ b/algorithms/active/aaar/src/test/java/de/learnlib/algorithm/aaar/generic/it/GenericAAARLearnerMooreIT.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/pom.xml b/algorithms/active/adt/pom.xml index 2423711603..46f9a658f6 100644 --- a/algorithms/active/adt/pom.xml +++ b/algorithms/active/adt/pom.xml @@ -1,7 +1,7 @@ - - com.google.guava - guava - - net.automatalib automata-api @@ -88,8 +87,8 @@ limitations under the License. - com.github.misberner.buildergen - buildergen + de.learnlib.tooling + annotations @@ -98,6 +97,11 @@ limitations under the License. learnlib-drivers-simulator test + + de.learnlib + learnlib-equivalence-oracles + test + de.learnlib.testsupport learnlib-learner-it-support @@ -111,6 +115,15 @@ limitations under the License. learnlib-membership-oracles test + + de.learnlib + learnlib-statistics + test + + + de.learnlib.testsupport + learnlib-test-support + net.automatalib @@ -123,4 +136,19 @@ limitations under the License. testng + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + @{argLine} --add-reads=de.learnlib.algorithm.adt=de.learnlib.filter.statistic + + + + + diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/ads/DefensiveADS.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/ads/DefensiveADS.java index 4a8e51a290..a4aad5201b 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/ads/DefensiveADS.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/ads/DefensiveADS.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,17 @@ */ package de.learnlib.algorithm.adt.ads; +import java.util.ArrayDeque; import java.util.BitSet; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import java.util.Queue; import java.util.Set; -import java.util.stream.Collectors; import de.learnlib.algorithm.adt.adt.ADTLeafNode; import de.learnlib.algorithm.adt.adt.ADTNode; @@ -33,16 +35,19 @@ import net.automatalib.automaton.concept.StateIDs; import net.automatalib.automaton.transducer.MealyMachine; import net.automatalib.common.smartcollection.ReflexiveMapView; +import net.automatalib.common.util.HashUtil; import net.automatalib.common.util.Pair; +import net.automatalib.util.automaton.ads.ADS; import net.automatalib.util.automaton.ads.ADSUtil; +import net.automatalib.util.automaton.ads.BacktrackingSearch; import net.automatalib.word.Word; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; /** - * A variant of the backtracking ADS search (see {@link net.automatalib.util.automaton.ads.ADS}, {@link - * net.automatalib.util.automaton.ads.BacktrackingSearch}), that works on partially defined automata. It tries to find an - * ADS based on defined transitions and successively closes transitions if no ADS has been found. + * A variant of the backtracking ADS search (see {@link ADS}, {@link BacktrackingSearch}), that works on partially + * defined automata. It tries to find an ADS based on defined transitions and successively closes transitions if no ADS + * has been found. * * @param * (hypothesis) state type @@ -134,7 +139,7 @@ private Optional> compute(Map mapping) { final long maximumSplittingWordLength = ADSUtil.computeMaximumSplittingWordLength(automaton.size(), mapping.size(), this.states.size()); - final Queue> splittingWordCandidates = new LinkedList<>(); + final Queue> splittingWordCandidates = new ArrayDeque<>(); final StateIDs stateIds = automaton.stateIDs(); final Set cache = new HashSet<>(); @@ -144,11 +149,12 @@ private Optional> compute(Map mapping) { @SuppressWarnings("nullness") // false positive https://github.com/typetools/checker-framework/issues/399 final @NonNull Word prefix = splittingWordCandidates.poll(); - final Map currentToInitialMapping = mapping.keySet() - .stream() - .collect(Collectors.toMap(x -> automaton.getSuccessor(x, - prefix), - mapping::get)); + final Map currentToInitialMapping = new LinkedHashMap<>(HashUtil.capacity(mapping.size())); + + for (Entry e : mapping.entrySet()) { + currentToInitialMapping.put(automaton.getSuccessor(e.getKey(), prefix), e.getValue()); + } + final BitSet currentSetAsBitSet = new BitSet(); for (S s : currentToInitialMapping.keySet()) { currentSetAsBitSet.set(stateIds.getStateId(s)); @@ -162,7 +168,7 @@ private Optional> compute(Map mapping) { for (I i : this.alphabet) { //check for missing transitions - final Set statesWithMissingTransitions = new HashSet<>(); + final Set statesWithMissingTransitions = new LinkedHashSet<>(); for (S s : currentToInitialMapping.keySet()) { if (!this.partialTransitionAnalyzer.isTransitionDefined(s, i)) { statesWithMissingTransitions.add(s); @@ -190,11 +196,11 @@ private Optional> compute(Map mapping) { final O nextOutput = automaton.getOutput(current, i); final Map nextMapping; - if (!successors.containsKey(nextOutput)) { + if (successors.containsKey(nextOutput)) { + nextMapping = successors.get(nextOutput); + } else { nextMapping = new HashMap<>(); successors.put(nextOutput, nextMapping); - } else { - nextMapping = successors.get(nextOutput); } // invalid input diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADT.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADT.java index 92bb2f3f51..9890e31f55 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADT.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADT.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,6 @@ import de.learnlib.algorithm.adt.api.LeafSplitter; import de.learnlib.algorithm.adt.config.LeafSplitters; import de.learnlib.algorithm.adt.util.ADTUtil; -import de.learnlib.oracle.SymbolQueryOracle; import net.automatalib.word.Word; /** @@ -74,6 +73,7 @@ public void replaceNode(ADTNode oldNode, ADTNode newNode) { this.root = newNode; } else if (ADTUtil.isResetNode(oldNode)) { final ADTNode endOfPreviousADS = oldNode.getParent(); + assert endOfPreviousADS != null; final O outputToReset = ADTUtil.getOutputForSuccessor(endOfPreviousADS, oldNode); newNode.setParent(endOfPreviousADS); @@ -84,6 +84,8 @@ public void replaceNode(ADTNode oldNode, ADTNode newNode) { assert ADTUtil.isResetNode(oldNodeParent); final ADTNode endOfPreviousADS = oldNodeParent.getParent(); + assert endOfPreviousADS != null; + final O outputToReset = ADTUtil.getOutputForSuccessor(endOfPreviousADS, oldNodeParent); final ADTNode newResetNode = new ADTResetNode<>(newNode); @@ -93,27 +95,6 @@ public void replaceNode(ADTNode oldNode, ADTNode newNode) { } } - /** - * Successively sifts a word through the ADT induced by the given node. Stops when reaching a leaf. - * - * @param word - * the word to sift - * @param subtree - * the node whose subtree is considered - * - * @return the leaf (see {@link ADTNode#sift(SymbolQueryOracle, Word)}) - */ - public ADTNode sift(SymbolQueryOracle oracle, Word word, ADTNode subtree) { - - ADTNode current = subtree; - - while (!ADTUtil.isLeafNode(current)) { - current = current.sift(oracle, word); - } - - return current; - } - /** * Splitting a leaf node by extending the trace leading into the node to split. * @@ -213,30 +194,35 @@ public LCAInfo findLCA(ADTNode s1, ADTNode s2) { final Map, ADTNode> s1ParentsToS1 = new HashMap<>(); ADTNode s1Iter = s1; - ADTNode s2Iter = s2; + ADTNode s1ParentIter = s1.getParent(); - while (s1Iter.getParent() != null) { - s1ParentsToS1.put(s1Iter.getParent(), s1Iter); - s1Iter = s1Iter.getParent(); + while (s1ParentIter != null) { + s1ParentsToS1.put(s1ParentIter, s1Iter); + s1Iter = s1ParentIter; + s1ParentIter = s1ParentIter.getParent(); } final Set> s1Parents = s1ParentsToS1.keySet(); - while (s2Iter.getParent() != null) { + ADTNode s2Iter = s2; + ADTNode s2ParentIter = s2.getParent(); + + while (s2ParentIter != null) { - if (s1Parents.contains(s2Iter.getParent())) { - if (!ADTUtil.isSymbolNode(s2Iter.getParent())) { + if (s1Parents.contains(s2ParentIter)) { + if (!ADTUtil.isSymbolNode(s2ParentIter)) { throw new IllegalStateException("Only Symbol Nodes should be LCAs"); } - final ADTNode lca = s2Iter.getParent(); + final ADTNode lca = s2ParentIter; final O s1Out = ADTUtil.getOutputForSuccessor(lca, s1ParentsToS1.get(lca)); final O s2Out = ADTUtil.getOutputForSuccessor(lca, s2Iter); return new LCAInfo<>(lca, s1Out, s2Out); } - s2Iter = s2Iter.getParent(); + s2Iter = s2ParentIter; + s2ParentIter = s2ParentIter.getParent(); } throw new IllegalStateException("Nodes do not share a parent node"); diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTLeafNode.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTLeafNode.java index 1f284d13b1..8cc6aba567 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTLeafNode.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTLeafNode.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,7 @@ */ package de.learnlib.algorithm.adt.adt; -import de.learnlib.oracle.SymbolQueryOracle; -import net.automatalib.graph.ads.AbstractRecursiveADSLeafNode; -import net.automatalib.word.Word; +import net.automatalib.graph.ads.impl.AbstractRecursiveADSLeafNode; import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -37,11 +35,6 @@ public ADTLeafNode(@Nullable ADTNode parent, @Nullable S hypothesisStat super(parent, hypothesisState); } - @Override - public ADTNode sift(SymbolQueryOracle oracle, Word prefix) { - throw new UnsupportedOperationException("Final nodes cannot sift words"); - } - @Override public NodeType getNodeType() { return NodeType.LEAF_NODE; diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTNode.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTNode.java index 87936c28b3..68f0cfa88e 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTNode.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTNode.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,10 +19,8 @@ import java.util.Map; import de.learnlib.algorithm.adt.util.ADTUtil; -import de.learnlib.oracle.SymbolQueryOracle; import net.automatalib.graph.ads.RecursiveADSNode; import net.automatalib.visualization.VisualizationHelper; -import net.automatalib.word.Word; /** * The ADT equivalent of {@link net.automatalib.graph.ads.ADSNode}. In contrast to regular adaptive distinguishing @@ -38,23 +36,16 @@ public interface ADTNode extends RecursiveADSNode> { /** - * Utility method, that sifts a given word through {@code this} ADTNode. If {@code this} node is a
  • symbol - * node, the symbol is applied to the system under learning and the corresponding child node (based on the observed - * output) is returned. If no matching child node is found, a new leaf node is returned instead
  • reset - * node, the system under learning is reset and the provided prefix is reapplied to the system
  • leaf node, - * an exception is thrown
+ * Convenience method for directly accessing this node's {@link #getChildren() children}. * - * @param oracle - * the oracle used to query the system under learning - * @param prefix - * the prefix to be re-applied after encountering a reset node + * @param output + * the output symbol to determine the child to returned * - * @return the corresponding child node - * - * @throws UnsupportedOperationException - * when invoked on a leaf node (see {@link #getNodeType()}). + * @return the child node that is mapped to given output. May be {@code null}, */ - ADTNode sift(SymbolQueryOracle oracle, Word prefix); + default ADTNode getChild(O output) { + return getChildren().get(output); + } // default methods for graph interface @Override @@ -73,7 +64,7 @@ public boolean getNodeProperties(ADTNode node, Map prop properties.put(NodeAttrs.LABEL, "reset"); } else if (ADTUtil.isLeafNode(node)) { properties.put(NodeAttrs.SHAPE, NodeShapes.BOX); - properties.put(NodeAttrs.LABEL, String.valueOf(node.getHypothesisState())); + properties.put(NodeAttrs.LABEL, String.valueOf(node.getState())); } else { properties.put(NodeAttrs.LABEL, node.toString()); properties.put(NodeAttrs.SHAPE, NodeShapes.OVAL); diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTResetNode.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTResetNode.java index a8b8a5e299..496b35d21c 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTResetNode.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTResetNode.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,6 @@ import java.util.Collections; import java.util.Map; -import de.learnlib.oracle.SymbolQueryOracle; -import net.automatalib.word.Word; -import org.checkerframework.checker.nullness.qual.Nullable; - /** * Reset node implementation. * @@ -42,8 +38,8 @@ public ADTResetNode(ADTNode successor) { } @Override - public @Nullable I getSymbol() { - return null; + public I getSymbol() { + throw new UnsupportedOperationException("Reset nodes do not have a symbol"); } @Override @@ -67,24 +63,13 @@ public Map> getChildren() { } @Override - public @Nullable S getHypothesisState() { - return null; - } - - @Override - public void setHypothesisState(S state) { + public S getState() { throw new UnsupportedOperationException("Reset nodes cannot reference a hypothesis state"); } @Override - public ADTNode sift(SymbolQueryOracle oracle, Word prefix) { - oracle.reset(); - - for (I i : prefix) { - oracle.query(i); - } - - return successor; + public void setState(S state) { + throw new UnsupportedOperationException("Reset nodes cannot reference a hypothesis state"); } @Override diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTSymbolNode.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTSymbolNode.java index 58ba336713..bde173c8f8 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTSymbolNode.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/adt/ADTSymbolNode.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,7 @@ */ package de.learnlib.algorithm.adt.adt; -import de.learnlib.oracle.SymbolQueryOracle; -import net.automatalib.graph.ads.AbstractRecursiveADSSymbolNode; -import net.automatalib.word.Word; +import net.automatalib.graph.ads.impl.AbstractRecursiveADSSymbolNode; import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -37,21 +35,6 @@ public ADTSymbolNode(@Nullable ADTNode parent, I symbol) { super(parent, symbol); } - @Override - public ADTNode sift(SymbolQueryOracle oracle, Word prefix) { - final O o = oracle.query(super.getSymbol()); - - final ADTNode successor = super.getChildren().get(o); - - if (successor == null) { - final ADTNode result = new ADTLeafNode<>(this, null); - super.getChildren().put(o, result); - return result; - } - - return successor; - } - @Override public NodeType getNodeType() { return NodeType.SYMBOL_NODE; diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/ADTExtender.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/ADTExtender.java index dd3dbbdc13..43405be414 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/ADTExtender.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/ADTExtender.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/LeafSplitter.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/LeafSplitter.java index 1d6655fac0..14b7464e63 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/LeafSplitter.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/LeafSplitter.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/PartialTransitionAnalyzer.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/PartialTransitionAnalyzer.java index 0e6e57f443..e84aff12ee 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/PartialTransitionAnalyzer.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/PartialTransitionAnalyzer.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ /** * During the refinement process of the hypothesis, ADS/Ts may be computed on partially defined automata. These - * computations may want to skip undefined transitions (as closing them results in resets, which we want to omit) and + * computations may want to skip undefined transitions (as closing them results in resets which we want to omit) and * only determine the information if necessary. This interface mediates between the learner and ADS/T computations by * defining the basic forms of communication. * diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/SubtreeReplacer.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/SubtreeReplacer.java index db1f5cc978..17bbd3ec27 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/SubtreeReplacer.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/api/SubtreeReplacer.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/automaton/ADTHypothesis.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/automaton/ADTHypothesis.java index 5e5709fd57..a03038b276 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/automaton/ADTHypothesis.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/automaton/ADTHypothesis.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/automaton/ADTState.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/automaton/ADTState.java index d69e201026..b4fe20118c 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/automaton/ADTState.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/automaton/ADTState.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/automaton/ADTTransition.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/automaton/ADTTransition.java index a931e41d64..4d1c9e7709 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/automaton/ADTTransition.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/automaton/ADTTransition.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/ADTExtenders.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/ADTExtenders.java index f808297bd0..0122753544 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/ADTExtenders.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/ADTExtenders.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/LeafSplitters.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/LeafSplitters.java index 1fa5d3453e..b0d71b73f6 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/LeafSplitters.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/LeafSplitters.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -159,12 +159,14 @@ public static ADTNode splitParent(ADTNode nodeToSpli newIter.next(); newSuffixOutput = oldIter.next(); - adsIter = adsIter.getChildren().get(newSuffixOutput); + adsIter = adsIter.getChild(newSuffixOutput); } - final ADTNode continuedADS = new ADTSymbolNode<>(adsIter.getParent(), suffixIter.next()); + final ADTNode parent = adsIter.getParent(); + final ADTNode continuedADS = new ADTSymbolNode<>(parent, suffixIter.next()); - adsIter.getParent().getChildren().put(newSuffixOutput, continuedADS); + assert parent != null; + parent.getChildren().put(newSuffixOutput, continuedADS); return finalizeSplit(nodeToSplit, continuedADS, suffixIter, oldIter, newIter); } diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/SubtreeReplacers.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/SubtreeReplacers.java index 45071c251b..d26c122e42 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/SubtreeReplacers.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/SubtreeReplacers.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/ADSCalculator.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/ADSCalculator.java index 5b42896c1e..f4a4461050 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/ADSCalculator.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/ADSCalculator.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/DefensiveADSCalculator.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/DefensiveADSCalculator.java index 138bd322e1..c8908fcb42 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/DefensiveADSCalculator.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/DefensiveADSCalculator.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/AbstractCalculator.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/AbstractCalculator.java index 7623735890..7c8006c136 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/AbstractCalculator.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/AbstractCalculator.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/BestEffortCalculator.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/BestEffortCalculator.java index 5032a363e4..726260ec8e 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/BestEffortCalculator.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/BestEffortCalculator.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/BestEffortDefensiveCalculator.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/BestEffortDefensiveCalculator.java index 5c25b495e5..c12b374a08 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/BestEffortDefensiveCalculator.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/BestEffortDefensiveCalculator.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/MinLengthCalculator.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/MinLengthCalculator.java index 261449916a..5fdc820984 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/MinLengthCalculator.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/MinLengthCalculator.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/MinSizeCalculator.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/MinSizeCalculator.java index 8a8bc7a1ca..37234961c0 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/MinSizeCalculator.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/calculator/MinSizeCalculator.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/extender/DefaultExtender.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/extender/DefaultExtender.java index 379b7bccab..afcee35aa5 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/extender/DefaultExtender.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/extender/DefaultExtender.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ */ package de.learnlib.algorithm.adt.config.model.extender; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -73,7 +73,7 @@ public ExtensionResult, I, O> computeExtension(ADTHypothes // apply parent trace for (int idx = 0; idx < inputTrace.length(); idx++) { - final Map, ADTState> nextCurrentToInitialMapping = new HashMap<>(); + final Map, ADTState> nextCurrentToInitialMapping = new LinkedHashMap<>(); final I input = inputTrace.getSymbol(idx); final O output = outputTrace.getSymbol(idx); @@ -123,7 +123,7 @@ public ExtensionResult, I, O> computeExtension(ADTHypothes // set the original initial states for (ADTNode, I, O> finalNode : ADTUtil.collectLeaves(extension)) { - finalNode.setHypothesisState(currentToInitialMapping.get(finalNode.getHypothesisState())); + finalNode.setState(currentToInitialMapping.get(finalNode.getState())); } return new ExtensionResult<>(extension); diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/replacer/ExhaustiveReplacer.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/replacer/ExhaustiveReplacer.java index 4c54b99c86..cc37baf79f 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/replacer/ExhaustiveReplacer.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/replacer/ExhaustiveReplacer.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Optional; import java.util.PriorityQueue; import java.util.Set; -import com.google.common.collect.Sets; import de.learnlib.algorithm.adt.adt.ADT; import de.learnlib.algorithm.adt.adt.ADTNode; import de.learnlib.algorithm.adt.api.SubtreeReplacer; @@ -31,6 +31,7 @@ import de.learnlib.algorithm.adt.util.ADTUtil; import net.automatalib.alphabet.Alphabet; import net.automatalib.automaton.transducer.MealyMachine; +import net.automatalib.common.util.HashUtil; public class ExhaustiveReplacer implements SubtreeReplacer { @@ -56,16 +57,15 @@ public Set> computeReplacements(MealyMachin return Collections.singleton(new ReplacementResult<>(adt.getRoot(), potentialResult.get())); } - final Set> candidates = ADTUtil.collectADSNodes(adt.getRoot()); - candidates.remove(adt.getRoot()); + final Set> candidates = ADTUtil.collectADSNodes(adt.getRoot(), false); final PriorityQueue> queue = new PriorityQueue<>(candidates.size(), Comparator.comparingInt(Set::size)); for (ADTNode node : candidates) { final Set> leaves = ADTUtil.collectLeaves(node); - final Set set = Sets.newHashSetWithExpectedSize(leaves.size()); + final Set set = new LinkedHashSet<>(HashUtil.capacity(leaves.size())); for (ADTNode l : leaves) { - set.add(l.getHypothesisState()); + set.add(l.getState()); } queue.add(set); diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/replacer/LevelOrderReplacer.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/replacer/LevelOrderReplacer.java index 45b42500dc..1dcaccd908 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/replacer/LevelOrderReplacer.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/replacer/LevelOrderReplacer.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import de.learnlib.algorithm.adt.util.ADTUtil; import net.automatalib.alphabet.Alphabet; import net.automatalib.automaton.transducer.MealyMachine; +import org.checkerframework.checker.nullness.qual.NonNull; public class LevelOrderReplacer implements SubtreeReplacer { @@ -55,7 +56,8 @@ public Set> computeReplacements(MealyMachin queue.add(adt.getRoot()); while (!queue.isEmpty()) { - final ADTNode node = queue.poll(); + @SuppressWarnings("nullness") // false positive https://github.com/typetools/checker-framework/issues/399 + final @NonNull ADTNode node = queue.poll(); final Set targetStates = ADTUtil.collectHypothesisStates(node); // try to extendLeaf the parent ADS diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/replacer/SingleReplacer.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/replacer/SingleReplacer.java index bb62091afa..4f685f471f 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/replacer/SingleReplacer.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/config/model/replacer/SingleReplacer.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ import java.util.Optional; import java.util.Set; -import com.google.common.collect.Maps; import de.learnlib.algorithm.adt.adt.ADT; import de.learnlib.algorithm.adt.adt.ADTNode; import de.learnlib.algorithm.adt.api.SubtreeReplacer; @@ -34,6 +33,7 @@ import net.automatalib.alphabet.Alphabet; import net.automatalib.automaton.transducer.MealyMachine; import net.automatalib.common.smartcollection.ReflexiveMapView; +import net.automatalib.common.util.Pair; import net.automatalib.word.Word; import org.checkerframework.checker.nullness.qual.Nullable; @@ -50,19 +50,19 @@ public Set> computeReplacements(MealyMachin Alphabet inputs, ADT adt) { - final Set> candidates = ADTUtil.collectADSNodes(adt.getRoot()); - candidates.remove(adt.getRoot()); - final Map, Double> candidatesScore = Maps.toMap(candidates, node -> { - final int resets = 1 + ADTUtil.collectResetNodes(node).size(); - final int finals = ADTUtil.collectLeaves(node).size(); + final Set> candidates = ADTUtil.collectADSNodes(adt.getRoot(), false); - return resets / (double) finals; - }); - - final List> sortedCandidates = new ArrayList<>(candidates); - sortedCandidates.sort(Comparator.comparingDouble(candidatesScore::get)); + // cache scores to prevent expensive recalculations during sorting + final List, Double>> sortedCandidates = new ArrayList<>(candidates.size()); + for (ADTNode candidate : candidates) { + final int resets = 1 + ADTUtil.collectResetNodes(candidate).size(); + final int leaves = ADTUtil.collectLeaves(candidate).size(); + sortedCandidates.add(Pair.of(candidate, resets / (double) leaves)); + } + sortedCandidates.sort(Comparator.comparingDouble(Pair::getSecond)); - for (ADTNode node : sortedCandidates) { + for (Pair, Double> candidate : sortedCandidates) { + final ADTNode node = candidate.getFirst(); final Set targetStates = ADTUtil.collectHypothesisStates(node); // check if we can extendLeaf the parent ADS @@ -110,12 +110,11 @@ public Set> computeReplacements(MealyMachin * * @return a ReplacementResult for the parent (reset) node, if a valid replacement is found. {@code null} otherwise. */ - @Nullable - static ReplacementResult computeParentExtension(MealyMachine hypothesis, - Alphabet inputs, - ADTNode node, - Set targetStates, - ADSCalculator adsCalculator) { + static @Nullable ReplacementResult computeParentExtension(MealyMachine hypothesis, + Alphabet inputs, + ADTNode node, + Set targetStates, + ADSCalculator adsCalculator) { final ADTNode parentReset = node.getParent(); assert ADTUtil.isResetNode(parentReset) : "should not happen"; @@ -148,7 +147,7 @@ static ReplacementResult computeParentExtension(MealyMachine< final ADTNode extension = potentialExtension.get(); for (ADTNode finalNode : ADTUtil.collectLeaves(extension)) { - finalNode.setHypothesisState(currentToInitialMapping.get(finalNode.getHypothesisState())); + finalNode.setState(currentToInitialMapping.get(finalNode.getState())); } return new ReplacementResult<>(parentReset, potentialExtension.get()); diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADSAmbiguityQuery.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADSAmbiguityQuery.java new file mode 100644 index 0000000000..7973dab339 --- /dev/null +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADSAmbiguityQuery.java @@ -0,0 +1,81 @@ +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . + * + * 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.adt.learner; + +import java.util.ArrayDeque; +import java.util.Deque; + +import de.learnlib.algorithm.adt.adt.ADTNode; +import de.learnlib.algorithm.adt.automaton.ADTState; +import net.automatalib.word.Word; + +/** + * Utility class to resolve ADS ambiguities. This query simply tracks the current ADT node for the given inputs. + * + * @param + * input symbol type + * @param + * output symbol type + */ +class ADSAmbiguityQuery extends AbstractAdaptiveQuery { + + private final Word accessSequence; + private final Deque oneShotPrefix; + + private int asIndex; + private boolean inOneShot; + + ADSAmbiguityQuery(Word accessSequence, Word oneShotPrefix, ADTNode, I, O> root) { + super(root); + this.accessSequence = accessSequence; + this.oneShotPrefix = new ArrayDeque<>(oneShotPrefix.asList()); + this.asIndex = 0; + this.inOneShot = false; + } + + @Override + public I getInput() { + if (this.asIndex < this.accessSequence.length()) { + return this.accessSequence.getSymbol(this.asIndex); + } else { + this.inOneShot = !this.oneShotPrefix.isEmpty(); + if (this.inOneShot) { + return oneShotPrefix.poll(); + } else { + return this.currentADTNode.getSymbol(); + } + } + } + + @Override + public Response processOutput(O out) { + if (this.asIndex < this.accessSequence.length()) { + asIndex++; + return Response.SYMBOL; + } else if (this.inOneShot) { + return Response.SYMBOL; + } else { + return super.processOutput(out); + } + } + + @Override + protected void resetProgress() { + this.asIndex = 0; + } +} + + diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADSVerificationQuery.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADSVerificationQuery.java new file mode 100644 index 0000000000..3296ae3081 --- /dev/null +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADSVerificationQuery.java @@ -0,0 +1,107 @@ +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . + * + * 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.adt.learner; + +import java.util.Objects; + +import de.learnlib.algorithm.adt.automaton.ADTState; +import de.learnlib.query.AdaptiveQuery; +import de.learnlib.query.DefaultQuery; +import net.automatalib.word.Word; +import net.automatalib.word.WordBuilder; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Utility class to verify ADSs. This query tracks the current ADT node for the given inputs and compares it with an + * expected output, potentially constructing a counterexample from the observed data. + * + * @param + * input symbol type + * @param + * output symbol type + */ +class ADSVerificationQuery implements AdaptiveQuery { + + private final Word prefix; + private final Word suffix; + private final Word expectedOutput; + private final WordBuilder outputBuilder; + private final ADTState state; + + private final int prefixLength; + private final int suffixLength; + private int idx; + private @Nullable DefaultQuery> counterexample; + + ADSVerificationQuery(Word prefix, Word suffix, Word expectedSuffixOutput, ADTState state) { + this.prefix = prefix; + this.suffix = suffix; + this.expectedOutput = expectedSuffixOutput; + this.outputBuilder = new WordBuilder<>(suffix.size()); + this.state = state; + + this.prefixLength = prefix.length(); + this.suffixLength = suffix.length(); + this.idx = 0; + } + + @Override + public I getInput() { + if (idx < prefixLength) { + return prefix.getSymbol(idx); + } else { + return suffix.getSymbol(idx - prefixLength); + } + } + + @Override + public Response processOutput(O out) { + if (idx < prefixLength) { + idx++; + return Response.SYMBOL; + } else { + outputBuilder.append(out); + + if (!Objects.equals(out, expectedOutput.getSymbol(idx - prefixLength))) { + counterexample = + new DefaultQuery<>(prefix, suffix.prefix(outputBuilder.size()), outputBuilder.toWord()); + return Response.FINISHED; + } else if (outputBuilder.size() < suffixLength) { + idx++; + return Response.SYMBOL; + } else { + return Response.FINISHED; + } + } + } + + @Nullable + DefaultQuery> getCounterexample() { + return counterexample; + } + + ADTState getState() { + return state; + } + + Word getSuffix() { + return suffix; + } + + Word getExpectedOutput() { + return expectedOutput; + } +} diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADTAdaptiveQuery.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADTAdaptiveQuery.java new file mode 100644 index 0000000000..a871997849 --- /dev/null +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADTAdaptiveQuery.java @@ -0,0 +1,94 @@ +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . + * + * 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.adt.learner; + +import de.learnlib.algorithm.adt.adt.ADTNode; +import de.learnlib.algorithm.adt.automaton.ADTState; +import de.learnlib.algorithm.adt.automaton.ADTTransition; +import de.learnlib.algorithm.adt.util.ADTUtil; +import net.automatalib.word.Word; + +/** + * Utility class to close transitions. This query simply tracks the current ADT node for the access sequence of the + * given transition and sets its output if the respective input symbol is traversed. + * + * @param + * input symbol type + * @param + * output symbol type + */ +class ADTAdaptiveQuery extends AbstractAdaptiveQuery { + + private final ADTTransition transition; + private final Word accessSequence; + + private int asIndex; + + ADTAdaptiveQuery(ADTTransition transition, ADTNode, I, O> root) { + super(root); + this.transition = transition; + this.accessSequence = transition.getSource().getAccessSequence(); + this.asIndex = 0; + } + + @Override + public I getInput() { + if (this.asIndex <= this.accessSequence.length()) { + + if (asIndex == this.accessSequence.length()) { + return transition.getInput(); + } + + return this.accessSequence.getSymbol(this.asIndex); + } else { + return super.currentADTNode.getSymbol(); + } + } + + @Override + public Response processOutput(O out) { + if (this.asIndex <= this.accessSequence.length()) { + if (this.asIndex == this.accessSequence.length()) { + this.transition.setOutput(out); + } + + // if the ADT only consists of a leaf, we just set the transition output + if (ADTUtil.isLeafNode(super.currentADTNode)) { + return Response.FINISHED; + } + + asIndex++; + return Response.SYMBOL; + } else { + return super.processOutput(out); + } + } + + @Override + protected void resetProgress() { + this.asIndex = 0; + } + + ADTTransition getTransition() { + return transition; + } + + Word getAccessSequence() { + return this.accessSequence; + } +} + + diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADTLearner.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADTLearner.java index ed662d31d5..ec2522848e 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADTLearner.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADTLearner.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,18 +17,18 @@ import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; -import java.util.Iterator; +import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Optional; +import java.util.Map.Entry; import java.util.Queue; import java.util.Set; -import java.util.stream.Collectors; -import com.github.misberner.buildergen.annotations.GenerateBuilder; import de.learnlib.Resumable; import de.learnlib.algorithm.LearningAlgorithm; import de.learnlib.algorithm.adt.adt.ADT; @@ -50,19 +50,21 @@ import de.learnlib.algorithm.adt.model.ObservationTree; import de.learnlib.algorithm.adt.model.ReplacementResult; import de.learnlib.algorithm.adt.util.ADTUtil; -import de.learnlib.algorithm.adt.util.SQOOTBridge; +import de.learnlib.counterexample.LocalSuffixFinder; import de.learnlib.counterexample.LocalSuffixFinders; import de.learnlib.logging.Category; -import de.learnlib.oracle.SymbolQueryOracle; +import de.learnlib.oracle.AdaptiveMembershipOracle; +import de.learnlib.oracle.MembershipOracle.MealyMembershipOracle; import de.learnlib.query.DefaultQuery; +import de.learnlib.tooling.annotation.builder.GenerateBuilder; import de.learnlib.util.MQUtil; import net.automatalib.alphabet.Alphabet; -import net.automatalib.alphabet.Alphabets; import net.automatalib.alphabet.SupportsGrowingAlphabet; import net.automatalib.automaton.transducer.MealyMachine; +import net.automatalib.common.util.HashUtil; import net.automatalib.common.util.Pair; import net.automatalib.word.Word; -import net.automatalib.word.WordBuilder; +import org.checkerframework.checker.nullness.qual.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,9 +72,9 @@ * The main learning algorithm. * * @param - * input alphabet type + * input symbol type * @param - * output alphabet type + * output symbol type */ public class ADTLearner implements LearningAlgorithm.MealyLearner, PartialTransitionAnalyzer, I>, @@ -82,7 +84,8 @@ public class ADTLearner implements LearningAlgorithm.MealyLearner, private static final Logger LOGGER = LoggerFactory.getLogger(ADTLearner.class); private final Alphabet alphabet; - private final SQOOTBridge oracle; + private final AdaptiveMembershipOracle oracle; + private final MealyMembershipOracle mqo; private final LeafSplitter leafSplitter; private final ADTExtender adtExtender; private final SubtreeReplacer subtreeReplacer; @@ -90,32 +93,36 @@ public class ADTLearner implements LearningAlgorithm.MealyLearner, private final Queue>> openCounterExamples; private final Set>> allCounterExamples; private final ObservationTree, I, O> observationTree; + private final LocalSuffixFinder> suffixFinder; private ADTHypothesis hypothesis; private ADT, I, O> adt; public ADTLearner(Alphabet alphabet, - SymbolQueryOracle oracle, + AdaptiveMembershipOracle oracle, LeafSplitter leafSplitter, ADTExtender adtExtender, SubtreeReplacer subtreeReplacer) { - this(alphabet, oracle, leafSplitter, adtExtender, subtreeReplacer, true); + this(alphabet, oracle, leafSplitter, adtExtender, subtreeReplacer, true, LocalSuffixFinders.RIVEST_SCHAPIRE); } @GenerateBuilder(defaults = BuilderDefaults.class) public ADTLearner(Alphabet alphabet, - SymbolQueryOracle oracle, + AdaptiveMembershipOracle oracle, LeafSplitter leafSplitter, ADTExtender adtExtender, SubtreeReplacer subtreeReplacer, - boolean useObservationTree) { + boolean useObservationTree, + LocalSuffixFinder> suffixFinder) { this.alphabet = alphabet; - this.observationTree = new ObservationTree<>(this.alphabet); - this.oracle = new SQOOTBridge<>(this.observationTree, oracle, useObservationTree); + this.observationTree = new ObservationTree<>(this.alphabet, oracle, useObservationTree); + this.oracle = this.observationTree; + this.mqo = new Adaptive2MembershipWrapper<>(oracle); this.leafSplitter = leafSplitter; this.adtExtender = adtExtender; this.subtreeReplacer = subtreeReplacer; + this.suffixFinder = suffixFinder; this.hypothesis = new ADTHypothesis<>(this.alphabet); this.openTransitions = new ArrayDeque<>(); @@ -130,7 +137,6 @@ public void startLearning() { final ADTState initialState = this.hypothesis.addInitialState(); initialState.setAccessSequence(Word.epsilon()); this.observationTree.initialize(initialState); - this.oracle.initialize(); this.adt.initialize(initialState); for (I i : this.alphabet) { @@ -156,15 +162,19 @@ public boolean refineHypothesis(DefaultQuery> ce) { // normal refinement step while (!this.openCounterExamples.isEmpty()) { - final DefaultQuery> currentCE = this.openCounterExamples.poll(); + @SuppressWarnings("nullness") + // false positive https://github.com/typetools/checker-framework/issues/399 + final @NonNull DefaultQuery> currentCE = this.openCounterExamples.poll(); this.allCounterExamples.add(currentCE); - while (this.refineHypothesisInternal(currentCE)) {} + while (this.refineHypothesisInternal(currentCE)) { + // refine exhaustively + } } // subtree replacements may reactivate old CEs for (DefaultQuery> oldCE : this.allCounterExamples) { - if (!this.hypothesis.computeOutput(oldCE.getInput()).equals(oldCE.getOutput())) { + if (MQUtil.isCounterexample(oldCE, this.hypothesis)) { this.openCounterExamples.add(oldCE); } } @@ -182,10 +192,7 @@ public boolean refineHypothesisInternal(DefaultQuery> ceQuery) { } // Determine a counterexample decomposition (u, a, v) - final int suffixIdx = LocalSuffixFinders.RIVEST_SCHAPIRE.findSuffixIndex(ceQuery, - this.hypothesis, - this.hypothesis, - this.oracle); + final int suffixIdx = suffixFinder.findSuffixIndex(ceQuery, this.hypothesis, this.hypothesis, this.mqo); if (suffixIdx == -1) { throw new IllegalStateException(); @@ -216,12 +223,7 @@ public boolean refineHypothesisInternal(DefaultQuery> ceQuery) { oldTrans.setTarget(newState); oldTrans.setIsSpanningTreeEdge(true); - final Set, I, O>> finalNodes = ADTUtil.collectLeaves(this.adt.getRoot()); - final ADTNode, I, O> nodeToSplit = finalNodes.stream() - .filter(n -> uaState.equals(n.getHypothesisState())) - .findFirst() - .orElseThrow(IllegalStateException::new); - + final ADTNode, I, O> nodeToSplit = findNodeForState(uaState); final ADTNode, I, O> newNode; // directly insert into observation tree, because we use it for finding a splitter @@ -229,22 +231,17 @@ public boolean refineHypothesisInternal(DefaultQuery> ceQuery) { this.observationTree.addTrace(newState, nodeToSplit); final Word previousTrace = ADTUtil.buildTraceForNode(nodeToSplit).getFirst(); - final Optional> extension = this.observationTree.findSeparatingWord(uaState, newState, previousTrace); - - if (extension.isPresent()) { - final Word completeSplitter = previousTrace.concat(extension.get()); - final Word oldOutput = this.observationTree.trace(uaState, completeSplitter); - final Word newOutput = this.observationTree.trace(newState, completeSplitter); + final Word extension = this.observationTree.findSeparatingWord(uaState, newState, previousTrace); - newNode = this.adt.extendLeaf(nodeToSplit, completeSplitter, oldOutput, newOutput, this.leafSplitter); - } else { + if (extension == null) { // directly insert into observation tree, because we use it for finding a splitter - this.observationTree.addTrace(uaState, v, this.oracle.answerQuery(uaAccessSequence, v)); - this.observationTree.addTrace(newState, v, this.oracle.answerQuery(uAccessSequenceWithA, v)); + this.observationTree.addTrace(uaState, v, this.mqo.answerQuery(uaAccessSequence, v)); + this.observationTree.addTrace(newState, v, this.mqo.answerQuery(uAccessSequenceWithA, v)); // in doubt, we will always find v final Word otSepWord = this.observationTree.findSeparatingWord(uaState, newState); final Word splitter; + assert otSepWord != null; if (otSepWord.length() < v.length()) { splitter = otSepWord; @@ -256,38 +253,60 @@ public boolean refineHypothesisInternal(DefaultQuery> ceQuery) { final Word newOutput = this.observationTree.trace(newState, splitter); newNode = this.adt.splitLeaf(nodeToSplit, splitter, oldOutput, newOutput, this.leafSplitter); + } else { + final Word completeSplitter = previousTrace.concat(extension); + final Word oldOutput = this.observationTree.trace(uaState, completeSplitter); + final Word newOutput = this.observationTree.trace(newState, completeSplitter); + + newNode = this.adt.extendLeaf(nodeToSplit, completeSplitter, oldOutput, newOutput, this.leafSplitter); } - newNode.setHypothesisState(newState); + newNode.setState(newState); final ADTNode, I, O> temporarySplitter = ADTUtil.getStartOfADS(nodeToSplit); - final List> newTransitions = alphabet.stream() - .map(i -> this.hypothesis.createOpenTransition(newState, - i, - this.adt.getRoot())) - .collect(Collectors.toList()); - final List> transitionsToRefine = nodeToSplit.getHypothesisState() - .getIncomingTransitions() - .stream() - .filter(x -> !x.isSpanningTreeEdge()) - .collect(Collectors.toList()); - - transitionsToRefine.forEach(x -> { + final List> newTransitions = new ArrayList<>(alphabet.size()); + + for (I i : alphabet) { + newTransitions.add(this.hypothesis.createOpenTransition(newState, i, this.adt.getRoot())); + } + + final List> transitionsToRefine = getIncomingNonSpanningTreeTransitions(uaState); + + for (ADTTransition x : transitionsToRefine) { x.setTarget(null); x.setSiftNode(temporarySplitter); - }); + } final ADTNode, I, O> finalizedSplitter = this.evaluateAdtExtension(temporarySplitter); - transitionsToRefine.stream().filter(ADTTransition::needsSifting).forEach(x -> { - x.setSiftNode(finalizedSplitter); - this.openTransitions.add(x); - }); - newTransitions.stream().filter(ADTTransition::needsSifting).forEach(this.openTransitions::add); + for (ADTTransition t : transitionsToRefine) { + if (t.needsSifting()) { + t.setSiftNode(finalizedSplitter); + this.openTransitions.add(t); + } + } + + for (ADTTransition t : newTransitions) { + if (t.needsSifting()) { + this.openTransitions.add(t); + } + } this.closeTransitions(); return true; } + private ADTNode, I, O> findNodeForState(ADTState state) { + + for (ADTNode, I, O> leaf : ADTUtil.collectLeaves(this.adt.getRoot())) { + if (leaf.getState().equals(state)) { + return leaf; + } + } + + throw new IllegalStateException("Cannot find leaf for state " + state); + + } + @Override public MealyMachine getHypothesisModel() { return this.hypothesis; @@ -298,58 +317,23 @@ public boolean refineHypothesisInternal(DefaultQuery> ceQuery) { */ private void closeTransitions() { while (!this.openTransitions.isEmpty()) { - this.closeTransition(this.openTransitions.poll()); - } - } - - /** - * Close the given transitions by means of sifting the associated long prefix through the ADT. - * - * @param transition - * the transition to close - */ - private void closeTransition(ADTTransition transition) { - - if (!transition.needsSifting()) { - return; - } - - final Word accessSequence = transition.getSource().getAccessSequence(); - final I symbol = transition.getInput(); - - this.oracle.reset(); - for (I i : accessSequence) { - this.oracle.query(i); - } - - transition.setOutput(this.oracle.query(symbol)); - - final Word longPrefix = accessSequence.append(symbol); - final ADTNode, I, O> finalNode = - this.adt.sift(this.oracle, longPrefix, transition.getSiftNode()); - - assert ADTUtil.isLeafNode(finalNode); - final ADTState targetState; + final Collection> queries = new ArrayList<>(this.openTransitions.size()); - // new state discovered while sifting - if (finalNode.getHypothesisState() == null) { - targetState = this.hypothesis.addState(); - targetState.setAccessSequence(longPrefix); - - finalNode.setHypothesisState(targetState); - transition.setIsSpanningTreeEdge(true); + //create a query object for every transition + for (ADTTransition transition : this.openTransitions) { + if (transition.needsSifting()) { + queries.add(new ADTAdaptiveQuery<>(transition, transition.getSiftNode())); + } + } - this.observationTree.addState(targetState, longPrefix, transition.getOutput()); + this.openTransitions.clear(); + this.oracle.processQueries(queries); - for (I i : this.alphabet) { - this.openTransitions.add(this.hypothesis.createOpenTransition(targetState, i, this.adt.getRoot())); + for (ADTAdaptiveQuery query : queries) { + processAnsweredQuery(query); } - } else { - targetState = finalNode.getHypothesisState(); } - - transition.setTarget(targetState); } @Override @@ -362,7 +346,9 @@ public void closeTransition(ADTState state, I input) { final ADTNode, I, O> ads = transition.getSiftNode(); final int oldNumberOfFinalStates = ADTUtil.collectLeaves(ads).size(); - this.closeTransition(transition); + final ADTAdaptiveQuery query = new ADTAdaptiveQuery<>(transition, transition.getSiftNode()); + this.oracle.processQueries(Collections.singleton(query)); + processAnsweredQuery(query); final int newNumberOfFinalStates = ADTUtil.collectLeaves(ads).size(); @@ -372,6 +358,50 @@ public void closeTransition(ADTState state, I input) { } } + private void processAnsweredQuery(ADTAdaptiveQuery query) { + if (query.needsPostProcessing()) { + final ADTNode, I, O> parent = query.getCurrentADTNode(); + final O out = query.getTempOut(); + final ADTNode, I, O> succ = parent.getChild(out); + + // first time we process the successor + if (succ == null) { + // add new state to the hypothesis and set the accessSequence + final ADTState newState = this.hypothesis.addState(); + final Word longPrefix = query.getAccessSequence().append(query.getTransition().getInput()); + newState.setAccessSequence(longPrefix); + + // configure the transition + final ADTTransition transition = query.getTransition(); + transition.setTarget(newState); + transition.setIsSpanningTreeEdge(true); + + // add new leaf node to ADT + final ADTNode, I, O> result = new ADTLeafNode<>(parent, newState); + parent.getChildren().put(out, result); + + // add the observations to the observation tree + O transitionOutput = query.getTransition().getOutput(); + this.observationTree.addState(newState, longPrefix, transitionOutput); + + // query successors + for (I i : this.alphabet) { + this.openTransitions.add(this.hypothesis.createOpenTransition(newState, i, this.adt.getRoot())); + } + } else { + assert ADTUtil.isLeafNode(succ); + // state has been created before, just update target + query.getTransition().setTarget(succ.getState()); + } + } else { + // update target + final ADTTransition transition = query.getTransition(); + final ADTNode, I, O> adtNode = query.getCurrentADTNode(); + assert ADTUtil.isLeafNode(adtNode); + transition.setTarget(adtNode.getState()); + } + } + @Override public boolean isTransitionDefined(ADTState state, I input) { final ADTTransition transition = this.hypothesis.getTransition(state, input); @@ -383,11 +413,11 @@ public boolean isTransitionDefined(ADTState state, I input) { public void addAlphabetSymbol(I symbol) { if (!this.alphabet.containsSymbol(symbol)) { - Alphabets.toGrowingAlphabetOrThrowException(this.alphabet).addSymbol(symbol); + this.alphabet.asGrowingAlphabetOrThrowException().addSymbol(symbol); } this.hypothesis.addAlphabetSymbol(symbol); - this.observationTree.getObservationTree().addAlphabetSymbol(symbol); + this.observationTree.addAlphabetSymbol(symbol); // check if we already have information about the symbol (then the transition is defined) so we don't post // redundant queries @@ -424,7 +454,6 @@ public void resume(ADTLearnerState, I, O> state) { this.observationTree.initialize(this.hypothesis.getStates(), ADTState::getAccessSequence, this.hypothesis::computeOutput); - this.oracle.initialize(); } } @@ -437,7 +466,7 @@ public void resume(ADTLearnerState, I, O> state) { */ private void ensureConsistency(ADTNode, I, O> leaf) { - final ADTState state = leaf.getHypothesisState(); + final ADTState state = leaf.getState(); final Word as = state.getAccessSequence(); final Word asOut = this.hypothesis.computeOutput(as); @@ -482,7 +511,8 @@ private ADTNode, I, O> evaluateAdtExtension(ADTNode, I, O> extension = potentialExtension.getReplacement(); final ADTNode, I, O> nodeToReplace = ads.getParent(); // reset node - assert this.validateADS(nodeToReplace, extension, Collections.emptySet()); + assert extension != null && nodeToReplace != null && + this.validateADS(nodeToReplace, extension, Collections.emptySet()); final ADTNode, I, O> replacement = this.verifyADS(nodeToReplace, extension, @@ -581,7 +611,7 @@ private boolean validateADS(ADTNode, I, O> oldADS, if (ADTUtil.isResetNode(oldADS)) { oldNodes = ADTUtil.collectResetNodes(this.adt.getRoot()); } else { - oldNodes = ADTUtil.collectADSNodes(this.adt.getRoot()); + oldNodes = ADTUtil.collectADSNodes(this.adt.getRoot(), true); } if (!oldNodes.contains(oldADS)) { @@ -589,10 +619,15 @@ private boolean validateADS(ADTNode, I, O> oldADS, } final Set, I, O>> newFinalNodes = ADTUtil.collectLeaves(newADS); + final Map, Pair, Word>> traces = + new HashMap<>(HashUtil.capacity(newFinalNodes.size())); + + for (ADTNode, I, O> n : newFinalNodes) { + traces.put(n.getState(), ADTUtil.buildTraceForNode(n)); + } final Set> oldFinalStates = ADTUtil.collectHypothesisStates(oldADS); - final Set> newFinalStates = - newFinalNodes.stream().map(ADTNode::getHypothesisState).collect(Collectors.toSet()); + final Set> newFinalStates = new HashSet<>(traces.keySet()); newFinalStates.addAll(cutout); if (!oldFinalStates.equals(newFinalStates)) { @@ -600,9 +635,6 @@ private boolean validateADS(ADTNode, I, O> oldADS, } final Word parentInputTrace = ADTUtil.buildTraceForNode(oldADS).getFirst(); - final Map, Pair, Word>> traces = newFinalNodes.stream() - .collect(Collectors.toMap(ADTNode::getHypothesisState, - ADTUtil::buildTraceForNode)); for (Map.Entry, Pair, Word>> entry : traces.entrySet()) { @@ -643,65 +675,47 @@ private ADTNode, I, O> verifyADS(ADTNode, I, O> no ADTNode, I, O> replacement, Set, I, O>> cachedLeaves, Set> cutout) { + final Set, I, O>> leaves = ADTUtil.collectLeaves(replacement); + final Map, Pair, Word>> traces = + new LinkedHashMap<>(HashUtil.capacity(leaves.size())); - final Map, Pair, Word>> traces = new LinkedHashMap<>(); - ADTUtil.collectLeaves(replacement) - .forEach(x -> traces.put(x.getHypothesisState(), ADTUtil.buildTraceForNode(x))); + for (ADTNode, I, O> leaf : leaves) { + traces.put(leaf.getState(), ADTUtil.buildTraceForNode(leaf)); + } final Pair, Word> parentTrace = ADTUtil.buildTraceForNode(nodeToReplace); - final Word parentInput = parentTrace.getFirst(); - final Word parentOutput = parentTrace.getSecond(); ADTNode, I, O> result = null; - // validate - for (Map.Entry, Pair, Word>> entry : traces.entrySet()) { - final ADTState state = entry.getKey(); - final Word accessSequence = state.getAccessSequence(); - - this.oracle.reset(); - accessSequence.forEach(this.oracle::query); - parentInput.forEach(this.oracle::query); + final List> queries = new ArrayList<>(traces.size()); - final Word adsInput = entry.getValue().getFirst(); - final Word adsOutput = entry.getValue().getSecond(); - - final WordBuilder inputWb = new WordBuilder<>(adsInput.size()); - final WordBuilder outputWb = new WordBuilder<>(adsInput.size()); - - final Iterator inputIter = adsInput.iterator(); - final Iterator outputIter = adsOutput.iterator(); - - boolean equal = true; - while (equal && inputIter.hasNext()) { - final I in = inputIter.next(); - final O realOut = this.oracle.query(in); - final O expectedOut = outputIter.next(); - - inputWb.append(in); - outputWb.append(realOut); + for (Entry, Pair, Word>> e : traces.entrySet()) { + final ADTState state = e.getKey(); + final Pair, Word> ads = e.getValue(); + queries.add(new ADSVerificationQuery<>(state.getAccessSequence().concat(parentTrace.getFirst()), + ads.getFirst(), + ads.getSecond(), + state)); + } - if (!expectedOut.equals(realOut)) { - equal = false; - } - } + this.oracle.processQueries(queries); - final Word traceInput = inputWb.toWord(); - final Word traceOutput = outputWb.toWord(); + for (ADSVerificationQuery query : queries) { + final ADTNode, I, O> trace; + final DefaultQuery> ce = query.getCounterexample(); - if (!equal) { - this.openCounterExamples.add(new DefaultQuery<>(accessSequence.concat(parentInput, traceInput), - this.hypothesis.computeOutput(state.getAccessSequence()) - .concat(parentOutput, traceOutput))); + if (ce != null) { + this.openCounterExamples.add(ce); + trace = ADTUtil.buildADSFromObservation(ce.getSuffix(), ce.getOutput(), query.getState()); + } else { + trace = ADTUtil.buildADSFromObservation(query.getSuffix(), query.getExpectedOutput(), query.getState()); } - final ADTNode, I, O> trace = ADTUtil.buildADSFromObservation(traceInput, traceOutput, state); - if (result == null) { result = trace; } else { if (!ADTUtil.mergeADS(result, trace)) { - this.resolveAmbiguities(nodeToReplace, result, state, cachedLeaves); + this.resolveAmbiguities(nodeToReplace, result, query.getState(), cachedLeaves); } } } @@ -733,39 +747,25 @@ private void resolveAmbiguities(ADTNode, I, O> nodeToReplace, Set, I, O>> cachedLeaves) { final Pair, Word> parentTrace = ADTUtil.buildTraceForNode(nodeToReplace); - final Word parentInput = parentTrace.getFirst(); - final Word effectiveAccessSequence = state.getAccessSequence().concat(parentInput); - - this.oracle.reset(); - effectiveAccessSequence.forEach(this.oracle::query); + final ADSAmbiguityQuery query = + new ADSAmbiguityQuery<>(state.getAccessSequence(), parentTrace.getFirst(), newADS); - ADTNode, I, O> iter = newADS; - while (!ADTUtil.isLeafNode(iter)) { + this.oracle.processQuery(query); - if (ADTUtil.isResetNode(iter)) { - this.oracle.reset(); - state.getAccessSequence().forEach(this.oracle::query); - iter = iter.getChildren().values().iterator().next(); - } else { - final O output = this.oracle.query(iter.getSymbol()); - final ADTNode, I, O> succ = iter.getChildren().get(output); - - if (succ == null) { - final ADTNode, I, O> newFinal = new ADTLeafNode<>(iter, state); - iter.getChildren().put(output, newFinal); - return; - } - - iter = succ; - } + if (query.needsPostProcessing()) { + final ADTNode, I, O> prev = query.getCurrentADTNode(); + final ADTNode, I, O> newFinal = new ADTLeafNode<>(prev, state); + prev.getChildren().put(query.getTempOut(), newFinal); + return; } + final ADTNode, I, O> finalNode = query.getCurrentADTNode(); ADTNode, I, O> oldReference = null, newReference = null; + for (ADTNode, I, O> leaf : cachedLeaves) { - final ADTState hypState = leaf.getHypothesisState(); - assert hypState != null; + final ADTState hypState = leaf.getState(); - if (hypState.equals(iter.getHypothesisState())) { + if (hypState.equals(finalNode.getState())) { oldReference = leaf; } else if (hypState.equals(state)) { newReference = leaf; @@ -776,6 +776,7 @@ private void resolveAmbiguities(ADTNode, I, O> nodeToReplace, } } + assert oldReference != null && newReference != null; final LCAInfo, I, O> lcaResult = this.adt.findLCA(oldReference, newReference); final ADTNode, I, O> lca = lcaResult.adtNode; final Pair, Word> lcaTrace = ADTUtil.buildTraceForNode(lca); @@ -785,7 +786,7 @@ private void resolveAmbiguities(ADTNode, I, O> nodeToReplace, final Word newOutputTrace = lcaTrace.getSecond().append(lcaResult.secondOutput); final ADTNode, I, O> oldTrace = - ADTUtil.buildADSFromObservation(sepWord, oldOutputTrace, iter.getHypothesisState()); + ADTUtil.buildADSFromObservation(sepWord, oldOutputTrace, finalNode.getState()); final ADTNode, I, O> newTrace = ADTUtil.buildADSFromObservation(sepWord, newOutputTrace, state); if (!ADTUtil.mergeADS(oldTrace, newTrace)) { @@ -793,9 +794,9 @@ private void resolveAmbiguities(ADTNode, I, O> nodeToReplace, } final ADTNode, I, O> reset = new ADTResetNode<>(oldTrace); - final ADTNode, I, O> parent = iter.getParent(); + final ADTNode, I, O> parent = finalNode.getParent(); assert parent != null; - final O parentOutput = ADTUtil.getOutputForSuccessor(parent, iter); + final O parentOutput = ADTUtil.getOutputForSuccessor(parent, finalNode); parent.getChildren().put(parentOutput, reset); reset.setParent(parent); @@ -815,13 +816,7 @@ private void resiftAffectedTransitions(Set, I, O>> states for (ADTNode, I, O> state : states) { - final List> transitionsToRefine = state.getHypothesisState() - .getIncomingTransitions() - .stream() - .filter(x -> !x.isSpanningTreeEdge()) - .collect(Collectors.toList()); - - for (ADTTransition trans : transitionsToRefine) { + for (ADTTransition trans : getIncomingNonSpanningTreeTransitions(state.getState())) { trans.setTarget(null); trans.setSiftNode(finalizedADS); this.openTransitions.add(trans); @@ -829,6 +824,19 @@ private void resiftAffectedTransitions(Set, I, O>> states } } + private List> getIncomingNonSpanningTreeTransitions(ADTState state) { + final Set> transitions = state.getIncomingTransitions(); + final List> result = new ArrayList<>(transitions.size()); + + for (ADTTransition t : transitions) { + if (!t.isSpanningTreeEdge()) { + result.add(t); + } + } + + return result; + } + public ADT, I, O> getADT() { return adt; } @@ -854,5 +862,10 @@ public static SubtreeReplacer subtreeReplacer() { public static boolean useObservationTree() { return true; } + + @SuppressWarnings("unchecked") + public static LocalSuffixFinder suffixFinder() { + return (LocalSuffixFinder) LocalSuffixFinders.RIVEST_SCHAPIRE; + } } } diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADTLearnerState.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADTLearnerState.java index da5e95c18b..991af46ca4 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADTLearnerState.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/ADTLearnerState.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/AbstractAdaptiveQuery.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/AbstractAdaptiveQuery.java new file mode 100644 index 0000000000..cfa7730330 --- /dev/null +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/AbstractAdaptiveQuery.java @@ -0,0 +1,74 @@ +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . + * + * 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.adt.learner; + +import de.learnlib.algorithm.adt.adt.ADTNode; +import de.learnlib.algorithm.adt.automaton.ADTState; +import de.learnlib.algorithm.adt.util.ADTUtil; +import de.learnlib.query.AdaptiveQuery; + +/** + * Utility class to share common implementations. + * + * @param + * input symbol type + * @param + * output symbol type + */ +abstract class AbstractAdaptiveQuery implements AdaptiveQuery { + + protected ADTNode, I, O> currentADTNode; + private O tempOut; + + AbstractAdaptiveQuery(ADTNode, I, O> currentADTNode) { + this.currentADTNode = currentADTNode; + } + + @Override + public Response processOutput(O out) { + + final ADTNode, I, O> succ = currentADTNode.getChild(out); + + if (succ == null) { + this.tempOut = out; + return Response.FINISHED; + } else if (ADTUtil.isResetNode(succ)) { + this.currentADTNode = succ.getChild(null); + resetProgress(); + return Response.RESET; + } else if (ADTUtil.isSymbolNode(succ)) { + this.currentADTNode = succ; + return Response.SYMBOL; + } else { + this.currentADTNode = succ; + return Response.FINISHED; + } + } + + protected abstract void resetProgress(); + + boolean needsPostProcessing() { + return this.tempOut != null; + } + + ADTNode, I, O> getCurrentADTNode() { + return currentADTNode; + } + + O getTempOut() { + return tempOut; + } +} diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/Adaptive2MembershipWrapper.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/Adaptive2MembershipWrapper.java new file mode 100644 index 0000000000..51b2d9bf5c --- /dev/null +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/learner/Adaptive2MembershipWrapper.java @@ -0,0 +1,59 @@ +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . + * + * 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.adt.learner; + +import java.util.ArrayList; +import java.util.Collection; + +import de.learnlib.oracle.AdaptiveMembershipOracle; +import de.learnlib.oracle.MembershipOracle.MealyMembershipOracle; +import de.learnlib.query.AdaptiveQuery; +import de.learnlib.query.Query; +import de.learnlib.util.mealy.PresetAdaptiveQuery; +import net.automatalib.word.Word; + +/** + * Utility class to answer regular {@link Query queries} with an {@link AdaptiveMembershipOracle}. + * + * @param + * input symbol type + * @param + * output symbol type + */ +class Adaptive2MembershipWrapper implements MealyMembershipOracle { + + private final AdaptiveMembershipOracle oracle; + + Adaptive2MembershipWrapper(AdaptiveMembershipOracle oracle) { + this.oracle = oracle; + } + + @Override + public void processQueries(Collection>> queries) { + + Collection> adaptiveQueries = new ArrayList<>(); + + for (Query> query : queries) { + if (query.getSuffix().isEmpty()) { + query.answer(Word.epsilon()); + } else { + adaptiveQueries.add(new PresetAdaptiveQuery<>(query)); + } + } + + this.oracle.processQueries(adaptiveQueries); + } +} diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/model/ExtensionResult.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/model/ExtensionResult.java index ebbfb32a27..195ce9ab7d 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/model/ExtensionResult.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/model/ExtensionResult.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,10 +22,14 @@ import org.checkerframework.checker.nullness.qual.Nullable; /** - * A class that describes the possible result a {@link ADTExtender} can return. Extending a parent trace can either
    - *
  • yield a counterexample, if the output of the parent trace does not match the output of the hypothesis states of - * the leaves
  • yield a valid replacement for the temporary splitter
  • yield no result, if the - * referenced hypothesis states cannot be distinguished by means of extending the parent trace
+ * A class that describes the possible result an {@link ADTExtender} can return. Extending a parent trace can either + *
    + *
  • yield a counterexample, if the output of the parent trace does not match the output of the hypothesis + * states of the leaves.
  • + *
  • yield a valid replacement for the temporary splitter.
  • + *
  • yield no result, if the referenced hypothesis states cannot be distinguished by means of extending the + * parent trace.
  • + *
* * @param * (hypothesis) state type diff --git a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/model/ObservationTree.java b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/model/ObservationTree.java index 240a743d0f..7caf149a34 100644 --- a/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/model/ObservationTree.java +++ b/algorithms/active/adt/src/main/java/de/learnlib/algorithm/adt/model/ObservationTree.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2013-2023 TU Dortmund - * This file is part of LearnLib, http://www.learnlib.de/. +/* Copyright (C) 2013-2025 TU Dortmund University + * This file is part of LearnLib . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,21 +17,22 @@ import java.util.Collection; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; -import java.util.Objects; -import java.util.Optional; import java.util.function.Function; import de.learnlib.algorithm.LearningAlgorithm; import de.learnlib.algorithm.adt.adt.ADTNode; import de.learnlib.algorithm.adt.util.ADTUtil; +import de.learnlib.filter.cache.mealy.AdaptiveQueryCache; +import de.learnlib.oracle.AdaptiveMembershipOracle; +import de.learnlib.query.AdaptiveQuery; import net.automatalib.alphabet.Alphabet; -import net.automatalib.automaton.transducer.FastMealy; -import net.automatalib.automaton.transducer.FastMealyState; +import net.automatalib.alphabet.SupportsGrowingAlphabet; +import net.automatalib.automaton.transducer.MealyMachine; import net.automatalib.common.util.Pair; import net.automatalib.util.automaton.equivalence.NearLinearEquivalenceTest; import net.automatalib.word.Word; +import org.checkerframework.checker.nullness.qual.Nullable; /** * A class, that stores observations of the system under learning in a tree-like structure. Can be used to