Commit 90600f0b authored by Michele's avatar Michele

working on existing methods to match new definitions

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