This repository contains the code for the Fandango-Learn project. The goal is to automatically learn fandango constraints from a set of inputs.
This notebook demonstrates how to use FDLearn, a pattern-based approach that automatically learns constraints to explain why a program fails.
The core idea of FandangoLearner is to identify patterns in inputs that lead to program errors or unexpected behaviors. Using these patterns, it generates constraints in the Fandango language to help developers understand input-related bugs.
We start by defining the grammar for our input language. This example focuses on arithmetic expressions using trigonometric and square root functions.
from fdlearn.interface.fandango import parse_contents
grammar = """
<start> ::= <arithexp>;
<arithexp> ::= <function>"("<number>")";
<function> ::= "sqrt" | "cos" | "sin" | "tan";
<number> ::= <maybeminus><onenine><maybedigits> | "0";
<maybeminus> ::= "-" | "";
<onenine> ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
<maybedigits> ::= <digit>*;
<digit>::= "0" | <onenine>;
"""
grammar, _ = parse_contents(grammar)
We supply a set of example inputs along with their expected outcomes (True
for failure, False
otherwise).
initial_inputs = {
("sqrt(-900)", True), # This input causes a failure.
("sqrt(-10)", True), # Another failure case.
("sqrt(0)", False), # This input works correctly.
("sin(-900)", False), # Works correctly.
("sqrt(2)", False), # Works correctly.
("cos(10)", False), # Works correctly.
}
Convert inputs to FandangoInput objects
from fdlearn.learner import FandangoInput
initial_inputs = {
FandangoInput.from_str(grammar, inp, oracle)
for inp, oracle in initial_inputs
}
We specify the non-terminals in the grammar that are likely related to the program's failure behavior. This step is optional but can help focus the learning process on specific parts of the grammar. Later, we will see that Avicenna can automatically learn relevant non-terminals.
from fdlearn.learner import NonTerminal
relevant_non_terminals = {
NonTerminal("<number>"),
NonTerminal("<maybeminus>"),
NonTerminal("<function>"),
}
Using the FandangoLearner
, we learn constraints that explain why certain inputs fail.
from fdlearn.learner import FandangoLearner
learner = FandangoLearner(grammar)
learned_constraints = learner.learn_constraints(
initial_inputs,
relevant_non_terminals=relevant_non_terminals
)
Finally, we analyze the constraints generated by FandangoLearner to understand the root cause of failures.
print("Learned Constraints:")
for constraint in learner.get_best_candidates():
print(constraint)
The output will show the constraints that best explain the failures in the initial inputs.
(int(<number>) <= -10 and str(<function>) == 'sqrt'),
Precision: 1.0, Recall: 1.0 (based on 2 failing and 4 passing inputs)
We can see that the constraint (int(<number>) <= -10 and str(<function>) == 'sqrt')
explains why the inputs sqrt(-900)
and sqrt(-10)
fail.
However, this constraint is too specific and does not generalize well to other inputs.
Thus, we need a feedback loop that automatically refines these constraints to generate general constraints that captures the essence of the failure.
We will use Avicenna to provide this feedback loop.
To use FandangoLearn, you need to install both FandangoFuzzer (from its learner
branch) and FandangoLearn. We recommend using a dedicated virtual environment:
python3.12 -m venv venv
source venv/bin/activate
pip install --upgrade pip
Now, install FandangoFuzzer from the learner
branch followed by FandangoLearn:
pip install git+https://github.com/fandango-fuzzer/fandango.git@learner
pip install fandangolearn
If you plan to extend or evaluate FandangoLearn locally, clone this repository:
git clone https://github.com/fandango-fuzzer/fandango-learn.git
cd fandango-learn
Create and activate a virtual environment (recommended):
python3.12 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
pip install -e .
To install the test dependencies and run the tests:
make install-tests
make tests
That’s it! You’re now set up to use, develop, and test FandangoLearn. If you encounter any issues or have suggestions, feel free to open an issue or submit a pull request.
Work in progress.
This repository contains jupyter notebooks that demonstrate how to use the FandangoLearner to learn constraints from a set of inputs. Furthermore, the notebooks show how FandangoLearner works and how it can be, for instance, integrated into Avicenna.
Current notebooks:
- Introduction to FandangoLearner: Demonstrates how to use the FandangoLearner to learn constraints from a set of inputs.
- Automated Refinement: Shows how we can automatically refine constraints learned by FandangoLearner.
- Adding Patterns: Demonstrates how to add new patterns to the FandangoLearner to improve the learning process.
More notebooks will be added soon.
The code should be reusable for other projects, such as Avicenna. Therefore, the code uses many abstract classes that are already implemented in Avicenna and AvicennaISLearn. This makes comparing both approaches FandangoLearn and ISLearn extremely easy.
Date: 2025-01-27 10:45:40
Git Branch: dev
Last Commit: 76d77e252f2dcc848e705e62ab7fc56485b008f6
Subject | Total | Correct | Percentage | #Seeds | Mean Precision | P-StdDev | Mean Recall | R-StdDev | Time (s) |
---|---|---|---|---|---|---|---|---|---|
Calculator | 1 | 1 | 100.00 | 5 | 1.0000 | 0.0000 | 0.9174 | 0.0000 | 0.0326 |
CalculatorRE | 1 | 1 | 100.00 | 5 | 1.0000 | 0.0000 | 1.0000 | 0.0000 | 3.2854 |
Heartbleed | 4 | 2 | 50.00 | 5 | 1.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0121 |
HeartbleedRE | 2 | 2 | 100.00 | 5 | 1.0000 | 0.0000 | 1.0000 | 0.0000 | 4.1509 |
Middle | 84 | 1 | 1.19 | 5 | 1.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0395 |
MiddleRE | 1 | 1 | 55.56 | 5 | 1.0000 | 0.0000 | 1.0000 | 0.0000 | 32.1872 |
Pysnooper2 | 2 | 1 | 50.00 | 5 | 1.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0492 |
Pysnooper3 | 2 | 2 | 100.00 | 5 | 1.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0592 |
- AttributeSearches
- Better Negation