Commit 19e69b7b authored by VIGNET Pierre's avatar VIGNET Pierre
Browse files

[lib] Refactor tests for CLUnfolder; add tests for init_forward_unfolding and shift

parent 232470de
......@@ -39,20 +39,49 @@
##
## Contributor(s):
##
'''
Unitary Test of the unfolder
'''
"""Unitary Test of the unfolder"""
from __future__ import print_function
import unittest
import pytest
import pkg_resources
from cadbiom.models.guard_transitions.chart_model import ChartModel
from cadbiom.models.clause_constraints.CLDynSys import Literal, Clause
from cadbiom.models.guard_transitions.analyser.ana_visitors import TableVisitor
from cadbiom.models.clause_constraints.CLDynSys import CLDynSys
from cadbiom.models.clause_constraints.mcl.MCLTranslators import GT2Clauses
from cadbiom.models.clause_constraints.mcl.MCLTranslators import GT2Clauses
from cadbiom.models.clause_constraints.mcl.CLUnfolder import CLUnfolder
from cadbiom.models.clause_constraints.mcl.MCLAnalyser import MCLAnalyser
from cadbiom.models.clause_constraints.mcl.MCLQuery import MCLSimpleQuery
class ErrorReporter(object):
"""error reporter of the compil type"""
def __init__(self):
self.context = ""
self.error = False
pass
def display(self, mess):
"""
just printing
"""
self.error = True
print('\n>> '+self.context+" "+mess)
def display_info(self, mess):
"""
just printing
"""
print('\n-- '+mess)
def set_context(self, cont):
"""
for transition compiling
"""
self.context = cont
# helper functions
def string_to_clause(text_clause):
......@@ -87,8 +116,7 @@ def model1():
def model2():
"""
A simple ChartModel with two nodes and a transition
and a cycle without start
ChartModel model1 + 3 nodes and 2 transitions and a cycle without start
"""
# build dynamical system
model = model1()
......@@ -103,8 +131,7 @@ def model2():
def model3():
"""
A simple ChartModel with two nodes and a transition
and a cycle without start
ChartModel model2 + 1 node + 1 start node and a transition
"""
# build dynamical system
model = model2()
......@@ -116,7 +143,11 @@ def model3():
def model4():
"""
Simple chart model with one input and two free clocks
ChartModel with:
- 1 input node: in1
- 5 nodes: n1, n2, n3, n4, n5
- 5 transitions: n1-n2, n3-n4, n4-n5, n5-n3, in1-n1
- 2 free clocks: hh1: (n3 or n4) on n1-n2, hh2: (n1 and n3) on n4-n5
"""
model = ChartModel("Test")
root = model.get_root()
......@@ -145,9 +176,7 @@ def model4():
return model
def create_unfolder(model):
"""
returns an unfolder for model
"""
"""Return an unfolder for the given model"""
tvisit = TableVisitor(None) # no error display
model.accept(tvisit)
cld = CLDynSys( tvisit.tab_symb, None)
......@@ -157,21 +186,37 @@ def create_unfolder(model):
# unfolder
return CLUnfolder(cld)
# simple reporter: register errors
class ErrorReporter(object):
"""
Simple reporter for tests
"""
def __init__(self):
self.error = False
@pytest.fixture()
def textual_properties():
"""start, invariant, final"""
return (
("M", "L", "C"), # No solution (M activated)
("", "L", "C and K"), # Solution: I E D F
("", "", "C"), # Solutions: F E L D, I E D F
)
@pytest.fixture()
def numerical_properties():
return (
([[13]], [[12]], [[3]]),
([], [[12]], [[3, -47], [11, -47], [-3, -11, 47], [47]]),
)
@pytest.fixture()
def feed_mclanalyser():
"""Setup MCLAnalyser with a bcx model"""
rep = ErrorReporter()
# Force __var_list to be sorted at the output of base_var_set from
# CLDynSys for reproductibility in tests (literals naming)
mcla = MCLAnalyser(rep, debug=True)
filename = pkg_resources.resource_filename(
__name__, # package name
"examples/example_model.bcx"
)
mcla.build_from_chart_file(filename)
return mcla
def display(self, mess):
"""
as said
"""
self.error = True
#print('\n>> '+mess)
class TestCLUnfolder(unittest.TestCase):
"""
......@@ -179,7 +224,7 @@ class TestCLUnfolder(unittest.TestCase):
"""
def test_var_name(self):
"""
Test of variables uncoding and decoding
Test of variables at initial state uncoding and decoding
"""
model = model1()
unfolder = create_unfolder(model)
......@@ -215,7 +260,7 @@ class TestCLUnfolder(unittest.TestCase):
"""
Test frontier computation and encoding
"""
# model1
## model1
model = model1()
unfolder = create_unfolder(model)
# test frontier: should be n1
......@@ -224,7 +269,7 @@ class TestCLUnfolder(unittest.TestCase):
res = res and (len(unfolder.frontier_values) == 1)
self.assert_(res,'Error in frontier: model1')
# model2 (one cycle without start)
## model2 (one cycle without start)
model = model2()
unfolder = create_unfolder(model)
......@@ -234,7 +279,7 @@ class TestCLUnfolder(unittest.TestCase):
res = res and (len(unfolder.frontier_values) == 1)
self.assert_(res,'Error in frontier: model2')
# model3: same as model2 but with a start on n3
## model3: same as model2 but with a start on n3
model = model3()
unfolder = create_unfolder(model)
......@@ -245,11 +290,10 @@ class TestCLUnfolder(unittest.TestCase):
res = res and unfolder.get_var_name(frontier_values[1]) == 'n3'
self.assert_(res,'Error in frontier: model3')
# model4
## model4 - No frontiers (even if we can get input node in1 in the solutions)
model = model4()
unfolder = create_unfolder(model)
frontier_values = unfolder.frontier_values
res = len(frontier_values) == 0
res = len(unfolder.frontier_values) == 0
self.assert_(res,'Error in frontier: model4')
......@@ -281,16 +325,464 @@ class TestCLUnfolder(unittest.TestCase):
res = found_names == {'in1'}
self.assert_(res,'Error in inputs: model4')
@unittest.skip("Test is not implemented")
def test_init_forward(self):
"""
test forward initialization for various models and property
"""
pass
# Dommage...
def init_forward_unfolding_part_1(unfolder):
"""Initialization step only"""
unfolder._CLUnfolder__shift_direction = 'FORWARD'
unfolder._CLUnfolder__current_step = 1
unfolder._CLUnfolder__shift_step = unfolder.shift_step_init # back to basic!
unfolder._CLUnfolder__aux_code_table = dict() # flush auxiliary variables
unfolder._CLUnfolder__aux_list = [] # idem
# Init properties to generate all variable num codes
unfolder._CLUnfolder__init_initial_constraint_0()
unfolder._CLUnfolder__init_final_constraint_0()
unfolder._CLUnfolder__init_invariant_constraint_0()
unfolder._CLUnfolder__init_variant_constraints_0()
def init_forward_unfolding_part_2(unfolder):
"""Shift step only"""
# Now shifting is possible
unfolder._CLUnfolder__forward_init_dynamic()
unfolder._CLUnfolder__shift_final()
unfolder._CLUnfolder__shift_invariant()
# PS: __variant_constraints si already initialized for the first step.
def test_init_unfolder(feed_mclanalyser):
mcla = feed_mclanalyser
unfolder = mcla.unfolder # shortcut
# Initialization step only
init_forward_unfolding_part_1(mcla.unfolder)
## Test CLDynSys
# 15 places
assert unfolder.get_var_number() == 46
# __var_list is built on casted set; MCLAnalyser is un debug mode
# so the list is sorted
expected = [
'##',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'P',
'_h2', '_h3', '_h4', '_h5', '_h6', '_h7', '_h_0', '_h_1', '_lit0', '_lit1',
'_lit10', '_lit11', '_lit12', '_lit13', '_lit14', '_lit15', '_lit16',
'_lit17', '_lit18', '_lit19', '_lit2', '_lit20', '_lit21', '_lit22',
'_lit3', '_lit4', '_lit5', '_lit6', '_lit7', '_lit8', '_lit9'
]
print(unfolder._CLUnfolder__var_list)
assert unfolder._CLUnfolder__var_list == expected
## Test mapping of variables names to values
expected_var_code_table = {
'_lit22': 39, '_lit20': 37, '_lit21': 38, '_lit19': 35,
'_lit18': 34, '_lit6': 43, '_lit17': 33, '_lit13': 29, '_lit9': 46,
'_lit8': 45, '_h_0': 22, '_h_1': 23, '_lit16': 32, '_lit12': 28,
'_lit3': 40, '_lit2': 36, '_lit1': 25, '_lit0': 24, '_lit7': 44,
'_lit11': 27, '_lit5': 42, '_lit4': 41,
'A': 1, 'C': 3, 'B': 2, 'E': 5, 'D': 4, 'G': 7, 'F': 6, 'I': 9, 'H': 8,
'K': 11, 'J': 10, 'M': 13, 'L': 12, '_lit15': 31, 'N': 14, 'P': 15,
'_lit14': 30, '_h4': 18, '_h5': 19, '_h6': 20, '_h7': 21, '_h2': 16,
'_h3': 17, '_lit10': 26
}
# Check presence of all basic literals (not future ones with "`" added)
found_var_code_table = list(unfolder._CLUnfolder__var_code_table.items())
print(found_var_code_table)
for lit_name_val in expected_var_code_table.items():
assert lit_name_val in found_var_code_table
# var_code_table is twice as big as __var_list items
# (future literals with "`" are added)
assert len(found_var_code_table) == 2 * len(expected_var_code_table)
## Test frontiers, places, values and names
print("Frontier values", unfolder.frontier_values)
assert set(unfolder.frontier_values) == {4, 5, 6, 7, 9, 12, 14} # D E F G I L N
frontiers_values_mapping = {
value: unfolder.get_var_name(value) for value in unfolder.frontier_values
}
print("Frontiers values mapping", frontiers_values_mapping)
assert frontiers_values_mapping == {4: 'D', 5: 'E', 6: 'F', 7: 'G', 9: 'I', 12: 'L', 14: 'N'}
found = unfolder.frontiers_negative_values
print("Frontiers negatives values", found)
assert found == frozenset([-14, -12, -9, -7, -6, -5, -4])
found = unfolder.frontiers_pos_and_neg
assert found == frozenset([4, 5, 6, 7, 9, 12, 14, -14, -12, -9, -7, -6, -5, -4])
# Check clocks
free_clocks = unfolder._CLUnfolder__free_clocks
print("Free clocks/events", free_clocks)
assert free_clocks == frozenset([16, 17, 18, 19, 20, 21, 22, 23])
clocks_values_mapping = {
value: unfolder.get_var_name(value) for value in unfolder._CLUnfolder__free_clocks
}
print("Clocks values mapping", clocks_values_mapping)
assert clocks_values_mapping == {16: '_h2', 17: '_h3', 18: '_h4', 19: '_h5', 20: '_h6', 21: '_h7', 22: '_h_0', 23: '_h_1'}
# Check input places
assert unfolder._CLUnfolder__inputs == frozenset()
# Not frontiers: A B C H J K M P
found = unfolder._CLUnfolder__no_frontier_init
print("Places not frontiers", found)
assert found == [[-1], [-2], [-3], [-8], [-10], [-11], [-13], [-15]]
def test_init_forward(feed_mclanalyser, textual_properties, numerical_properties):
"""Test forward initialization for various models and properties types
Specific call:
pytest cadbiom/models/clause_constraints/mcl/TestCLUnfolder.py::test_init_forward
Query attributes:
self.dim_start = [] # list<DClause>
self.dim_inv = [] # list<DClause>
self.dim_final = [] # list<DClause>
self.dim_variant_prop = [] # list<list<DClause>>
CLUnfolder attributes that contain query attributes:
self.__initial_property = None # logical formula - literal boolean expression
self.__dimacs_initial = None # list of DIMACS clauses
self.__final_property = None # logical formula
self.__dimacs_final = None # list of DIMACS clauses
self.__invariant_property = None # logical formula
self.__dimacs_invariant = None # list of DIMACS clauses
self.__variant_property = None # list<logic formulas>
self.__dimacs_variant = None # list<list<DIMACS clauses>>
CLUnfolder attributes that contain clauses:
self.__list_variant_constraints = None # list<list<DIMACS clauses>>
dynamic_constraints => __dynamic_constraints
initial_constraints => __initial_constraints
invariant_constraints => __invariant_constraints
variant_constraints => __variant_constraints
final_constraints => __final_constraints
Mangling prefix: unfolder._CLUnfolder
TODO:
- search solutions for the given properties
- test variant properties
"""
mcla = feed_mclanalyser
## First properties
### Textual properties
start, invariant, final = textual_properties[0]
query = MCLSimpleQuery(start, invariant, final)
mcla.unfolder.init_with_query(query)
init_forward_unfolding_solution_1(mcla)
## TODO: search sol
### DIMACS properties
dim_start, dim_inv, dim_final = numerical_properties[0]
query = MCLSimpleQuery(None, None, None)
query.dim_start = dim_start
query.dim_inv = dim_inv
query.dim_final = dim_final
mcla.unfolder.init_with_query(query)
init_forward_unfolding_solution_1(mcla)
## Second properties
### Textual properties
start, invariant, final = textual_properties[1]
query = MCLSimpleQuery(start, invariant, final)
mcla.unfolder.init_with_query(query)
init_forward_unfolding_solution_2(mcla)
## TODO: search sol
### DIMACS properties: TODO: marche pas: les litéraux supplémentaires ne sont pas pris en compte...
dim_start, dim_inv, dim_final = numerical_properties[1]
query = MCLSimpleQuery(None, None, None)
query.dim_start = dim_start
query.dim_inv = dim_inv
query.dim_final = dim_final
mcla.unfolder.init_with_query(query)
init_forward_unfolding_solution_2(mcla)
def init_forward_unfolding_solution_1(mcla):
"""
- Test first part of init_forward_unfolding: init of constraints
- Test second part of init_forward_unfolding: shift of initialized constraints
("M", "L", "C"), # No solution (M activated)
"""
unfolder = mcla.unfolder # shortcut
init_forward_unfolding_part_1(mcla.unfolder)
############################################################################
print(unfolder.dynamic_constraints)
print(unfolder.initial_constraints)
print(unfolder.invariant_constraints)
print(unfolder.variant_constraints)
print(unfolder.final_constraints)
# No auxiliary variables
assert unfolder._CLUnfolder__aux_code_table == dict()
assert unfolder._CLUnfolder__aux_list == []
# MCLA could be reused, so __dynamic_constraints is not empty at this step
# artificial reset
# unfolder._CLUnfolder__dynamic_constraints = []
# assert unfolder.dynamic_constraints == []
# no frontiers + M (start place)
assert unfolder.initial_constraints == [[-1], [-2], [-3], [-8], [-10], [-11], [-13], [-15], [13]]
# L
assert unfolder.invariant_constraints == [[[12]]]
# No variant constraint
assert unfolder.variant_constraints == []
# C
assert unfolder.final_constraints == [[3]]
############################################################################
init_forward_unfolding_part_2(unfolder)
print(unfolder.dynamic_constraints)
print(unfolder.invariant_constraints)
print(unfolder.final_constraints)
# Shift of the system clauses (and syst aux clauses)
# The maximum value is 46 since there is 46 literals
expected = [
[[-24, 16], [-24, 4], [-16, -4, 24], [-15, 42], [-12, 42], [15, 12, -42],
[-42, 41], [-11, 41], [42, 11, -41], [2, -40], [41, -40], [-2, -41, 40],
[40, -36], [-13, -36], [-40, 13, 36], [1, -25], [36, -25], [-1, -36, 25],
[-43, 22], [-43, 25], [-22, -25, 43], [-47, 24, 1], [-47, 24, -43],
[47, -24], [47, -1, 43], [5, -44], [6, -44], [-5, -6, 44], [-45, 17],
[-45, 44], [-17, -44, 45], [1, -27], [41, -27], [-1, -41, 27], [27, -26],
[-13, -26], [-27, 13, 26], [2, -46], [26, -46], [-2, -26, 46], [-30, 22],
[-30, 46], [-22, -46, 30], [-48, 45, 2], [-48, 45, -30], [48, -45],
[48, -2, 30], [31, -30], [31, -43], [30, 43, -31], [-49, 31, 3], [49, -31],
[49, -3], [-50, 4], [-50, -24], [50, -4, 24], [-51, 5], [-51, -45],
[51, -5, 45], [-52, 6], [52, -6], [-32, 21], [-32, 7], [-21, -7, 32],
[-53, 7], [-53, -32], [53, -7, 32], [-54, 32, 8], [54, -32], [54, -8],
[-33, 20], [-33, 11], [-20, -11, 33], [-34, 18], [-34, 9], [-18, -9, 34],
[-55, 33, 9], [-55, 33, -34], [55, -33], [55, -9, 34], [-35, 19], [-35, 10],
[-19, -10, 35], [-56, 34, 10], [-56, 34, -35], [56, -34], [56, -10, 35],
[-57, 35, 11], [-57, 35, -33], [57, -35], [57, -11, 33], [-58, 12],
[58, -12], [-37, 23], [-37, 14], [-23, -14, 37], [-38, 23], [-38, 14],
[-23, -14, 38], [39, -37], [39, -38], [37, 38, -39], [-60, 14], [-60, -39],
[60, -14, 39], [-59, 37, 13], [59, -37], [59, -13], [-61, 38, 15], [61, -38],
[61, -15], [-18, 9], [-19, 10], [-20, 11], [-21, 7], [-16, 4], [-17, 5],
[-22, 1, 2], [-23, 14, 14]]]
# Crap
assert unfolder.dynamic_constraints == expected
# L
assert unfolder.invariant_constraints == [[[12]], [[58]]]
# No variant constraint (not shifted in second part of init_forward_unfolding)
assert unfolder.variant_constraints == []
# C + 46
assert unfolder.final_constraints == [[49]]
def init_forward_unfolding_solution_2(mcla):
"""
- Test first part of init_forward_unfolding: init of constraints
- Test second part of init_forward_unfolding: shift of initialized constraints
("", "L", "C and K") # I E D F
"""
unfolder = mcla.unfolder # shortcut
init_forward_unfolding_part_1(mcla.unfolder)
############################################################################
print(unfolder.dynamic_constraints)
print(unfolder.initial_constraints)
print(unfolder.invariant_constraints)
print(unfolder.variant_constraints)
print(unfolder.final_constraints)
# No auxiliary variables: "C and K" property is added
assert unfolder._CLUnfolder__aux_code_table == {'_lit47': 47}
assert unfolder._CLUnfolder__aux_list == ['_lit47']
# MCLA could be reused, so __dynamic_constraints is not empty at this step
# artificial reset
# unfolder._CLUnfolder__dynamic_constraints = []
# assert unfolder.dynamic_constraints == []
# no frontiers + nothing (no start place)
assert unfolder.initial_constraints == [[-1], [-2], [-3], [-8], [-10], [-11], [-13], [-15]]
# L
assert unfolder.invariant_constraints == [[[12]]]
# No variant constraint
assert unfolder.variant_constraints == []
# "C and K" = _lit47
# C, not _lit47
# K, not _lit47
# not C, not K, _lit47
# _lit47
assert unfolder.final_constraints == [[3, -47], [11, -47], [-3, -11, 47], [47]]
# PS:
# "C or K" = _lit47
# not C, _lit47
# not K, _lit47
# C, K, not _lit47
# _lit47
#[[-3, 47], [-11, 47], [3, 11, -47], [47]]
############################################################################
init_forward_unfolding_part_2(unfolder)
print(unfolder.dynamic_constraints)
print(unfolder.invariant_constraints)
print(unfolder.final_constraints)
# Shift of the system clauses (and syst aux clauses)
# The maximum value is now 48 since there is 47 literals
assert unfolder.get_var_number() == 46
assert unfolder.get_shift_step() == 47
expected = [
[[-24, 16], [-24, 4], [-16, -4, 24], [-15, 42], [-12, 42], [15, 12, -42],
[-42, 41], [-11, 41], [42, 11, -41], [2, -40], [41, -40], [-2, -41, 40],
[40, -36], [-13, -36], [-40, 13, 36], [1, -25], [36, -25], [-1, -36, 25],
[-43, 22], [-43, 25], [-22, -25, 43], [-48, 24, 1], [-48, 24, -43],
[48, -24], [48, -1, 43], [5, -44], [6, -44], [-5, -6, 44], [-45, 17],
[-45, 44], [-17, -44, 45], [1, -27], [41, -27], [-1, -41, 27], [27, -26],
[-13, -26], [-27, 13, 26], [2, -46], [26, -46], [-2, -26, 46], [-30, 22],
[-30, 46], [-22, -46, 30], [-49, 45, 2], [-49, 45, -30], [49, -45],
[49, -2, 30], [31, -30], [31, -43], [30, 43, -31], [-50, 31, 3], [50, -31],
[50, -3], [-51, 4], [-51, -24], [51, -4, 24], [-52, 5], [-52, -45],
[52, -5, 45], [-53, 6], [53, -6], [-32, 21], [-32, 7], [-21, -7, 32],
[-54, 7], [-54, -32], [54, -7, 32], [-55, 32, 8], [55, -32], [55, -8],
[-33, 20], [-33, 11], [-20, -11, 33], [-34, 18], [-34, 9], [-18, -9, 34],
[-56, 33, 9], [-56, 33, -34], [56, -33], [56, -9, 34], [-35, 19], [-35, 10],
[-19, -10, 35], [-57, 34, 10], [-57, 34, -35], [57, -34], [57, -10, 35],
[-58, 35, 11], [-58, 35, -33], [58, -35], [58, -11, 33