From 2711f96ecb82598fb62cf0717e039ab0fe86d64b Mon Sep 17 00:00:00 2001 From: John Proudlove Date: Tue, 22 Feb 2022 13:23:50 +0000 Subject: [PATCH] The set type is no longer ordered in Python 3.7+, meaning that pmt actions are no longer reproducible with non-zero seeds. Use fromkeys and list as a workaround (another solution would be ordered-set from PyPi). Note there are multiple other occurrences of sets remaining, which may cause ordering issues elsewhere - the same workaround could be applied to them if necessary. --- pymodel/ModelProgram.py | 8 ++++++ pymodel/ProductModelProgram.py | 50 ++++++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/pymodel/ModelProgram.py b/pymodel/ModelProgram.py index 706d18e..e5995ed 100644 --- a/pymodel/ModelProgram.py +++ b/pymodel/ModelProgram.py @@ -11,10 +11,15 @@ import itertools from pymodel.model import Model import collections +import pprint + +DEBUG = False class ModelProgram(Model): def __init__(self, module, exclude, include): + if DEBUG: + self.pp = pprint.PrettyPrinter(width=120) Model.__init__(self, module, exclude, include) def post_init(self): @@ -80,6 +85,9 @@ def make_argslist(self, a): argslists = list(zip(*domains)) elif combination == 'all': # Cartesian product argslists = itertools.product(*domains) + if DEBUG: + print('list(itertools.product(*domains)):') + self.pp.pprint(list(itertools.product(*domains))) # might be nice to support 'pairwise' also # return tuple not list, hashable so it can be key in dictionary # also handle special case (None,) indicates empty argslist in domains diff --git a/pymodel/ProductModelProgram.py b/pymodel/ProductModelProgram.py index d7fdb97..d4a6e45 100644 --- a/pymodel/ProductModelProgram.py +++ b/pymodel/ProductModelProgram.py @@ -18,6 +18,7 @@ and translates aname string to action function a: a = getattr(module, aname) """ +import pprint from operator import concat from collections import defaultdict @@ -25,6 +26,8 @@ from pymodel.TestSuite import TestSuite from pymodel.ModelProgram import ModelProgram from functools import reduce + +DEBUG = False class ProductModelProgram(object): @@ -33,6 +36,9 @@ def __init__(self, options, args): self.module = dict() # dict of modules keyed by name self.mp = dict() # dict of model programs keyed by same module name + if DEBUG: + self.pp = pprint.PrettyPrinter(width=120) + # Populate self.module and self.mp from modules named in command line args # Models that users write are just modules, not classes (types) # so we find out what type to wrap each one in @@ -124,7 +130,9 @@ def EnabledTransitions(self, cleanup): enabledScenarioActions = \ dict([(m, self.mp[m].EnabledTransitions(cleanup)) for m in self.mp if (not isinstance(self.mp[m], ModelProgram))]) - # print 'enabledScenarioActions %s' % enabledScenarioActions # DEBUG + + if DEBUG: + print('enabledScenarioActions %s' % enabledScenarioActions) # dict from action to sequence of argslists argslists = defaultdict(list) @@ -135,26 +143,37 @@ def EnabledTransitions(self, cleanup): # If more than one scenario in product, there may be duplicates - use sets scenarioArgslists = dict([(action, set(args)) for (action,args) in list(argslists.items())]) - # print 'scenarioArgslists %s' % scenarioArgslists + if DEBUG: + print('scenarioArgslists %s' % scenarioArgslists) # Pass scenarioArgslists to ModelProgram EnabledTransitions # so any observable actions can use these argslists enabledModelActions = \ dict([(m, self.mp[m].EnabledTransitions(scenarioArgslists, cleanup)) for m in self.mp if isinstance(self.mp[m], ModelProgram)]) - # print 'enabledModelActions %s' % enabledModelActions # DEBUG + if DEBUG: + print('enabledModelActions:') + self.pp.pprint(enabledModelActions) # Combine enabled actions dictionaries (they have distinct keys) enabledActions = dict() enabledActions.update(enabledScenarioActions) # FSM and TestSuite enabledActions.update(enabledModelActions) # ModelProgam - # print 'enabledActions %s' % enabledActions # DEBUG + if DEBUG: + print('enabledActions:') + self.pp.pprint(enabledActions) - # set (with no duplicates) of all (aname, args, result) in enabledActions - transitions = set([(a.__name__, args, result) - for (a,args,result,next,properties) in - reduce(concat,list(enabledActions.values()))]) - # print 'transitions %s' % transitions + # list (with no duplicates) of all (aname, args, result) in enabledActions + # transitions should ideally be an ordered set. Unfortunately the built in + # set type is not guaranteed to be ordered (this appears to have changed in + # Python 3.7+), so we use fromkeys and list here as a workaround. (Could + # instead have used ordered-set from PyPi) + transitions = list(dict.fromkeys([(a.__name__, args, result) + for (a,args,result,next,properties) in + reduce(concat,list(enabledActions.values()))])) + if DEBUG: + print('transitions:') + self.pp.pprint(transitions) # dict from (aname, args, result) # to set of all m where (aname, args, result) is enabled @@ -166,7 +185,10 @@ def EnabledTransitions(self, cleanup): [(a.__name__, argsx, resultx) # argsx,resultx is inner loop for (a,argsx,resultx,next,properties) in enabledActions[m]]])) for (aname, args, result) in transitions ]) - # print 'invocations %s' % invocations # DEBUG + + if DEBUG: + print('invocations:') + self.pp.pprint(invocations) # list of all (aname, args, result) that are enabled in the product # (aname,args,result) enabled in product if (aname,args,result) is enabled @@ -180,6 +202,10 @@ def EnabledTransitions(self, cleanup): set()) == self.vocabularies[aname]] + if DEBUG: + print('enabledAnameArgs:') + self.pp.pprint(enabledAnameArgs) + # Now we have all enabled (action,args,result), now rearrange the data # for each enabled (aname,args), associate next states and properties by mp @@ -200,7 +226,9 @@ def EnabledTransitions(self, cleanup): for m in self.mp ])) for (aname, args, result) in enabledAnameArgs ] - # print 'enabledTs %s' % enabledTs # DEBUG + if DEBUG: + print('enabledTs:') + self.pp.pprint(enabledTs) # combine result and properties from all the mp # list, all enabled [(aname,args,result,{m1:next1,m2:next2},properties),...]