Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0a705a8
initial work on CLI application
mtf90 Jul 20, 2026
a66d7f9
update build profiles
mtf90 Jul 21, 2026
fb574e9
github: add worflow for attaching cli artifacts
mtf90 Jul 21, 2026
42dc4de
cli: include OS classifiers in artifact name
mtf90 Jul 21, 2026
aebbb34
do not include cli module in releases for now
mtf90 Jul 21, 2026
7ab9ad1
update docs
mtf90 Jul 21, 2026
2d8d150
tidy:pom
mtf90 Jul 21, 2026
f743669
add support for SAF serialization
mtf90 Jul 21, 2026
def64cd
cli: add ADT and LSHARP algorithm
mtf90 Jul 23, 2026
ed50b8d
cleanup CLI oracles
mtf90 Jul 24, 2026
38f8774
experiment: allow for logging intermediate hypotheses
mtf90 Jul 24, 2026
0530809
jacoco: also depend on cli module before aggregating reports
mtf90 Jul 24, 2026
2183cb3
cli: improve documentation
mtf90 Jul 24, 2026
e40b54a
fix code-analysis
mtf90 Jul 24, 2026
273273e
cli: add support for parallel oracles
mtf90 Jul 24, 2026
480d1bb
cli: add unit- and integration-tests
mtf90 Jul 26, 2026
285db43
add support for snapshotting
mtf90 Jul 29, 2026
696e39b
use LocalDateTime
mtf90 Jul 30, 2026
ba9fcb6
simplify --eqo-* param names
mtf90 Jul 30, 2026
1b466d3
Revert "simplify --eqo-* param names"
mtf90 Jul 30, 2026
15b6048
add MalerPnueli + RivestSchapire learner and adjust learner names
mtf90 Jul 30, 2026
b590ce1
include license information in jlink artifact
mtf90 Jul 31, 2026
7689b39
cleanup checkerframework config
mtf90 Jul 31, 2026
5c29490
add rudimentary class validation during deserialization + cleanups
mtf90 Aug 4, 2026
608d866
add changelog
mtf90 Aug 10, 2026
e61eb83
add missing --add-opens + test
mtf90 Aug 11, 2026
c9c5d53
cleanup
mtf90 Aug 11, 2026
10a8a72
wording
mtf90 Aug 11, 2026
8f7a675
handle python availability more gracefully
mtf90 Aug 11, 2026
3a68b03
conditionally set --add-opens option
mtf90 Aug 11, 2026
47d1269
cli: invoke script via python interpreter
mtf90 Aug 11, 2026
22417fe
sett full (and platform-specific) path in jlink IT
mtf90 Aug 11, 2026
64d7544
wording
mtf90 Aug 11, 2026
35e16c0
coveralls: s/Example/Experiment/
mtf90 Aug 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
mvn -B install -DskipTests
cd $GITHUB_WORKSPACE
- name: Run Maven
run: mvn -B install site -Pintegration-tests,code-analysis,bundles,jlink
run: mvn -B install site -Pintegration-tests,code-analysis,bundles
platform-integration:
name: "Platform Integration (JDK: ${{ matrix.jdk }}, OS: ${{ matrix.os }})"
needs: [ tests-and-analysis ]
Expand Down Expand Up @@ -81,7 +81,7 @@ jobs:
mvn -B install -DskipTests
cd $GITHUB_WORKSPACE
- name: Run Maven
run: mvn -B install -Pjlink
run: mvn -B install -Pcli
coverage:
name: "Coverage"
needs: [ platform-integration ]
Expand Down
35 changes: 35 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Attach artifacts to GitHub release
on:
release:
types: [published]
jobs:
publish:
name: "Build and attach artifacts"
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-22.04, windows-2022, macos-15-intel, macos-15 ]
permissions:
contents: write
steps:
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
with:
java-version: '25'
distribution: 'temurin'
cache: 'maven'
- name: Build AutomataLib # can be removed on actual stable releases
shell: bash
run: |
git clone -b develop --single-branch https://github.com/LearnLib/automatalib.git ${HOME}/automatalib-git
cd ${HOME}/automatalib-git
mvn -B install -DskipTests
cd $GITHUB_WORKSPACE
- name: Build
run: mvn -B package -DskipTests -Pcli
- name: Release
uses: softprops/action-gh-release@v3
with:
working_directory: cli/target
files: learnlib-cli-*.zip
fail_on_unmatched_files: true
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Added

