Commit 12d69d20 authored by Michele's avatar Michele

added algorithm for double set in observatin table

parent 0fdb63f9
......@@ -46,13 +46,6 @@ class LearningAlgorithm:
# this update uses a realistic teacher. If I need an output to happen I
# cannot force it to happen.
def updateTable(self):
# TODO
# at the moment current update function is too prone to missing traces,
# thus enlarging the table too much. I use old function while working
# on other parts of the implementation
#self.oldUpdateTable()
#return
# First, try to avoid impossible traces: ask observation query
for trace in self._table.getObservableTraces():
observedOutputs = self._table.getOutputs(trace)
......@@ -235,46 +228,39 @@ class LearningAlgorithm:
# this update function uses teacher.process(trace)
# in case a InputOutputTeacher is used, outputs in trace are forced to happen
# this is not realistic, but still useful at the moment.
def oldUpdateTable(self):
temp = 0
tot = 0
for c in range(200):
for trace in self._table.getObservableTraces():
observedOutputs = self._table.getOutputs(trace)
output = self._teacher.process(trace)
for i in range(10):
# try again if retrieving output is unsuccesful
if output != None:
break
output = self._teacher.process(trace)
tot += 1
if output != None:
# Could not process entire trace
# TODO: We need a clever way of handling this, for isinstance
# we could organize getObservableTraces in a tree, then we follow
# the tree for outputs. I expect that in this way the number of
# traces we are not able to process is smaller.
# Only if trace is a prefix in S, then
# add trace + output to row (S cdot L_delta)
if self._table.isInS(trace):
self._table.addOneLetterExtension(trace, output)
# Update set of outputs for traces where deltas are removed
for deltaTrace in self._table.getDeltaTraces(trace):
self._table.updateEntry(deltaTrace, output)
# Add this output to the set of outputs observed after trace
observedOutputs.add(output)
else:
temp += 1
observation = self._oracle.observation(trace, observedOutputs)
self._table.updateEntry(trace, output, observation)
# # this update function uses teacher.process(trace)
# # in case a InputOutputTeacher is used, outputs in trace are forced to happen
# # this is not realistic, but still useful at the moment.
# def oldUpdateTable(self):
# temp = 0
# tot = 0
# for c in range(200):
# for trace in self._table.getObservableTraces():
# observedOutputs = self._table.getOutputs(trace)
# output = self._teacher.process(trace)
# for i in range(10):
# # try again if retrieving output is unsuccesful
# if output != None:
# break
# output = self._teacher.process(trace)
# tot += 1
# if output != None:
# # Only if trace is a prefix in S, then
# # add trace + output to row (S cdot L_delta)
# if self._table.isInS(trace):
# self._table.addOneLetterExtension(trace, output)
#
# # Update set of outputs for traces where deltas are removed
# for deltaTrace in self._table.getDeltaTraces(trace):
# self._table.updateEntry(deltaTrace, output)
#
# # Add this output to the set of outputs observed after trace
# observedOutputs.add(output)
# else:
# temp += 1
#
# observation = self._oracle.observation(trace, observedOutputs)
# self._table.updateEntry(trace, output, observation)
......
......@@ -62,8 +62,7 @@ class Table:
def isInRows(self, trace):
return trace in self._rows
# Given two rows, check if they belong to the same equivalence class for
# HPLUS
# Given two rows, check if they belong to the same equivalence class
def _rowEquality(self, row1, row2, withObservation=False):
for column in self._columns:
entry1 = th.flatten(row1 + column, self._quiescence)
......@@ -304,7 +303,7 @@ class Table:
# the first entry is (set(), false).
# In this case (it can only be an input) we
# send the input to chaotic_delta
if self._entries[current[0] + (label,)] == (set(), False):
if self._entries[current[0] + (label,)] == self._emptyEntry:
newRow1 = "chaos_quiescence"
else:
self._logger.error("Quiescence reducibility check: cannot obtain new pair of states. Row1: "
......@@ -325,7 +324,7 @@ class Table:
# the first entry is (set(), false).
# In this case (it can only be an input) we
# send the input to chaotic_delta
if self._entries[current[1] + (label,)] == (set(), False):
if self._entries[current[1] + (label,)] == self._emptyEntry():
newRow2 = "chaos_quiescence"
else:
self._logger.error("Quiescence reducibility check: cannot obtain new pair of states."
......@@ -355,14 +354,13 @@ class Table:
# get all labels in a set
labels = self._inputs.union(self._outputs)
labels.add(self._quiescence)
# get all combinations of rows in S, without repetitions
combi = combinations(self._rowsInS, 2)
newColumns = set()
for row1, row2 in combi:
# If the rows are in the same equivalence class
if self._rowEquality(row1, row2, chaos):
#print(str(row1) + " and " + str(row2))
for label in labels:
rowExt1 = row1 + (label,)
rowExt2 = row2 + (label,)
......@@ -428,6 +426,22 @@ class Table:
self._createEntries()
return changed
# get an empty nonfinal entry
def _emptyEntry(self):
return (set(), False)
# get an empty final entry
def _emptyFinalEntry(self):
return (set(), True)
# get a final entry with given set of outputs
def _finalEntry(self, outputs):
return (outputs.copy(), True)
# is the entry final? If False, we can still observe some outputs
def _isFinal(self, entry):
return self._entries[entry][1]
# Private method for creating entries from rows and columns
def _createEntries(self):
for row in self._rows:
......@@ -444,38 +458,37 @@ class Table:
len(column) > 0 and column[0] in self._outputs):
# If row ends in delta and column starts with an output,
# then there are no outputs after row + column
self._entries[filteredEntry] = (set(),True)
self._entries[filteredEntry] = self._emptyFinalEntry()
continue
allOutputs = self._outputs.union(set((self._quiescence,)))
if (len(column) > 0 and
column[0] in allOutputs and
row in self._entries.keys() and
column[0] not in self._entries[row][0] and
self._entries[row][1] == True):
self._isFinal(row)):
# 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
# output
self._entries[filteredEntry] = (set(),True)
self._entries[filteredEntry] = self._emptyFinalEntry()
continue
# Otherwise add it to the dictionary
self._entries[filteredEntry] = (set(),False)
self._entries[filteredEntry] = self._emptyEntry()
# In some cases the entry can be filled without asking queries:
# If row + column ends with quiescence, then
# set the entry to ({delta},True)
if filteredEntry[-1] == self._quiescence:
quiescence = set()
quiescence.add(self._quiescence)
self._entries[filteredEntry] = (quiescence,
True)
self._entries[filteredEntry] = self._finalEntry(quiescence)
# special case, row is a quiescence loop
if self._isDeltaLoop(row):
loopState = th.flatten(row[:-1] + column, self._quiescence)
if loopState in self._entries:
self._entries[filteredEntry] = self._entries[loopState]
else:
self._entries[filteredEntry] = (set(),False)
self._entries[filteredEntry] = self._emptyEntry()
# In a special case we are sure there is a quiescence loop
# If False is returned, then there is no certainty that there
......@@ -487,7 +500,7 @@ class Table:
# entry for the proper prefix [VT15]
if len(row) > 0 and row[-1] == self._quiescence:
(outputs, observed) = self._entries[row[:-1]]
if observed and outputs == set((self._quiescence,)):
if self._isFinal(row[:-1]) and outputs == set((self._quiescence,)):
return True
return False
......@@ -500,11 +513,11 @@ class Table:
removedDeltas.remove(flattened)
return removedDeltas
# Return set of (tuples) traces, those which observation is False
# Return set of (tuples) traces, those that are not final
def getObservableTraces(self):
observableTraces = set()
for trace, (outputs, observed) in self._entries.items():
if not observed:
for trace in self._entries.keys():
if not self._isFinal(trace):
# observed is false
observableTraces.add(trace)
return observableTraces
......@@ -588,3 +601,47 @@ class DoubleSetTable(Table):
closeStrategy = None, logger=None):
Table.__init__(self, inputs, outputs, quiescence,
closeStrategy, logger)
# _entries is a dictionary (tuple of actions) -> (set(outputs), set(outputs))
# start with the empty sequence of actions and one letter extensions
self._entries = {():(set(),outputs.union(set((quiescence,))))}
for label in self._inputs:
self._entries[tuple(label)] = (set(),outputs.union(set((quiescence,))))
# get an empty nonfinal entry
def _emptyEntry(self):
return (set(), self._outputs.union(set((self._quiescence,))))
# get an empty final entry
def _emptyFinalEntry(self):
return (set(), set())
# is the entry final? If False, we can still observe some outputs
def _isFinal(self, entry):
return self._entries[entry][0] == self._entries[entry][1]
# get a final entry with given set of outputs
def _finalEntry(self, outputs):
return (outputs.copy(), outputs.copy())
# Update an entry in the table. If it does not exist, create it
def updateEntry(self, trace, output=None, observation=None):
trace = th.flatten(trace, self._quiescence)
if trace in self._entries:
(outputs, observed) = self._entries[trace]
if output != None:
outputs.add(output)
if observation == None or observation == False:
self._entries[trace] = (outputs.copy(), observed)
else:
self._entries[trace] = (outputs.copy(), outputs.copy())
else:
outputs = set()
if output != None:
outputs.add(output)
if observation == None or observation == False:
self._entries[trace] = self._emptyEntry()
observed = self._entries[trace][1]
else:
observed = outputs
self._entries[trace] = (outputs.copy(), observed.copy())
......@@ -8,10 +8,15 @@ from teachers.ltsoracles import InputOutputPowerOracle
import helpers.graphhelper as gh
import helpers.bisimulation as bi
from testing.randomtesting import RandomTester
import logging
class TestLearningAlgorithm2:
def setUp(self):
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
inputs = set(['a','b'])
outputs = set(['x','y'])
quiescence = 'delta'
......@@ -32,13 +37,13 @@ class TestLearningAlgorithm2:
self.I1.addTransition(4,'b',4)
self.I1.addTransition(4,'a',0)
# self.I1.addTransition(4,'x',0)
# self.I1.addTransition(1,'a',0)
# self.I1.addTransition(3,'b',1)
# self.I1.addTransition(3,'a',0)
# self.I1.addTransition(4,'b',2)
# self.I1.addTransition(2,'a',3)
# self.I1.addTransition(4,'y',0)
self.I1.addTransition(4,'x',0)
self.I1.addTransition(1,'a',0)
self.I1.addTransition(3,'b',1)
self.I1.addTransition(3,'a',0)
self.I1.addTransition(4,'b',2)
self.I1.addTransition(2,'a',3)
self.I1.addTransition(4,'y',0)
self.I1.makeInputEnabled()
......@@ -47,7 +52,7 @@ class TestLearningAlgorithm2:
self.tester = RandomTester(self.T1, 1000, 20)
self.L1 = LearningAlgorithm2Sets(self.T1, self.O1, self.tester)
self.L1 = LearningAlgorithm2Sets(self.T1, self.O1, self.tester, logger=logger)
def creation_test(self):
......
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