Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
66 changes: 33 additions & 33 deletions Examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,7 @@ def passive_vpa_learning_arithmetics():
def passive_vpa_learning_on_all_benchmark_models():
from aalpy.learning_algs import run_PAPNI
from aalpy.utils.BenchmarkVpaModels import vpa_L1, vpa_L12, vpa_for_odd_parentheses
from aalpy.utils import generate_input_output_data_from_vpa, convert_i_o_traces_for_RPNI
from aalpy.utils import generate_input_output_data_from_vpa

for gt in [vpa_L1(), vpa_L12(), vpa_for_odd_parentheses()]:
vpa_alphabet = gt.input_alphabet
Expand Down Expand Up @@ -1200,7 +1200,7 @@ def gsm_edsm():
from aalpy import load_automaton_from_file
from aalpy.utils.Sampling import get_io_traces, sample_with_length_limits
from aalpy.learning_algs.general_passive.GeneralizedStateMerging import run_GSM
from aalpy.learning_algs.general_passive.ScoreFunctionsGSM import ScoreCalculation
from aalpy.learning_algs.general_passive.ScoreFunctionsGSM import SimpleScoreCalculation
from aalpy.learning_algs.general_passive.GsmNode import GsmNode

automaton = load_automaton_from_file("DotModels/car_alarm.dot", "moore")
Expand All @@ -1212,7 +1212,7 @@ def EDSM_score(part: Dict[GsmNode, GsmNode]):
nr_merged = len(part)
return nr_merged - nr_partitions

score = ScoreCalculation(score_function=EDSM_score)
score = SimpleScoreCalculation(score_function=EDSM_score)
learned_model = run_GSM(traces, output_behavior="moore", transition_behavior="deterministic", score_calc=score)
learned_model.visualize()

Expand All @@ -1221,7 +1221,7 @@ def gsm_likelihood_ratio():
from typing import Dict
from scipy.stats import chi2
from aalpy.learning_algs.general_passive.GeneralizedStateMerging import run_GSM
from aalpy.learning_algs.general_passive.ScoreFunctionsGSM import ScoreFunction, differential_info, ScoreCalculation
from aalpy.learning_algs.general_passive.ScoreFunctionsGSM import ScoreFunction, differential_info, SimpleScoreCalculation
from aalpy.learning_algs.general_passive.GsmNode import GsmNode
from aalpy.utils.Sampling import get_io_traces, sample_with_length_limits
from aalpy import load_automaton_from_file
Expand All @@ -1246,55 +1246,58 @@ def score_fun(part: Dict[GsmNode, GsmNode]):

return score_fun

score = ScoreCalculation(score_function=likelihood_ratio_score())
score = SimpleScoreCalculation(score_function=likelihood_ratio_score())
learned_model = run_GSM(traces, output_behavior="moore", transition_behavior="stochastic", score_calc=score)
learned_model.visualize()


def example_Alergia_extension():
from typing import Any
from aalpy.learning_algs.general_passive.DataHandler import CountOnPTADataHandler
from aalpy.learning_algs.general_passive.GeneralizedStateMerging import run_GSM
from aalpy.learning_algs.general_passive.ScoreFunctionsGSM import hoeffding_compatibility, ScoreCalculation
from aalpy.learning_algs.general_passive.GsmNode import GsmNode
from aalpy.learning_algs.general_passive.ScoreFunctionsGSM import hoeffding_compatibility, SimpleFutureBasedCompatibility, SpecialScores
from aalpy.utils.Sampling import get_io_traces, sample_with_length_limits
from aalpy import load_automaton_from_file

automaton = load_automaton_from_file("DotModels/MDPs/faulty_car_alarm.dot", "mdp")
input_traces = sample_with_length_limits(automaton.get_input_alphabet(), 2000, 20, 30)
traces = get_io_traces(automaton, input_traces)

# NOTE THAT This example is equivalent to a call to a function run_Alergia_EDSM
# NOTE: a more general version of this is provided in aalpy.learning_algs.general_passive.ScoreFunctionsGSM
class ScoreIOAlergiaWithEDSM(SimpleFutureBasedCompatibility):
def __init__(self, eps: float):
self.compat = hoeffding_compatibility(eps)
SimpleFutureBasedCompatibility.__init__(self, compatibility_on_pta=True)
self.score = None

class IOAlergiaWithEDSM(ScoreCalculation):
def __init__(self, epsilon):
super().__init__()
self.ioa_compatibility = hoeffding_compatibility(epsilon)
self.evidence = 0

def reset(self):
self.evidence = 0
def early_score(self, red: GsmNode, blue: GsmNode) -> Any:
self.score = 0
verdict = super().early_score(red, blue)
if verdict is SpecialScores.ImmediateReject:
return verdict
return self.score

def local_compatibility(self, a: GsmNode, b: GsmNode):
self.evidence += 1
return self.ioa_compatibility(a, b)

def score_function(self, part):
return self.evidence
def local_compatibility(self, red: GsmNode, blue: GsmNode) -> float:
self.score += 1
return self.compat(red, blue)

epsilon = 0.05
scores = {
"IOA": ScoreCalculation(hoeffding_compatibility(epsilon)),
"IOA+EDSM": IOAlergiaWithEDSM(epsilon),
"IOA": SimpleFutureBasedCompatibility(local_compatibility=hoeffding_compatibility(epsilon, True), compatibility_on_pta=True),
"IOA+EDSM": ScoreIOAlergiaWithEDSM(epsilon),
}

