Source code for sortingDigraphs

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Python implementation of digraphs
# Current revision $Revision: 2093 $
# Copyright (C) 2006-2008  Raymond Bisdorff
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, write to the Free Software Foundation, Inc.,
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
#######################
from digraphsTools import *
from digraphs import *
from outrankingDigraphs import *
from sortingDigraphs import *

[docs]class SortingDigraph(BipolarOutrankingDigraph,PerformanceTableau): """ Specialisation of the digraphs.BipolarOutrankingDigraph Class for Condorcet based multicriteria sorting of alternatives. Besides a valid PerformanceTableau instance we require a sorting profile, i.e.: * a dictionary <categories> of categories with 'name', 'order' and 'comment' * a dictionary <criteriaCategoryLimits> with double entry: [criteriakey][categoryKey] containing a ['minimum'] and a ['maximum'] value in the scale of the criterion respecting the order of the categories. Template of required data for a 4-sorting:: categories = { 'c1': { 'name': 'week','order': 1, 'lowLimit': 0,'highLimit': 25, 'comment': 'lowest category',}, 'c2': { 'name': 'ok','order': 2, 'lowLimit': 25,'highLimit': 50, 'comment': 'medium category',}, 'c3': { 'name': 'good','order': 3, 'lowLimit': 50,'highLimit': 75, 'comment': 'highest category',}, 'c4': { 'name': 'excellent','order': 4, 'lowLimit': 75,'highLimit': 100, 'comment': 'highest category',}, } criteriaCategoryLimits['LowerClosed'] = True # default criteriaCategoryLimits[g] = { 'c1': {'minimum':0, 'maximum':25}, 'c2': {'minimum':25, 'maximum':50}, 'c3': {'minimum':50, 'maximum':75}, 'c4': {'minimum':75, 'maximum':200}, } A template named tempProfile.py is providied in the digraphs module distribution. .. note:: We generally require a performanceTableau instance and a filename where categories and a profile my be read from. If no such filename is given, then a default profile with five, equally spaced, categories is used on each criteria. By default lower-closed limts of categories are supposed to be used in the sorting. Example Python3 session: >>> from sortingDigraphs import SortingDigraph >>> from randomPerfTabs import RandomPerformanceTableau >>> t = RandomPerformanceTableau(seed=1) >>> [x for x in t.actions] ['a01', 'a02', 'a03', 'a04', 'a05', 'a06', 'a07', 'a08', 'a09', 'a10', 'a11', 'a12', 'a13'] >>> so = SortingDigraph(t,scaleSteps=5) # so gives a sorting result into five lower closed ordered # categories enumerated from 0 to 5. >>> so.showSorting() *--- Sorting results in descending order ---* ]> - 4]: ['a02', 'a03', 'a11'] ]4 - 3]: ['a04', 'a07', 'a08', 'a09', 'a10', 'a11', 'a12', 'a13'] ]3 - 2]: ['a04', 'a05', 'a06', 'a09', 'a12'] ]2 - 1]: ['a01'] ]1 - 0]: [] # Notice that some alternatives, like a04, a09, a11 and a12 are sorted # into more than one adjacent category. Weak ordering the sorting result # into ordered adjacent categories gives following result: >>> so.showWeakOrder(strategy='average',Descending=True) Weak ordering by average normalized 5-sorting limits ] > -80.0] : ['a02', 'a03'] ]100.0-60.0] : ['a11'] ] 80.0-60.0] : ['a07', 'a08', 'a10', 'a13'] ] 80.0-40.0] : ['a04', 'a09', 'a12'] ] 60.0-40.0] : ['a05', 'a06'] ] 40.0-20.0] : ['a01'] """ def __init__(self,argPerfTab=None,\ argProfile=None,\ scaleSteps=5,\ minValuation=-100.0,\ maxValuation=100.0,\ isRobust=False,\ hasNoVeto=False,\ LowerClosed=True,\ StoreSorting=True,\ Threading=False,\ tempDir=None,\ nbrCores=None,\ Debug=False): """ Constructor for SortingDigraph instances. """ from copy import copy, deepcopy from decimal import Decimal from collections import OrderedDict from time import time # import or generate a performance tableau tt = time() if argPerfTab == None: print('!!! No valid performance tableau given!!') print('!!! Random standard performance tableau generated!!') from randomPerfTabs import RandomPerformanceTableau perfTab = RandomPerformanceTableau(numberOfActions=10, numberOfCriteria=13) elif isinstance(argPerfTab,str): try: perfTab = XMCDA2PerformanceTableau(argPerfTab) except: print('Performance Tableau not in XMCDA2 format!') perfTab = None else: perfTab = argPerfTab # normalize the actions as a dictionary construct if isinstance(perfTab.actions,list): actions = OrderedDict() for x in perfTab.actions: actions[x] = {'name': str(x)} self.actions = actions else: self.actions = deepcopy(perfTab.actions) self.criteria = deepcopy(perfTab.criteria) self.convertWeightFloatToDecimal() self.evaluation = deepcopy(perfTab.evaluation) self.convertEvaluationFloatToDecimal() # keep a copy of the original actions set before adding the profiles actionsOrig = deepcopy(perfTab.actions) self.actionsOrig = actionsOrig # input the profiles if argProfile != None: defaultProfiles = False # normalize the actions as a dictionary construct if isinstance(perfTab.actions,list): actions = OrderedDict() for x in perfTab.actions: actions[x] = {'name': str(x)} self.actions = actions else: self.actions = deepcopy(perfTab.actions) self.criteria = deepcopy(perfTab.criteria) self.convertWeightFloatToDecimal() self.evaluation = deepcopy(perfTab.evaluation) self.convertEvaluationFloatToDecimal() if isinstance(argProfile,str): # input from stored instantiation fileName = argProfile fileNameExt = fileName + '.py' profile = OrderedDict() exec(compile(open(fileNameExt).read(), fileNameExt, 'exec'),profile) #print(profile) self.name = fileName self.categories = profile['categories'] self.criteriaCategoryLimits = profile['criteriaCategoryLimits'] # self.profiles = profile['profiles'] # self.profileLimits = set(self.profiles['min']) | \ # set(self.profiles['max']) else: # input from a profiles dictionary self.name = 'sorting_with_given_profile' self.categories = argProfile['categories'].copy() self.criteriaCategoryLimits = argProfile['criteriaCategoryLimits'].copy() # self.profiles = {} else: defaultProfiles = True self.name = 'sorting_with_default_profiles' normPerfTab = NormalizedPerformanceTableau(perfTab) self.actions = normPerfTab.actions self.criteria = normPerfTab.criteria self.convertWeightFloatToDecimal() self.evaluation = normPerfTab.evaluation self.convertEvaluationFloatToDecimal() # supposing all criteria scales between 0.0 and 100.0 lowValue = Decimal('0.0') highValue = Decimal('100.0') # with preference direction = max categories = OrderedDict() k = highValue / Decimal(str(scaleSteps)) nd = len(str(scaleSteps)) for i in range(scaleSteps): categories[str(i)] = {'name':('c%%0%dd' % (nd)) % (i+1),\ 'order':i,\ 'lowLimit': Decimal('%.1f' % (i*k)),\ 'highLimit': Decimal('%.1f' % ((i+1)*k))} self.categories = categories criteriaCategoryLimits = OrderedDict() criteriaCategoryLimits['LowerClosed'] = LowerClosed for g in self.criteria: criteriaCategoryLimits[g] = {} k = (self.criteria[g]['scale'][1] - \ self.criteria[g]['scale'][0]) / scaleSteps i = 0 for c in categories: if i < (scaleSteps-1): gHighLimit = Decimal('%.1f' %\ (self.criteria[g]['scale'][0] + (i+1)*k)) else: gHighLimit = Decimal('%.1f' % (2*self.criteria[g]['scale'][1])) if i > 0: gLowLimit = Decimal('%.1f' %\ (self.criteria[g]['scale'][0] + (i)*k)) else: gLowLimit = Decimal('%.1f' % (-self.criteria[g]['scale'][1])) criteriaCategoryLimits[g][c]={ 'minimum': Decimal('%.1f' % gLowLimit), 'maximum': Decimal('%.1f' % gHighLimit) } i += 1 self.criteriaCategoryLimits = criteriaCategoryLimits # set the category limits type (LowerClosed = True) self.criteriaCategoryLimits['LowerClosed'] = LowerClosed self.runTimes={'dataInput': time()-tt} # add the catogory limits to the actions set actions = self.actions criteria = self.criteria evaluation = self.evaluation categories = self.categories criteriaCategoryLimits = self.criteriaCategoryLimits t0 = time() profiles = {'min':{},'max':{}} profileLimits = set() for c in categories.keys(): cMinKey = c+'-m' cMaxKey = c+'-M' profileLimits.add(cMinKey) profileLimits.add(cMaxKey) actions[cMinKey] = {'name': 'categorical low limits', 'comment': 'Inferior or equal limits for category membership assessment'} actions[cMaxKey] = {'name': 'categorical high limits', 'comment': 'Lower or equal limits for category membership assessment'} profiles['min'][cMinKey] = {'category': c, 'name': 'categorical low limits', 'comment': 'Inferior or equal limits for category membership assessment'} profiles['max'][cMaxKey] = {'category': c, 'name': 'categorical high limits', 'comment': 'Lower or equal limits for category membership assessment'} for g in criteria.keys(): try: if criteria[g]['preferenceDirection'] == 'max': evaluation[g][cMinKey] = Decimal(str(criteriaCategoryLimits[g][c]['minimum'])) evaluation[g][cMaxKey] = Decimal(str(criteriaCategoryLimits[g][c]['maximum'])) elif criteria[g]['preferenceDirection'] == 'min': if not defaultProfiles: highValueg = Decimal(str(criteria[g]['scale'][1])) else: highValueg = Decimal('%.1f' % highValue) #print 'highValue = ', highValue evaluation[g][cMinKey] = -(highValueg - Decimal(str(criteriaCategoryLimits[g][c]['minimum']))) evaluation[g][cMaxKey] = -(highValueg - Decimal(str(criteriaCategoryLimits[g][c]['maximum']))) else: print('===>>>>> Error') except: evaluation[g][cMinKey] = Decimal(str(criteriaCategoryLimits[g][c]['minimum'])) evaluation[g][cMaxKey] = Decimal(str(criteriaCategoryLimits[g][c]['maximum'])) #print 'LowerClosed', LowerClosed self.profiles = profiles self.profileLimits = profileLimits self.evaluation = evaluation self.convertEvaluationFloatToDecimal() self.LowerClosed = LowerClosed ## else ## profiles = self.profiles ## profileLimits = self.profileLimits #self.actions = actions #self.profiles = profiles #self.profileLimits = profileLimits #self.criteria = criteria self.runTimes['computeProfiles'] = time()-t0 # construct outranking relation t0 = time() if isRobust: g = RobustOutrankingDigraph(self) self.valuationdomain = g.valuationdomain self.relation = g.relation else: Min = Decimal('%.4f' % minValuation) Max = Decimal('%.4f' % maxValuation) Med = (Max + Min)/Decimal('2.0') self.valuationdomain = {'min': Min, 'med':Med ,'max':Max } if LowerClosed: relation = BipolarOutrankingDigraph._constructRelationWithThreading(self,criteria, self.evaluation, initial=actionsOrig, terminal=profileLimits, hasNoVeto=hasNoVeto, hasBipolarVeto=True, Threading=Threading, tempDir=tempDir, WithConcordanceRelation=False, WithVetoCounts=False, Debug=Debug) else: relation = BipolarOutrankingDigraph._constructRelationWithThreading(self,criteria, self.evaluation, terminal=actionsOrig, initial=profileLimits, hasNoVeto=hasNoVeto, hasBipolarVeto=True, Threading=Threading, tempDir=tempDir, WithConcordanceRelation=False, WithVetoCounts=False, Debug=Debug) if LowerClosed: for x in actionsOrig.keys(): rx = relation[x] for y in actionsOrig.keys(): rx[y] = Med for x in profileLimits: relation[x] = {} rx = relation[x] for y in actions.keys(): rx[y] = Med else: for x in actionsOrig.keys(): relation[x] = {} rx = relation[x] for y in actionsOrig.keys(): rx[y] = Med for y in profileLimits: for x in actions.keys(): relation[x][y] = Med self.relation = relation self.runTimes['computeRelation'] = time()-t0 # compute weak ordering t0 = time() sortingRelation = self.computeSortingRelation(Debug=Debug,) relation = self.relation for x in actionsOrig.keys(): rx = relation[x] srx = sortingRelation[x] for y in actionsOrig.keys(): rx[y] = srx[y] # reset original action set self.actions = actionsOrig # compute weak ordering by choosing # self.computeRankingByChoosing() !!! not scalable !!! # obsolete: replaced by self.computeWeakOrder() # init general digraph Data self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.runTimes['weakOrdering'] = time()-t0 self.runTimes['totalTime'] = time()-tt ######### def __repr__(self): """ Default presentation method for SortingDigraph instance. """ print('*----- show short --------------*') print('Instance name : %s' % self.name) print('# Actions : %d' % self.order) print('# Criteria : %d' % len(self.criteria)) print('# Categories : %d' % len(self.categories)) print('Lowerclosed : %s' % str(self.criteriaCategoryLimits['LowerClosed'])) print('Size : %d' % self.computeSize()) print('Determinateness : %.3f' % (self.computeDeterminateness()) ) print('---- Constructor run times (in sec.) ----') print('#Threads : %d' % self.nbrThreads) print('Total time : %.5f' % self.runTimes['totalTime']) print('Data input : %.5f' % self.runTimes['dataInput']) print('Compute profiles : %.5f' % self.runTimes['computeProfiles']) print('Compute relation : %.5f' % self.runTimes['computeRelation']) print('weak ordering : %.5f' % self.runTimes['weakOrdering']) return '%s instance' % str(self.__class__)
[docs] def saveCategories(self,fileName='tempCategories'): fileName += '.py' print('*--- Saving sorting categories in file: <%s> ---*' % fileName) with open(fileName,'w') as fo: fo.write('# categories save method from the SortingDigraph class\n') fo.write('from collections import OrderedDict\n') fo.write('from decimal import Decimal\n') # save categories fo.write('categories = OrderedDict([\n') for c in self.categories: fo.write('(\'%s\',%s),\n' % (c,str(self.categories[c]))) fo.write('])\n') ## # save profiles ## fo.write('profiles = {\n') ## fo.write('\'min\': {\n') ## for p in self.profiles['min']: ## fo.write('\'%s\': %s,\n' % (p,str(self.profiles['min'][p]))) ## fo.write('},\n') ## fo.write('\'max\': {\n') ## for p in self.profiles['max']: ## fo.write('\'%s\': %s,\n' % (p,str(self.profiles['max'][p]))) ## fo.write('}}\n') # save criteria category limits fo.write('criteriaCategoryLimits = OrderedDict([\n') fo.write('(\'LowerClosed\',%s),\n' %\ (str(self.criteriaCategoryLimits['LowerClosed'])) ) for g in self.criteria: fo.write('(\'%s\',{\n' % (g)) for c in self.categories: fo.write('\'%s\' :%s,\n' %\ (c,str(self.criteriaCategoryLimits[g][c])) ) fo.write('}),\n') fo.write('])\n')
# close
[docs] def htmlCriteriaCategoryLimits(self,tableTitle='Category limits'): """ Renders category minimum and maximum limits for each criterion as a html table. """ s = '' s += '<h1>%s</h1>' % tableTitle s += '<table border="1">' criterionKeys = [x for x in self.criteria] categoryKeys = [x for x in self.categories] s += '<tr><th>Criteria</th>' for g in criterionKeys: s += '<th>%s</th>' % g s += '</tr>' for g in criterionKeys: s += '<tr><th>%s</th></tr>' % (g) s += '<tr><th>Lower limit</th>' for c in categoryKeys: #print '\t', c, (self.criteriaCategoryLimits[g][c]['minimum'],self.criteriaCategoryLimits[g][c]['maximum']) s += '<td>%2.f</td>' % (self.criteriaCategoryLimits[g][c]['minimum']) s += '</tr>' s += '<tr><th>Upper limit</th>' for c in categoryKeys: #print '\t', c, (self.criteriaCategoryLimits[g][c]['minimum'],self.criteriaCategoryLimits[g][c]['maximum']) s += '<td>%2.f</td>' % (self.criteriaCategoryLimits[g][c]['maximum']) s += '</tr>' s += '</table>' return s
[docs] def computeWeakOrder(self,Descending=False,strategy='average',\ Comments=False,Debug=False): """ specialisation of the showWeakOrder method. The weak ordering strategy may be: "optimistic" (ranked by highest category limits), "pessimistic" (ranked by lowest category limits) or "average" (ranked by average category limits) """ actions = self.actions categories = self.categories actionsCategories = {} actionsCategoryLimits = {} for x in actions.keys(): a,lowCateg,highCateg,credibility = self.showActionCategories(x,Comments=Debug) if Debug: print(actions[x],a,lowCateg,highCateg,credibility) print(strategy) if strategy == 'average': # average by default lc = categories[lowCateg]['lowLimit'] hc = categories[highCateg]['highLimit'] ac = (lc+hc)/Decimal('2.0') try: actionsCategories[(ac,categories[highCateg]['highLimit'],\ categories[lowCateg]['lowLimit'])]['categoryContent'].append(a) except: actionsCategories[(ac,categories[highCateg]['highLimit'],\ categories[lowCateg]['lowLimit'])] =\ {'categoryContent': [a], 'categoryInterval': (lowCateg,highCateg)} else: # strategy == "optimistic" or "pessimistic": try: actionsCategories[(categories[highCateg]['highLimit'],\ categories[lowCateg]['lowLimit'])]['categoryContent'].append(a) except: actionsCategories[(categories[highCateg]['highLimit'],\ categories[lowCateg]['lowLimit'])] =\ {'categoryContent': [a], 'categoryInterval': (lowCateg,highCateg)} ## elif strategy == "pessimistic": ## try: ## actionsCategories[(categories[lowCateg]['lowLimit'],\ ## categories[highCateg]['highLimit'])].append(a) ## except: ## actionsCategories[(categories[lowCateg]['lowLimit'],\ ## categories[highCateg]['highLimit'])] = [a] ## else: # optimistic by default ## try: ## actionsCategories[(int(highCateg),int(lowCateg))].append(a) ## except: ## actionsCategories[(int(highCateg),int(lowCateg))] = [a] #print(actionsCategories) actionsCategIntervals = [] for interval in actionsCategories: #print(interval) actionsCategIntervals.append([interval,\ actionsCategories[interval]]) actionsCategIntervals.sort(reverse=Descending) weakOrdering = [] catKeys = [x for x in self.categories] #print(catKeys) if Comments: k = len(self.categories) print('Weak ordering with %s normalized %d-sorting limits' % (strategy,k) ) for ci,item in enumerate(actionsCategIntervals): #print(item[1]) interval = item[1]['categoryInterval'] content = item[1]['categoryContent'] if Comments: ## if strategy == "average": if Descending: if interval[1] == interval[0]: catName0 = self.categories[interval[0]]['name'] print('[%s] : %s' % (catName0,content) ) else: catName0 = self.categories[interval[0]]['name'] catName1 = self.categories[interval[1]]['name'] print('[%s-%s] : %s' % (catName1,\ catName0,\ content ) ) else: if interval[0] == interval[1]: catName1 = self.categories[interval[1]]['name'] print('[%s] : %s' % (catName1,\ content ) ) else: catName0 = self.categories[interval[0]]['name'] catName1 = self.categories[interval[1]]['name'] print('[%s-%s] : %s' % (catName0,\ catName1,\ content ) ) weakOrdering.append(content) return weakOrdering
[docs] def showWeakOrder(self,Descending=False,strategy='average'): """ dummy for computeWeakOrder with Comments=True """ self.computeWeakOrder(Descending=Descending,strategy=strategy,Comments=True)
[docs] def computeSortingRelation(self,categoryContents=None,StoreSorting=True,Debug=False): """ constructs a bipolar sorting relation using the category contents. """ if categoryContents == None: categoryContents = self.computeCategoryContents(StoreSorting=True,) categoryKeys = self.orderedCategoryKeys() Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] Min = self.valuationdomain['min'] actions = [x for x in self.actionsOrig] currActions = set(actions) #sortedActions = set() sortingRelation = {} for x in actions: sortingRelation[x] = {} srx = sortingRelation[x] for y in actions: srx[y] = Med if Debug: print('categoryContents',categoryContents) for i in categoryKeys: ibch = set(categoryContents[i]) ribch = set(currActions) - ibch if Debug: print('ibch,ribch',ibch,ribch) for x in ibch: ## for y in sortedActions: ## sortingRelation[x][y] = Max ## sortingRelation[y][x] = Min for y in ibch: sortingRelation[x][y] = Med sortingRelation[y][x] = Med for y in ribch: sortingRelation[x][y] = Min sortingRelation[y][x] = Max currActions = currActions - ibch ## sortedActions = sortedActions | ibch return sortingRelation
[docs] def showCriteriaCategoryLimits(self): """ Shows category minimum and maximum limits for each criterion. """ try: LowerClosed = self.criteriaCategoryLimits['LowerClosed'] except: LowerClosed = True criteria = self.criteria categories = self.categories #criterionKeys = [x for x in self.criteria] #categoryKeys = [x for x in self.categories] for g in criteria: print(g) for c in categories: if LowerClosed: print('\t%s [%s; %s[' % (c, self.criteriaCategoryLimits[g][c]['minimum'],self.criteriaCategoryLimits[g][c]['maximum'])) else: print('\t%s ]%s; %s]' % (c, self.criteriaCategoryLimits[g][c]['minimum'],self.criteriaCategoryLimits[g][c]['maximum']))
[docs] def getActionsKeys(self,action=None,withoutProfiles=True): """ extract normal actions keys() """ profiles_m = set(self.profiles['min']) profiles_M = set(self.profiles['max']) if action == None: actionsExt = set(self.actions) if withoutProfiles: return actionsExt - profiles_m - profiles_M else: return actionsExt | profiles_m | profiles_M else: return set([action])
[docs] def orderedCategoryKeys(self,Reverse=False): """ Renders the ordered list of category keys based on self.categories['order'] numeric values. """ ## categoriesSort = [] ## for c in list(self.categories.keys()): ## categoriesSort.append((self.categories[c]['order'],c)) ## categoriesSort.sort() orderedCategoryKeys = list(self.categories.keys()) if Reverse: orderedCategoryKeys.reverse() return orderedCategoryKeys
def _computeWeakOrder(self,Descending=True,strategy=None,Comments=None,Debug=False): """ Specialisation for QuantilesSortingDigraphs. """ from decimal import Decimal cC = self.computeCategoryContents() if Descending: cCKeys = self.orderedCategoryKeys(Reverse = True) else: cCKeys = self.orderedCategoryKeys(Reverse = False) if Debug: print(cCKeys) n = len(cC) n2 = n//2 ordering = [] for i in range(n2): if i == 0: x = cC[cCKeys[i]] y = cC[cCKeys[n-i-1]] setx = set(x) sety = set(y) - setx else: x = list(set(cC[cCKeys[i]]) - (setx | sety)) setx = setx | set(x) y = list(set(cC[cCKeys[n-i-1]]) - (setx | sety)) sety = sety | set(y) if x != [] or y != []: ordering.append( ( (Decimal(str(i+1)),x),(Decimal(str(n-i)),y) ) ) if 2*n2 < n: if n2 == 0: x = cC[cCKeys[n2]] else: x = list(set(cC[cCKeys[n2]]) - (setx | sety)) ordering.append( ( (Decimal(str(n2+1)),x),(Decimal(str(n2+1)),[]) ) ) if Debug: print(ordering) orderingList = [] n = len(ordering) for i in range(n): x = ordering[i][0][1] if x != []: orderingList.append(x) for i in range(n): y = ordering[n-i-1][1][1] if y != []: orderingList.append(y) return orderingList
[docs] def showOrderedRelationTable(self,direction="decreasing"): """ Showing the relation table in decreasing (default) or increasing order. """ if direction == "decreasing": Descending = True else: Descending = False weakOrdering = self.computeWeakOrder(Descending) actionsList = [] for eq in weakOrdering: #print(eq) eq.sort() for x in eq: actionsList.append(x) if len(actionsList) != len(self.actions): print('Error !: missing action(s) %s in ordered table.') Digraph.showRelationTable(self,actionsSubset=actionsList,\ relation=self.relation,\ Sorted=False,\ ReflexiveTerms=False)
[docs] def exportDigraphGraphViz(self,fileName=None, bestChoice=set(),worstChoice=set(),noSilent=True,graphType='png',graphSize='7,7'): """ export GraphViz dot file for digraph drawing filtering. """ Digraph.exportGraphViz(self, fileName=fileName,\ bestChoice=bestChoice,\ worstChoice=worstChoice,\ noSilent=noSilent,\ graphType=graphType,\ graphSize=graphSize)
[docs] def exportGraphViz(self,fileName=None,direction='decreasing',\ noSilent=True,graphType='png',\ graphSize='7,7',\ fontSize=10, relation=None, Debug=False): """ export GraphViz dot file for weak order (Hasse diagram) drawing filtering from SortingDigraph instances. """ import os from copy import copy, deepcopy def _safeName(t0): try: t = t0.split(sep="-") t1 = t[0] n = len(t) if n > 1: for i in range(1,n): t1 += '%s%s' % ('_',t[i]) return t1 except: print('Error in nodeName: %s !!' % t0, type(t0)) return t0 if direction == 'decreasing': ordering = self.computeWeakOrder(Descending=True) else: ordering = self.computeWeakOrder(Descending=False) if Debug: print(ordering) ## try: ## rankingByChoosing = self.rankingByBestChoosing['result'] ## except: ## self.computeRankingByBestChoosing() ## rankingByChoosing = self.rankingByBestChoosing['result'] ## else: ## try: ## rankingByChoosing = self.rankingByLastChoosing['result'] ## except: ## self.computeRankingByLastChoosing() ## rankingByChoosing = self.rankingByLastChoosing['result'] if noSilent: print('*---- exporting a dot file for GraphViz tools ---------*') actionKeys = [x for x in self.actions] n = len(actionKeys) if relation == None: relation = self.relation Med = self.valuationdomain['med'] i = 0 if fileName == None: name = self.name else: name = fileName dotName = name+'.dot' if noSilent: print('Exporting to '+dotName) ## if bestChoice != set(): ## rankBestString = '{rank=max; ' ## if worstChoice != set(): ## rankWorstString = '{rank=min; ' fo = open(dotName,'w') fo.write('digraph G {\n') fo.write('graph [ bgcolor = cornsilk, ordering = out, fontname = "Helvetica-Oblique",\n fontsize = 12,\n label = "') fo.write('\\nweakOrders module (graphviz)\\n R. Bisdorff, 2014", size="') fo.write(graphSize),fo.write('",fontsize=%d];\n' % fontSize) # nodes for x in actionKeys: try: nodeName = self.actions[x]['shortName'] except: nodeName = str(x) node = '%s [shape = "circle", label = "%s", fontsize=%d];\n'\ % (str(_safeName(x)),_safeName(nodeName),fontSize) fo.write(node) # same ranks for Hasses equivalence classes k = len(ordering) for i in range(k): sameRank = '{ rank = same; ' ich = ordering[i] for x in ich: sameRank += str(_safeName(x))+'; ' sameRank += '}\n' print(i,sameRank) fo.write(sameRank) # save original relation originalRelation = copy(self.relation) self.relation = relation self.closeTransitive(Reverse=True) for i in range(k-1): ich = ordering[i] for x in ich: for j in range(i+1,k): jch = ordering[j] for y in jch: #edge = 'n'+str(i+1)+'-> n'+str(i+2)+' [dir=forward,style="setlinewidth(1)",color=black, arrowhead=normal] ;\n' if self.relation[x][y] > self.valuationdomain['med']: arcColor = 'black' edge = '%s-> %s [style="setlinewidth(%d)",color=%s] ;\n' % (_safeName(x),_safeName(y),1,arcColor) fo.write(edge) elif self.relation[y][x] > self.valuationdomain['med']: arcColor = 'black' edge = '%s-> %s [style="setlinewidth(%d)",color=%s] ;\n' % (_safeName(y),_safeName(x),1,arcColor) fo.write(edge) fo.write('}\n \n') fo.close() # restore original relation self.relation = copy(originalRelation) commandString = 'dot -Grankdir=TB -T'+graphType+' ' +dotName+' -o '+name+'.'+graphType #commandString = 'dot -T'+graphType+' ' +dotName+' -o '+name+'.'+graphType if noSilent: print(commandString) try: os.system(commandString) except: if noSilent: print('graphViz tools not avalaible! Please check installation.')
[docs] def computeSortingCharacteristics(self, action=None, StoreSorting=True,\ Comments=False, Debug=False,\ Threading=False, nbrOfCPUs=None): """ Renders a bipolar-valued bi-dictionary relation representing the degree of credibility of the assertion that "action x in A belongs to category c in C", ie x outranks low category limit and does not outrank the high category limit. """ Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] Max = self.valuationdomain['max'] actions = list(self.getActionsKeys(action)) na = len(actions) #categories = list(self.orderedCategoryKeys()) categories = self.categories try: LowerClosed = self.criteriaCategoryLimits['LowerClosed'] except: LowerClosed = True if Threading: from multiprocessing import Process, active_children from pickle import dumps, loads, load from os import cpu_count class myThread(Process): def __init__(self, threadID, tempDirName, actions, catKeys,LowerClosed,Debug): Process.__init__(self) self.threadID = threadID self.workingDirectory = tempDirName self.actions = actions self.catKeys = catKeys self.LowerClosed = LowerClosed self.Debug = Debug def run(self): from pickle import dumps, loads from os import chdir chdir(self.workingDirectory) if self.Debug: print("Starting working in %s on %s" % (self.workingDirectory, self.name)) print('actions,catKeys',self.actions,self.catKeys) fi = open('dumpSelfRelation.py','rb') #context = loads(fi.read()) relation = loads(fi.read()) fi.close() Min = context.valuationdomain['min'] Max = context.valuationdomain['max'] actions = self.actions catKeys = self.catKeys LowerClosed = self.LowerClosed #relation = context.relation sorting = {} for x in actions: sorting[x] = {} for c in catKeys: sorting[x][c] = {} cMinKey= c+'-m' cMaxKey= c+'-M' if LowerClosed: lowLimit = relation[x][cMinKey] notHighLimit = Max - relation[x][cMaxKey] + Min else: lowLimit = Max - relation[cMinKey][x] + Min notHighLimit = relation[cMaxKey][x] if self.Debug: print('%s in %s: low = %.2f, high = %.2f' % \ (x, c,lowLimit,notHighLimit), end=' ') categoryMembership = min(lowLimit,notHighLimit) sorting[x][c]['lowLimit'] = lowLimit sorting[x][c]['notHighLimit'] = notHighLimit sorting[x][c]['categoryMembership'] = categoryMembership if self.Debug: print('\t %.2f \t %.2f \t %.2f' % (sorting[x][c]['lowLimit'],\ sorting[x][c]['notHighLimit'], sorting[x][c]['categoryMembership'])) if self.Debug: print(sorting[x]) foName = 'sorting-'+str(self.threadID)+'.py' fo = open(foName,'wb') fo.write(dumps(sorting,-1)) fo.close() print('Threaded computing of sorting characteristics ...') from tempfile import TemporaryDirectory,mkdtemp tempDirName = mkdtemp() td = time() selfFileName = tempDirName +'/dumpSelfRelation.py' if Debug: print('temDirName, selfFileName', tempDirName,selfFileName) fo = open(selfFileName,'wb') pd = dumps(self.relation,-1) fo.write(pd) fo.close() if Comments: print('Relation dump: %.5f' % (time()-td)) if nbrOfCPUs == None: nbrOfCPUs = cpu_count()-1 print('Nbr of actions',na) nbrOfJobs = na//nbrOfCPUs if nbrOfJobs*nbrOfCPUs < na: nbrOfJobs += 1 print('Nbr of threads = ',nbrOfCPUs) print('Nbr of jobs/thread',nbrOfJobs) nbrOfThreads = 0 for j in range(nbrOfCPUs): print('thread = %d/%d' % (j+1,nbrOfCPUs),end="...") start= j*nbrOfJobs if (j+1)*nbrOfJobs < na: stop = (j+1)*nbrOfJobs else: stop = na thActions = actions[start:stop] if Debug: print(thActions) if thActions != []: process = myThread(j,tempDirName,thActions,categories,LowerClosed,Debug) process.start() nbrOfThreads += 1 while active_children() != []: pass #sleep(1) print('Exit %d threads' % nbrOfThreads) sorting = {} for th in range(nbrOfThreads): if Debug: print('job',th) fiName = tempDirName+'/sorting-'+str(th)+'.py' fi = open(fiName,'rb') sortingThread = loads(fi.read()) if Debug: print('sortingThread',sortingThread) sorting.update(sortingThread) # end of Threading else: # with out Threading sorting = {} for x in actions: sorting[x] = {} for c in categories: sorting[x][c] = {} cMinKey= c+'-m' cMaxKey= c+'-M' if LowerClosed: lowLimit = self.relation[x][cMinKey] notHighLimit = Max - self.relation[x][cMaxKey] + Min else: lowLimit = Max - self.relation[cMinKey][x] + Min notHighLimit = self.relation[cMaxKey][x] if Debug: print('%s in %s: low = %.2f, high = %.2f' % \ (x, c,lowLimit,notHighLimit), end=' ') categoryMembership = min(lowLimit,notHighLimit) sorting[x][c]['lowLimit'] = lowLimit sorting[x][c]['notHighLimit'] = notHighLimit sorting[x][c]['categoryMembership'] = categoryMembership if Debug: print('\t %.2f \t %.2f \t %.2f' % (sorting[x][c]['lowLimit'], sorting[x][c]['notHighLimit'], sorting[x][c]['categoryMembership'])) if StoreSorting: self.sorting = sorting return sorting
[docs] def showSortingCharacteristics(self, action=None): """ Renders a bipolar-valued bi-dictionary relation representing the degree of credibility of the assertion that "action x in A belongs to category c in C", ie x outranks low category limit and does not outrank the high category limit. """ Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] Max = self.valuationdomain['max'] actions = self.getActionsKeys(action) categoryKeys = self.orderedCategoryKeys() try: LowerClosed = self.criteriaCategoryLimits['LowerClosed'] except: LowerClosed = True sorting = {} if LowerClosed: print('x in K_k\t r(x >= m_k)\t r(x < M_k)\t r(x in K_k)') else: print('x in K_k\t r(m_k < x)\t r(M_k >= x)\t r(x in K_k)') for x in actions: sorting[x] = {} for i in range(len(categoryKeys)): sorting[x][categoryKeys[i]] = {} cMinKey= categoryKeys[i]+'-m' cMaxKey= categoryKeys[i]+'-M' if LowerClosed: lowLimit = self.relation[x][cMinKey] notHighLimit = Max - self.relation[x][cMaxKey] + Min else: lowLimit = Max - self.relation[cMinKey][x] + Min notHighLimit = self.relation[cMaxKey][x] if LowerClosed: if i < (len(categoryKeys)-1): print('%s in [%s - %s[\t' % (x, categoryKeys[i],categoryKeys[i+1]), end=' ') else: print('%s in [%s - %s[\t' % (x, categoryKeys[i],' '), end=' ') else: if i == 0: print('%s in ]%s - %s]\t' % (x, ' ',categoryKeys[i]), end=' ') else: print('%s in ]%s - %s]\t' % (x, categoryKeys[i-1],categoryKeys[i]), end=' ') categoryMembership = min(lowLimit,notHighLimit) sorting[x][categoryKeys[i]]['lowLimit'] = lowLimit sorting[x][categoryKeys[i]]['notHighLimit'] = notHighLimit sorting[x][categoryKeys[i]]['categoryMembership'] = categoryMembership print('%.2f\t\t %.2f\t\t %.2f' % (sorting[x][categoryKeys[i]]['lowLimit'],\ sorting[x][categoryKeys[i]]['notHighLimit'],\ sorting[x][categoryKeys[i]]['categoryMembership']))
def _computePessimisticSorting(self, Comments=False): """ Returns a dictionary with category keys gathering the actions per ordered category on the basis of a bipolar valued outranking relation Stilde with low and high category limt profiles. An action x is added to cotegory c if (a Stilde c_min) > Med and a Stilde C_Max <= Med. """ actions = self.getActionsKeys() categories = self.orderedCategoryKeys() Med = self.valuationdomain['med'] sorts = {} for c in categories: sorts[c] = set() for x in actions: if Comments: print(x) for c in categories: overMin=True overMax = True cMinKey= c+'-m' cMaxKey= c+'-M' if Comments: print('\t %s: low = %.2f, high = %.2f' % (c,self.relation[x][cMinKey],self.relation[x][cMaxKey])) if self.relation[x][cMinKey] > Med: overMin = True else: break if self.relation[x][cMaxKey] <= Med: overMax = False #print '\t %s: low = %.2f, high = %.2f' % (c,self.relation[x][cMinKey],self.relation[x][cMaxKey]) sorts[c].add(x) break if overMin and overMax: #print '\t %s: low = %.2f, high = %.2f' % (c,self.relation[x][cMinKey],self.relation[x][cMaxKey]) sorts[c].add(x) if Comments: print('Sorting results') for c in self.orderedCategoryKeys(): print('%s: %s' % (c, str(sorts[c]))) return sorts
[docs] def computeCategoryContents(self,Reverse=False,StoreSorting=True,Comments=False): """ Computes the sorting results per category. """ Med = self.valuationdomain['med'] actions = list(self.getActionsKeys()) actions.sort() try: sorting=self.sorting except: sorting = self.computeSortingCharacteristics(StoreSorting=StoreSorting,Comments=Comments) categoryContent = {} for c in self.orderedCategoryKeys(Reverse=Reverse): categoryContent[c] = [] for x in actions: if sorting[x][c]['categoryMembership'] >= Med: categoryContent[c].append(x) if StoreSorting: self.categoryContent = categoryContent return categoryContent
[docs] def showSorting(self,Reverse=True,isReturningHTML=False): """ Shows sorting results in decreasing or increasing (Reverse=False) order of the categories. If isReturningHTML is True (default = False) the method returns a htlm table with the sorting result. """ #from string import replace try: categoryContent = self.categoryContent except: categoryContent = self.computeCategoryContents(StoreSorting=True) try: LowerClosed = self.criteriaCategoryLimits['LowerClosed'] except: LowerClosed = true if Reverse: print('\n*--- Sorting results in descending order ---*\n') prev_c = '>' if isReturningHTML: prev_c = '&gt;' html = '<h2>Sorting results in descending order</h2>' html += '<table style="background-color:White;" border="1"><tr bgcolor="#9acd32"><th>Categories</th><th>Assorting</th></tr>' for c in self.orderedCategoryKeys(Reverse=Reverse): cName = self.categories[c]['name'] if LowerClosed: print(']%s - %s]:' % (prev_c,cName), end=' ') print('\t',categoryContent[c]) if isReturningHTML: html += '<tr><td bgcolor="#FFF79B">]%s - %s]</td>' % (prev_c,c) catString = str(categoryContent[c]) html += '<td>%s</td></tr>' % catString.replace('\'','&apos;') else: print('[%s - %s[:' % (prev_c,cName), end=' ') print('\t',categoryContent[c]) if isReturningHTML: html += '<tr><td bgcolor="#FFF79B">[%s - %s[</td>' % (prev_c,c) catString = str(categoryContent[c]) html += '<td>%s</td></tr>' % catString.replace('\'','&apos;') prev_c = cName else: print('\n*--- Sorting results in ascending order ---*\n') if isReturningHTML: html = '<h2>Sorting results in ascending order</h2>' html += '<table style="background-color:White;" border="1"><tr bgcolor="#9acd32"><th>Categories</th><th>Assorting</th></tr>' cat = [x for x in self.orderedCategoryKeys(Reverse=Reverse)] cat.append('<') catNames = [self.categories[x]['name'] for x in self.orderedCategoryKeys(Reverse=Reverse)] if isReturningHTML: catNames.append('&lt;') else: catNames.append('<') for i in range(len(cat)-1): if LowerClosed: print('[%s - %s[:' % (catNames[i],catNames[i+1]), end=' ') print('\t',categoryContent[cat[i]]) if isReturningHTML: html += '<tr><td bgcolor="#FFF79B">]%s - %s]</td>' % (cat[i],cat[i+1]) catString = str(categoryContent[cat[i]]) html += '<td>%s</td></tr>' % catString.replace('\'','&apos;') else: print(']%s - %s]:' % (catNames[i],catNames[i+1]), end=' ') print('\t',categoryContent[cat[i]]) if isReturningHTML: html += '<tr><td bgcolor="#FFF79B">[%s - %s[</td>' % (cat[i],cat[i+1]) catString = str(categoryContent[cat[i]]) html += '<td>%s</td></tr>' % catString.replace('\'','&apos;') if isReturningHTML: html += '</table>' return html
[docs] def showActionCategories(self,action,Debug=False,Comments=True,\ Threading=False,nbrOfCPUs=None): """ Renders the union of categories in which the given action is sorted positively or null into. Returns a tuple : action, lowest category key, highest category key, membership credibility ! """ Med = self.valuationdomain['med'] try: sorting = self.sorting except: sorting = self.computeSortingCharacteristics(action=action,\ Comments=Debug,\ Threading=Threading, StoreSorting=False, nbrOfCPUs=nbrOfCPUs) if Debug: print(sorting) #keys = [] catKeys = self.orderedCategoryKeys() lowLimit = sorting[action][catKeys[0]]['lowLimit'] notHighLimit = sorting[action][catKeys[-1]]['notHighLimit'] keys = [catKeys[0],catKeys[-1]] # action is sorted by default in all categories for c in self.orderedCategoryKeys(): if sorting[action][c]['categoryMembership'] >= Med: if sorting[action][c]['lowLimit'] > Med: lowLimit = sorting[action][c]['lowLimit'] keys[0] = c # the highest lowLimit is remembered if sorting[action][c]['notHighLimit'] > Med: notHighLimit = sorting[action][c]['notHighLimit'] keys[1] = c # the highest notHighLimit (lowest HigLimit) is remembered if Debug: print(action, c, sorting[action][c],keys) credibility = min(lowLimit,notHighLimit) if Comments: print('%s - %s: %s with credibility: %.2f = min(%.2f,%.2f)' % (\ self.categories[keys[0]]['name'],\ self.categories[keys[1]]['name'],\ action,\ credibility,lowLimit,notHighLimit) ) return action,\ keys[0],\ keys[1],\ credibility
[docs] def showActionsSortingResult(self,actionSubset=None,Debug=False): """ shows the quantiles sorting result all (default) of a subset of the decision actions. """ if actionSubset == None: actions = [x for x in self.actions] actions.sort() else: actions = [x for x in flatten(actionSubset)] print('Quantiles sorting result per decision action') for x in actions: self.showActionCategories(x,Debug=Debug)
[docs] def saveProfilesXMCDA2(self,fileName='temp',category='XMCDA 2.0 format',user='sortinDigraphs Module (RB)',version='saved from Python session',title='Sorting categories in XMCDA-2.0 format.',variant='Rubis',valuationType='bipolar',isStringIO=False,stringNA='NA',comment='produced by saveProfilesXMCDA2()'): """ Save profiles object self in XMCDA 2.0 format. """ import codecs if not isStringIO: print('*----- saving sorting profiles in XMCDA 2.0 format -------------*') nameExt = fileName+'.xml' if isStringIO: comment='produced by stringIO()' import io ## ms = 100 * len(self.actions) + 500 * len(self.criteria) * 20 * len(self.evaluation) ## print 'estimated mapped memory size = %d' % (ms) ##fo = mmap.mmap(-1,ms) fo = io.StringIO() else: #nameExt = fileName+'.xmcda2' fo = codecs.open(nameExt,'w',encoding='utf-8') fo.write('<?xml version="1.0" encoding="UTF-8"?>\n') fo.write('<?xml-stylesheet type="text/xsl" href="xmcda2Rubis.xsl"?>\n') fo.write(str('<xmcda:XMCDA xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.decision-deck.org/2010/XMCDA-2.1.0-Rubis http://leopold-loewenheim.uni.lu/XMCDA2/XMCDA-2.1.0-Rubis.xsd" xmlns:xmcda="http://www.decision-deck.org/2010/XMCDA-2.1.0-Rubis" instanceID="void">\n')) # write description fo.write('<projectReference id="%s" name="%s">\n' % (fileName,nameExt)) fo.write('<title>%s</title>\n' % (str(title)) ) fo.write('<author>%s</author>\n' % (user) ) fo.write('<version>%s</version>\n' % (version) ) fo.write('<comment>%s</comment>\n' % (str(comment)) ) fo.write('</projectReference>\n') # save categories categoriesList = [x for x in self.categories] categoriesList.sort() na = len(categoriesList) categories = self.categories fo.write('<categories mcdaConcept="%s">\n' % ('categories')) fo.write('<description>\n') fo.write('<subTitle>Sorting categories.</subTitle>\n') fo.write('</description>\n') for i in range(na): try: categoryName = str(categories[categoriesList[i]]['name']) except: categoryName = categoriesList[i] fo.write('<category id="%s" name="%s" mcdaConcept="%s">\n' % (categoriesList[i],categoryName,'sortingCategory')) fo.write('<description>\n') fo.write('<comment>') try: fo.write(str(categories[categoriesList[i]]['comment'])) except: fo.write('None') fo.write('</comment>\n') fo.write('</description>\n') fo.write('<type>real</type>\n') fo.write('<active>true</active>\n') fo.write('</category>\n') fo.write('</categories>\n') # save criteriaCategoryLimits criteriaList = [x for x in self.criteria] criteriaList.sort() categoriesList = [x for x in self.categories] categoriesList.sort() criteria = self.criteria fo.write('<criteriaCategoryLimits mcdaConcept="categoryProfiles">\n') fo.write('<description>\n') fo.write('<subTitle>Sorting profiles.</subTitle>\n') fo.write('</description>\n') for g in criteriaList: for c in categoriesList: try: criterionName = str(criteria[g]['id']) except: criterionName = g try: categoryName = str(category[c]['id']) except: categoryName = c fo.write('<criterionCategoryLimits id="lim_%s_%s" mcdaConcept="%s">\n' % (criterionName,categoryName,'criterionCategoryLimits' ) ) fo.write('<description>\n') fo.write('<comment>%s</comment>\n' % ('No comment') ) fo.write('<version>%s</version>\n' % ('Rubis k-sorting') ) fo.write('</description>\n') fo.write('<criterionID>%s</criterionID>\n' % (criterionName) ) fo.write('<categoryID>%s</categoryID>\n' % (categoryName) ) fo.write('<lowLimit><real>%.2f</real></lowLimit>\n' % (self.criteriaCategoryLimits[g][c]['minimum']) ) fo.write('<highLimit><real>%.2f</real></highLimit>\n' % (self.criteriaCategoryLimits[g][c]['maximum']) ) fo.write('</criterionCategoryLimits>\n') fo.write('</criteriaCategoryLimits>\n') ######################### fo.write('</xmcda:XMCDA>\n') if isStringIO: problemText = fo.getvalue() fo.close return problemText else: fo.close() print('File: ' + nameExt + ' saved !')
[docs] def recodeValuation(self,newMin=-1.0,newMax=1.0,Debug=False): """ Recodes the characteristic valuation domain according to the parameters given. .. note:: Default values gives a normalized valuation domain """ from copy import copy, deepcopy oldMax = self.valuationdomain['max'] oldMin = self.valuationdomain['min'] oldMed = self.valuationdomain['med'] oldAmplitude = oldMax - oldMin if Debug: print(oldMin, oldMed, oldMax, oldAmplitude) newMin = Decimal(str(newMin)) newMax = Decimal(str(newMax)) newMed = Decimal('%.3f' % ((newMax + newMin)/Decimal('2.0'))) newAmplitude = newMax - newMin if Debug: print(newMin, newMed, newMax, newAmplitude) actions = self.getActionsKeys(withoutProfiles=False) oldrelation = copy(self.relation) newrelation = {} for x in actions: newrelation[x] = {} for y in actions: if oldrelation[x][y] == oldMax: newrelation[x][y] = newMax elif oldrelation[x][y] == oldMin: newrelation[x][y] = newMin elif oldrelation[x][y] == oldMed: newrelation[x][y] = newMed else: newrelation[x][y] = newMin + ((self.relation[x][y] - oldMin)/oldAmplitude)*newAmplitude if Debug: print(x,y,self.relation[x][y],newrelation[x][y]) # install new values in self self.valuationdomain['max'] = newMax self.valuationdomain['min'] = newMin self.valuationdomain['med'] = newMed self.valuationdomain['hasIntegerValuation'] = False self.relation = copy(newrelation)
#-------------
[docs]class QuantilesSortingDigraph(SortingDigraph): """ Specialisation of the sortingDigraph Class for sorting of a large set of alternatives into quantiles delimited ordered classes. .. note:: We generally require an PerformanceTableau instance or a valid filename. If none is given, then a default profile with the limiting quartiles Q0,Q1,Q2, Q3 and Q4 is used on each criteria. By default upper closed limits of categories are supposed to be used in the sorting. Example Python3 session: >>> from sortingDigraphs import QuantilesSortingDigraph >>> from randomPerfTabs import RandomCBPerformanceTableau >>> t = RandomCBPerformanceTableau(numberOfActions=7,numberOfCriteria=5, ... weightDistribution='equiobjectives') >>> qs = QuantilesSortingDigraph(t,limitingQuantiles=7) >>> qs.showSorting() *--- Sorting results in descending order ---* ]0.86 - 1.00]: [] ]0.71 - 0.86]: ['a03'] ]0.57 - 0.71]: ['a04'] ]0.43 - 0.57]: ['a04', 'a05', 'a06'] ]0.29 - 0.43]: ['a01', 'a02', 'a06', 'a07'] ]0.14 - 0.29]: [] ]< - 0.14]: [] >>> qs.showQuantileOrdering() ]0.71-0.86] : ['a03'] ]0.43-0.71] : ['a04'] ]0.43-0.57] : ['a05'] ]0.29-0.57] : ['a06'] ]0.29-0.43] : ['a07', 'a02', 'a01'] >>> qs.exportGraphViz('quantilesSorting') .. image:: quantilesSorting.png """ def __init__(self,argPerfTab=None,\ limitingQuantiles=None,\ LowerClosed=False,\ PrefThresholds=True,\ hasNoVeto=False,\ outrankingType = "bipolar",\ WithSortingRelation=True,\ CompleteOutranking = False,\ StoreSorting=False,\ CopyPerfTab=False,\ Threading=False,\ tempDir=None,\ nbrCores=None,\ nbrOfProcesses=None,\ Comments=False, Debug=False): """ Constructor for QuantilesSortingBigDigraph instances. """ from time import time from copy import copy, deepcopy if CopyPerfTab: copy2self = deepcopy else: copy2self = copy from decimal import Decimal # import the performance tableau tt = time() if argPerfTab == None: print('Error: a valid performance tableau is required!') ## perfTab = RandomPerformanceTableau(numberOfActions=10, ## numberOfCriteria=13) else: perfTab = argPerfTab # normalize the actions as a dictionary construct if isinstance(perfTab.actions,list): actions = OrderedDict() for x in perfTab.actions: actions[x] = {'name': str(x)} else: actions = copy2self(perfTab.actions) actions = actions self.actions = actions # keep a copy of the original actions set before adding the profiles actionsOrig = OrderedDict(actions) self.actionsOrig = actionsOrig # normalizing the performance tableau normPerfTab = NormalizedPerformanceTableau(perfTab) # instantiating the performance tableau part criteria = normPerfTab.criteria self.criteria = criteria self.convertWeightFloatToDecimal() evaluation = normPerfTab.evaluation self.evaluation = evaluation self.convertEvaluationFloatToDecimal() self.runTimes = {'dataInput': time()-tt} # compute the limiting quantiles t0 = time() if isinstance(limitingQuantiles,list): self.name = 'sorting_with_given_quantiles' newLimitingQuantiles = [] for x in limitingQuantiles: newLimitingQuantiles.append(Decimal(str(x))) limitingQuantiles = newLimitingQuantiles if Debug: print('convert to decimal!',limitingQuantiles) else: limitingQuantiles = self._computeQuantiles(limitingQuantiles,Debug=Debug) self.limitingQuantiles = limitingQuantiles if Debug: print('limitingQuantiles',self.limitingQuantiles) # supposing all criteria scales between 0.0 and 100.0 # with preference direction = max self.LowerClosed = LowerClosed lowValue = 0.0 highValue = 100.00 categories = OrderedDict() k = len(limitingQuantiles)-1 if LowerClosed: for i in range(0,k-1): categories[str(i+1)] = {'name':'[%.2f - %.2f['\ %(limitingQuantiles[i],limitingQuantiles[i+1]),\ 'order':i+1,\ 'lowLimit': '[%.2f' % (limitingQuantiles[i]), 'highLimit': '%.2f[' % (limitingQuantiles[i+1]), 'quantile': limitingQuantiles[i]} categories[str(k)] = {'name':'[%.2f - <['\ %(limitingQuantiles[k-1]), 'order':k,\ 'lowLimit': '[%.2f' % (limitingQuantiles[k-1]),\ 'highLimit': '<[', 'quantile': limitingQuantiles[k-1] } else: categories[str(1)] = {'name':']< - %.2f]'\ %(limitingQuantiles[1]), 'order':1, 'highLimit': '%.2f]' % (limitingQuantiles[1]),\ 'lowLimit': ']<', 'quantile': limitingQuantiles[1]} for i in range(1,k): categories[str(i+1)] = {'name':']%.2f - %.2f]'\ %(limitingQuantiles[i],limitingQuantiles[i+1]), 'order':i+1, 'lowLimit': ']%.2f' % (limitingQuantiles[i]), 'highLimit': '%.2f]' % (limitingQuantiles[i+1]), 'quantile': limitingQuantiles[i+1]} self.categories = categories if Debug: print('categories',self.categories) print('list',list(dict.keys(categories))) criteriaCategoryLimits = {} criteriaCategoryLimits['LowerClosed'] = LowerClosed self.criteriaCategoryLimits = criteriaCategoryLimits for g in dict.keys(criteria): gQuantiles = self._computeLimitingQuantiles(g,\ PrefThresholds=PrefThresholds,Debug=Debug) ## if Debug: ## print(g,gQuantiles) criteriaCategoryLimits[g] = gQuantiles ## for c in categories: ## criteriaCategoryLimits[g][c]={ ## 'minimum':gQuantiles[(int(c)-1)], ## 'maximum':gQuantiles[int(c)] ## } self.criteriaCategoryLimits = criteriaCategoryLimits if Debug: print('CriteriaCategoryLimits',criteriaCategoryLimits) # set the category limits type (LowerClosed = True is default) # self.criteriaCategoryLimits['LowerClosed'] = LowerClosed # print 'LowerClosed', LowerClosed # add the catogory limits to the actions set profiles = OrderedDict() #profileLimits = set() for c in categories: if LowerClosed: cKey = c+'-m' else: cKey = c+'-M' #profileLimits.add(cKey) if LowerClosed: actions[cKey] = {'name': 'categorical low limits', 'comment': 'Inferior or equal limits for category membership assessment'} profiles[cKey] = {'category': c, 'name': 'categorical low limits', 'comment': 'Inferior or equal limits for category membership assessment'} else: actions[cKey] = {'name': 'categorical high limits', 'comment': 'Lower or equal limits for category membership assessment'} profiles[cKey] = {'category': c, 'name': 'categorical high limits', 'comment': 'Lower or equal limits for category membership assessment'} for g in dict.keys(criteria): if LowerClosed: evaluation[g][cKey] = Decimal(str(criteriaCategoryLimits[g][int(c)-1])) else: evaluation[g][cKey] = Decimal(str(criteriaCategoryLimits[g][int(c)])) self.profiles = profiles profileLimits = list(profiles.keys()) #profileLimits.sort() self.profileLimits = profileLimits if Debug: print('self.profiles',profiles) print('self.profileLimits',profileLimits) #self.convertEvaluationFloatToDecimal() self.runTimes['computeProfiles'] = time() - t0 # construct outranking relation t0 = time() self.hasNoVeto = hasNoVeto minValuation = -100.0 maxValuation = 100.0 if CompleteOutranking: g = BipolarOutrankingDigraph(normPerfTab,hasNoVeto=hasNoVeto, Threading=Threading,nbrCores=nbrCores) g.recodeValuation(minValuation,maxValuation) self.relationOrig = g.relation Min = g.valuationdomain['min'] Max = g.valuationdomain['max'] self.valuationdomain = g.valuationdomain else: Min = Decimal(str(minValuation)) Max = Decimal(str(maxValuation)) ## Min = Decimal('-100') ## Max = Decimal('100') Med = (Max + Min)/Decimal('2.0') self.valuationdomain = {'min': Min, 'med':Med ,'max':Max } if LowerClosed: initial=actionsOrig terminal=profiles else: initial=profiles terminal=actionsOrig relation = self._constructRelationWithThreading(criteria, evaluation, initial=initial, terminal=terminal, hasNoVeto=hasNoVeto, hasBipolarVeto=True, WithConcordanceRelation=False, WithVetoCounts=False, Threading=Threading, tempDir=tempDir, nbrCores=nbrCores, Comments=Comments, WithSortingRelation=WithSortingRelation, StoreSorting=StoreSorting) ## else: ## relation = self._constructRelationWithThreading(criteria, ## evaluation, ## terminal=actionsOrig, ## initial=profiles, ## hasNoVeto=hasNoVeto, ## hasBipolarVeto=True, ## WithConcordanceRelation=False, ## WithVetoCounts=False, ## Threading=Threading, ## nbrCores=nbrCores, ## Comments=Comments) if WithSortingRelation: if LowerClosed: for x in dict.keys(actionsOrig): rx = relation[x] for y in dict.keys(actionsOrig): rx[y] = Med for x in dict.keys(profiles): relation[x] = {} rx = relation[x] for y in dict.keys(actions): rx[y] = Med else: for x in dict.keys(actionsOrig): relation[x] = {} rx = relation[x] for y in dict.keys(actionsOrig): rx[y] = Med for y in dict.keys(profiles): for x in dict.keys(actions): relation[x][y] = Med self.relation = relation self.runTimes['computeRelation'] = time() - t0 # compute weak ordering t0 = time() if WithSortingRelation: if nbrOfProcesses == None: nbrOfProcesses = nbrCores sortingRelation = self.computeSortingRelation(StoreSorting=StoreSorting,\ Debug=Debug,Comments=Comments,\ Threading=Threading,\ nbrOfCPUs=nbrOfProcesses) for x in dict.keys(actionsOrig): rx = self.relation[x] srx = sortingRelation[x] for y in dict.keys(actionsOrig): rx[y] = srx[y] self.runTimes['weakOrdering'] = time() - t0 # reset original action set if WithSortingRelation: self.actions = actionsOrig self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.runTimes['totalTime'] = time() - tt ## else: ## self.computeCategoryContents(StoreSorting=StoreSorting,\ ## Threading=Threading,\ ## nbrOfCPUs=nbrOfProcesses,\ ## Comments=Comments) def _constructRelationWithThreading(self,criteria,\ evaluation,\ initial=None,\ terminal=None,\ hasNoVeto=False,\ hasBipolarVeto=True,\ Debug=False,\ hasSymmetricThresholds=True,\ Threading=False,\ tempDir=None,\ WithConcordanceRelation=True,\ WithVetoCounts=True,\ WithSortingRelation=True,\ StoreSorting=True,\ nbrCores=None,Comments=False): """ Specialization of the corresponding BipolarOutrankingDigraph method """ from multiprocessing import cpu_count LowerClosed = self.criteriaCategoryLimits['LowerClosed'] if not Threading or cpu_count() < 2: # set parameters for non threading self.nbrThreads = 1 Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] Max = self.valuationdomain['max'] # compute sorting relation # !! concordance relation and veto counts need a complex constructor if (not hasBipolarVeto) or WithConcordanceRelation or WithVetoCounts: constructRelation = self._constructRelation else: constructRelation = self._constructRelationSimple relation = constructRelation(criteria,\ evaluation,\ initial=initial,\ terminal=terminal,\ hasNoVeto=hasNoVeto,\ hasBipolarVeto=hasBipolarVeto,\ WithConcordanceRelation=WithConcordanceRelation,\ WithVetoCounts=WithVetoCounts,\ Debug=Debug,\ hasSymmetricThresholds=hasSymmetricThresholds) if WithSortingRelation: self.relation = relation # compute quantiles sorting result if LowerClosed: actions = initial else: actions = terminal categories = self.categories.keys() sorting = {} nq = len(self.limitingQuantiles) - 1 for x in actions: sorting[x] = {} for c in categories: sorting[x][c] = {} if LowerClosed: cKey= c+'-m' else: cKey= c+'-M' if LowerClosed: lowLimit = relation[x][cKey] if int(c) < nq: cMaxKey = str(int(c)+1)+'-m' notHighLimit = Max - relation[x][cMaxKey] + Min else: notHighLimit = Max else: if int(c) > 1: cMinKey = str(int(c)-1)+'-M' lowLimit = Max - relation[cMinKey][x] + Min else: lowLimit = Max notHighLimit = relation[cKey][x] categoryMembership = min(lowLimit,notHighLimit) sorting[x][c]['lowLimit'] = lowLimit sorting[x][c]['notHighLimit'] = notHighLimit sorting[x][c]['categoryMembership'] = categoryMembership if StoreSorting: self.sorting = sorting # compute category contents categoryContent = {} for c in self.orderedCategoryKeys(): categoryContent[c] = [] for x in actions: if sorting[x][c]['categoryMembership'] >= self.valuationdomain['med']: categoryContent[c].append(x) self.categoryContent = categoryContent return relation ## else: # parallel computation from copy import copy, deepcopy from io import BytesIO from pickle import Pickler, dumps, loads, load from multiprocessing import Process, Lock,\ active_children, cpu_count #Debug=True class myThread(Process): def __init__(self, threadID,\ InitialSplit, tempDirName,\ splitActions,\ hasNoVeto, hasBipolarVeto,\ hasSymmetricThresholds, Debug): Process.__init__(self) self.threadID = threadID self.InitialSplit = InitialSplit self.workingDirectory = tempDirName self.splitActions = splitActions self.hasNoVeto = hasNoVeto self.hasBipolarVeto = hasBipolarVeto, hasSymmetricThresholds = hasSymmetricThresholds, self.Debug = Debug def run(self): from io import BytesIO from pickle import Pickler, dumps, loads from os import chdir from outrankingDigraphs import BipolarOutrankingDigraph chdir(self.workingDirectory) ## if Debug: ## print("Starting working in %s on thread %s" % (self.workingDirectory, str(self.threadId))) fi = open('dumpSelf.py','rb') digraph = loads(fi.read()) fi.close() Min = digraph.valuationdomain['min'] Med = digraph.valuationdomain['med'] Max = digraph.valuationdomain['max'] splitActions = self.splitActions constructRelation = BipolarOutrankingDigraph._constructRelation if self.InitialSplit: initialIn = splitActions terminalIn = digraph.profiles else: initialIn = digraph.profiles terminalIn = splitActions splitRelation = constructRelation( digraph,digraph.criteria,\ digraph.evaluation,\ initial=initialIn,\ terminal=terminalIn,\ hasNoVeto=hasNoVeto,\ hasBipolarVeto=hasBipolarVeto,\ WithConcordanceRelation=False,\ WithVetoCounts=False,\ #WithSortingRelation=True,\ #StoreSorting=True,\ Debug=False,\ hasSymmetricThresholds=hasSymmetricThresholds) foName = 'splitRelation-'+str(self.threadID)+'.py' fo = open(foName,'wb') fo.write(dumps(splitRelation,-1)) fo.close() # compute quantiles sorting result LowerClosed = digraph.criteriaCategoryLimits['LowerClosed'] nq = len(digraph.limitingQuantiles) - 1 categories = digraph.categories.keys() sorting = {} nq = len(digraph.limitingQuantiles) - 1 for x in splitActions: sorting[x] = {} for c in categories: sorting[x][c] = {} if LowerClosed: cKey= c+'-m' else: cKey= c+'-M' if LowerClosed: lowLimit = splitRelation[x][cKey] if int(c) < nq: cMaxKey = str(int(c)+1)+'-m' notHighLimit = Max - splitRelation[x][cMaxKey] + Min else: notHighLimit = Max else: if int(c) > 1: cMinKey = str(int(c)-1)+'-M' lowLimit = Max - splitRelation[cMinKey][x] + Min else: lowLimit = Max notHighLimit = splitRelation[cKey][x] categoryMembership = min(lowLimit,notHighLimit) sorting[x][c]['lowLimit'] = lowLimit sorting[x][c]['notHighLimit'] = notHighLimit sorting[x][c]['categoryMembership'] = categoryMembership if StoreSorting: #self.sorting = sorting foName = 'splitSorting-'+str(self.threadID)+'.py' fo = open(foName,'wb') fo.write(dumps(sorting,-1)) fo.close() # compute category contents categoryContent = {} for c in digraph.orderedCategoryKeys(): categoryContent[c] = [] for x in splitActions: if sorting[x][c]['categoryMembership'] >= digraph.valuationdomain['med']: categoryContent[c].append(x) #self.categoryContent = categoryContent foName = 'splitCategoryContent-'+str(self.threadID)+'.py' fo = open(foName,'wb') fo.write(dumps(categoryContent,-1)) fo.close() # ....... if Comments: print('Threading ...') from tempfile import TemporaryDirectory with TemporaryDirectory(dir=tempDir) as tempDirName: from copy import copy, deepcopy #selfDp = copy(self) selfFileName = tempDirName +'/dumpSelf.py' if Debug: print('temDirName, selfFileName', tempDirName,selfFileName) fo = open(selfFileName,'wb') fo.write(dumps(self,-1)) fo.close() if nbrCores == None: nbrCores = cpu_count() if Comments: print('Nbr of cpus = ',nbrCores) # set number of threads self.nbrThreads = nbrCores ni = len(initial) nt = len(terminal) if LowerClosed: n = ni actions2Split = list(initial) InitialSplit = True else: n = nt actions2Split = list(terminal) InitialSplit = False ## if Debug: ## print('InitialSplit, actions2Split', InitialSplit, actions2Split) nit = n//nbrCores nbrOfJobs = nbrCores if nit*nbrCores < n: nit += 1 while nit*(nbrOfJobs-1) >= n: nbrOfJobs -= 1 if Comments: print('nbr of actions to split',n) print('nbr of jobs = ',nbrOfJobs) print('nbr of splitActions = ',nit) relation = {} Med = self.valuationdomain['med'] for x in initial: relation[x] = {} rx = relation[x] for y in terminal: rx[y] = Med i = 0 actionsRemain = set(actions2Split) splitActionsList = [] for j in range(nbrOfJobs): if Comments: print('Thread = %d/%d' % (j+1,nbrOfJobs),end=" ") splitActions=[] for k in range(nit): if j < (nbrOfJobs -1) and i < n: splitActions.append(actions2Split[i]) else: splitActions = list(actionsRemain) i += 1 if Comments: print('%d' % (len(splitActions)) ) ## if Debug: ## print(splitActions) actionsRemain = actionsRemain - set(splitActions) ## if Debug: ## print(actionsRemain) splitActionsList.append(splitActions) ## foName = tempDirName+'/splitActions-'+str(j)+'.py' ## fo = open(foName,'wb') ## spa = dumps(splitActions,-1) ## fo.write(spa) ## fo.close() splitThread = myThread(j,InitialSplit, tempDirName,splitActions, hasNoVeto,hasBipolarVeto, hasSymmetricThresholds,Debug) splitThread.start() while active_children() != []: pass if Comments: print('Exiting computing threads') sorting = {} categoryContent = {} relation = {} for j in range(len(splitActionsList)): # update category contents fiName = tempDirName+'/splitCategoryContent-'+str(j)+'.py' fi = open(fiName,'rb') splitCategoryContent = loads(fi.read()) categoryContent.update(splitCategoryContent) # update sorting result if StoreSorting: fiName = tempDirName+'/splitSorting-'+str(j)+'.py' fi = open(fiName,'rb') splitSorting = loads(fi.read()) sorting.update(splitSorting) # update complete sorting relation if WithSortingRelation: splitActions = splitActionsList[j] ## if Debug: ## print('splitActions',splitActions) fiName = tempDirName+'/splitRelation-'+str(j)+'.py' fi = open(fiName,'rb') splitRelation = loads(fi.read()) ## if Debug: ## print('splitRelation',splitRelation) fi.close() #relation update with splitRelation) if LowerClosed: #for x,y in product(splitActions,terminal): for x in splitActions: try: rx = relation[x] except: relation[x] = {} rx = relation[x] sprx = splitRelation[x] for y in self.profiles: rx[y] = sprx[y] else: #for x,y in product(initial,splitActions): for x in self.profiles: try: rx = relation[x] except KeyError: relation[x] = {} rx = relation[x] sprx = splitRelation[x] for y in splitActions: rx[y] = sprx[y] self.categoryContent = categoryContent if StoreSorting: self.sorting = sorting if WithSortingRelation: return relation
[docs] def showActionCategories(self,action,Debug=False,Comments=True,\ Threading=False,nbrOfCPUs=None): """ Renders the union of categories in which the given action is sorted positively or null into. Returns a tuple : action, lowest category key, highest category key, membership credibility ! """ Med = self.valuationdomain['med'] try: sorting = self.sorting except: sorting = self.computeSortingCharacteristics(action=action,\ Comments=Debug,\ Threading=Threading, StoreSorting=False, nbrOfCPUs=nbrOfCPUs) catKeys = self.orderedCategoryKeys() keys = [catKeys[0],[catKeys[-1]]] lowLimit = sorting[action][catKeys[0]]['lowLimit'] notHighLimit = sorting[action][catKeys[-1]]['lowLimit'] for c in self.orderedCategoryKeys(): if sorting[action][c]['categoryMembership'] >= Med: if sorting[action][c]['lowLimit'] > Med: lowLimit = sorting[action][c]['lowLimit'] keys[0] = c if sorting[action][c]['notHighLimit'] > Med: notHighLimit = sorting[action][c]['notHighLimit'] keys[1] = c #keys.append(c) if Debug: print(action, c, sorting[action][c], keys) credibility = min(lowLimit,notHighLimit) if Comments: print('%s - %s: %s with credibility: %.2f = min(%.2f,%.2f)' % (\ self.categories[keys[0]]['lowLimit'],\ self.categories[keys[-1]]['highLimit'],\ action,\ credibility,lowLimit,notHighLimit) ) return action,\ keys[0],\ keys[1],\ credibility
[docs] def showActionsSortingResult(self,actionSubset=None,Debug=False): """ shows the quantiles sorting result all (default) of a subset of the decision actions. """ if actionSubset == None: actions = [x for x in self.actions] actions.sort() else: actions = [x for x in flatten(actionSubset)] print('Quantiles sorting result per decision action') for x in actions: self.showActionCategories(x,Debug=Debug)
[docs] def showWeakOrder(self,Descending=True): """ Specialisation for QuantilesSortingDigraphs. """ from decimal import Decimal from weakOrders import WeakOrder try: cC = self.categoryContent except: cC = self.computeCategoryContents(StoreSorting=True) if Descending: cCKeys = self.orderedCategoryKeys(Reverse = True) else: cCKeys = self.orderedCategoryKeys(Reverse = False) n = len(cC) n2 = n//2 ordering = [] for i in range(n2): if i == 0: x = cC[cCKeys[i]] y = cC[cCKeys[n-i-1]] setx = set(x) sety = set(y) - setx else: x = list(set(cC[cCKeys[i]]) - (setx | sety)) setx = setx | set(x) y = list(set(cC[cCKeys[n-i-1]]) - (setx | sety)) sety = sety | set(y) if x != [] or y != []: ordering.append( ( (Decimal(str(i+1)),x),(Decimal(str(n-i)),y) ) ) if 2*n2 < n: if n2 == 0: x = cC[cCKeys[n2]] else: x = list(set(cC[cCKeys[n2]]) - (setx | sety)) ordering.append( ( (Decimal(str(n2+1)),x),(Decimal(str(n2+1)),x) ) ) ## orderingList = [] ## for i in range(n2): ## x = ordering[i][0][1] ## if x != []: ## orderingList.append(x) ## if 2*n2 < n: ## x = ordering[i][0][1] ## y = ordering[i][1][1] ## if x != []: ## orderingList.append(x) ## if y != []: ## orderingList.append(y) ## for i in range(n2): ## y = ordering[n2-i-1][1][1] ## if y != []: ## orderingList.append(y) ## weakOrdering = {'result':ordering} WeakOrder.showWeakOrder(self,weakOrdering)
## return orderingList def _computeQuantileOrdering(self,strategy=None, Descending=True, Debug=False): """ Renders the *Parameters*: * Descending: listing in *decreasing* (default) or *increasing* quantile order. * strategy: ordering in an {'optimistic' | 'pessimistic' | 'average' (default)} in the uppest, the lowest or the average potential quantile. """ if strategy == None: try: strategy = self.sortingParameters['strategy'] except: strategy = 'average' actionsCategories = {} for x in self.actions: a,lowCateg,highCateg,credibility =\ self.showActionCategories(x,Comments=Debug) if strategy == "optimistic": try: actionsCategories[(int(highCateg),int(lowCateg))].append(a) except: actionsCategories[(int(highCateg),int(lowCateg))] = [a] elif strategy == "pessimistic": try: actionsCategories[(int(lowCateg),int(highCateg))].append(a) except: actionsCategories[(int(lowCateg),int(highCateg))] = [a] elif strategy == "average": lc = float(lowCateg) hc = float(highCateg) ac = (lc+hc)/2.0 try: actionsCategories[(ac,int(highCateg),int(lowCateg))].append(a) except: actionsCategories[(ac,int(highCateg),int(lowCateg))] = [a] else: # optimistic by default try: actionsCategories[(int(highCateg),int(lowCateg))].append(a) except: actionsCategories[(int(highCateg),int(lowCateg))] = [a] actionsCategIntervals = [] for interval in actionsCategories: actionsCategIntervals.append([interval,\ actionsCategories[interval]]) actionsCategIntervals.sort(reverse=Descending) return actionsCategIntervals
[docs] def computeQuantileOrdering(self,strategy=None, Descending=True, HTML=False, Comments=False, Debug=False): """ *Parameters*: * Descending: listing in *decreasing* (default) or *increasing* quantile order. * strategy: ordering in an {'optimistic' (default) | 'pessimistic' | 'average'} in the uppest, the lowest or the average potential quantile. """ if strategy == None: strategy = 'optimistic' if HTML: html = '<h1>Quantiles preordering</h1>' html += '<table style="background-color:White;" border="1">' html += '<tr bgcolor="#9acd32"><th>quantile limits</th>' html += '<th>%s sorting</th>' % strategy html += '</tr>' actionsCategories = {} for x in self.actions: a,lowCateg,highCateg,credibility =\ self.showActionCategories(x,Comments=Debug) if strategy == "optimistic": try: actionsCategories[(int(highCateg),int(lowCateg))].append(a) except: actionsCategories[(int(highCateg),int(lowCateg))] = [a] elif strategy == "pessimistic": try: actionsCategories[(int(lowCateg),int(highCateg))].append(a) except: actionsCategories[(int(lowCateg),int(highCateg))] = [a] elif strategy == "average": lc = float(lowCateg) hc = float(highCateg) ac = (lc+hc)/2.0 try: actionsCategories[(ac,int(highCateg),int(lowCateg))].append(a) except: actionsCategories[(ac,int(highCateg),int(lowCateg))] = [a] else: # optimistic by default try: actionsCategories[(int(highCateg),int(lowCateg))].append(a) except: actionsCategories[(int(highCateg),int(lowCateg))] = [a] actionsCategIntervals = [] for interval in actionsCategories: actionsCategIntervals.append([interval,\ actionsCategories[interval]]) actionsCategIntervals.sort(reverse=Descending) weakOrdering = [] for item in actionsCategIntervals: #print(item) if Comments: if strategy == "optimistic": if self.criteriaCategoryLimits['LowerClosed']: if HTML: html += '<tr><tdbgcolor="#FFF79B">%s-%s</td>' % (self.categories[str(item[0][1])]['lowLimit'],\ self.categories[str(item[0][0])]['highLimit']) html += '<td>%s</td></tr>' % str(item[1]) else: print('%s-%s : %s' % (self.categories[str(item[0][1])]['lowLimit'],\ self.categories[str(item[0][0])]['highLimit'],\ str(item[1])) ) else: if HTML: html += '<tr><td bgcolor="#FFF79B">%s-%s</td>' % (self.categories[str(item[0][1])]['lowLimit'],\ self.categories[str(item[0][0])]['highLimit']) html += '<td>%s</td></tr>' % str(item[1]) else: print('%s-%s : %s' % (self.categories[str(item[0][1])]['lowLimit'],\ self.categories[str(item[0][0])]['highLimit'],\ str(item[1])) ) elif strategy == "pessimistic": if self.criteriaCategoryLimits['LowerClosed']: if HTML: html += '<tr><td bgcolor="#FFF79B">%s-%s</td>' % (self.categories[str(item[0][0])]['lowLimit'],\ self.categories[str(item[0][1])]['highLimit']) html += '<td>%s</td></tr>' % str(item[1]) else: print('%s-%s : %s' % (self.categories[str(item[0][0])]['lowLimit'],\ self.categories[str(item[0][1])]['highLimit'],\ str(item[1])) ) else: if HTML: html += '<tr><td bgcolor="#FFF79B">%s-%s</td>' % (self.categories[str(item[0][0])]['lowLimit'],\ self.categories[str(item[0][1])]['highLimit']) html += '<td>%s</td></tr>' % str(item[1]) else: print('%s-%s : %s' % (self.categories[str(item[0][0])]['lowLimit'],\ self.categories[str(item[0][1])]['highLimit'],\ str(item[1])) ) elif strategy == "average": if self.criteriaCategoryLimits['LowerClosed']: if HTML: html += '<tr><td bgcolor="#FFF79B">%s-%s</td>' % (self.categories[str(item[0][2])]['lowLimit'],\ self.categories[str(item[0][1])]['highLimit']) html += '<td>%s</td></tr>' % str(item[1]) else: print('%s-%s : %s' % (self.categories[str(item[0][2])]['lowLimit'],\ self.categories[str(item[0][1])]['highLimit'],\ str(item[1])) ) else: if HTML: html += '<tr><td bgcolor="#FFF79B">%s-%s</td>' % (self.categories[str(item[0][2])]['lowLimit'],\ self.categories[str(item[0][2])]['highLimit']) html += '<td>%s</td></tr>' % str(item[1]) else: print('%s-%s : %s' % (self.categories[str(item[0][2])]['lowLimit'],\ self.categories[str(item[0][1])]['highLimit'],\ str(item[1])) ) weakOrdering.append(item[1]) if HTML: html += '</table>' return html else: return weakOrdering
[docs] def showQuantileOrdering(self,strategy=None): """ Dummy show method for the commenting computeQuantileOrdering() method. """ self.computeQuantileOrdering(strategy=strategy,Comments=True)
[docs] def computeWeakOrder(self,Descending=True,Debug=False): """ Specialisation for QuantilesSortingDigraphs. """ from decimal import Decimal try: cC = self.categoryContent except: cC = self.computeCategoryContents(StoreSorting=True) if Debug: print(cC) if Descending: cCKeys = self.orderedCategoryKeys(Reverse = True) else: cCKeys = self.orderedCategoryKeys(Reverse = False) if Debug: print('cCKeys',cCKeys) n = len(cC) n2 = n//2 if Debug: print('n,n2',n,n2) ordering = [] for i in range(n2): if i == 0: x = cC[cCKeys[i]] y = cC[cCKeys[n-i-1]] setx = set(x) sety = set(y) - setx else: x = list(set(cC[cCKeys[i]]) - (setx | sety)) setx = setx | set(x) y = list(set(cC[cCKeys[n-i-1]]) - (setx | sety)) sety = sety | set(y) if Debug: print('i,x,y,setx,sety',i,x,y,setx,sety) if x != [] or y != []: ordering.append( ( (Decimal(str(i+1)),x),(Decimal(str(n-i)),y) )) if Debug: print(i, ( (Decimal(str(i+1)),x),(Decimal(str(n-i)),y) ) ) if 2*n2 < n: if n2 == 0: x = cC[cCKeys[n2]] else: x = list(set(cC[cCKeys[n2]]) - (setx | sety)) ordering.append( ( (Decimal(str(n2+1)),x),(Decimal(str(n2+1)),[]) ) ) if Debug: print('median term',( (Decimal(str(n2+1)),x),(Decimal(str(n2+1)),[]) )) if Debug: print(ordering) orderingList = [] n = len(ordering) for i in range(n): x = ordering[i][0][1] if x != []: orderingList.append(x) for i in range(n): y = ordering[n-i-1][1][1] if y != []: orderingList.append(y) ## ## ## weakOrdering = {'result':ordering} ## ## WeakOrder.showWeakOrder(self,weakOrdering) return orderingList
[docs] def showOrderedRelationTable(self,direction="decreasing"): """ Showing the relation table in decreasing (default) or increasing order. """ if direction == "decreasing": Descending = True else: Descending = False weakOrdering = self.computeWeakOrder(Descending) actionsList = [] for eq in weakOrdering: #print(eq) eq.sort() for x in eq: actionsList.append(x) if len(actionsList) != len(self.actions): print('Error !: missing action(s) %s in ordered table.') Digraph.showRelationTable(self,actionsSubset=actionsList,\ relation=self.relation,\ Sorted=False,\ ReflexiveTerms=False)
def _computeQuantiles(self,x,Debug=False): """ renders the limiting quantiles """ from math import floor if isinstance(x,int): n = x elif x == None: n = 4 elif x == 'bitiles': n = 2 elif x == 'tritiles': n = 3 elif x == 'quartiles': n = 4 elif x == 'quintiles': n = 5 elif x == 'sextiles': n = 6 elif x == 'septiles': n = 7 elif x == 'octiles': n = 8 elif x == 'deciles': n = 10 elif x == 'dodeciles': n = 20 elif x == 'centiles': n = 100 elif x == 'automatic': pth = [5] for g in self.criteria: try: pref = self.criteria[g]['thresholds']['ind'][0] + \ (self.criteria[g]['thresholds']['ind'][1]*Decimal('100')) pth.append(pref) except: pass amp = max(Decimal('1'),min(pth)) n = int(floor(Decimal('100')/amp)) if Debug: print('Detected preference thresholds = ',pth) print('amplitude, n',amp,n) limitingQuantiles = [] for i in range(n+1): limitingQuantiles.append( Decimal(str(i)) / Decimal(str(n)) ) self.name = 'sorting_with_%d-tile_limits' % n return limitingQuantiles def _computeLimitingQuantiles(self,g,Debug=False,PrefThresholds=True): """ Renders the list of limiting quantiles on criteria g """ from math import floor from copy import copy, deepcopy LowerClosed = self.criteriaCategoryLimits['LowerClosed'] gValues = [] for x in self.actionsOrig: if Debug: print('g,x,evaluation[g][x]',g,x,self.evaluation[g][x]) if self.evaluation[g][x] != Decimal('-999'): gValues.append(self.evaluation[g][x]) gValues.sort() if PrefThresholds: try: gPrefThrCst = self.criteria[g]['thresholds']['pref'][0] gPrefThrSlope = self.criteria[g]['thresholds']['pref'][1] except: gPrefThrCst = Decimal('0') gPrefThrSlope = Decimal('0') n = len(gValues) if Debug: print('g,n,gValues',g,n,gValues) ## if n > 0: ## nf = Decimal(str(n+1)) nf = Decimal(str(n)) limitingQuantiles = copy(self.limitingQuantiles) limitingQuantiles.sort() if Debug: print(limitingQuantiles) if LowerClosed: limitingQuantiles = limitingQuantiles[:-1] else: limitingQuantiles = limitingQuantiles[1:] if Debug: print(limitingQuantiles) # computing the quantiles on criterion g gQuantiles = [] if LowerClosed: # we ignore the 1.00 quantile and replace it with +infty for q in self.limitingQuantiles: r = (Decimal(str(nf)) * q) rq = int(floor(r)) if Debug: print('r,rq',r,rq, end=' ') if rq < (n-1): quantile = gValues[rq]\ + ((r-Decimal(str(rq)))*(gValues[rq+1]-gValues[rq])) if rq > 0 and PrefThresholds: quantile += gPrefThrCst + quantile*gPrefThrSlope else : if self.criteria[g]['preferenceDirection'] == 'min': quantile = Decimal('100.0') else: quantile = Decimal('200.0') if Debug: print('quantile',quantile) gQuantiles.append(quantile) else: # upper closed categories # we ignore the quantile 0.0 and replace it with -\infty for q in self.limitingQuantiles: r = (Decimal(str(nf)) * q) rq = int(floor(r)) if Debug: print('r,rq',r,rq, end=' ') if rq == 0: if self.criteria[g]['preferenceDirection'] == 'min': quantile = Decimal('-200.0') else: quantile = Decimal('-100.0') elif rq < (n-1): quantile = gValues[rq]\ + ((r-Decimal(str(rq)))*(gValues[rq+1]-gValues[rq])) if PrefThresholds: quantile -= gPrefThrCst - quantile*gPrefThrSlope else: if n > 0: quantile = gValues[n-1] else: if self.criteria[g]['preferenceDirection'] == 'min': quantile = Decimal('-200.0') else: quantile = Decimal('-100.0') if Debug: print('quantile',quantile) gQuantiles.append(quantile) ## else: ## gQuantiles = [] if Debug: print(g,LowerClosed,self.criteria[g]['preferenceDirection'],gQuantiles) return gQuantiles
[docs] def getActionsKeys(self,action=None,withoutProfiles=True): """ extract normal actions keys() """ profiles = set([x for x in list(self.profiles.keys())]) if action == None: actionsExt = set([x for x in list(self.actions.keys())]) if withoutProfiles: return actionsExt - profiles else: return actionsExt | profiles else: return set([action])
[docs] def computeCategoryContents(self,Reverse=False,Comments=False,StoreSorting=True,\ Threading=False,nbrOfCPUs=None): """ Computes the sorting results per category. """ actions = list(self.getActionsKeys()) actions.sort() try: sorting = self.sorting except: sorting = self.computeSortingCharacteristics(Comments=Comments,\ StoreSorting=StoreSorting,\ Threading=Threading,\ nbrOfCPUs=nbrOfCPUs) categoryContent = {} for c in self.orderedCategoryKeys(Reverse=Reverse): categoryContent[c] = [] for x in actions: if sorting[x][c]['categoryMembership'] >= self.valuationdomain['med']: categoryContent[c].append(x) return categoryContent
[docs] def computeSortingCharacteristics(self, action=None,Comments=False,\ StoreSorting=False,Debug=False,\ Threading=False, nbrOfCPUs=None): """ Renders a bipolar-valued bi-dictionary relation representing the degree of credibility of the assertion that "action x in A belongs to category c in C", ie x outranks low category limit and does not outrank the high category limit. """ Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] Max = self.valuationdomain['max'] try: return self.sorting except: pass if action != None: storeSorting = False actions = list(self.getActionsKeys(action)) na = len(actions) ## if Debug: ## print(actions) #categories = list(self.orderedCategoryKeys()) categories = list(self.categories.keys()) selfRelation = self.relation try: LowerClosed = self.criteriaCategoryLimits['LowerClosed'] except: LowerClosed = True if Threading and action==None: from multiprocessing import Process, active_children from pickle import dumps, loads, load from os import cpu_count from time import time ## if Comments: ## self.Debug = True class myThread(Process): def __init__(self, threadID, tempDirName, nq, Min, Max, LowerClosed, Debug): Process.__init__(self) self.threadID = threadID self.workingDirectory = tempDirName #self.actions = actions self.nq = nq self.Min = Min self.Max = Max self.LowerClosed = LowerClosed self.Debug = Debug def run(self): from pickle import dumps, loads from os import chdir chdir(self.workingDirectory) ## if self.Debug: ## print("Starting working in %s on %s" % (self.workingDirectory, str(self.threadID))) ## print('actions,catKeys',self.actions,self.catKeys) fi = open('dumpSelfRelation.py','rb') #context = loads(fi.read()) relation = loads(fi.read()) fi.close() fi = open('dumpCategories.py','rb') #context = loads(fi.read()) catKeys = loads(fi.read()) fi.close() fi = open('dumpActions%d.py' % self.threadID,'rb') #context = loads(fi.read()) actions = loads(fi.read()) fi.close() ## Min = context.valuationdomain['min'] ## Max = context.valuationdomain['max'] Min = self.Min Max = self.Max LowerClosed = self.LowerClosed sorting = {} nq = self.nq #nq = len(context.limitingQuantiles) - 1 #actions = self.actions #catKeys = self.catKeys #relation = context.relation for x in actions: sorting[x] = {} sorx = sorting[x] for c in catKeys: sorx[c] = {} if LowerClosed: cKey= c+'-m' else: cKey= c+'-M' if LowerClosed: lowLimit = relation[x][cKey] if int(c) < nq: cMaxKey = str(int(c)+1)+'-m' notHighLimit = Max - relation[x][cMaxKey] + Min else: notHighLimit = Max else: if int(c) > 1: cMinKey = str(int(c)-1)+'-M' lowLimit = Max - relation[cMinKey][x] + Min else: lowLimit = Max notHighLimit = relation[cKey][x] ## cMinKey= c+'-m' ## cMaxKey= c+'-M' ## if LowerClosed: ## lowLimit = context.relation[x][cMinKey] ## notHighLimit = Max - context.relation[x][cMaxKey] + Min ## else: ## lowLimit = Max - context.relation[cMinKey][x] + Min ## notHighLimit = context.relation[cMaxKey][x] if Debug: print('%s in %s: low = %.2f, high = %.2f' % \ (x, c,lowLimit,notHighLimit), end=' ') categoryMembership = min(lowLimit,notHighLimit) sorx[c]['lowLimit'] = lowLimit sorx[c]['notHighLimit'] = notHighLimit sorx[c]['categoryMembership'] = categoryMembership ## if self.Debug: ## print('\t %.2f \t %.2f \t %.2f\n' % (sorting[x][c]['lowLimit'],\ ## sorting[x][c]['notHighLimit'], sorting[x][c]['categoryMembership'])) ## if self.Debug: ## print(sorting[x]) foName = 'sorting-'+str(self.threadID)+'.py' fo = open(foName,'wb') fo.write(dumps(sorting,-1)) fo.close() if Comments: print('Threaded computing of sorting characteristics ...') from tempfile import TemporaryDirectory,mkdtemp tempDirName = mkdtemp() td = time() selfFileName = tempDirName +'/dumpSelfRelation.py' ## if Debug: ## print('temDirName, selfFileName', tempDirName,selfFileName) fo = open(selfFileName,'wb') pd = dumps(selfRelation,-1) fo.write(pd) fo.close() selfFileName = tempDirName +'/dumpCategories.py' ## if Debug: ## print('temDirName, selfFileName', tempDirName,selfFileName) fo = open(selfFileName,'wb') pd = dumps(categories,-1) fo.write(pd) fo.close() if nbrOfCPUs == None: nbrOfCPUs = cpu_count()-1 if Comments: print('Dump relation: %.5f' % (time()-td)) print('Nbr of actions',na) nbrOfJobs = na//nbrOfCPUs if nbrOfJobs*nbrOfCPUs < na: nbrOfJobs += 1 if Comments: print('Nbr of threads = ',nbrOfCPUs) print('Nbr of jobs/thread',nbrOfJobs) nbrOfThreads = 0 nq = len(self.limitingQuantiles) -1 Max = self.valuationdomain['max'] Min = self.valuationdomain['min'] for j in range(nbrOfCPUs): if Comments: print('thread = %d/%d' % (j+1,nbrOfCPUs),end="...") start= j*nbrOfJobs if (j+1)*nbrOfJobs < na: stop = (j+1)*nbrOfJobs else: stop = na thActions = actions[start:stop] ## if Debug: ## print(thActions) if thActions != []: selfFileName = tempDirName +'/dumpActions%d.py' % j fo = open(selfFileName,'wb') pd = dumps(thActions,-1) fo.write(pd) fo.close() process = myThread(j,tempDirName,nq,Min,Max, LowerClosed,Debug) process.start() nbrOfThreads += 1 while active_children() != []: pass #sleep(1) if Comments: print('Exit %d threads' % nbrOfThreads) sorting = {} for th in range(nbrOfThreads): ## if Debug: ## print('job',th) fiName = tempDirName+'/sorting-'+str(th)+'.py' fi = open(fiName,'rb') sortingThread = loads(fi.read()) ## if Debug: ## print('sortingThread',sortingThread) sorting.update(sortingThread) # end of Threading else: # with out Threading sorting = {} nq = len(self.limitingQuantiles) - 1 for x in actions: sorting[x] = {} for c in categories: sorting[x][c] = {} if LowerClosed: cKey= c+'-m' else: cKey= c+'-M' if LowerClosed: lowLimit = selfRelation[x][cKey] if int(c) < nq: cMaxKey = str(int(c)+1)+'-m' notHighLimit = Max - selfRelation[x][cMaxKey] + Min else: notHighLimit = Max else: if int(c) > 1: cMinKey = str(int(c)-1)+'-M' lowLimit = Max - selfRelation[cMinKey][x] + Min else: lowLimit = Max notHighLimit = selfRelation[cKey][x] ## cMinKey= c+'-m' ## cMaxKey= c+'-M' ## if LowerClosed: ## lowLimit = self.relation[x][cMinKey] ## notHighLimit = Max - self.relation[x][cMaxKey] + Min ## else: ## lowLimit = Max - self.relation[cMinKey][x] + Min ## notHighLimit = self.relation[cMaxKey][x] ## if Debug: ## print('%s in %s: low = %.2f, high = %.2f' % \ ## (x, c,lowLimit,notHighLimit), end=' ') categoryMembership = min(lowLimit,notHighLimit) sorting[x][c]['lowLimit'] = lowLimit sorting[x][c]['notHighLimit'] = notHighLimit sorting[x][c]['categoryMembership'] = categoryMembership ## if Debug: ## print('\t %.2f \t %.2f \t %.2f' % (sorting[x][c]['lowLimit'], sorting[x][c]['notHighLimit'], sorting[x][c]['categoryMembership'])) if StoreSorting: self.sorting = sorting return sorting
def _computeSortingCharacteristicsOld(self, action=None, Comments=False): """ Renders a bipolar-valued bi-dictionary relation representing the degree of credibility of the assertion that "action x in A belongs to category c in C", ie x outranks low category limit and does not outrank the high category limit. """ Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] Max = self.valuationdomain['max'] actions = self.getActionsKeys(action) categories = self.orderedCategoryKeys() try: LowerClosed = self.criteriaCategoryLimits['LowerClosed'] except: LowerClosed = True if Comments: if LowerClosed: print('x in K_k\t r(x >= m_k)\t r(x < M_k)\t r(x in K_k)') else: print('x in K_k\t r(m_k < x)\t r(M_k >= x)\t r(x in K_k)') sorting = {} nq = len(self.limitingQuantiles) - 1 for x in actions: sorting[x] = {} for c in categories: sorting[x][c] = {} if LowerClosed: cKey= c+'-m' else: cKey= c+'-M' if LowerClosed: lowLimit = self.relation[x][cKey] if int(c) < nq: cMaxKey = str(int(c)+1)+'-m' notHighLimit = Max - self.relation[x][cMaxKey] + Min else: notHighLimit = Max else: if int(c) > 1: cMinKey = str(int(c)-1)+'-M' lowLimit = Max - self.relation[cMinKey][x] + Min else: lowLimit = Max notHighLimit = self.relation[cKey][x] #if Comments: # print('%s in %s: low = %.2f, high = %.2f' % \ # (x, c,lowLimit,notHighLimit), end=' ') if Comments: print('%s in %s - %s\t' % (x, self.categories[c]['lowLimit'],self.categories[c]['highLimit'],), end=' ') categoryMembership = min(lowLimit,notHighLimit) sorting[x][c]['lowLimit'] = lowLimit sorting[x][c]['notHighLimit'] = notHighLimit sorting[x][c]['categoryMembership'] = categoryMembership if Comments: #print('\t %.2f \t %.2f \t %.2f' % (sorting[x][c]['lowLimit'], sorting[x][c]['notHighLimit'], sorting[x][c]['categoryMembership'])) print('%.2f\t\t %.2f\t\t %.2f\n' % (sorting[x][c]['lowLimit'], sorting[x][c]['notHighLimit'], sorting[x][c]['categoryMembership'])) return sorting
[docs] def showSortingCharacteristics(self, action=None): """ Renders a bipolar-valued bi-dictionary relation representing the degree of credibility of the assertion that "action x in A belongs to category c in C", ie x outranks low category limit and does not outrank the high category limit. """ try: sorting = self.sorting except: sorting = self.computeSortingCharacteristics(action=action,StoreSorting=True) actions = self.getActionsKeys(action) categories = self.orderedCategoryKeys() try: LowerClosed = self.criteriaCategoryLimits['LowerClosed'] except: LowerClosed = True if LowerClosed: print('x in K_k\t r(x >= m_k)\t r(x < M_k)\t r(x in K_k)') else: print('x in K_k\t r(m_k < x)\t r(M_k >= x)\t r(x in K_k)') for x in actions: for c in categories: print('%s in %s - %s\t' % (x, self.categories[c]['lowLimit'],\ self.categories[c]['highLimit'],), end=' ') print('%.2f\t\t %.2f\t\t %.2f' %\ (sorting[x][c]['lowLimit'],\ sorting[x][c]['notHighLimit'], sorting[x][c]['categoryMembership'])) print()
[docs] def showHTMLQuantileOrdering(self,Descending=True,strategy='optimistic'): """ Shows the html version of the quantile preordering in a browser window. The ordring strategy is either: * **optimistic**, following the upper quantile limits (default), * **pessimistic**, following the lower quantile limits, * **average**, following the averag of the upper and lower quantile limits. """ import webbrowser fileName = '/tmp/preOrdering.html' fo = open(fileName,'w') fo.write(self.computeQuantileOrdering(Descending=Descending, strategy=strategy, HTML=True, Comments=True)) fo.close() url = 'file://'+fileName webbrowser.open_new(url)
[docs] def showHTMLSorting(self,Reverse=True): """ shows the html version of the sorting result in a browser window. """ import webbrowser fileName = '/tmp/sorting.html' fo = open(fileName,'w') fo.write(self.showSorting(Reverse=Reverse,isReturningHTML=True)) fo.close() url = 'file://'+fileName webbrowser.open_new(url)
[docs] def showSorting(self,Reverse=True,isReturningHTML=False,Debug=False): """ Shows sorting results in decreasing or increasing (Reverse=False) order of the categories. If isReturningHTML is True (default = False) the method returns a htlm table with the sorting result. """ #from string import replace from copy import copy, deepcopy try: categoryContent = self.categoryContent except: categoryContent = self.computeCategoryContents(StoreSorting=True) categoryKeys = self.orderedCategoryKeys(Reverse=Reverse) try: LowerClosed = self.criteriaCategoryLimits['LowerClosed'] except: LowerClosed = True if Reverse: print('\n*--- Sorting results in descending order ---*\n') if isReturningHTML: html = '<h2>Sorting results in descending order</h2>' html += '<table style="background-color:White;" border="1"><tr bgcolor="#9acd32"><th>Categories</th><th>Assorting</th></tr>' else: print('\n*--- Sorting results in ascending order ---*\n') if isReturningHTML: html = '<h2>Sorting results in ascending order</h2>' html += '<table style="background-color:White;" border="1"><tr bgcolor="#9acd32"><th>Categories</th><th>Assorting</th></tr>' for c in categoryKeys: print('%s:' % (self.categories[c]['name']), end=' ') print('\t',categoryContent[c]) if isReturningHTML: #html += '<tr><td bgcolor="#FFF79B">[%s - %s[</td>' % (limprevc,limc) html += '<tr><td bgcolor="#FFF79B">%s</td>' % (self.categories[c]['name']) catString = str(categoryContent[c]) html += '<td>%s</td></tr>' % catString.replace('\'','&apos;') if isReturningHTML: html += '</table>' return html
[docs] def computeSortingRelation(self,categoryContents=None,Debug=False,StoreSorting=True, Threading=False,nbrOfCPUs=None,Comments=False): """ constructs a bipolar sorting relation using the category contents. """ try: categoryContents = self.categoryContent except: pass if categoryContents == None: categoryContents = self.computeCategoryContents(StoreSorting=StoreSorting,\ Threading=Threading,nbrOfCPUs=nbrOfCPUs,Comments=Comments) #categoryKeys = self.orderedCategoryKeys() #categoryKeys = list(self.categories.keys()) Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] Min = self.valuationdomain['min'] actions = [x for x in self.actionsOrig] currActions = set(actions) sortingRelation = {} for x in actions: sortingRelation[x] = {} for y in actions: sortingRelation[x][y] = Med if Debug: print('categoryContents',categoryContents) #for i in categoryKeys: for c in self.categories.keys(): ibch = set(categoryContents[c]) ribch = set(currActions) - ibch if Debug: print('ibch,ribch',ibch,ribch) for x in ibch: for y in ibch: sortingRelation[x][y] = Med sortingRelation[y][x] = Med for y in ribch: sortingRelation[x][y] = Min sortingRelation[y][x] = Max currActions = currActions - ibch return sortingRelation
###------------- ## ##class _QuantilesSortingDigraph(SortingDigraph): ## """ ## Specialisation of the sortingDigraph Class ## for sorting of alternatives into quantiles delimited ordered classes. ## ## .. warning:: ## ## Obsolete ! ## ## .. note:: ## ## We generally require an PerformanceTableau instance or a valid filename. ## If none is given, then a default profile with the limiting quartiles Q0,Q1,Q2, Q3 and Q4 is used on each criteria. ## By default lower closed limits of categories are supposed to be used in the sorting. ## ## Example Python3 session: ## ## >>> from sortingDigraphs import * ## >>> t = RandomCBPerformanceTableau(numberOfActions=7,numberOfCriteria=5, ## ... weightDistribution='equiobjectives') ## >>> qs = QuantilesSortingDigraph(t,limitingQuantiles=10) ## >>> qs.showSorting() ## *--- Sorting results in descending order ---* ## [0.90 - <[: [] ## [0.80 - 0.90[: [] ## [0.70 - 0.80[: [] ## [0.60 - 0.70[: ['a02', 'a07'] ## [0.50 - 0.60[: ['a02', 'a04', 'a05', 'a06'] ## [0.40 - 0.50[: [] ## [0.30 - 0.40[: [] ## [0.20 - 0.30[: ['a03'] ## [0.10 - 0.20[: ['a01'] ## [0.00 - 0.10[: [] ## >>> qs.exportGraphViz('quantilesSorting') ## ## .. image:: quantilesSorting.png ## """ ## ## def __init__(self,argPerfTab=None,\ ## limitingQuantiles=None,\ ## LowerClosed=False,\ ## PrefThresholds=True,\ ## hasNoVeto=False,\ ## outrankingType="bipolar",\ ## CompleteOutranking=True,\ ## StoreSorting=True,\ ## DeepcopyPerfTab=True,\ ## Threading=False,\ ## nbrCores=None,\ ## Debug=False): ## """ ## Constructor for QuantilesSortingDigraph instances. ## ## """ ## ## from copy import copy, deepcopy ## from decimal import Decimal ## ## # import the performance tableau ## if argPerfTab == None: ## perfTab = RandomPerformanceTableau(numberOfActions=10, ## numberOfCriteria=13) ## else: ## perfTab = argPerfTab ## # normalize the actions as a dictionary construct ## if isinstance(perfTab.actions,list): ## actions = {} ## for x in perfTab.actions: ## actions[x] = {'name': str(x)} ## perfTab.actions = actions ## ## # keep a copy of the original actions set before adding the profiles ## self.actionsOrig = deepcopy(self.actions) ## ## # normalizing the performance tableau ## normPerfTab = NormalizedPerformanceTableau(perfTab) ## self.actions = normPerfTab.actions ## actionsOrig = deepcopy(self.actions) ## self.criteria = normPerfTab.criteria ## self.convertWeightFloatToDecimal() ## self.evaluation = normPerfTab.evaluation ## self.convertEvaluationFloatToDecimal() ## ## # compute the limiting quantiles ## if isinstance(limitingQuantiles,list): ## self.name = 'sorting_with_given_quantiles' ## newLimitingQuantiles = [] ## for x in limitingQuantiles: ## newLimitingQuantiles.append(Decimal(str(x))) ## limitingQuantiles = newLimitingQuantiles ## if Debug: ## print('convert to decimal!',limitingQuantiles) ## else: ## limitingQuantiles = self._computeQuantiles(limitingQuantiles,Debug=Debug) ## self.limitingQuantiles = limitingQuantiles ## ## if Debug: ## print('limitingQuantiles',self.limitingQuantiles) ## ## # supposing all criteria scales between 0.0 and 100.0 ## ## lowValue = 0.0 ## highValue = 100.00 ## # with preference direction = max ## categories = {} ## k = len(limitingQuantiles)-1 ## if LowerClosed: ## for i in range(0,k-1): ## categories[str(i+1)] = {'name':'[%.2f - %.2f['\ ## %(limitingQuantiles[i],limitingQuantiles[i+1]),\ ## 'order':i+1,\ ## 'lowLimit': '[%.2f' % (limitingQuantiles[i]), ## 'highLimit': '%.2f[' % (limitingQuantiles[i+1])} ## categories[str(k)] = {'name':'[%.2f - <['\ ## %(limitingQuantiles[k-1]), 'order':k,\ ## 'lowLimit': '[%.2f' % (limitingQuantiles[k-1]),\ ## 'highLimit': '<['} ## else: ## categories[str(1)] = {'name':']< - %.2f]'\ ## %(limitingQuantiles[1]), 'order':1, ## 'highLimit': '%.2f]' % (limitingQuantiles[1]),\ ## 'lowLimit': ']<'} ## for i in range(1,k): ## categories[str(i+1)] = {'name':']%.2f - %.2f]'\ ## %(limitingQuantiles[i],limitingQuantiles[i+1]), 'order':i+1, ## 'lowLimit': ']%.2f' % (limitingQuantiles[i]), ## 'highLimit': '%.2f]' % (limitingQuantiles[i+1])} ## self.categories = categories ## if Debug: ## print('categories',self.categories) ## ## criteriaCategoryLimits = {} ## criteriaCategoryLimits['LowerClosed'] = LowerClosed ## self.criteriaCategoryLimits = copy(criteriaCategoryLimits) ## for g in self.criteria: ## gQuantiles = self._computeLimitingQuantiles(g,\ ## PrefThresholds=PrefThresholds,Debug=Debug) ## criteriaCategoryLimits[g] = {} ## for c in categories: ## criteriaCategoryLimits[g][c]={ ## 'minimum':gQuantiles[(int(c)-1)], ## 'maximum':gQuantiles[int(c)] ## } ## self.criteriaCategoryLimits = criteriaCategoryLimits ## if Debug: ## print('CriteriaCategoryLimits',self.criteriaCategoryLimits) ## ## # set the category limits type (LowerClosed = True is default) ## # self.criteriaCategoryLimits['LowerClosed'] = LowerClosed ## # print 'LowerClosed', LowerClosed ## ## # add the catogory limits to the actions set ## self.profiles = {'min':{},'max':{}} ## self.profileLimits = set() ## for c in list(self.categories.keys()): ## cMinKey = c+'-m' ## cMaxKey = c+'-M' ## self.profileLimits.add(cMinKey) ## self.profileLimits.add(cMaxKey) ## self.actions[cMinKey] = {'name': 'categorical low limits', 'comment': 'Inferior or equal limits for category membership assessment'} ## self.actions[cMaxKey] = {'name': 'categorical high limits', 'comment': 'Lower or equal limits for category membership assessment'} ## self.profiles['min'][cMinKey] = {'category': c, 'name': 'categorical low limits', 'comment': 'Inferior or equal limits for category membership assessment'} ## self.profiles['max'][cMaxKey] = {'category': c, 'name': 'categorical high limits', 'comment': 'Lower or equal limits for category membership assessment'} ## for g in list(self.criteria.keys()): ## try: ## if self.criteria[g]['preferenceDirection'] == 'max': ## self.evaluation[g][cMinKey] = Decimal(str(self.criteriaCategoryLimits[g][c]['minimum'])) ## self.evaluation[g][cMaxKey] = Decimal(str(self.criteriaCategoryLimits[g][c]['maximum'])) ## elif self.criteria[g]['preferenceDirection'] == 'min': ## if not defaultProfiles: ## highValueg = Decimal(str(self.criteria[g]['scale'][1])) ## else: ## highValueg = Decimal(str(highValue)) ## #print 'highValue = ', highValue ## self.evaluation[g][cMinKey] = -(highValueg - Decimal(str(self.criteriaCategoryLimits[g][c]['minimum']))) ## self.evaluation[g][cMaxKey] = -(highValueg - Decimal(str(self.criteriaCategoryLimits[g][c]['maximum']))) ## else: ## print('===>>>>> Error') ## except: ## ## self.evaluation[g][cMinKey] = Decimal(str(self.criteriaCategoryLimits[g][c]['minimum'])) ## self.evaluation[g][cMaxKey] = Decimal(str(self.criteriaCategoryLimits[g][c]['maximum'])) ## ## if Debug: ## print('Profiles',self.profiles) ## print('ProfileLimits',self.profileLimits) ## ## self.convertEvaluationFloatToDecimal() ## ## # construct outranking relation ## self.hasNoVeto = hasNoVeto ## if outrankingType == "robust": ## g = RobustOutrankingDigraph(self) ## self.valuationdomain = g.valuationdomain ## self.relation = g.relation ## elif outrankingType == "likely": ## g = StochasticBipolarOutrankingDigraph(self, ## sampleSize = 50, ## samplingSeed = None, ## hasNoVeto = hasNoVeto, ## Debug = Debug, ## spread = 1.0, ## likelihood = 0.9, ## distribution = 'triangular') ## self.valuationdomain = g.valuationdomain ## self.relation = g.relation ## ## else: ## minValuation = -100.0 ## maxValuation = 100.0 ## if CompleteOutranking: ## g = BipolarOutrankingDigraph(normPerfTab,hasNoVeto=hasNoVeto) ## g.recodeValuation(minValuation,maxValuation) ## relationOrig = g.relation ## Min = g.valuationdomain['min'] ## Max = g.valuationdomain['max'] ## else: ## Min = Decimal(str(minValuation)) ## Max = Decimal(str(maxValuation)) #### Min = Decimal('-100') #### Max = Decimal('100') ## Med = (Max + Min)/Decimal('2.0') ## self.valuationdomain = {'min': Min, 'med':Med ,'max':Max } ## if LowerClosed: ## relation = self._constructRelation(self.criteria, ## self.evaluation, ## initial=self.actionsOrig, ## terminal=self.profileLimits, ## hasNoVeto=hasNoVeto, ## hasBipolarVeto=True, ## Threading=Threading, ## nbrCores=nbrCores) ## else: ## relation = self._constructRelation(self.criteria, ## self.evaluation, ## terminal=self.actionsOrig, ## initial=self.profileLimits, ## hasNoVeto=hasNoVeto, ## hasBipolarVeto=True, ## Threading=Threading, ## nbrCores=nbrCores) ## if LowerClosed: ## for x in actionsOrig: ## for y in actionsOrig: ## relation[x][y] = Med ## for x in self.profileLimits: ## relation[x] = {} ## for y in self.actions: ## relation[x][y] = Med ## else: ## for x in actionsOrig: ## relation[x] = {} ## for y in actionsOrig: ## relation[x][y] = Med ## for y in self.profileLimits: ## for x in self.actions: ## relation[x][y] = Med ## self.relation = relation ## ## # compute weak ordering ## sortingRelation = self.computeSortingRelation(StoreSorting=StoreSorting,Debug=Debug) ## for x in actionsOrig: ## for y in actionsOrig: ## self.relation[x][y] = sortingRelation[x][y] ## ## # reset original action set ## self.actions = actionsOrig ## self.order = len(self.actions) ## ## # compute weak ordering by choosing ## #### if self.order < 20: #### self.computeRankingByChoosing(CoDual=True) ## ## # init general digraph Data ## self.gamma = self.gammaSets() ## self.notGamma = self.notGammaSets() ## ## def showWeakOrder(self,Descending=True): ## """ ## Specialisation for QuantilesSortingDigraphs. ## """ ## from decimal import Decimal ## from weakOrders import WeakOrder ## try: ## cC = self.categoryContent ## except: ## cC = self.computeCategoryContents(StoreSorting=True) ## ## if Descending: ## cCKeys = self.orderedCategoryKeys(Reverse = True) ## else: ## cCKeys = self.orderedCategoryKeys(Reverse = False) ## n = len(cC) ## n2 = n//2 ## ordering = [] ## ## for i in range(n2): ## if i == 0: ## x = cC[cCKeys[i]] ## y = cC[cCKeys[n-i-1]] ## setx = set(x) ## sety = set(y) - setx ## else: ## x = list(set(cC[cCKeys[i]]) - (setx | sety)) ## setx = setx | set(x) ## y = list(set(cC[cCKeys[n-i-1]]) - (setx | sety)) ## sety = sety | set(y) ## if x != [] or y != []: ## ordering.append( ( (Decimal(str(i+1)),x),(Decimal(str(n-i)),y) ) ) ## if 2*n2 < n: ## if n2 == 0: ## x = cC[cCKeys[n2]] ## else: ## x = list(set(cC[cCKeys[n2]]) - (setx | sety)) ## ordering.append( ( (Decimal(str(n2+1)),x),(Decimal(str(n2+1)),x) ) ) ## ## weakOrdering = {'result':ordering} ## WeakOrder.showWeakOrder(self,weakOrdering) ## ## def computeWeakOrder(self,Descending=True,Debug=False): ## """ ## Specialisation for QuantilesSortingDigraphs. ## """ ## from decimal import Decimal ## try: ## cC = self.categoryContent ## except: ## cC = self.computeCategoryContents(StoreSorting=True) ## if Debug: ## print(cC) ## if Descending: ## cCKeys = self.orderedCategoryKeys(Reverse = True) ## else: ## cCKeys = self.orderedCategoryKeys(Reverse = False) ## if Debug: ## print('cCKeys',cCKeys) ## n = len(cC) ## n2 = n//2 ## if Debug: ## print('n,n2',n,n2) ## ordering = [] ## ## for i in range(n2): ## if i == 0: ## x = cC[cCKeys[i]] ## y = cC[cCKeys[n-i-1]] ## setx = set(x) ## sety = set(y) - setx ## else: ## x = list(set(cC[cCKeys[i]]) - (setx | sety)) ## setx = setx | set(x) ## y = list(set(cC[cCKeys[n-i-1]]) - (setx | sety)) ## sety = sety | set(y) ## if Debug: ## print('i,x,y,setx,sety',i,x,y,setx,sety) ## if x != [] or y != []: ## ordering.append( ( (Decimal(str(i+1)),x),(Decimal(str(n-i)),y) )) ## if Debug: ## print(i, ( (Decimal(str(i+1)),x),(Decimal(str(n-i)),y) ) ) ## if 2*n2 < n: ## if n2 == 0: ## x = cC[cCKeys[n2]] ## else: ## x = list(set(cC[cCKeys[n2]]) - (setx | sety)) ## ordering.append( ( (Decimal(str(n2+1)),x),(Decimal(str(n2+1)),[]) ) ) ## if Debug: ## print('median term',( (Decimal(str(n2+1)),x),(Decimal(str(n2+1)),[]) )) ## if Debug: ## print(ordering) ## ## orderingList = [] ## n = len(ordering) ## for i in range(n): ## x = ordering[i][0][1] ## if x != []: ## orderingList.append(x) ## for i in range(n): ## y = ordering[n-i-1][1][1] ## if y != []: ## orderingList.append(y) ## ## return orderingList ## ## def showOrderedRelationTable(self,direction="decreasing"): ## """ ## Showing the relation table in decreasing (default) or increasing order. ## """ ## if direction == "decreasing": ## Descending = True ## else: ## Descending = False ## ## weakOrdering = self.computeWeakOrder(Descending) ## ## actionsList = [] ## for eq in weakOrdering: ## #print(eq) ## eq.sort() ## for x in eq: ## actionsList.append(x) ## if len(actionsList) != len(self.actions): ## print('Error !: missing action(s) %s in ordered table.') ## ## Digraph.showRelationTable(self,actionsSubset=actionsList,\ ## relation=self.relation,\ ## Sorted=False,\ ## ReflexiveTerms=False) ## ## ## def _computeQuantiles(self,x,Debug=False): ## """ ## Renders the limiting quantiles. ## """ ## from math import floor ## if isinstance(x,int): ## n = x ## elif x == None: ## n = 4 ## elif x == 'bitiles': ## n = 2 ## elif x == 'tritiles': ## n = 3 ## elif x == 'quartiles': ## n = 4 ## elif x == 'quintiles': ## n = 5 ## elif x == 'sextiles': ## n = 6 ## elif x == 'septiles': ## n = 7 ## elif x == 'octiles': ## n = 8 ## elif x == 'deciles': ## n = 10 ## elif x == 'dodeciles': ## n = 20 ## elif x == 'centiles': ## n = 100 ## elif x == 'automatic': ## pth = [5] ## for g in self.criteria: ## try: ## pref = self.criteria[g]['thresholds']['ind'][0] + \ ## (self.criteria[g]['thresholds']['ind'][1]*Decimal('100')) ## pth.append(pref) ## except: ## pass ## amp = max(Decimal('1'),min(pth)) ## n = int(floor(Decimal('100')/amp)) ## if Debug: ## print('Detected preference thresholds = ',pth) ## print('amplitude, n',amp,n) ## ## limitingQuantiles = [] ## for i in range(n+1): ## limitingQuantiles.append( Decimal(str(i)) / Decimal(str(n)) ) ## self.name = 'sorting_with_%d-tile_limits' % n ## return limitingQuantiles ## ## def _computeLimitingQuantiles(self,g,Debug=False,PrefThresholds=True): ## """ ## Renders the list of limiting quantiles on criteria g ## """ ## from math import floor ## from copy import copy, deepcopy ## gValues = [] ## for x in self.actionsOrig: ## if Debug: ## print('g,x,evaluation[g][x]',g,x,self.evaluation[g][x]) ## if self.evaluation[g][x] != Decimal('-999'): ## gValues.append(self.evaluation[g][x]) ## gValues.sort() ## if PrefThresholds: ## try: ## gPrefThrCst = self.criteria[g]['thresholds']['pref'][0] ## gPrefThrSlope = self.criteria[g]['thresholds']['pref'][1] ## except: ## gPrefThrCst = Decimal('0') ## gPrefThrSlope = Decimal('0') ## n = len(gValues) ## if Debug: ## print('g,n,gValues',g,n,gValues) ## nf = Decimal(str(n+1)) ## limitingQuantiles = copy(self.limitingQuantiles) ## limitingQuantiles.sort() ## if Debug: ## print(limitingQuantiles) ## LowerClosed = self.criteriaCategoryLimits['LowerClosed'] ## if LowerClosed: ## limitingQuantiles = limitingQuantiles[:-1] ## else: ## limitingQuantiles = limitingQuantiles[1:] ## if Debug: ## print(limitingQuantiles) ## # computing the quantiles on criterion g ## gQuantiles = [] ## if LowerClosed: ## # we ignore the 1.00 quantile and replace it with +infty ## for q in self.limitingQuantiles: ## r = (Decimal(str(nf)) * q) ## rq = int(floor(r)) ## if Debug: ## print('r,rq',r,rq, end=' ') ## if rq < (n-1): ## quantile = gValues[rq]\ ## + ((r-Decimal(str(rq)))*(gValues[rq+1]-gValues[rq])) ## if rq > 0 and PrefThresholds: ## quantile += gPrefThrCst + quantile*gPrefThrSlope ## else : ## if self.criteria[g]['preferenceDirection'] == 'min': ## quantile = Decimal('100.0') ## else: ## quantile = Decimal('200.0') ## if Debug: ## print('quantile',quantile) ## gQuantiles.append(quantile) ## ## else: # upper closed categories ## # we ignore the quantile 0.0 and replace it with -\infty ## for q in self.limitingQuantiles: ## r = (Decimal(str(nf)) * q) ## rq = int(floor(r)) ## if Debug: ## print('r,rq',r,rq, end=' ') ## if rq == 0: ## if self.criteria[g]['preferenceDirection'] == 'min': ## quantile = Decimal('-200.0') ## else: ## quantile = Decimal('-100.0') ## elif rq < (n-1): ## quantile = gValues[rq]\ ## + ((r-Decimal(str(rq)))*(gValues[rq+1]-gValues[rq])) ## if PrefThresholds: ## quantile -= gPrefThrCst - quantile*gPrefThrSlope ## else: ## if n > 0: ## quantile = gValues[n-1] ## else: ## if self.criteria[g]['preferenceDirection'] == 'min': ## quantile = Decimal('-200.0') ## else: ## quantile = Decimal('-100.0') ## if Debug: ## print('quantile',quantile) ## gQuantiles.append(quantile) ## if Debug: ## print(g,LowerClosed,self.criteria[g]['preferenceDirection'],gQuantiles) ## return gQuantiles ## ## def showSorting(self,Reverse=True,isReturningHTML=False,Debug=False): ## """ ## Shows sorting results in decreasing or increasing (Reverse=False) ## order of the categories. If isReturningHTML is True (default = False) ## the method returns a htlm table with the sorting result. ## ## """ ## #from string import replace ## from copy import copy, deepcopy ## try: ## categoryContent = self.categoryContent ## except: ## categoryContent = self.computeCategoryContents(StoreSorting=True) ## categoryKeys = self.orderedCategoryKeys(Reverse=Reverse) ## try: ## LowerClosed = self.criteriaCategoryLimits['LowerClosed'] ## except: ## LowerClosed = True ## if Reverse: ## print('\n*--- Sorting results in descending order ---*\n') ## if isReturningHTML: ## html = '<h2>Sorting results in descending order</h2>' ## html += '<table style="background-color:White;" border="1"><tr bgcolor="#9acd32"><th>Categories</th><th>Assorting</th></tr>' ## else: ## print('\n*--- Sorting results in ascending order ---*\n') ## if isReturningHTML: ## html = '<h2>Sorting results in ascending order</h2>' ## html += '<table style="background-color:White;" border="1"><tr bgcolor="#9acd32"><th>Categories</th><th>Assorting</th></tr>' ## for c in categoryKeys: ## print('%s:' % (self.categories[c]['name']), end=' ') ## print('\t',categoryContent[c]) ## if isReturningHTML: ## #html += '<tr><td bgcolor="#FFF79B">[%s - %s[</td>' % (limprevc,limc) ## html += '<tr><td bgcolor="#FFF79B">%</td>' % (self.categories[c]['name']) ## catString = str(categoryContent[c]) ## html += '<td>%s</td></tr>' % catString.replace('\'','&apos;') ## if isReturningHTML: ## html += '</table>' ## return html ## ## def computeSortingRelation(self,categoryContents=None,Debug=False,StoreSorting=True): ## """ ## constructs a bipolar sorting relation using the category contents. ## """ ## try: ## categoryContents = self.categoryContent ## except: ## pass ## if categoryContents == None: ## categoryContents = self.computeCategoryContents(StoreSorting=True) ## categoryKeys = self.orderedCategoryKeys() ## Max = self.valuationdomain['max'] ## Med = self.valuationdomain['med'] ## Min = self.valuationdomain['min'] ## actions = [x for x in self.actionsOrig] ## currActions = set(actions) ## sortingRelation = {} ## for x in actions: ## sortingRelation[x] = {} ## for y in actions: ## sortingRelation[x][y] = Med ## ## if Debug: ## print('categoryContents',categoryContents) ## for i in categoryKeys: ## ibch = set(categoryContents[i]) ## ribch = set(currActions) - ibch ## if Debug: ## print('ibch,ribch',ibch,ribch) ## for x in ibch: ## for y in ibch: ## sortingRelation[x][y] = Med ## sortingRelation[y][x] = Med ## for y in ribch: ## sortingRelation[x][y] = Min ## sortingRelation[y][x] = Max ## currActions = currActions - ibch ## return sortingRelation #----------test SortingDigraph class ---------------- if __name__ == "__main__": from time import time from perfTabs import * from outrankingDigraphs import * from sortingDigraphs import * from weakOrders import * print(""" **************************************************** * Python sortingDigraphs module * * depends on BipolarOutrankingDigraph and * * $Revision: 2093 $ * * Copyright (C) 2010 Raymond Bisdorff * * The module comes with ABSOLUTELY NO WARRANTY * * to the extent permitted by the applicable law. * * This is free software, and you are welcome to * * redistribute it if it remains free software. * **************************************************** """) print('*-------- Testing class and methods -------') MP = True ## t = PerformanceTableau('auditor2_1') ## t.showHTMLPerformanceHeatmap(ndigits=0,quantiles=7,Correlations=True,Debug=False) ## t = XMCDA2PerformanceTableau('spiegel2004') ## t = XMCDA2PerformanceTableau('ex1') t = Random3ObjectivesPerformanceTableau(numberOfActions=25, numberOfCriteria=13, weightDistribution='equiobjectives', missingDataProbability=0.05, seed=1) nt = NormalizedPerformanceTableau(t) ## so = SortingDigraph(t,scaleSteps=5,LowerClosed=True,Debug=True) #### so = SortingDigraph('grafittiPerfTab','grafittiCategories') #### so = SortingDigraph(t,scaleSteps=7,Debug=True) ## print(so.categories) ## so.saveCategories('testCategories') #### print(so.profiles) #### print(so.criteriaCategoryLimits) ## so.showSortingCharacteristics() ## so.showSorting(Reverse=False) ## so.showSorting() ## print('optimistic') ## so.showWeakOrder(Descending=True,strategy='optimistic') ## print('pessimistic') ## so.showWeakOrder(strategy='pessimistic') ## print('average') ## so.showWeakOrder() so1 = SortingDigraph(nt,scaleSteps=10,LowerClosed=False) so1.computeWeakOrder(Comments=True) so1.showPerformanceTableau(actionsSubset=so1.profiles['min']) so1.showPerformanceTableau(actionsSubset=so1.profiles['max']) ## so1.showSorting() ## so1.showSortingCharacteristics() ## so.computeWeakOrder(Debug=True) ## so1.computeWeakOrder(Comments=True,Debug=True) ## so.saveProfiles('testProfile') ## t.save() ## nt = NormalizedPerformanceTableau(t) ## so1 = SortingDigraph(nt,'testProfile') ## so1.showSorting() ## categoriesData = {'categories': so.categories,\ ## 'criteriaCategoryLimits': so.criteriaCategoryLimits} ## so2 = SortingDigraph(nt,categoriesData) ## so2.showSorting() ## t.saveXMCDA2('test',servingD3=False) #t = XMCDA2PerformanceTableau('test') #t.showHTMLPerformanceHeatmap(colorLevels=5,ndigits=0,Correlations=True) ## qs = QuantilesSortingDigraph(t,limitingQuantiles=7,LowerClosed=False, ## Threading=MP,tempDir='.',Comments=True, ## Debug=True) ## qs.showHTMLQuantileOrdering(strategy='average') ## qs.showWeakOrder() ## qs.showQuantileOrdering(strategy='average') ## qs.showActionsSortingResult() ## qr = QuantilesRankingDigraph(t,7,LowerClosed=True,PrefThresholds=True, ## Comments=True, ## Threading=MP) ## qr.showRanking() ## qr.showSorting() ## qs0 = _QuantilesSortingDigraph(t,15,LowerClosed=False, ## Threading=False, ## Debug=False) ## qs0.showSorting() ## qs0.showSortingCharacteristics('a01') #qs0.showWeakOrder() #qs.showQuantileOrdering(strategy=None) #qs0.exportGraphViz('test') #qs0.showActionsSortingResult() ## qs0.showOrderedRelationTable() ## qs0.exportGraphViz() ## qs0.showSorting() ## qs0.showActionsSortingResult(Debug=False) ## qs0.computeWeakOrder(Debug=True) ## qs0.recodeValuation() ## qs0.showSorting() ## qs0.showActionsSortingResult(Debug=False) ## qs0.computeWeakOrder(Debug=True) ## from weakOrders import QuantilesRankingDigraph ## qsrbc = QuantilesRankingDigraph(t,3,LowerClosed=False,Threading=True) ## qsrbc.showSorting() ## qsrbc.showActionsSortingResult() ## qsrbc.computeWeakOrder(Comments=True) ## qsrbc.exportSortingGraphViz('testqs17') ## g = BipolarOutrankingDigraph(t,Normalized=True) ## print(g.computeOrdinalCorrelation(qs0)) ## print(g.computeOrdinalCorrelation(qsrbc)) print('*------------------*') print('If you see this line all tests were passed successfully :-)') print('Enjoy !') print('*************************************') print('* R.B. december 2010 *') print('* $Revision: 2093 $ *') print('*************************************') ############################# # Log record for changes: # $Log: sortingDigraphs.py,v $ #############################