* GitHub releases now provide `learnlib-cli` artifacts for using LearnLib via the command-line interface without the need for Java or Maven.
* Added a new (L*-based) learning algorithm for *Mealy machines with local timers* (MMLTs), including support for parallel queries, caching, and conformance testing (thanks to [Paul Kogel](https://github.com/pdev55)).
* Added the L<sup>s</sup> active learning algorithm for Mealy machines (thanks to [Wolffhardt Schwabe](https://github.com/stateMachinist)).
* Added an `EarlyExitEQOracle` which for a given `AdaptiveMembershipOracle` and `TestWordGenerator` stops the evaluation of (potentially long) Mealy-based equivalence tests as soon as a mismatch with the hypothesis is detected, potentially improving the symbol performance of the given equivalence oracle.
Expand All @@ -21,12 +22,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Statistics collection has received a major rework. Previously, classes would implement the `StatisticCollector` interface and return a `StatisticData` object which 1) only allows for describing a very limited amount of data, and 2) requires you to keep track of all the objects that collect data. This approach has been *replaced* by a new `StatisticsService`. Instances of this service can be obtained similar to a logger via `Statistics.getService()` and require you to provide an implementation of this service on the classpath (a default one is provided by the `learnlib-statistics` module). The new service allows arbitrary components to collect various data which can be conveniently extracted based on the new `StatisticsKey`s used by the components. For more details on advanced scenarios (such as multi-threaded benchmarking), see the documentation of the respective classes. While this may require you to adjust the way you are collecting statistics, all functionality from beforehand should still be available.
* `SimpleProfiler` has been replaced by the new clock-based statistics.
* Most learners now more rigorously implement the `LearningAlgorithm` contract that, e.g., duplicate invocations of `startLearning` or calling `refineHypothesis` / `getHypothesisModel` before `startLearning` throw `IllegalStateException`s.
* `Experiment` now has type variables for the input symbol type and output domain type.
* `{DFA,Mealy,Moore}Experiment` have been moved to the `de.learnlib.util` package.
* The `generateTestWords` method of `AbstractTestWordEQOracle` now needs to be public.
* The classes of `de.learnlib.testsupport.it.learner` have been split into the packages `de.learnlib.testsupport.it{,testcase,util,variant}` in the same module (`de.learnlib.testsupport:learnlib-learner-it-support`).

### Removed

* All *adapters* from the `learnlib-procedural` learner have been removed to due main learners implementing `AccessSequenceTransformer` now. Use the constructors of the main learners instead.
* All *adapters* from the `learnlib-procedural` learner have been removed due to main learners implementing `AccessSequenceTransformer` now. Use the constructors of the main learners instead.

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ public void testAbstractHypothesisEquivalence() {
final WpMethodTestsIterator<I> iter = new WpMethodTestsIterator<>(automaton, alphabet);
final List<Word<I>> testCases = IteratorUtil.list(iter);

final SampleSetEQOracle<I, D> eqo = new SampleSetEQOracle<>();
eqo.addAll(new SimulatorOracle<>(automaton), testCases);
final SampleSetEQOracle<I, D> eqo =
new SampleSetEQOracle<I, D>().addAll(new SimulatorOracle<>(automaton), testCases);

final LearningAlgorithm<A, I, D> learner =
new TranslatingLearnerWrapper<>((AbstractAAARLearner<?, A, A, I, I, D>) aaarLearner);
final Experiment<A> exp = new Experiment<>(learner, eqo, alphabet);
final Experiment<A, I, D> exp = new Experiment<>(learner, eqo, alphabet);

exp.run();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import de.learnlib.query.DefaultQuery;
import de.learnlib.tooling.annotation.builder.GenerateBuilder;
import de.learnlib.util.MQUtil;
import de.learnlib.util.mealy.Adaptive2MembershipWrapper;
import net.automatalib.alphabet.Alphabet;
import net.automatalib.alphabet.SupportsGrowingAlphabet;
import net.automatalib.automaton.transducer.MealyMachine;
Expand Down Expand Up @@ -104,12 +105,26 @@ public class ADTLearner<I, O> implements LearningAlgorithm.MealyLearner<I, O>,
private ADTHypothesis<I, O> hypothesis;
private ADT<ADTState<I, O>, I, O> adt;

public ADTLearner(Alphabet<I> alphabet, AdaptiveMembershipOracle<I, O> oracle) {
this(alphabet,
oracle,
BuilderDefaults.leafSplitter(),
BuilderDefaults.adtExtender(),
BuilderDefaults.subtreeReplacer());
}

public ADTLearner(Alphabet<I> alphabet,
AdaptiveMembershipOracle<I, O> oracle,
LeafSplitter leafSplitter,
ADTExtender adtExtender,
SubtreeReplacer subtreeReplacer) {
this(alphabet, oracle, leafSplitter, adtExtender, subtreeReplacer, true, LocalSuffixFinders.RIVEST_SCHAPIRE);
this(alphabet,
oracle,
leafSplitter,
adtExtender,
subtreeReplacer,
BuilderDefaults.useObservationTree(),
BuilderDefaults.suffixFinder());
}

@GenerateBuilder(defaults = BuilderDefaults.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
import de.learnlib.testsupport.MQ2AQWrapper;
import de.learnlib.testsupport.it.AbstractMealyLearnerIT;
import de.learnlib.testsupport.it.variant.LearnerVariantList;
import de.learnlib.util.Experiment.MealyExperiment;
import de.learnlib.util.MealyExperiment;
import net.automatalib.alphabet.Alphabet;
import net.automatalib.automaton.transducer.impl.CompactMealy;
import net.automatalib.exception.FormatException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import de.learnlib.oracle.MembershipOracle.DFAMembershipOracle;
import de.learnlib.oracle.equivalence.SampleSetEQOracle;
import de.learnlib.oracle.membership.DFASimulatorOracle;
import de.learnlib.util.Experiment.DFAExperiment;
import de.learnlib.util.Experiment;
import net.automatalib.alphabet.Alphabet;
import net.automatalib.alphabet.impl.Alphabets;
import net.automatalib.automaton.fsa.DFA;
Expand Down Expand Up @@ -80,7 +80,8 @@ public void testPop() {
final Word<Character> b = new WordBuilder<>('b', 9).toWord();
eqOracle.addAll(mqOracle, Word.fromWords(b, a, b, a, b, a, b, a));

final DFAExperiment<Character> experiment = new DFAExperiment<>(learner, eqOracle, alphabet);
final Experiment<DFA<?, Character>, Character, Boolean> experiment =
new Experiment<>(learner, eqOracle, alphabet);
experiment.run();

final DFA<?, Character> result = experiment.getFinalHypothesis();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import de.learnlib.query.DefaultQuery;
import de.learnlib.testsupport.it.AbstractMealyLearnerIT;
import de.learnlib.testsupport.it.variant.LearnerVariantList.MealyLearnerVariantList;
import de.learnlib.util.Experiment.MealyExperiment;
import de.learnlib.util.MealyExperiment;
import de.learnlib.util.mealy.MealyUtil;
import net.automatalib.alphabet.Alphabet;
import net.automatalib.automaton.transducer.MealyMachine;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ public void testIssue70() {

final NLStarLearner<Character> learner = new NLStarLearner<>(alphabet, mqOracle);

final Experiment<NFA<?, Character>> experiment = new Experiment<>(learner, eqOracle, alphabet);
final Experiment<NFA<?, Character>, Character, Boolean> experiment =
new Experiment<>(learner, eqOracle, alphabet);
experiment.run();
final NFA<?, Character> hyp = experiment.getFinalHypothesis();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public DTVisualizationTest() {
final SimulatorEQOracle<Character> eqo = new SimulatorEQOracle<>(vpa);
this.learner = new OPLearnerVPA<>(alphabet, mqo, AcexAnalyzers.BINARY_SEARCH_FWD);

final Experiment<OneSEVPA<?, Character>> exp = new Experiment<>(learner, eqo, alphabet);
final Experiment<OneSEVPA<?, Character>, Character, Boolean> exp = new Experiment<>(learner, eqo, alphabet);
exp.run();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public void testOptimizations() {

final SBALearner<Character, ?> learner = new SBALearner<>(alphabet, mqo, TTTLearnerDFA::new);

final Experiment<SBA<?, Character>> experiment = new Experiment<>(learner, eqo, alphabet);
final Experiment<SBA<?, Character>, Character, Boolean> experiment = new Experiment<>(learner, eqo, alphabet);

experiment.run();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ public void testOptimizations() {
final SPMMLearner<Character, Character, ?> learner =
new SPMMLearner<>(alphabet, spmm.getErrorOutput(), mqo, TTTLearnerMealy::new);

final Experiment<SPMM<?, Character, ?, Character>> experiment = new Experiment<>(learner, eqo, alphabet);
final Experiment<SPMM<?, Character, ?, Character>, Character, Word<Character>> experiment =
new Experiment<>(learner, eqo, alphabet);

experiment.run();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import de.learnlib.oracle.equivalence.DFAWMethodEQOracle;
import de.learnlib.oracle.membership.DFASimulatorOracle;
import de.learnlib.statistic.Statistics;
import de.learnlib.util.Experiment.DFAExperiment;
import de.learnlib.util.DFAExperiment;
import net.automatalib.alphabet.Alphabet;
import net.automatalib.alphabet.impl.Alphabets;
import net.automatalib.automaton.fsa.DFA;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import de.learnlib.oracle.equivalence.DFAWMethodEQOracle;
import de.learnlib.oracle.membership.DFASimulatorOracle;
import de.learnlib.statistic.Statistics;
import de.learnlib.util.Experiment.DFAExperiment;
import de.learnlib.util.DFAExperiment;
import net.automatalib.alphabet.Alphabet;
import net.automatalib.alphabet.impl.Alphabets;
import net.automatalib.automaton.fsa.DFA;
Expand Down
13 changes: 6 additions & 7 deletions build-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ limitations under the License.
<exclude>de/learnlib/oracle/property/Mealy*Oracle.class</exclude>
<exclude>de/learnlib/oracle/property/DFA*Chain.class</exclude>
<exclude>de/learnlib/oracle/property/Mealy*Chain.class</exclude>

<!-- generated refinements for Experiment -->
<exclude>de/learnlib/util/DFAExperiment.class</exclude>
<exclude>de/learnlib/util/MealyExperiment.class</exclude>
<exclude>de/learnlib/util/MooreExperiment.class</exclude>
</excludes>
</configuration>
</plugin>
Expand Down Expand Up @@ -260,13 +265,7 @@ limitations under the License.
<configuration>
<failOnWarning>true</failOnWarning>
<fork>true</fork>
<!--
Setting a different output directly can fail the build when using other annotation
processors as well. However, with using the same output directory, we pack Java 11
code in the jars which will fail other analysis tools. Currently, checkerframework
cannot be run with our other analysis steps...
-->
<!--outputDirectory>${project.build.directory}/checkerframework</outputDirectory-->
<outputDirectory>${project.build.directory}/test-checkerframework</outputDirectory>
<annotationProcessorPaths>
<path>
<groupId>org.checkerframework</groupId>
Expand Down
Loading
Loading