Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
23ad245
Update types, docstrings, and bump minimal python version to 3.10
emuskardin Aug 3, 2026
e74a2bb
consistent return type in cache query
emuskardin Aug 4, 2026
0deb020
return constructed NDMooreMachine in to_state_setup
emuskardin Aug 4, 2026
6b45d8d
Fix VPA execute_sequance and to_state_setup
emuskardin Aug 4, 2026
885001a
minor bug fixes
emuskardin Aug 4, 2026
4fe0832
minor bug fixes
emuskardin Aug 4, 2026
17ea016
remove hardcoded direction from linear_cex_processing
emuskardin Aug 4, 2026
ed731ff
Fix lower bound check in KWayStateCoverageEqOracle.py
emuskardin Aug 4, 2026
65a8810
Fix sampling for Mealy machines
emuskardin Aug 4, 2026
0ae01cb
fix sampling bug and infinite recursion in Score Function
emuskardin Aug 4, 2026
3e7ecc9
Add automata tests
emuskardin Aug 4, 2026
cdf01f0
Update minimum python version and add test cases to GitHubs CI/CD
emuskardin Aug 4, 2026
1ee81c6
Add base and oracle tests
emuskardin Aug 4, 2026
7de29ed
Add SULs and utils tests
emuskardin Aug 4, 2026
9c56f7f
Add tests for learning_algs package
emuskardin Aug 4, 2026
3b42e4b
Remove old test files
emuskardin Aug 4, 2026
4ffadf4
update version
emuskardin Aug 4, 2026
2bf7ab9
Update python version in CI/CD
emuskardin Aug 4, 2026
6894de5
Update typing
emuskardin Aug 4, 2026
c4eec08
Update python-app.yml
emuskardin Aug 4, 2026
58cba98
Update python-app.yml
emuskardin Aug 4, 2026
c175c0b
Update python-app.yml
emuskardin Aug 4, 2026
e6cedb9
Update python-app.yml
emuskardin Aug 4, 2026
7d96868
Merge remote-tracking branch 'origin/aalpy-v2' into aalpy-v2
emuskardin Aug 4, 2026
791a63e
Update python-app.yml
emuskardin Aug 4, 2026
a6d3577
Update failing patch in test_user_input_eq_oracle.py
emuskardin Aug 4, 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
38 changes: 18 additions & 20 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: Python application

on:
Expand All @@ -11,24 +8,25 @@ on:

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest pytest-timeout pydot
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi

- name: Lint with flake8
run: |
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics

- name: Run tests
run: pytest -q
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

