Repository navigation
Add black-box checking and runtime monitoring - #113
emuskardin merged 62 commits into
Conversation
…oncrete model checking oracle
|
Due to lack of time, I did initial detailed review with Claude, went through it, and picked up from it most important points. These should all be easy to address, and once addressed, I will look at it again. Also please add a minimal example at the bottom of Examples.py for the functionality that you have added.
Nine names are used in type annotations without being imported. 1.1 Why this passed locally Python 3.14 implements PEP 649: annotations are evaluated lazily. Before 3.14 they are evaluated eagerly at def time, i.e. at import. Same
All nine undefined names sit in annotation position, which is exactly why the suite looked green. pyproject.toml declares requires-python on four of the five supported versions. Exact fix, per file
On item 6: InputType is not re-exported from aalpy.base. It must come from aalpy.base.Automaton, the same way aalpy/base/Oracle.py imports it.
2.1 IUOBugDfaModelCheckingOracle.find_cex hangs forever on cyclic models IUOBugDfaModelCheckingOracle.py:99-101 writes BFS parent pointers unconditionally, before the if next_pair not in explored guard: When the product BFS revisits an already-explored pair — including the initial pair — this overwrites that node's parent. Overwriting lettir] destroys the None sentinel that witness reconstruction relies on: The parent chain becomes cyclic and the loop spins forever while word grows unbounded.
2.2 MonitoringSUL.query crashes on the empty word — DFA/Moore learning unusable MonitoringSUL.py:183: MonitoringSUL.step passes self.inputs by reference; it keeps growing for the rest of the query. A callback that stores its argument — the obvious use — gets the whole query word, not the counterexample prefix: captured at callback time should be ['a','b'] -> actually: ['a','b','a','a'] This silently corrupts every counterexample the feature exists to deliver. Pass tuple(self.inputs); BBCEqOracle should match. Property counterexamples return as lists while the eq_oracle path returns tuples (cex = tuple(cex)). The base contract is tuple[InputType, ...] | None, and downstream cex processing hashes/indexes these. Also collapse the redundant double if cex is not None:. Minor stuff
Design Stuff
|
…eing passed to the callback
…nitoringSUL's unit tests
… property violation callback from list to tuple
…ed and issued as tuples
…IUO_dfa_from_IXO_dfa
… IUO_dfa_from_IXO_dfa
…lphabet and SUL arguments because it uses the values of the wrapped equivalence Oracle
…ecause this resulted in the wrapped SUL's counters being overwritten
…UL and BBCEqOracle
…after calling self.pre() and SUL.num_steps right before taking a self.step, so that these counters have the correct values if they are read from a MonitoringSUL violation callback
…nger relies on < being supported between symbols
|
I added two Oracle-specific counters to the IUOBugDfaModelCheckingOracle: one for the number of checks performed and one for the number of counterexamples that were found when performing these checks. It still uses the Oracle interface. I also added randomly-generated exhaustive and non-exhaustive tests for both black-box checking and runtime monitoring, which I based on the instance you pointed me to (https://github.com/DES-Lab/AALpy/blob/master/tests/learning_algs/deterministic/test_random_learning_runs_exhaustive.py). The exhaustive tests only run with def reproduce():
from aalpy.automata import MealyMachine
from aalpy.base import Oracle, SUL
from aalpy.learning_algs import run_KV
from aalpy.SULs import AutomatonSUL
mealy = MealyMachine.from_state_setup({
's1': {'I0': ('O0', 's2'), 'I1': ('O1', 's3')},
's2': {'I0': ('O2', 's1'), 'I1': ('O2', 's3')},
's3': {'I0': ('O1', 's3'), 'I1': ('O2', 's1')},
})
sul = AutomatonSUL(mealy)
counterexample = ('I0', 'I0', 'I1')
class ExampleOracle(Oracle):
def __init__(self, alphabet: list, sul: SUL):
super().__init__(alphabet, sul)
def find_cex(self, hypothesis: Automaton):
# This hard-coded counterexample is valid for the initial hypothesis, which is the hypothesis for which the crash occurs
assert self.sul.query(counterexample) != hypothesis.execute_sequence(hypothesis.initial_state, counterexample)
return counterexample
eq_oracle = ExampleOracle(mealy.get_input_alphabet(), sul)
run_KV(mealy.get_input_alphabet(), sul, eq_oracle, automaton_type="mealy", print_level=3)All 5 of my failing tests pass when I ensure that the model checker counterexamples returned by the BBCEqOracle for hypothesis refinement are minimal (by taking their shortest prefixes that lead to distinct outputs between the hypothesis and the SUL). I changed the BBCEqOracle to always return these minimal prefixes (for model checker counterexamples, because these counterexamples are found between the property and the hypothesis and there is no particular reason to assume that they are minimal with respect to the hypothesis and the SUL). My tests now pass, but I wanted to inform you about these crashes because their root cause (in the ClassificationTree) still persists. I fixed the faulty docstring you found in |
|
@bpellen , thanks, I will do (hopefully) a final check this workweek. |
|
Regarding the bug you pointed out, that is not really a bug per se, as it would never happen in classic AL with oracles that return cex at the first occurring differance. I will keep it in mind. Is that something that your approach has? That the cex is non-minimal, that is, it is not trimmed to the first difference? If yes, I would suggest fixing that if possible, much more elegant if cex shows the difference clearly. Also, some are of the opinion that if you cex is a sequance of inputs, the output of it is just the last output, which would make the cex not a cex if some middle input shows the differance. I do not subscribe to that school of thought, but some do. So please check BBC oracle line 104 if I remember correctly, where you call SUL query to rather step the sul, and reset with pre and post appropriately. You will save some queries, and be consistent with other oracles. |
…o runtime monitoring
…p_cex_was_found option
…when returning the first property counterexample that it finds
…mples are minimal
…iary states with fresh names
|
@emuskardin, thank you for the additional feedback. I addressed your five points by: Adding the missing imports. I now perform all of my testing from a Docker container that has Python 3.11 installed. I re-ran all of my unit tests and Examples, and I added every import that was revealed to be missing by the resulting crashes. As requested, the BBCEqOracle now runs the property counterexamples as if they were test cases in any other equivalence oracle, using the I added the missing assertions to I removed the unused imports that you mentioned. I corrected the print statement to "Runtime monitoring confirmed counterexample...". Cleanup. |

Add support for black-box checking and runtime monitoring.