for name, score in scores.items():
learned_model = run_GSM(traces, output_behavior="moore", transition_behavior="stochastic", score_calc=score,
compatibility_on_pta=True, compatibility_on_futures=True)
data_handler=CountOnPTADataHandler())
learned_model.visualize(name)


def gsm_IOAlergia_domain_knowldege():
from aalpy.learning_algs.general_passive.GeneralizedStateMerging import run_GSM
from aalpy.learning_algs.general_passive.ScoreFunctionsGSM import hoeffding_compatibility, ScoreCalculation
from aalpy.learning_algs.general_passive.ScoreFunctionsGSM import hoeffding_compatibility, SimpleFutureBasedCompatibility
from aalpy.learning_algs.general_passive.DataHandler import CountOnPTADataHandler
from aalpy.learning_algs.general_passive.GsmNode import GsmNode
from aalpy.utils.Sampling import get_io_traces, sample_with_length_limits
from aalpy import load_automaton_from_file
Expand All @@ -1320,12 +1323,12 @@ def ioa_compat_domain_knowledge(a: GsmNode, b: GsmNode):
return parity and ioa

scores = {
"IOA": ScoreCalculation(ioa_compat),
"IOA+DK": ScoreCalculation(ioa_compat_domain_knowledge),
"IOA": SimpleFutureBasedCompatibility(local_compatibility=ioa_compat, compatibility_on_pta=True),
"IOA+DK": SimpleFutureBasedCompatibility(local_compatibility=ioa_compat_domain_knowledge, compatibility_on_pta=True),
}
for name, score in scores.items():
learned_model = run_GSM(traces, output_behavior="moore", transition_behavior="stochastic", score_calc=score,
compatibility_on_pta=True, compatibility_on_futures=True)
data_handler=CountOnPTADataHandler())
learned_model.visualize(name)

def k_tails_example():
Expand All @@ -1336,16 +1339,13 @@ def k_tails_example():
input_alphabet_size=3,
output_alphabet_size=3)

# data is a list of sequences in this format [(i1, o1), (i2, o1), (i1, o3)]
data = generate_input_output_data_from_automata(model, num_sequences=2000,
min_seq_len=1, max_seq_len=12,
sequance_type='io_traces')

# k-trails works with prefix-closed input output traces, not labeled sequences like RPNI
# data is a list of sequences in this format [(i1, o1), (i2, o1), (i1, o3)]

# run k_tails with two different k's
k_trails_1 = run_k_tails(data, k=3, automaton_type='moore', print_info=True)

k_tails_1 = run_k_tails(data, k=3, automaton_type='moore', print_info=True)
k_tails_2 = run_k_tails(data, k=8, automaton_type='mealy', print_info=True)


Expand Down
2 changes: 1 addition & 1 deletion aalpy/learning_algs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
from .deterministic_passive.PAPNI import run_PAPNI
from .deterministic_passive.active_RPNI import run_active_RPNI
from .general_passive.GeneralizedStateMerging import run_GSM
from .general_passive.GsmAlgorithms import run_EDSM, run_Alergia_EDSM, run_k_tails
from .general_passive.GsmAlgorithms import run_EDSM, run_Alergia_GSM, run_Alergia_EDSM, run_k_tails
from .resetless.hW import run_hW
from .resetless.resetless_oracles import hWOracle, RandomhWOracle, RandomWphWOracle
63 changes: 63 additions & 0 deletions aalpy/learning_algs/general_passive/AssociatedData.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import math
from abc import abstractmethod, ABC
from collections import defaultdict
from typing import Any


ProbabilityDict = dict[Any, dict[Any, float]]

class StochasticData(ABC):
"""
Interface class for data used with `transition_behavior` set to "stochastic".
"""
@abstractmethod
def get_probabilities(self) -> ProbabilityDict:
"""
Method for extracting transition probabilities when converting to automaton models.

:return ProbabilityDict: Nested dictionary of transition probabilities.
"""
pass

CountDict = dict[Any, dict[Any, int]]

def int_dict_increment(c_dict, out_sym, cnt):
c_dict[out_sym] = c_dict.get(out_sym, 0) + cnt

class CountData(StochasticData):
def __init__(self):
# TODO get rid of this indirection
self.transition_count: CountDict = defaultdict(dict)

def local_log_likelihood_contribution(self):
llc = 0
for in_sym, trans in self.transition_count.items():
total_count = 0
for out_sym, count in trans.items():
total_count += count
llc += count * math.log(count)
if total_count != 0:
llc -= total_count * math.log(total_count)
return llc

def count(self):
return sum(sum(trans.values()) for trans in self.transition_count.values())

def get_probabilities(self) -> ProbabilityDict:
ret = dict()
for in_sym, trans in self.transition_count.items():
total_count = sum(trans.values())
ret[in_sym] = {out_sym: count / total_count for out_sym, count in trans.items()}
return ret


ShadowPTA = dict[Any, dict[Any, 'GsmNode']]
class ShadowPTAData:
def __init__(self):
self.shadow_pta: ShadowPTA = defaultdict(dict)

class CountOnPTAData(ShadowPTAData, CountData):
def __init__(self):
ShadowPTAData.__init__(self)
CountData.__init__(self)
self.pta_count: CountDict = defaultdict(dict)
Loading
Loading