[![GitHub issues](https://img.shields.io/github/issues/DES-Lab/AALpy)](https://github.com/DES-Lab/AALpy/issues)
![GitHub pull requests](https://img.shields.io/github/issues-pr/des-lab/aalpy)
[![Python 3.6](https://img.shields.io/badge/python-3.6%2B-blue)](https://www.python.org/downloads/release/python-360/)
[![Python 3.10](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/release/python-3100/)
![PyPI - Wheel](https://img.shields.io/pypi/wheel/aalpy)
[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/Naereen/StrapDown.js/graphs/commit-activity)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
Expand Down Expand Up @@ -52,7 +52,9 @@ To install current version of the master branch (it might contain bugfixes and a
```bash
pip install https://github.com/DES-Lab/AALpy/archive/master.zip
```
The minimum required version of Python is 3.6.
The minimum required version of Python is 3.10.
If you want to use older Python version (>= 3.6), you can use AALpy versions up to 1.6.2.

Ensure that you have [Graphviz](https://graphviz.org/) installed and added to your path if you want to visualize models.

For manual installation, clone the repo and install `pydot` (the only dependency).
Expand Down Expand Up @@ -145,6 +147,24 @@ If you use AALpy in your research, please cite us with of the following:
If you have research suggestions or you need specific help concerning your research, feel free to start a [discussion](https://github.com/DES-Lab/AALpy/discussions).
We are happy to help you and consult you in developing automata learning algorithms and in applying automata learning in various domains.


## Testing

Install the test dependencies once (`pytest` and `pytest-timeout` aren't needed to use AALpy itself, only to run its test suite):
```bash
pip install -e .[test]
```

Then, from the repository root, run AALpy's test suite:
```bash
pytest
```

For a more comprehensive test suite, which covers more seeds per learning experiment, run:
```bash
pytest --exhaustive
```

## Contributing
Pull requests are welcome. For significant changes, please open an issue first to discuss what you would like to change.
In case of any questions or possible bugs, please open issues.
32 changes: 28 additions & 4 deletions aalpy/SULs/AutomataSUL.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,43 @@
# SUL wrapping an in-memory automaton, used to expose learned/reference automata as systems under learning.
from typing import Any

from aalpy.base import Automaton
from aalpy.base import SUL


class AutomatonSUL(SUL):
def __init__(self, automaton: Automaton):
"""
System under learning that wraps an in-memory automaton, delegating steps to it.
"""

def __init__(self, automaton: Automaton) -> None:
"""
Creates a SUL wrapping an automaton.

:param Automaton automaton: The automaton to wrap.
"""
super().__init__()
self.automaton: Automaton = automaton

def pre(self):
def pre(self) -> None:
"""
Resets the wrapped automaton to its initial state.
"""
self.automaton.reset_to_initial()

def step(self, letter=None):
def step(self, letter: Any = None) -> Any:
"""
Executes a single input on the wrapped automaton.

:param Any letter: Single input that is executed on the wrapped automaton.
:return Any: Output received after executing the input.
"""
return self.automaton.step(letter)

def post(self):
def post(self) -> None:
"""
Performs no cleanup, as the wrapped automaton requires none between queries.
"""
pass


Expand Down
48 changes: 25 additions & 23 deletions aalpy/SULs/PyMethodSUL.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# SUL for learning the behavior of an arbitrary Python class through its methods.
from typing import Any

from aalpy.base import SUL


Expand All @@ -6,22 +9,24 @@ class FunctionDecorator:
Decorator of methods found in the SUL class.
"""

def __init__(self, function, args=None):
def __init__(self, function: Any, args: Any = None) -> None:
"""
Args:

function: function of the class to be learned
Creates a function decorator.

args: arguments to be passed to the function. Either a single argument, or a list of arguments if
function has more than one parameter.
:param Any function: Function of the class to be learned.
:param Any args: Arguments to be passed to the function. Either a single argument, or a list of arguments
if the function has more than one parameter.
"""

self.function = function
self.args = None
if args:
self.args = [args] if not isinstance(args, (list, tuple)) else args

def __repr__(self):
def __repr__(self) -> str:
"""
:return str: A string representation of the function call.
"""
if self.args:
return f'{self.function.__name__}{self.args}'
return self.function.__name__
Expand All @@ -31,37 +36,34 @@ class PyClassSUL(SUL):
"""
System under learning for inferring python classes.
"""
def __init__(self, python_class):
def __init__(self, python_class: type) -> None:
"""
Args:
Creates a SUL for a Python class.

python_class: class to be learned
:param type python_class: Class to be learned.
"""
super().__init__()
self._class = python_class
self.sul: object = None

def pre(self):
def pre(self) -> None:
"""
Do the reset by initializing the class again or call reset method of the class
Do the reset by initializing the class again or call reset method of the class.
"""
self.sul = self._class()

def post(self):
def post(self) -> None:
"""
Performs no additional cleanup, as a fresh instance is created on every pre() call.
"""
pass

def step(self, letter):
def step(self, letter: FunctionDecorator) -> Any:
"""
Executes the function(with arguments) found in letter against the SUL

Args:

letter: single input of type FunctionDecorator

Returns:

output of the function
Executes the function(with arguments) found in letter against the SUL.

:param FunctionDecorator letter: Single input of type FunctionDecorator.
:return Any: Output of the function.
"""
if letter.args:
return getattr(self.sul, letter.function.__name__, letter)(*letter.args)
Expand Down
34 changes: 21 additions & 13 deletions aalpy/SULs/RegexSUL.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,44 @@
from aalpy.base import SUL
# SUL for learning a regular expression as a DFA-like acceptor.
import re

from aalpy.base import SUL


class RegexSUL(SUL):
"""
An example implementation of a system under learning that can be used to learn any regex expression.
Note that the $ is added to the expression as in this SUL only exact matches are learned.
"""
def __init__(self, regex: str):
def __init__(self, regex: str) -> None:
"""
Creates a SUL for a regular expression.

:param str regex: The regular expression to learn. A trailing '$' is added if missing.
"""
super().__init__()
self.regex = regex if regex[-1] == '$' else regex + '$'
self.string = ""

def pre(self):
def pre(self) -> None:
"""
Resets the accumulated input string.
"""
self.string = ""
pass

def post(self):
def post(self) -> None:
"""
Resets the accumulated input string.
"""
self.string = ""
pass

def step(self, letter):
def step(self, letter: str | None) -> bool:
"""
Appends the letter to the accumulated string and checks whether it matches the regex.

Args:

letter: single element of the input alphabet

Returns:

Whether the current string (previous string + letter) is accepted

:param str | None letter: Single element of the input alphabet.
:return bool: Whether the current string (previous string + letter) is accepted.
"""
if letter is not None:
self.string += str(letter)
Expand Down
Loading
Loading