Commit 90600f0b authored by Michele's avatar Michele
Browse files

working on existing methods to match new definitions

parent ac26df47
...@@ -382,6 +382,7 @@ class LearningAlgorithm: ...@@ -382,6 +382,7 @@ class LearningAlgorithm:
if label in self._teacher.getInputAlphabet(): if label in self._teacher.getInputAlphabet():
hyp.addTransition(assignments[row], label, hyp.addTransition(assignments[row], label,
hyp.getChaosDelta()) hyp.getChaosDelta())
# TODO: getPossibleOutputs is deprecated
elif label in self._table.getPossibleOutputs(row): elif label in self._table.getPossibleOutputs(row):
hyp.addTransition(assignments[row], label, hyp.addTransition(assignments[row], label,
hyp.getChaos()) hyp.getChaos())
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
import csv import csv
import os, inspect import os, inspect
from itertools import permutations, product from itertools import permutations, product, combinations
import logging import logging
import helpers.traces as th import helpers.traces as th
import helpers.deprecator as d import helpers.deprecator as d
...@@ -285,15 +285,12 @@ class Table: ...@@ -285,15 +285,12 @@ class Table:
return modified return modified
def isNotQuiescenceReducible(self): def isNotQuiescenceReducible(self):
newSuffixes = set() newSuffixes = self._isNotReducible()
hMinusSuffixes = self._isNotReducible() if newSuffixes:
newSuffixes = newSuffixes.union(hMinusSuffixes)
if newSuffixes != set():
# priority to hMinus suffixes # priority to hMinus suffixes
return newSuffixes return newSuffixes
else: else:
hPlusSuffixes = self._isNotReducible(chaos=True) return self._isNotReducible(chaos=True)
return newSuffixes.union(hPlusSuffixes)
def _isNotReducible(self, chaos=False): def _isNotReducible(self, chaos=False):
# function used for filtering rows, we want only those which # function used for filtering rows, we want only those which
...@@ -301,7 +298,7 @@ class Table: ...@@ -301,7 +298,7 @@ class Table:
rowsWithQuiescence = lambda x: self._quiescence in self._entries[x][0] rowsWithQuiescence = lambda x: self._quiescence in self._entries[x][0]
# past contains the pair of rows already visited. # past contains the pair of rows already visited.
# If I visited already a pair for one loop of the following for loop, # If I visited already a pair for one loop,
# I do not need to check it again for other loops. Thus past is outside # I do not need to check it again for other loops. Thus past is outside
# the for loop. # the for loop.
past = set() past = set()
...@@ -313,21 +310,25 @@ class Table: ...@@ -313,21 +310,25 @@ class Table:
moreSpecific = lambda x: lambda y: self._moreSpecificRow(y, x, chaos) moreSpecific = lambda x: lambda y: self._moreSpecificRow(y, x, chaos)
listOfRows = list(filter(moreSpecific(row1Extended), self._rowsInS)) listOfRows = list(filter(moreSpecific(row1Extended), self._rowsInS))
# I want the most specific one rowsToCheck = set()
row2 = listOfRows.pop() # get representative of each equivalence class
for row in listOfRows: eqClasses = self.getEquivalenceClasses(chaos)
if self._moreSpecificRow(row, row2, chaos): for row in eqClasses:
row2 = row if row in listOfRows:
if row1 == row2: rowsToCheck.add(row)
# same row, simulation is trivial
continue
if (row1, row2) in past:
# I already visited this pair. Go to next state enabling quiescence
continue
wait = set() wait = set()
wait.add((row1, row2, ())) for row in rowsToCheck:
if row1 == row:
# same row, simulation is trivial
continue
if (row1, row) in past:
# I already visited this pair. Go to next state enabling quiescence
continue
wait.add((row1, row, ()))
while wait: while wait:
current = wait.pop() current = wait.pop()
past.add((current[0],current[1])) past.add((current[0],current[1]))
...@@ -337,10 +338,9 @@ class Table: ...@@ -337,10 +338,9 @@ class Table:
# If we are in chaos_quiescence only quiescence is enabled # If we are in chaos_quiescence only quiescence is enabled
enabledRow2.add(self._quiescence) enabledRow2.add(self._quiescence)
else: else:
# input enabledness outputs = self.getOutputs(current[1])
outputs = self._entries[current[1]][0]
enabledInputs = self._possibleInputs(current[1], outputs) enabledInputs = self._possibleInputs(current[1], outputs)
enabledRow2 = self._entries[current[1]][0].union(enabledInputs) enabledRow2 = outputs.union(enabledInputs)
for label in enabledRow2: for label in enabledRow2:
enabledRow1 = set() enabledRow1 = set()
if current[0] == "chaos_quiescence": if current[0] == "chaos_quiescence":
...@@ -348,9 +348,9 @@ class Table: ...@@ -348,9 +348,9 @@ class Table:
# enabled # enabled
enabledRow1.add(self._quiescence) enabledRow1.add(self._quiescence)
else: else:
outputs = self._entries[current[0]][0] outputs = self.getOutputs(current[0])
enabledInputs = self._possibleInputs(current[0], outputs) enabledInputs = self._possibleInputs(current[0], outputs)
enabledRow1 = self._entries[current[0]][0].union(enabledInputs) enabledRow1 = outputs.union(enabledInputs)
if label not in enabledRow1: if label not in enabledRow1:
suffixes = set() suffixes = set()
suffixes.add(current[2]) suffixes.add(current[2])
...@@ -365,13 +365,15 @@ class Table: ...@@ -365,13 +365,15 @@ class Table:
newRow1 = "chaos_quiescence" newRow1 = "chaos_quiescence"
else: else:
try: try:
# the table is closed, there should be a row # the table is closed, there should be some row
# more specific than current[0] + (label,) in S # more specific than current[0] + (label,) in S
listOfRows = list(filter(moreSpecific(current[0] + (label,)), self._rowsInS)) listOfRows = list(filter(moreSpecific(current[0] + (label,)), self._rowsInS))
newRow1 = listOfRows.pop() newRow1 = set()
for row in listOfRows: # get representative of each equivalence class
if self._moreSpecificRow(row, newRow1, chaos): eqClasses = self.getEquivalenceClasses(chaos)
newRow1 = row for row in eqClasses:
if row in listOfRows:
newRow1.add(row)
except IndexError as error: except IndexError as error:
# current[0] + (label,) might not have # current[0] + (label,) might not have
# corresponding row in S. This is the case when # corresponding row in S. This is the case when
...@@ -393,10 +395,12 @@ class Table: ...@@ -393,10 +395,12 @@ class Table:
else: else:
try: try:
listOfRows = list(filter(moreSpecific(current[1] + (label,)), self._rowsInS)) listOfRows = list(filter(moreSpecific(current[1] + (label,)), self._rowsInS))
newRow2 = listOfRows.pop() newRow2 = set()
for row in listOfRows: # get representative of each equivalence class
if self._moreSpecificRow(row, newRow2, chaos): eqClasses = self.getEquivalenceClasses(chaos)
newRow2 = row for row in eqClasses:
if row in listOfRows:
newRow2.add(row)
except IndexError as error: except IndexError as error:
# current[1] + (label,) might not have # current[1] + (label,) might not have
# corresponding row in S. This is the case when # corresponding row in S. This is the case when
...@@ -412,40 +416,39 @@ class Table: ...@@ -412,40 +416,39 @@ class Table:
self._logger.error("Table printed in ./tables/_error_table.csv") self._logger.error("Table printed in ./tables/_error_table.csv")
self._logger.error(error) self._logger.error(error)
raise raise
if (newRow1,newRow2) not in past and newRow1 != newRow2: for (newSingle1,newSingle2) in product(newRow1, newRow2):
wait.add((newRow1, newRow2, current[2] + (label,))) if (newSingle1,newSingle2) not in past and newSingle1 != newSingle2:
wait.add((newSingle1, newSingle2, current[2] + (label,)))
return set() return set()
def isNotGloballyConsistent(self): def isNotGloballyConsistent(self):
newSuffixes = set() newSuffixes = self._isNotConsistent()
hMinusSuffixes = self._isNotConsistent()
newSuffixes.union(hMinusSuffixes)
if newSuffixes: if newSuffixes:
# priority to hMinus suffixes # priority to hMinus suffixes
return newSuffixes return newSuffixes
else: else:
hPlusSuffixes = self._isNotConsistent(chaos=True) return self._isNotConsistent(chaos=True)
return newSuffixes.union(hPlusSuffixes)
def _isNotConsistent(self, chaos=False): def _isNotConsistent(self, chaos=False):
# TODO refactor entire method? # get all combinations of rows in S, with repetitions for hPlus
# get all combinations of rows in S, with repetitions combi = combinations(self._rowsInS, 2)
combi = permutations(self._rowsInS, 2) if not chaos:
combi = permutations(self._rowsInS, 2)
newColumns = set() newColumns = set()
for row1, row2 in combi: for row1, row2 in combi:
# If the rows are in the same equivalence class # If the rows are in the same equivalence class
if self._moreSpecificRow(row1, row2, chaos): if self._moreSpecificRow(row1, row2, chaos):
#row2 is in the equivalence class defined by row1 #row2 is in the equivalence class defined by row1
# get all possible (enabled) labels # get all possible (enabled) labels of row1
inputs = self._possibleInputs(row1) inputs = self._possibleInputs(row1)
outputs = self._entries[row1][0] outputs = self.getOutputs(row1)
labels = inputs.union(outputs) labels = inputs.union(outputs)
for label in labels: for label in labels:
rowExt1 = th.flatten(row1 + (label,),self._quiescence) rowExt1 = th.flatten(row1 + (label,),self._quiescence)
rowExt2 = rowExt1 = th.flatten(row2 + (label,),self._quiescence) rowExt2 = th.flatten(row2 + (label,),self._quiescence)
# rowExt1 must be in Rows, because label is either an # rowExt1 must be in Rows, because label is either an
# enabled input after row, or an observed output. # enabled input after row, or an observed output.
if rowExt1 not in self._rows: if rowExt1 not in self._rows:
...@@ -455,7 +458,6 @@ class Table: ...@@ -455,7 +458,6 @@ class Table:
# of _moreSpecificRow() # of _moreSpecificRow()
if chaos == False: if chaos == False:
# if we are checking Hminus, then there is an error # if we are checking Hminus, then there is an error
# TODO: might be that I did not add a one letter extension when needed?
self._logger.error("Error while checking consistency for Hminus: the prefix "+str(rowExt1)+" should be a row, but it is not.") self._logger.error("Error while checking consistency for Hminus: the prefix "+str(rowExt1)+" should be a row, but it is not.")
self.printTable(prefix="_error") self.printTable(prefix="_error")
self._logger.error("Table printed in ./tables/_error_table.csv") self._logger.error("Table printed in ./tables/_error_table.csv")
...@@ -463,7 +465,7 @@ class Table: ...@@ -463,7 +465,7 @@ class Table:
else: else:
# if we are checking Hplus (and label is an output) # if we are checking Hplus (and label is an output)
# then there is a transition to a chaotic state # then there is a transition to a chaotic state
if label in self._possibleOutputs(row1): if label in outputs:
if label == self._quiescence: if label == self._quiescence:
# rowExt1 is chaos_quiescence # rowExt1 is chaos_quiescence
rowExt1 = "chaos_quiescence" rowExt1 = "chaos_quiescence"
...@@ -471,22 +473,30 @@ class Table: ...@@ -471,22 +473,30 @@ class Table:
# rowExt1 is chaos # rowExt1 is chaos
rowExt1 = "chaos" rowExt1 = "chaos"
else: else:
# label is an input, error.
self._logger.error("Error while checking consistency for Hplus: the prefix "+str(rowExt1)+" should be a row, but it is not.") self._logger.error("Error while checking consistency for Hplus: the prefix "+str(rowExt1)+" should be a row, but it is not.")
self.printTable(prefix="_error") self.printTable(prefix="_error")
self._logger.error("Table printed in ./tables/_error_table.csv") self._logger.error("Table printed in ./tables/_error_table.csv")
if not rowExt2 in self._rows: if not rowExt2 in self._rows:
# rowExt2 is not in rows. If label is an input then if chaos == False:
# row2 does not enable it: row1 should not be more # if we are checking Hminus, then there is an error
# specific than row2 self._logger.error("Error while checking consistency for Hminus: the prefix "+str(rowExt2)+" should be a row, but it is not.")
if label in inputs:
self._logger.error("Error while checking consistency: the prefix "+str(rowExt2)+" should be a row, but it is not.")
self.printTable(prefix="_error") self.printTable(prefix="_error")
self._logger.error("Table printed in ./tables/_error_table.csv") self._logger.error("Table printed in ./tables/_error_table.csv")
raise raise
# If label is an output, then ok (being less specific,
# this is allowed)
else: else:
continue # rowExt2 is not in rows. If label is an input then
# row2 does not enable it: row1 should not be more
# specific than row2
if label in inputs:
self._logger.error("Error while checking consistency: the prefix "+str(rowExt2)+" should be a row, but it is not.")
self.printTable(prefix="_error")
self._logger.error("Table printed in ./tables/_error_table.csv")
raise
# If label is an output, then ok (being less specific,
# this is allowed)
else:
continue
# rowExt1 in self._rows and rowExt2 in self._rows # rowExt1 in self._rows and rowExt2 in self._rows
# I could call self._moreSpecificRow() but then # I could call self._moreSpecificRow() but then
...@@ -498,9 +508,15 @@ class Table: ...@@ -498,9 +508,15 @@ class Table:
entry2 = th.flatten(rowExt2 + column, self._quiescence) entry2 = th.flatten(rowExt2 + column, self._quiescence)
if (rowExt1 == "chaos_quiescence" or rowExt1 == "chaos"): if (rowExt1 == "chaos_quiescence" or rowExt1 == "chaos"):
#TODO: check if entry2+column has chaotic behaviour # We are checking hPlus
print(erfefr) # Does entry2 has chaotic behaviour?
continue extendedOutputs = self._outputs.union((self._quiescence,))
if self._entries[entry2] != extendedOutputs:
self._logger.warning("Checking chaotic behaviour of entry2: not chaotic")
newColumns.add((label,) + column)
break
else:
continue
entry1 = th.flatten(rowExt1 + column, self._quiescence) entry1 = th.flatten(rowExt1 + column, self._quiescence)
if (entry1 not in self._entries and if (entry1 not in self._entries and
...@@ -518,11 +534,11 @@ class Table: ...@@ -518,11 +534,11 @@ class Table:
newColumns.add((label,) + column) newColumns.add((label,) + column)
break break
if newColumns: # if newColumns:
# TODO: only one suffix per round, ok? # # Only one suffix per round, ok?
break # break
if newColumns: # if newColumns:
break # break
return newColumns return newColumns
...@@ -565,7 +581,9 @@ class Table: ...@@ -565,7 +581,9 @@ class Table:
# is the entry final? If False, we can still observe some outputs # is the entry final? If False, we can still observe some outputs
def _isFinal(self, entry): def _isFinal(self, entry):
# TODO: check if entries contains entry if entry not in self._entries.keys():
self._logger.warning("Checking if entry is final: entry is not in the table.")
return False
return self._entries[entry][0] == self._entries[entry][1] return self._entries[entry][0] == self._entries[entry][1]
# get a final entry with given set of outputs # get a final entry with given set of outputs
...@@ -575,7 +593,6 @@ class Table: ...@@ -575,7 +593,6 @@ class Table:
# Private method for creating entries from rows and columns # Private method for creating entries from rows and columns
def _createEntries(self): def _createEntries(self):
for row in self._rows: for row in self._rows:
# Row should be flattened.
for column in self._columns: for column in self._columns:
newEntry = row + column newEntry = row + column
# If there are multiple quiescence in sequence, reduce to # If there are multiple quiescence in sequence, reduce to
...@@ -584,6 +601,7 @@ class Table: ...@@ -584,6 +601,7 @@ class Table:
# Check if the concatenation of the two tuples is # Check if the concatenation of the two tuples is
# already in _entries: # already in _entries:
if filteredEntry not in self._entries.keys(): if filteredEntry not in self._entries.keys():
# Special cases:
if (len(row) > 0 and row[-1] == self._quiescence and if (len(row) > 0 and row[-1] == self._quiescence and
len(column) > 0 and column[0] in self._outputs): len(column) > 0 and column[0] in self._outputs):
# If row ends in delta and column starts with an output, # If row ends in delta and column starts with an output,
...@@ -599,7 +617,7 @@ class Table: ...@@ -599,7 +617,7 @@ class Table:
# if columns starts with output x (or quiescence) and # if columns starts with output x (or quiescence) and
# x not in entry[row+()] then mark row+column as # x not in entry[row+()] then mark row+column as
# 'it could be impossible to obtain' # 'it could be impossible to obtain'
# if row + () is true, we will NEVER observe that # if row + () is final, we will NEVER observe that
# output # output
self._entries[filteredEntry] = self._emptyFinalEntry(filteredEntry) self._entries[filteredEntry] = self._emptyFinalEntry(filteredEntry)
continue continue
...@@ -627,10 +645,12 @@ class Table: ...@@ -627,10 +645,12 @@ class Table:
# quiescence, then the entry for row is the same as the # quiescence, then the entry for row is the same as the
# entry for the proper prefix [VT15] # entry for the proper prefix [VT15]
if len(row) > 0 and row[-1] == self._quiescence: if len(row) > 0 and row[-1] == self._quiescence:
# TODO: check that entries contains row[:-1] if row[:-1] not in self._entries.keys():
(outputs, observed) = self._entries[row[:-1]] self._logger.warning("Checking delta loops: shorter trace is not in the table.")
if self._isFinal(row[:-1]) and outputs == set((self._quiescence,)): else:
return True (outputs, observed) = self._entries[row[:-1]]
if self._isFinal(row[:-1]) and outputs == set((self._quiescence,)):
return True
return False return False
def getDeltaTraces(self, trace): def getDeltaTraces(self, trace):
...@@ -659,6 +679,7 @@ class Table: ...@@ -659,6 +679,7 @@ class Table:
# Return the set of outputs that can be observed after trace # Return the set of outputs that can be observed after trace
# TODO: not clear the purpose of this method. rename? # TODO: not clear the purpose of this method. rename?
@d.deprecated
def getPossibleOutputs(self, trace): def getPossibleOutputs(self, trace):
if trace in self._entries and self._entries[trace][1]: if trace in self._entries and self._entries[trace][1]:
return self._entries[trace][0] return self._entries[trace][0]
...@@ -666,6 +687,7 @@ class Table: ...@@ -666,6 +687,7 @@ class Table:
return self._possibleOutputs(trace, None) return self._possibleOutputs(trace, None)
# Update an entry in the table. If it does not exist, create it # Update an entry in the table. If it does not exist, create it
# observation is the answer of the observation orale
def updateEntry(self, trace, output=None, observation=None): def updateEntry(self, trace, output=None, observation=None):
trace = th.flatten(trace, self._quiescence) trace = th.flatten(trace, self._quiescence)
if trace in self._entries: if trace in self._entries:
...@@ -682,10 +704,8 @@ class Table: ...@@ -682,10 +704,8 @@ class Table:
outputs.add(output) outputs.add(output)
if observation == None or observation == False: if observation == None or observation == False:
self._entries[trace] = self._emptyEntry(trace) self._entries[trace] = self._emptyEntry(trace)
observed = self._entries[trace][1]
else: else:
observed = outputs self._entries[trace] = self._finalEntry(trace, outputs)
self._entries[trace] = (outputs.copy(), observed.copy())
# given a trace, query outputPurpose for the outputs that are possibly # given a trace, query outputPurpose for the outputs that are possibly
# enabled after it # enabled after it
...@@ -706,7 +726,8 @@ class Table: ...@@ -706,7 +726,8 @@ class Table:
# Given two entries, check if the former is more specific than the latter # Given two entries, check if the former is more specific than the latter
# entry1 ⊑ entry2 => entry2.first is subset of entry1.first and entry1.second is subset of entry2.second # entry1 ⊑ entry2 => entry2.first is subset of entry1.first and entry1.second is subset of entry2.second
def _moreSpecificEntry(self, entry1, entry2, plus): def _moreSpecificEntry(self, entry1, entry2, plus):
# TODO: check if entry1 and entry2 are in entries if (entry1 not in self._entries.values() or entry2 not in self._entries.values()):
self._logger.warning("Checking relation between entries: one or both are not in the table.")
if not plus: if not plus:
return entry1[0] == entry2[0] # equality return entry1[0] == entry2[0] # equality
#return entry2[0].issubset(entry1[0]) # inclusion #return entry2[0].issubset(entry1[0]) # inclusion
...@@ -717,7 +738,7 @@ class Table: ...@@ -717,7 +738,7 @@ class Table:
# row1 ⊑ row2 => for each column, row1[column] ⊑ row2[column] # row1 ⊑ row2 => for each column, row1[column] ⊑ row2[column]
def _moreSpecificRow(self, row1, row2, plus=False): def _moreSpecificRow(self, row1, row2, plus=False):
# First: if they do not enable the same inputs, they are not in such # First: if they do not enable the same inputs, they are not in such
# a relation # a relation TODO: should this check be in moreSpecificEntry instead?
outputs1 = self._entries[th.flatten(row1,self._quiescence)][0] outputs1 = self._entries[th.flatten(row1,self._quiescence)][0]
outputs2 = self._entries[th.flatten(row2,self._quiescence)][0] outputs2 = self._entries[th.flatten(row2,self._quiescence)][0]
inputs1 = self._possibleInputs(th.flatten(row1,self._quiescence), outputs1) inputs1 = self._possibleInputs(th.flatten(row1,self._quiescence), outputs1)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment