Source code for digraphs

#!/Usr/bin/env python3
#########################
"""
Python3+ implementation of the digraphs module, root module of the Digraph3 resources.

Copyright (C) 2006-2023  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.

"""
#######################

__version__ = "Branch: Python3.10 $"

from digraphsTools import *
#from digraphs import *
from perfTabs import *
from randomPerfTabs import *
from multiprocessing import Process

#-----------
class _myThread2(Process):
    def __init__(self, target,args):
        Process.__init__(self)
        self.threadID = target
        self.workingDirectory = args[0]
        self.Debug = args[1]
    def run(self):
        from pickle import dumps, loads
        from os import chdir
        from decimal import Decimal
        chdir(self.workingDirectory)
        Debug = self.Debug
        #Debug=False
        if Debug:
            print("Starting working in %s on %s" % (self.workingDirectory, self.name))
        fi = open('dumpActions.py','rb')
        actionsList = loads(fi.read())
        fi.close()
        if Debug:
            print(self.threadID,actionsList)
        fi = open('dumpRelation.py','rb')
        relation = loads(fi.read())
        fi.close()
        if Debug:
            print(self.threadID,relation)
        fi = open('dumpOtherRelation.py','rb')
        otherRelation = loads(fi.read())
        fi.close()
        if Debug:
            print(self.threadID,relation)
        fiName = 'splitActions-'+str(self.threadID)+'.py'
        fi = open(fiName,'rb')
        splitActions = loads(fi.read())
        fi.close()
        if Debug:
            print(self.threadID,splitActions)
        # compute partial correlation
        correlation = Decimal('0')
        determination = Decimal('0')
        for x in splitActions:
            grx = relation[x]
            orx = otherRelation[x]
            for y in actionsList:
                if x != y:
                    correlation += min( max(-grx[y],orx[y]),\
                                max(grx[y],-orx[y]) )
                    determination += min( abs(grx[y]),\
                                          abs(orx[y]) )
                    #if Debug:
                    #    print(x,y,g.relation[x][y],otherRelation[x][y],correlation,determination)
        splitCorrelation = {'correlation': correlation,
                            'determination': determination}
        # write partial correlation relation 
        foName = 'splitCorrelation-'+str(self.threadID)+'.py'
        fo = open(foName,'wb')
        fo.write(dumps(splitCorrelation,-1))
        fo.close()
 
[docs] class Digraph(object): """ Genuine root class of all Digraph3 modules. See Digraph3 tutorials. All instances of the :py:class:`digraphs.Digraph` class contain at least the following components: 1. A collection of digraph nodes called **actions** (decision alternatives): a list, set or (ordered) dictionary of nodes with 'name' and 'shortname' attributes, 2. A logical characteristic **valuationdomain**, a dictionary with three decimal entries: the minimum (-1.0, means certainly false), the median (0.0, means missing information) and the maximum characteristic value (+1.0, means certainly true), 3. The digraph **relation** : a double dictionary indexed by an oriented pair of actions (nodes) and carrying a characteristic value in the range of the previous valuation domain, 4. Its associated **gamma function** : a dictionary containing the direct successors, respectively predecessors of each action, automatically added by the object constructor, 5. Its associated **notGamma function** : a dictionary containing the actions that are not direct successors respectively predecessors of each action, automatically added by the object constructor. A previously stored :py:class:`digraphs.Digraph` instance may be reloaded with the *file* argument:: >>> from randomDigraphs import RandomValuationDigraph >>> dg = RandomValuationDigraph(order=3,Normalized=True,seed=1) >>> dg.save('testdigraph') Saving digraph in file: <testdigraph.py> >>> from digraphs import Digraph >>> dg = Digraph(file='testdigraph') # without the .py extenseion >>> dg.__dict__ {'name': 'testdigraph', 'actions': {'a1': {'name': 'random decision action', 'shortName': 'a1'}, 'a2': {'name': 'random decision action', 'shortName': 'a2'}, 'a3': {'name': 'random decision action', 'shortName': 'a3'}}, 'valuationdomain': {'min': Decimal('-1.0'), 'med': Decimal('0.0'), 'max': Decimal('1.0'), 'hasIntegerValuation': False,}, 'relation': {'a1': {'a1': Decimal('0.0'), 'a2': Decimal('-0.66'), 'a3': Decimal('0.44')}, 'a2': {'a1': Decimal('0.94'), 'a2': Decimal('0.0'), 'a3': Decimal('-0.84')}, 'a3': {'a1': Decimal('-0.36'), 'a2': Decimal('-0.70'), 'a3': Decimal('0.0')}}, 'order': 3, 'gamma': {'a1': ({'a3'}, {'a2'}), 'a2': ({'a1'}, set()), 'a3': (set(), {'a1'})}, 'notGamma': {'a1': ({'a2'}, {'a3'}), 'a2': ({'a3'}, {'a1', 'a3'}), 'a3': ({'a1', 'a2'}, {'a2'})}} >>> """ def __repr__(self): """ Default presentation method for Digraph instances. """ reprString = '*------- Digraph instance description ------*\n' reprString += 'Instance class : %s\n' % self.__class__.__name__ reprString += 'Instance name : %s\n' % self.name reprString += 'Digraph Order : %d\n' % self.order reprString += 'Digraph Size : %d\n' % self.computeSize() reprString += 'Valuation domain : [%.2f;%.2f]\n'\ % (self.valuationdomain['min'],self.valuationdomain['max']) reprString += 'Determinateness (%%) : %.2f\n' % self.computeDeterminateness(InPercents=True) reprString += 'Attributes : %s\n' % list(self.__dict__.keys()) return reprString
[docs] def showAttributes(self): """ Prints out the attributes of self. """ reprString = '*------- Attributes of Digraph ------*\n' reprString += 'Instance name : %s\n' % self.name reprString += 'Instance class: %s\n' % self.__class__.__name__ atts = list(self.__dict__.keys()) atts.sort() reprString += 'Attributes : %s\n' % atts print(reprString)
def __init__(self,file=None,order=7): #import digraphs,sys,copy from randomDigraphs import RandomValuationDigraph from decimal import Decimal if file is None: g = RandomValuationDigraph(order=order) self.name = g.name self.actions = g.actions self.order = len(self.actions) self.valuationdomain = g.valuationdomain self.convertValuationToDecimal() self.relation = g.relation self.convertRelationToDecimal() self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() else: fileName = file+'.py' argDict = {} fi = open(fileName,'r') fileText = fi.read() fi.close() exec(compile(fileText, fileName, 'exec'), argDict) self.name = file try: self.actions = argDict['actions'] except: # for compatibility with Digraph2 versions self.actions = argDict['actionset'] self.order = len(self.actions) self.valuationdomain = argDict['valuationdomain'] self.convertValuationToDecimal() self.relation = argDict['relation'] self.convertRelationToDecimal() self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() try: self.reflections = argDict['reflections'] self.rotations = argDict['rotations'] except: pass def __neg__(self): """ Make the negation operator -self available for Digraph instances. Returns a DualDigraph instance of self. """ new = DualDigraph(self) new.__class__ = self.__class__ return new def __invert__(self): """ Make the inverting operator ~self available for Digraph instances. Returns a ConverseDigraph instance of self. """ new = ConverseDigraph(self) new.__class__ = self.__class__ return new #-----------Dias/Castonguay/Longo/Jradi--------* def _triplets(self,Comments=False,Debug=False): """ p.15 """ Med = self.valuationdomain['med'] tG = [] self.circuitsList = [] for u in self.actions: outAsymGammaU = self.gamma[u][0] - self.gamma[u][1] inAsymGammaU = self.gamma[u][1] - self.gamma[u][0] for x in outAsymGammaU: for y in inAsymGammaU: if x != y: if str(u) < str(x) and str(u) < str(y): ## if Debug: ## print('x,u,y',x,u,y) if self.relation[y][x] <= Med and\ self.relation[x][y] <= Med: if Comments: print('Initial triplet: ',x,u,y) tG.append((x,u,y)) elif self.relation[x][y] > Med and\ self.relation[y][x] <= Med: circ = [y,u,x] if Comments: print('Circuit certificate:', circ) self.circuitsList.append((circ,frozenset(circ))) return tG #@timefn
[docs] def computeChordlessCircuitsMP(self,Odd=False, Threading=False,nbrOfCPUs=None, startMethod=None, Comments=False,Debug=False): """ Multiprocessing version of computeChordlessCircuits(). Renders the set of all chordless odd circuits detected in a digraph. Result (possible empty list) stored in <self.circuitsList> holding a possibly empty list tuples with at position 0 the list of adjacent actions of the circuit and at position 1 the set of actions in the stored circuit. Inspired by Dias, Castonguay, Longo, Jradi, Algorithmica (2015). Returns a possibly empty list of tuples (circuit,frozenset(circuit)). If Odd == True, only circuits of odd length are retained in the result. """ tG = self._triplets(Comments=Comments) if Comments: print('There are %d starting triplets !' % len(tG) ) blocked = {} for x in self.actions: blocked[x] = 0 self.blocked = blocked if Threading: self.Odd = Odd self.Comments = Comments self.Debug = Debug #from multiprocessing import Pool #from os import cpu_count import multiprocessing as mp if startMethod is None: startMethod = 'spawn' mpctx = mp.get_context(startMethod) Pool = mpctx.Pool cpu_count = mpctx.cpu_count if nbrOfCPUs is None: nbrOfCPUs= cpu_count() with Pool(nbrOfCPUs) as proc: circuits = proc.map(self._computeChordlessPathsFromInitialTriplet,tG) #print(circuits) for i in range(len(tG)): if Debug: print(i,circuits[i]) if circuits[i] != []: for circ in circuits[i]: #print(circ) self.circuitsList.append(circ) else: for p in tG: u = p[1] ## if Debug: ## print('===>>>',p,u) gammaU = (self.gamma[u][1] | self.gamma[u][0]) for x in gammaU: #print(x) self.blocked[x] += 1 self._ccVisit(p,u,Odd=Odd,Comments=Comments) for x in gammaU: if self.blocked[x] > 0: self.blocked[x] -= 1 if Debug: print(self.circuitsList) return self.circuitsList
def _computeChordlessPathsFromInitialTriplet(self,p): if self.Comments: print('===>> thread : ',p) Debug = self.Debug u = p[1] #blocked = self.blocked blocked = {} for x in self.actions: blocked[x] = 0 circuits = [] gammaU = (self.gamma[u][1] | self.gamma[u][0]) for x in gammaU: blocked[x] += 1 circuits,blocked = self._ccVisitMP(circuits,blocked,p,u,Odd=self.Odd) for x in gammaU: if blocked[x] > 0: blocked[x] -= 1 if self.Comments: print(p,circuits) ## for x in self.actions: ## blocked[x] = 0 if Debug: print(p,'return',circuits) return circuits def _ccVisitMP(self,circuits,blocked,p,u, Odd=False): """ p.15 """ Comments = self.Comments Debug = self.Debug Med = self.valuationdomain['med'] ut = p[-1] u1 = p[0] inAsymGammaUt = self.gamma[ut][1] - self.gamma[ut][0] gammaUt = self.gamma[ut][0] | self.gamma[ut][1] if Debug: print(p,self.gamma[ut][1],ut,self.gamma[ut][0]) for x in gammaUt: blocked[x] += 1 for v in inAsymGammaUt: if str(v) > str(u) and blocked[v] == 1: p1 = p + tuple([v]) if Debug: print(p,p1) if self.relation[u1][v] > Med and\ self.relation[v][u1] <= Med: if Odd: if (len(p1) % 2) != 1: OddFlag=False else: OddFlag = True else: OddFlag = True if OddFlag: circ = list(reversed(p1)) if Comments: print(p,'circuit certificate: ',circ) circuits.append((circ,frozenset(circ))) elif self.relation[u1][v] <= Med and\ self.relation[v][u1] <= Med : if Debug: print(p,'continue with ', p1) circuits,blocked = self._ccVisitMP(circuits,blocked, p1,u,Odd=Odd) ## circuits.append(circuits1) if Debug: print(p,circuits) for x in (gammaUt): if blocked[x] > 0: blocked[x] -= 1 return circuits,blocked ################################### #@timefn def _computeChordlessCircuits(self,Odd=False,Comments=False,Debug=False): """ Renders the set of all chordless odd circuits detected in a digraph. Result (possible empty list) stored in <self.circuitsList> holding a possibly empty list tuples with at position 0 the list of adjacent actions of the circuit and at position 1 the set of actions in the stored circuit. Inspired by Dias, Castonguay, Longo, Jradi, Algorithmica (2015). Returns a possibly empty list of tuples (circuit,frozenset(circuit)). If Odd == True, only circuits of odd length are retained in the result. """ tG = self._triplets(Comments=Comments) if Comments: print('There are %d starting triplets !' % len(tG) ) self.blocked = {} for u in self.actions: self.blocked[u] = 0 for p in tG: u = p[1] ## if Debug: ## print('===>>>',p,u) gammaU = (self.gamma[u][1] | self.gamma[u][0]) for x in gammaU: #print(x) self.blocked[x] += 1 self._ccVisit(p,u,Odd=Odd,Comments=Comments) for x in gammaU: if self.blocked[x] > 0: self.blocked[x] -= 1 return self.circuitsList def _ccVisit(self,p,u,Odd=False,Comments=False,Debug=False): """ p.15 """ Med = self.valuationdomain['med'] ut = p[-1] u1 = p[0] inAsymGammaUt = self.gamma[ut][1] - self.gamma[ut][0] gammaUt = self.gamma[ut][0] | self.gamma[ut][1] ## if Debug: ## print(self.gamma[ut][1],ut,self.gamma[ut][0]) for x in gammaUt: self.blocked[x] += 1 for v in inAsymGammaUt: if str(v) > str(u) and self.blocked[v] == 1: p1 = p + tuple([v]) ## if Debug: ## print(p1) if self.relation[u1][v] > Med and\ self.relation[v][u1] <= Med: if Odd: if (len(p1) % 2) != 1: OddFlag=False else: OddFlag = True else: OddFlag = True if OddFlag: circ = list(reversed(p1)) if Comments: print('circuit certificate: ',circ) self.circuitsList.append((circ,frozenset(circ))) elif self.relation[u1][v] <= Med and\ self.relation[v][u1] <= Med : ## if Debug: ## print('continue with ', p1) self._ccVisit(p1,u,Odd=Odd,Comments=Comments) for x in (gammaUt): if self.blocked[x] > 0: self.blocked[x] -= 1 return #----------------------------------------
[docs] def computeMaxHoleSize(self,Comments=False): """ Renders the length of the largest chordless cycle in the corresponding disjunctive undirected graph. """ g = self.digraph2Graph(ConjunctiveConversion=False) cycles = g.computeChordlessCycles() nbrOfHoles = len(cycles) maxHS = 0 for c in cycles: nc = len(c) if nc > maxHS: if Comments: print('Cycle %s of length %d' %(str(c),nc) ) maxHS = nc if Comments: print('# holes = %d ' % nbrOfHoles ) print('Maximal hole size = %d ' % maxHS ) self.nbrOfHoles = nbrOfHoles self.maxHoleSize = maxHS return maxHS
#--------interoperability resources ----
[docs] def addValuationAttribute(self): """ Adds the numpy valuation attribute """ import numpy as np order = self.order valuation = np.zeros([order,order]) actionsList = [x for x in self.actions] relation = self.relation for i,x in enumerate(actionsList): for j,y in enumerate(actionsList): valuation[i,j] = relation[x][y] self.valuation = valuation
[docs] def relationFct(self,x,y): """ wrapper for self.relation dictionary access to ensure interoperability with the sparse and big outranking digraph implementation model. """ return self.relation[x][y]
#------------------------------------
[docs] def topologicalSort(self,Debug=False): """ If self is acyclic, adds topological sort number to each node of self and renders ordered list of nodes. Otherwise renders None. Source: M. Golumbic Algorithmic Graph heory and Perfect Graphs, Annals Of Discrete Mathematics 57 2nd Ed. , Elsevier 2004, Algorithm 2.4 p.44. """ def topSort(v,Debug=False): if Debug: print('in',self.i,v,self.gamma[v],self.dfsNbr[v],self.tsNbr[v]) self.i += 1 self.dfsNbr[v] = self.i for w in self.gamma[v][0]: if Debug: print('successer',w,'of',v) if self.dfsNbr[w] == 0: topSort(w,Debug=Debug) else: if self.tsNbr[w] == 0: self.Acyclic = False self.tsNbr[v]=self.j self.j -= 1 if Debug: print('out',v,self.dfsNbr[v],self.tsNbr[v]) self.Acyclic = True self.dfsNbr = {} self.tsNbr = {} for x in self.actions: self.dfsNbr[x]=0 self.tsNbr[x]=0 self.j = len(self.actions) self.i = 0 for x in self.actions: if Debug: print(x,self.gamma[x]) if self.dfsNbr[x] == 0: topSort(x,Debug=Debug) if self.Acyclic: tsLevels = [(x,self.tsNbr[x]) for x in self.tsNbr] ordering = [x[0] for x in sorted(tsLevels,\ key = lambda tsLevels: tsLevels[1])] if Debug: print(tsLevels,ordering) return ordering else: if Debug: print('Digraph instance %s is not acyclic!' % self.name) print(self.dfsNbr,self.tsNbr) return None
[docs] def computeTopologicalRanking(self,Debug=False): """ Mimetic Wrapper of the topologicalSort() method. """ self.topologicalSort(Debug=Debug)
#---------------
[docs] def digraph2Graph(self,valuationDomain={'min':-1,'med':0,'max':1}, Debug=False,ConjunctiveConversion=True): """ Convert a Digraph instance to a Graph instance. """ from graphs import Graph from copy import copy, deepcopy g = Graph() g.name = self.name + '_graph' if type(self.actions) == list: g.vertices = {} for x in self.actions: g.vertices[x] = {'name': x, 'shortName': x} else: g.vertices = deepcopy(self.actions) g.order = len(g.vertices) g.valuationDomain = valuationDomain gMin = valuationDomain['min'] gMed = valuationDomain['med'] gMax = valuationDomain['max'] g.edges = {} verticesKeys = list(g.vertices.keys()) dgMed = self.valuationdomain['med'] for i in range(g.order): for j in range(i+1,g.order): x = verticesKeys[i] y = verticesKeys[j] vertex = frozenset([x,y]) if ConjunctiveConversion: edgeValue = min(self.relation[x][y],self.relation[y][x]) else: edgeValue = max(self.relation[x][y],self.relation[y][x]) if edgeValue > dgMed: g.edges[vertex] = gMax elif edgeValue < dgMed: g.edges[vertex] = gMin else: g.edges[vertex] = gMed if Debug: print('x,y,self.relation[x][y],self.relation[y][x],vertex,g.edges[vertex]', x,y,self.relation[x][y],self.relation[y][x],vertex,g.edges[vertex]) g.gamma = g.gammaSets() return g
[docs] def computeRelationalStructure(self,Debug=False): """ Renders the counted decomposition of the valued relations into the following type of links: gt '>', eq '=', lt '<', incomp '<>', leq '<=', geq '>=', indeterm '?' """ counts = {'>':0,'=':0,'<':0,'<>':0,'<=':0,'>=':0,'?':0} actions = [x for x in self.actions] n = len(actions) relation = self.relation for x in actions: for y in actions: if Debug: print(x,y, relation[x][y],relation[y][x], end=' ') if x != y: if relation[x][y] > self.valuationdomain['med']: if relation[y][x] > self.valuationdomain['med']: counts['='] += 1 elif relation[y][x] < self.valuationdomain['med']: counts['>'] += 1 else: counts['>='] += 1 elif relation[x][y] < self.valuationdomain['med']: if relation[y][x] > self.valuationdomain['med']: counts['<'] += 1 elif relation[y][x] < self.valuationdomain['med']: counts['<>'] += 1 else: counts['<='] += 1 else: # relation[y][x] == self.valuationdomain['med'] if relation[y][x] > self.valuationdomain['med']: counts['<='] += 1 elif relation[y][x] < self.valuationdomain['med']: counts['>='] += 1 else: counts['?'] += 1 if Debug: print(counts) nd = Decimal(str(n)) if nd != Decimal('0'): counts['<'] = Decimal(str(counts['<']))/(nd*(nd-1)) counts['<='] = Decimal(str(counts['<=']))/(nd*(nd-1)) counts['>'] = Decimal(str(counts['>']))/(nd*(nd-1)) counts['>='] = Decimal(str(counts['>=']))/(nd*(nd-1)) counts['<>'] = Decimal(str(counts['<>']))/(nd*(nd-1)) counts['='] = Decimal(str(counts['=']))/(nd*(nd-1)) counts['?'] = Decimal(str(counts['?']))/(nd*(nd-1)) return counts
[docs] def computeRankingByLastChoosing(self,CoDual=False,Debug=False): """ Computes a weak preordring of the self.actions by iterating worst choice elagations. Stores in self.rankingByLastChoosing['result'] a list of (P-,worstChoice) pairs where P- gives the worst choice complement outranked average valuation via the computePairwiseClusterComparison method. If self.rankingByChoosing['CoDual'] is True, the ranking-by-last-chossing was computed on the codual of self. """ from copy import copy, deepcopy if CoDual: currG = CoDualDigraph(self) else: currG = deepcopy(self) remainingActions = [x for x in self.actions] rankingByLastChoosing = [] worstChoice = (None,None) i = 0 while len(remainingActions) > 1 and worstChoice[1] != []: i += 1 currG.actions = remainingActions ## if CoDual: ## currGcd = CoDualDigraph(currG) ## else: currGcd = deepcopy(currG) currGcd.computeBestChoiceRecommendation(CoDual=CoDual,Debug=False) k1 = currGcd.flatChoice(currGcd.worstChoice) if Debug: print('k1',k1) ck1 = list(set(currG.actions)-set(k1)) if len(k1) > 0: if len(ck1) > 0: k1Outranked = currG.computePairwiseClusterComparison(k1,ck1) if Debug: print('worst', k1, k1Outranked) worstChoice = ( min(-k1Outranked['P+'],k1Outranked['P-']), k1 ) else: worstChoice = ( self.valuationdomain['max'], k1 ) else: worstChoice = ( self.valuationdomain['med'], [] ) if Debug: print('worstChoice', i, worstChoice) if (worstChoice[1] != []): rankingByLastChoosing.append(worstChoice) for x in worstChoice[1]: try: remainingActions.remove(x) except: pass if Debug: print( i, worstChoice, remainingActions, rankingByLastChoosing) if (worstChoice[1] == []): #### only a singleton choice or a failure quadruple left to rank if Debug: print(worstChoice) worstChoice = (self.valuationdomain['max'],remainingActions) rankingByLastChoosing.append(worstChoice) if Debug: print(rankingByLastChoosing) elif len(remainingActions) == 1: #### only a singleton choice or a failure quadruple left to rank if Debug: print(worstChoice) worstChoice = (self.valuationdomain['max'],remainingActions) rankingByLastChoosing.append(worstChoice) if Debug: print(rankingByLastChoosing) self.rankingByLastChoosing = {'CoDual': CoDual, 'result': rankingByLastChoosing} return {'CoDual': CoDual, 'result': rankingByLastChoosing}
def _computeRankingByLastChoosing(self,CoDual=False,Debug=False): """ Computes a weak preordring of the self.actions by iterating worst choice elagations. Stores in self.rankingByLastChoosing['result'] a list of (P-,worstChoice) pairs where P- gives the worst choice complement outranked average valuation via the computePairwiseClusterComparison method. If self.rankingByChoosing['CoDual'] is True, the ranking-by-last-chossing was computed on the codual of self. """ from copy import copy, deepcopy from operator import itemgetter currG = deepcopy(self) remainingActions = [x for x in self.actions] rankingByLastChoosing = [] worstChoice = (None,None) i = 0 while len(remainingActions) > 1 and worstChoice[1] != []: i += 1 currG.actions = remainingActions if CoDual: currGcd = CoDualDigraph(currG) else: currGcd = deepcopy(currG) currGcd.computeRubisChoice(Comments=False) #currGcd.computeGoodChoices(Comments=Debug) #currGcd.computeBadChoices(Comments=Debug) worstChoiceCandidates = [] j = 0 for ch in currGcd.badChoices: k1 = currGcd.flatChoice(ch[5]) if Debug: print(ch[5],k1) ck1 = list(set(currG.actions)-set(k1)) if len(ck1) > 0: j += 1 k1Outranked = currG.computePairwiseClusterComparison(k1,ck1) if Debug: print('worst', j, ch[5], k1, k1Outranked) worstChoiceCandidates.append( ( min(-k1Outranked['P+'],k1Outranked['P-']), k1 ) ) else: worstChoiceCandidates.append((self.valuationdomain['max'],k1)) worstChoiceCandidates.sort(reverse=True,key=itemgetter(0)) try: worstChoice = worstChoiceCandidates[0] except: #print 'Error: no worst choice in currGcd' #currGcd.save('currGcd_errorWorst') worstChoice=(self.valuationdomain['med'],[]) if Debug: print('worstChoice', i, worstChoice, worstChoiceCandidates) if (worstChoice[1] != []): rankingByLastChoosing.append(worstChoice) if len(worstChoice[1]) > 0: for x in worstChoice[1]: try: remainingActions.remove(x) except: pass if Debug: print( i, worstChoice, remainingActions, rankingByLastChoosing) if (worstChoice[1] == []): #### only a singleton choice or a failure quadruple left to rank if Debug: print(worstChoice) worstChoice = (self.valuationdomain['max'],remainingActions) rankingByLastChoosing.append(worstChoice) if Debug: print(rankingByLastChoosing) elif len(remainingActions) == 1: #### only a singleton choice or a failure quadruple left to rank if Debug: print(worstChoice) worstChoice = (self.valuationdomain['max'],remainingActions) rankingByLastChoosing.append(worstChoice) if Debug: print(rankingByLastChoosing) self.rankingByLastChoosing = {'CoDual': CoDual, 'result': rankingByLastChoosing} return {'CoDual': CoDual, 'result': rankingByLastChoosing}
[docs] def computeRankingByChoosing(self,actionsSubset=None,Debug=False,CoDual=False): """ Computes a weak preordring of the self.actions by iterating jointly first and last choice elagations. Stores in self.rankingByChoosing['result'] a list of ((P+,bestChoice),(P-,worstChoice)) pairs where P+ (resp. P-) gives the best (resp. worst) choice complement outranking (resp. outranked) average valuation via the computePairwiseClusterComparison method. If self.rankingByChoosing['CoDual'] is True, the ranking-by-choosing was computed on the codual of self. """ from copy import copy, deepcopy from operator import itemgetter if CoDual: currG = CoDualDigraph(self) else: currG = deepcopy(self) if actionsSubset is None: remainingActions = [x for x in self.actions] else: remainingActions = actionsSubset rankingByChoosing = [] bestChoice = (None,None) worstChoice = (None,None) i = 0 while len(remainingActions) > 2 and (bestChoice[1] != [] or worstChoice[1] != []): i += 1 currG.actions = remainingActions currGcd = deepcopy(currG) currGcd.computeRubisChoice(Comments=Debug) #currGcd.computeGoodChoices(Comments=Debug) bestChoiceCandidates = [] j = 0 for ch in currGcd.goodChoices: k1 = currGcd.flatChoice(ch[5]) if Debug: print(ch[5],k1) ck1 = list(set(currG.actions)-set(k1)) if len(ck1) > 0: j += 1 k1Outranking = currG.computePairwiseClusterComparison(k1,ck1) if Debug: print('good', j, ch[5], k1, k1Outranking) #bestChoiceCandidates.append((k1Outranking['P+'],k1)) bestChoiceCandidates.append( ( min(k1Outranking['P+'],-k1Outranking['P-']), k1 ) ) else: bestChoiceCandidates.append((self.valuationdomain['max'],k1)) #bestChoiceCandidates.sort(reverse=True) bestChoiceCandidates = sorted(bestChoiceCandidates, key=lambda choice: str(choice[1]) ) # lexigr choices bestChoiceCandidates = sorted(bestChoiceCandidates, key=lambda choice: -choice[0]) # sort by outranking power try: bestChoice = bestChoiceCandidates[0] except: #print 'Error: no best choice in currGcd!' #currGcd.save('currGcd_errorBest') bestChoice = (self.valuationdomain['med'],[]) if Debug: print('bestChoice', i, bestChoice, bestChoiceCandidates) #currGcd.computeBadChoices(Comments=Debug) worstChoiceCandidates = [] j = 0 for ch in currGcd.badChoices: k1 = currGcd.flatChoice(ch[5]) if Debug: print(ch[5],k1) ck1 = list(set(currG.actions)-set(k1)) if len(ck1) > 0: j += 1 k1Outranked = currG.computePairwiseClusterComparison(k1,ck1) if Debug: print('worst', j, ch[5], k1, k1Outranked) worstChoiceCandidates.append( ( min(-k1Outranked['P+'],k1Outranked['P-']), k1 ) ) else: worstChoiceCandidates.append((self.valuationdomain['max'],k1)) worstChoiceCandidates.sort(reverse=True, key=itemgetter(0)) try: worstChoice = worstChoiceCandidates[0] except: #print 'Error: no worst choice in currGcd' #currGcd.save('currGcd_errorWorst') worstChoice=(self.valuationdomain['med'],[]) if Debug: print('worstChoice', i, worstChoice, worstChoiceCandidates) if (bestChoice[1] != [] or worstChoice[1] != []): rankingByChoosing.append((bestChoice,worstChoice)) if len(bestChoice[1]) > 0: for x in bestChoice[1]: remainingActions.remove(x) if len(worstChoice[1]) > 0: for x in worstChoice[1]: try: remainingActions.remove(x) except: pass #print i, bestChoice, worstChoice, remainingActions, rankingByChoosing if (bestChoice[1] == [] and worstChoice[1] == []): #### only a singleton choice or a failure quadruple left to rank if Debug: print(bestChoice,worstChoice) bestChoice = (self.valuationdomain['max'],remainingActions) worstChoice = (self.valuationdomain['max'],remainingActions) rankingByChoosing.append((bestChoice,worstChoice)) if Debug: print(rankingByChoosing) elif len(remainingActions) == 2: i += 1 currG.actions = remainingActions if CoDual: currGcd = CoDualDigraph(currG) else: currGcd = deepcopy(currG) currGcd.computeRubisChoice(Comments=Debug) #currGcd.computeGoodChoices(Comments=Debug) bestChoiceCandidates = [] j = 0 for ch in currGcd.goodChoices: k1 = currGcd.flatChoice(ch[5]) if Debug: print(ch[5],k1) ck1 = list(set(currG.actions)-set(k1)) if len(ck1) > 0: j += 1 k1Outranking = currG.computePairwiseClusterComparison(k1,ck1) if Debug: print('good', j, ch[5], k1, k1Outranking) #bestChoiceCandidates.append((k1Outranking['P+'],k1)) bestChoiceCandidates.append( ( min(k1Outranking['P+'],-k1Outranking['P-']), k1 ) ) else: bestChoiceCandidates.append((self.valuationdomain['max'],k1)) bestChoiceCandidates.sort(reverse=True, key=itemgetter(0)) try: bestChoice = bestChoiceCandidates[0] except: #print 'Error: no best choice in currGcd!' #currGcd.save('currGcd_errorBest') bestChoice = (self.valuationdomain['med'],[]) if Debug: print('bestChoice', i, bestChoice, bestChoiceCandidates) ## ### unique worst choice left k1 = list(set(currG.actions)-set(bestChoice[1])) if Debug: print('singleton worst choice left',k1) if len(k1) > 0: ck1 = list(set(currG.actions)-set(k1)) k1Outranked = currG.computePairwiseClusterComparison(k1,ck1) worstChoice = ( min(-k1Outranked['P+'],k1Outranked['P-']), k1 ) else: worstChoice = (self.valuationdomain['max'],bestChoice[1]) if Debug: print('worstChoice', i, worstChoice) rankingByChoosing.append((bestChoice,worstChoice)) elif len(remainingActions) == 1: #### only a singleton choice or a failure quadruple left to rank if Debug: print(bestChoice,worstChoice) bestChoice = (self.valuationdomain['max'],remainingActions) worstChoice = (self.valuationdomain['max'],remainingActions) rankingByChoosing.append((bestChoice,worstChoice)) if Debug: print(rankingByChoosing) self.rankingByChoosing = {'CoDual': CoDual, 'result': rankingByChoosing} return {'CoDual': CoDual, 'result': rankingByChoosing}
[docs] def computeRankingByBestChoosing(self,CoDual=False,Debug=False,): """ Computes a weak preordering of the self.actions by recursive best choice elagations. Stores in self.rankingByBestChoosing['result'] a list of (P+,bestChoice) tuples where P+ gives the best choice complement outranking average valuation via the computePairwiseClusterComparison method. If self.rankingByBestChoosing['CoDual'] is True, the ranking-by-choosing was computed on the codual of self. """ if Debug: print("===>>>> debugging computeByBestChoosing() digraphs methods") from copy import copy, deepcopy if CoDual: currG = CoDualDigraph(self) else: currG = deepcopy(self) remainingActions = [x for x in self.actions] rankingByBestChoosing = [] bestChoice = (self.valuationdomain['med'],None) i = 0 while len(remainingActions) > 1 and bestChoice[1] != []: i += 1 currG.actions = remainingActions ## if CoDual: ## currGcd = CoDualDigraph(currG) ## else: currGcd = deepcopy(currG) currGcd.computeBestChoiceRecommendation(CoDual=CoDual,Comments=Debug) k1 = currGcd.flatChoice(currGcd.bestChoice) if Debug: print('flatening the choice:',currGcd.bestChoice,k1) ck1 = list(set(currG.actions)-set(k1)) if len(k1) > 0: if len(ck1) > 0: k1Outranking = currG.computePairwiseClusterComparison(k1,ck1) if Debug: print('good',currGcd.bestChoice, k1, k1Outranking) #bestChoiceCandidates.append((k1Outranking['P+'],k1)) bestChoice = ( min(k1Outranking['P+'],-k1Outranking['P-']), k1 ) else: bestChoice = ( self.valuationdomain['max'], k1 ) else: bestChoice = (self.valuationdomain['med'],[]) if Debug: print('bestChoice', i, bestChoice) if bestChoice[1] != []: if Debug: print('bestChoice[1] != []:', bestChoice[1]) rankingByBestChoosing.append(bestChoice) for x in bestChoice[1]: try: remainingActions.remove(x) except: pass if Debug: print(i, bestChoice, remainingActions, rankingByBestChoosing) if bestChoice[1] == []: #### no best choice detacted, close the ranking with the remaining actions if Debug: print('bestChoice[1] == []:', bestChoice) bestChoice = (self.valuationdomain['max'],remainingActions) rankingByBestChoosing.append(bestChoice) if Debug: print(rankingByBestChoosing) if len(remainingActions) == 1: #### only a singleton choice or a failure quadruple left to rank if Debug: print('!!! len(remainingActions) == 1: !!!', remainingActions) bestChoice = (self.valuationdomain['max'],remainingActions) rankingByBestChoosing.append(bestChoice) if Debug: print(rankingByBestChoosing) self.rankingByBestChoosing = {'CoDual': CoDual, 'result': rankingByBestChoosing} return {'CoDual': CoDual, 'result': rankingByBestChoosing}
def _computeRankingByBestChoosing(self,CoDual=False,Debug=False,): """ Computes a weak preordering of the self.actions by recursive best choice elagations. Stores in self.rankingByBestChoosing['result'] a list of (P+,bestChoice) tuples where P+ gives the best choice complement outranking average valuation via the computePairwiseClusterComparison method. If self.rankingByBestChoosing['CoDual'] is True, the ranking-by-choosing was computed on the codual of self. """ from operator import itemgetter if Debug: print("===>>>> debugging computeByBestChoosing() digraphs methods") from copy import copy, deepcopy currG = copy(self) remainingActions = [x for x in self.actions] rankingByBestChoosing = [] bestChoice = (None,None) i = 0 while len(remainingActions) > 2 and bestChoice[1] != []: i += 1 currG.actions = remainingActions if CoDual: currGcd = CoDualDigraph(currG) else: currGcd = copy(currG) currGcd.computeRubisChoice(Comments=Debug) #currGcd.computeGoodChoices(Comments=Debug) bestChoiceCandidates = [] j = 0 for ch in currGcd.goodChoices: k1 = currGcd.flatChoice(ch[5]) if Debug: print('flatening the choice:',ch[5],k1) ck1 = list(set(currG.actions)-set(k1)) if len(ck1) > 0: j += 1 k1Outranking = currG.computePairwiseClusterComparison(k1,ck1) if Debug: print('good', j, ch[5], k1, k1Outranking) #bestChoiceCandidates.append((k1Outranking['P+'],k1)) bestChoiceCandidates.append( ( min(k1Outranking['P+'],-k1Outranking['P-']), k1 ) ) else: bestChoiceCandidates.append((self.valuationdomain['max'],k1)) #bestChoiceCandidates.sort(reverse=True) bestChoiceCandidates = sorted(bestChoiceCandidates, key=lambda choice: str(choice[1]) ) # lexigr choices bestChoiceCandidates = sorted(bestChoiceCandidates, key=lambda choice: -choice[0]) # sort by outranking power try: bestChoice = bestChoiceCandidates[0] except: if Debug: print('Error: no best choice in currGcd!') #currGcd.save('currGcd_errorBest') bestChoice = (self.valuationdomain['med'],[]) if Debug: print('bestChoice', i, bestChoice, bestChoiceCandidates) if bestChoice[1] != []: if Debug: print('bestChoice[1] != []:', bestChoice[1]) rankingByBestChoosing.append(bestChoice) if len(bestChoice[1]) > 0: for x in bestChoice[1]: remainingActions.remove(x) if Debug: print(i, bestChoice, remainingActions, rankingByBestChoosing) if bestChoice[1] == []: #### only a singleton choice or a failure quadruple left to rank if Debug: print('bestChoice[1] == []:', bestChoice) bestChoice = (self.valuationdomain['max'],remainingActions) rankingByBestChoosing.append(bestChoice) if Debug: print(rankingByBestChoosing) elif len(remainingActions) == 2: if Debug: print('len(remainingActions) == 2:',remainingActions) i += 1 currG.actions = remainingActions if CoDual: currGcd = CoDualDigraph(currG) else: currGcd = copy(currG) currGcd.computeRubisChoice(Comments=Debug) #currGcd.computeGoodChoices(Comments=Debug) bestChoiceCandidates = [] j = 0 for ch in currGcd.goodChoices: k1 = currGcd.flatChoice(ch[5]) if Debug: print(ch[5],k1) ck1 = list(set(currG.actions)-set(k1)) if len(ck1) > 0: j += 1 k1Outranking = currG.computePairwiseClusterComparison(k1,ck1) if Debug: print('good', j, ch[5], k1, k1Outranking) #bestChoiceCandidates.append((k1Outranking['P+'],k1)) bestChoiceCandidates.append( ( min(k1Outranking['P+'],-k1Outranking['P-']), k1 ) ) else: bestChoiceCandidates.append((self.valuationdomain['max'],k1)) bestChoiceCandidates.sort( reverse=True, key=itemgetter(0) ) if Debug: print('bestChoice', i, bestChoice, bestChoiceCandidates) try: bestChoice = bestChoiceCandidates[0] except: #print 'Error: no best choice in currGcd!' #currGcd.save('currGcd_errorBest') bestChoice = (self.valuationdomain['med'],[]) rankingByBestChoosing.append(bestChoice) for x in bestChoice[1]: remainingActions.remove(x) if len(remainingActions) > 0: lastBestChoice = (self.valuationdomain['max'],remainingActions) rankingByBestChoosing.append(lastBestChoice) if Debug: print('lastBestChoice', i+1, lastBestChoice) elif len(remainingActions) == 1: #### only a singleton choice or a failure quadruple left to rank if Debug: print('!!! len(remainingActions) == 1: !!!', remainingActions) bestChoice = (self.valuationdomain['max'],remainingActions) rankingByBestChoosing.append(bestChoice) if Debug: print(rankingByBestChoosing) self.rankingByBestChoosing = {'CoDual': CoDual, 'result': rankingByBestChoosing} return {'CoDual': CoDual, 'result': rankingByBestChoosing}
[docs] def iterateRankingByChoosing(self,Odd=False,CoDual=False,Comments=True,Debug=False,Limited=None): """ Renders a ranking by choosing result when progressively eliminating all chordless (odd only) circuits with rising valuation cut levels. Parameters CoDual = False (default)/True Limited = proportion (in [0,1]) * (max - med) valuationdomain """ from copy import copy, deepcopy from time import time if Debug: Comments=True gcd = copy(self) qualmaj0 = gcd.valuationdomain['med'] if Limited is not None: maxLevel = gcd.valuationdomain['med'] + (gcd.valuationdomain['max']-gcd.valuationdomain['med'])*Decimal(str(Limited)) else: maxLevel = gcd.valuationdomain['max'] if Comments: print('Ranking by choosing and rejecting after progressive cut elimination of chordless (odd = %s) circuits' % (str(Odd)) ) print('Evaluation domain: [ %.3f ; %.3f ]' % (gcd.valuationdomain['min'],gcd.valuationdomain['max'])) print('Initial determinateness of the outranking relation: %.3f' % self.computeDeterminateness()) print('Maximum level of circuits elimination: %.3f' % (maxLevel)) i = 0 qualmaj = gcd.minimalValuationLevelForCircuitsElimination(Odd=Odd,Debug=Debug,Comments=Comments) self.rankingByChoosing = None while qualmaj > qualmaj0: i += 1 if Comments: print('--> Iteration %d' % (i)) t0 = time() if Limited: if qualmaj <= maxLevel: if qualmaj < gcd.valuationdomain['max']: # strict cut only possible if < max pg = PolarisedDigraph(gcd,qualmaj,StrictCut=True) else: pg = PolarisedDigraph(gcd,qualmaj,StrictCut=False) else: qualmaj = qualmaj0 if qualmaj < gcd.valuationdomain['max']: pg = PolarisedDigraph(gcd,qualmaj,StrictCut=True) else: pg = PolarisedDigraph(gcd,qualmaj,StrictCut=False) else: if qualmaj < gcd.valuationdomain['max']: pg = PolarisedDigraph(gcd,qualmaj,StrictCut=True) else: pg = PolarisedDigraph(gcd,qualmaj,StrictCut=False) if Comments: print('Polarised determinateness = %.3f' % pg.computeDeterminateness()) if qualmaj > gcd.valuationdomain['med']: self.rankingByChoosing = pg.computeRankingByChoosing(CoDual=CoDual,Debug=Debug) self.rankingByChoosing['PolarizationLevel'] = qualmaj elif i==1: self.rankingByChoosing = pg.computeRankingByChoosing(CoDual=CoDual,Debug=Debug) self.rankingByChoosing['PolarizationLevel'] = qualmaj if Comments: self.showRankingByChoosing() print('Execution time:', time()-t0, 'sec.') ## pgRankingByChoosingRelation = self.computeRankingByChoosingRelation() ## corr = self.computeOrdinalCorrelation(pgRankingByChoosingRelation) ## print 'Ordinal (Kendall) correlation with outranking relation: %.3f (%.3f)' % (corr['correlation'],corr['determination']) ## corr = self.computeOrdinalCorrelation(pgRankingByChoosingRelation,MedianCut=True,Debug=Debug) ## print 'Ordinal (Kendall) correlation with median cut outranking relation: %.3f (%.3f)' % (corr['correlation'],corr['determination']) qualmaj0 = qualmaj newLevel = pg.minimalValuationLevelForCircuitsElimination(Debug=Debug,Comments=Comments) if Limited is not None: qualmaj = min(maxLevel,newLevel) else: qualmaj = newLevel if Comments: print(i,qualmaj0,newLevel,qualmaj) if i==0: self.rankingByChoosing = gcd.computeRankingByChoosing(CoDual=CoDual,Debug=Debug) self.rankingByChoosing['PolarizationLevel'] = qualmaj return self.rankingByChoosing
def _optimalRankingByChoosing(self,Odd=True,CoDual=False,Comments=False,Debug=False,Limited=None): """ Renders a ranking by choosing result when progressively eliminating all chordless (odd only by default) circuits with rising valuation cut levels. Parameters: * CoDual = False (default)/True * Limited = proportion (in [0,1]) * (max - med) of valuationdomain (default = None) Returns the highest correlated rankingByChoosing with self or codual of self, depending on the CoDual flagg. """ from copy import copy, deepcopy from operator import itemgetter if Debug: Comments=True g = copy(self) if CoDual: gcd = ~(-g) else: gcd = copy(g) gcdcd = ~(-gcd) qualmaj0 = gcd.valuationdomain['min'] if Limited is not None: maxLevel = gcd.valuationdomain['med'] + (gcd.valuationdomain['max']-gcd.valuationdomain['med'])*Decimal(str(Limited)) else: maxLevel = gcd.valuationdomain['max'] if Comments: print('Ranking by choosing and rejecting after progressive cut elimination of chordless (odd = %s) circuits' % (str(Odd)) ) print('Evaluation domain: [ %.3f ; %.3f ]' % (gcd.valuationdomain['min'],gcd.valuationdomain['max'])) print('Initial determinateness of the outranking relation: %.3f' % self.computeDeterminateness()) print('Maximum level of circuits elimination: %.3f' % (maxLevel)) i = 0 #qualmaj = gcd.minimalValuationLevelForCircuitsElimination(Odd=Odd,Debug=Debug,Comments=Comments) qualmaj = gcd.valuationdomain['med'] self.rankingByChoosing = None rankings = [] while qualmaj > qualmaj0 and qualmaj <= maxLevel: i += 1 if Comments: print('--> Iteration %d' % (i)) if Limited is not None: if qualmaj <= maxLevel: if qualmaj < gcd.valuationdomain['max']: ## strict cut only possible if cut level qualmaj < max pg = PolarisedDigraph(gcd,qualmaj,StrictCut=True) else: pg = PolarisedDigraph(gcd,qualmaj,StrictCut=False) else: qualmaj = qualmaj0 #if qualmaj < gcd.valuationdomain['max']: # pg = PolarisedDigraph(gcd,qualmaj,StrictCut=True) #else: # pg = PolarisedDigraph(gcd,qualmaj,StrictCut=False) else: if qualmaj < gcd.valuationdomain['max']: pg = PolarisedDigraph(gcd,qualmaj,StrictCut=True) else: pg = PolarisedDigraph(gcd,qualmaj,StrictCut=False) if Comments: print('Polarised determinateness = %.3f' % pg.computeDeterminateness()) rkg = pg.computeRankingByChoosing(CoDual=False,Debug=Debug) pgr = pg.computeRankingByChoosingRelation() if CoDual: corr = g.computeOrdinalCorrelation(pgr) else: corr = gcdcd.computeOrdinalCorrelation(pgr) rankings.append((corr['correlation'],qualmaj,rkg)) #rankings.append((corr['correlation']*corr['determination'],qualmaj,rkg)) if Comments: if Debug: print(rankings) if CoDual: g.showRankingByChoosing(rkg) else: gcdcd.showRankingByChoosing(rkg) qualmaj0 = qualmaj newLevel = pg.minimalValuationLevelForCircuitsElimination(Odd=Odd,Debug=Debug,Comments=Comments) if Limited is not None: if newLevel <= maxLevel: qualmaj = newLevel else: qualmaj0 = qualmaj else: qualmaj = newLevel if Debug: print('i,qualmaj0,newLevel,maxLevel,qualmaj',i,qualmaj0,newLevel,maxLevel,qualmaj) if i==0: self.rankingByChoosing = gcd.computeRankingByChoosing(CoDual=CoDual,Debug=Debug) self.rankingByChoosing['PolarizationLevel'] = qualmaj else: rankings.sort(reverse=True,key=itemgetter(0)) self.rankingByChoosing = rankings[0][2] self.rankingByChoosing['PolarizationLevel'] = rankings[0][1] if Comments: if Debug: print(rankings) if CoDual: g.showRankingByChoosing(self.rankingByChoosing) else: gcdcd.showRankingByChoosing(self.rankingByChoosing) return self.rankingByChoosing def _computePrudentBestChoiceRecommendation(self,CoDual=False,Comments=False,Debug=False,Limited=None): """ Renders the best choice recommendation after eliminating all odd chordless circuits with a minimal cut of the valuation. """ from copy import copy as deepcopy self.optimalRankingByChoosing(CoDual=CoDual,Comments=Comments,Debug=Debug,Limited=Limited) #if Comments: # self.showRankingByChoosing() try: self.rankingByChoosing['result'][0][0][1].sort() return self.rankingByChoosing['result'][0][0][1] except: print("Error: no ranking by choosing result !!") return None
[docs] def computePreRankingRelation(self,preRanking,Normalized=True,Debug=False): """ Renders the bipolar-valued relation obtained from a given preRanking in decreasing levels (list of lists) result. """ if Normalized: Max = Decimal('1') Med = Decimal('0') Min = Decimal('-1') else: Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] Min = self.valuationdomain['min'] actions = list(self.actions.keys()) currentActions = set(actions) preRankingRelation = {} for x in actions: preRankingRelation[x] = {} for y in actions: preRankingRelation[x][y] = Med for eqcl in preRanking: currRest = currentActions - set(eqcl) if Debug: print(currentActions, eqcl, currRest) for x in eqcl: for y in eqcl: if x != y: preRankingRelation[x][y] = Max preRankingRelation[y][x] = Max for x in eqcl: for y in currRest: preRankingRelation[x][y] = Max preRankingRelation[y][x] = Min currentActions = currentActions - set(eqcl) return preRankingRelation
[docs] def computePreorderRelation(self,preorder,Normalized=True,Debug=False): """ Renders the bipolar-valued relation obtained from a given preordering in increasing levels (list of lists) result. """ if Normalized: Max = Decimal('1') Med = Decimal('0') Min = Decimal('-1') else: Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] Min = self.valuationdomain['min'] actions = list(self.actions.keys()) currentActions = set(actions) preorderRelation = {} for x in actions: preorderRelation[x] = {} for y in actions: preorderRelation[x][y] = Med for eqcl in preorder: currRest = currentActions - set(eqcl) if Debug: print(currentActions, eqcl, currRest) for x in eqcl: for y in eqcl: if x != y: preorderRelation[x][y] = Max preorderRelation[y][x] = Max for x in eqcl: for y in currRest: preorderRelation[x][y] = Min preorderRelation[y][x] = Max currentActions = currentActions - set(eqcl) return preorderRelation
[docs] def computeRankingByChoosingRelation(self,rankingByChoosing=None,actionsSubset=None,Debug=False): """ Renders the bipolar-valued relation obtained from the self.rankingByChoosing result. """ from digraphsTools import omax, omin from copy import copy, deepcopy if rankingByChoosing==None: try: rankingByChoosing = self.rankingByChoosing['result'] except: print('Error: first run computeRankingByChoosing(CoDual=T/F) !') return None if Debug: print('actionsSubset,rankingByChoosing',actionsSubset,rankingByChoosing) Max = Decimal('1') Med = Decimal('0') Min = Decimal('-1') if actionsSubset==None: actions = [x for x in self.actions] else: actions = copy(actionsSubset) if Debug: print(actions) currActions = set(actions) relation = self.relation rankingRelation = {} for x in actions: rankingRelation[x] = {} for y in actions: rankingRelation[x][y] = Med #print(x,y,rankingRelation[x][y]) n = len(rankingByChoosing) for i in range(n): ibch = set(rankingByChoosing[i][0][1]) iwch = set(rankingByChoosing[i][1][1]) ribch = set(currActions) - ibch if Debug: print(ibch,iwch,ribch) for x in ibch: for y in ibch: if x != y: rankingRelation[x][y] = omax(Med,[rankingRelation[x][y],abs(relation[x][y])]) rankingRelation[y][x] = omax(Med,[rankingRelation[x][y],abs(relation[y][x])]) for y in ribch: #print(x,y) #print(rankingRelation[x][y]) #print(relation[x][y]) rankingRelation[x][y] = omax(Med,[rankingRelation[x][y],abs(relation[x][y])]) rankingRelation[y][x] = omax(Med,[rankingRelation[y][x],-abs(relation[y][x])]) riwch = set(currActions) - iwch for y in iwch: for x in iwch: if x != y: rankingRelation[x][y] = omax(Med,[rankingRelation[x][y],abs(relation[x][y])]) rankingRelation[y][x] = omax(Med,[rankingRelation[y][x],abs(relation[y][x])]) for x in riwch: rankingRelation[x][y] = omax(Med,[rankingRelation[x][y],abs(relation[x][y])]) rankingRelation[y][x] = omax(Med,[rankingRelation[y][x],-abs(relation[x][y])]) currActions = currActions - (ibch | iwch) return rankingRelation
[docs] def computeRankingByBestChoosingRelation(self,rankingByBestChoosing=None,Debug=False): """ Renders the bipolar-valued relation obtained from the self.rankingByBestChoosing result. """ if rankingByBestChoosing==None: try: rankingByBestChoosing = self.rankingByBestChoosing['result'] except: print('Error: first run computeRankingByBestChoosing(CoDual=T/F) !') return None Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] Min = self.valuationdomain['min'] actions = [x for x in self.actions] currActions = set(actions) relation = self.relation rankingRelation = {} for x in actions: rankingRelation[x] = {} for y in actions: rankingRelation[x][y] = Med n = len(rankingByBestChoosing) if Debug: print('rankingByBestChoosing',rankingByBestChoosing) for i in range(n): ibch = set(rankingByBestChoosing[i][1]) ribch = set(currActions) - ibch if Debug: print('ibch,ribch',ibch,ribch) for x in ibch: for y in ibch: if x != y: rankingRelation[x][y] = min( [abs(relation[x][y]),abs(relation[y][x])] ) rankingRelation[y][x] = min( [abs(relation[y][x]),abs(relation[x][y])] ) ## rankingRelation[x][y] = self.omin( [rankingRelation[x][y],abs(relation[y][x])] ) ## rankingRelation[y][x] = self.omin( [rankingRelation[y][x],abs(relation[x][y])] ) ## if Debug and (x == 'a10' or y == 'a07') : ## print(x,y,rankingRelation[x][y],relation[x][y]) ## print(y,x,rankingRelation[y][x],relation[y][x]) for y in ribch: ## rankingRelation[x][y] = self.omin( [rankingRelation[x][y],abs(relation[y][x])] ) ## rankingRelation[y][x] = self.omin( [rankingRelation[y][x],-abs(relation[x][y])] ) rankingRelation[x][y] = min( [abs(relation[x][y]),abs(relation[y][x])] ) rankingRelation[y][x] = -min( [abs(relation[y][x]),abs(relation[x][y])] ) ## if Debug and (x == 'a10' or y == 'a07'): ## print('+',x,y,rankingRelation[x][y],relation[x][y]) ## print('-',y,x,rankingRelation[y][x],relation[y][x]) currActions = currActions - ibch return rankingRelation
[docs] def computeRankingByLastChoosingRelation(self,rankingByLastChoosing=None,Debug=False): """ Renders the bipolar-valued relation obtained from the self.rankingByLastChoosing result. """ if rankingByLastChoosing==None: try: rankingByLastChoosing = self.rankingByLastChoosing['result'] except: print('Error: first run computeRankingByLastChoosing(CoDual=T/F) !') return None Max = Decimal('1') Med = Decimal('0') Min = Decimal('-1') actions = [x for x in self.actions] currActions = set(actions) relation = self.relation rankingRelation = {} for x in actions: rankingRelation[x] = {} for y in actions: rankingRelation[x][y] = Med n = len(rankingByLastChoosing) for i in range(n): iwch = set(rankingByLastChoosing[i][1]) riwch = set(currActions) - iwch for x in iwch: for y in iwch: ## if Debug and (x == 'a10' and y == 'a08') : ## print(x,y,rankingRelation[x][y],relation[x][y]) ## print(y,x,rankingRelation[x][y],relation[y][x]) if x != y: ## rankingRelation[x][y] = self.omin( [rankingRelation[x][y],abs(relation[y][x])] ) ## rankingRelation[y][x] = self.omin( [rankingRelation[y][x],abs(relation[x][y])] ) rankingRelation[x][y] = min( [abs(relation[x][y]),abs(relation[y][x])] ) rankingRelation[y][x] = min( [abs(relation[y][x]),abs(relation[x][y])] ) for y in riwch: ## if Debug and (x == 'a10' and y == 'a08') : ## print(x,y,rankingRelation[x][y],relation[x][y]) ## print(y,x,rankingRelation[x][y],relation[y][x]) ## rankingRelation[x][y] = self.omin( [rankingRelation[x][y],-abs(relation[y][x])] ) ## rankingRelation[y][x] = self.omin( [rankingRelation[y][x],abs(relation[x][y])] ) rankingRelation[x][y] = -min( [abs(relation[x][y]),abs(relation[y][x])] ) rankingRelation[y][x] = min( [abs(relation[y][x]),abs(relation[x][y])] ) currActions = currActions - iwch return rankingRelation
[docs] def showRankingByChoosing(self,rankingByChoosing=None,WithCoverCredibility=False): """ A show method for self.rankinByChoosing result. When parameter *WithCoverCredibility* is set to True, the credibility of outranking, respectively being outranked is indicated at each selection step. .. warning:: The self.computeRankingByChoosing(CoDual=False/True) method instantiating the self.rankingByChoosing slot is pre-required ! """ if rankingByChoosing is None: try: rankingByChoosing = self.rankingByChoosing['result'] except: print('Error: You must first run self.computeRankingByChoosing(CoDual=False(default)|True) !') #rankingByChoosing = self.computeRankingByChoosing(Debug,CoDual) return else: rankingByChoosing = rankingByChoosing['result'] print('Ranking by Choosing and Rejecting') space = '' n = len(rankingByChoosing) for i in range(n): if i+1 == 1: nstr='st' elif i+1 == 2: nstr='nd' elif i+1 == 3: nstr='rd' else: nstr='th' ibch = set(rankingByChoosing[i][0][1]) iwch = set(rankingByChoosing[i][1][1]) iach = iwch & ibch #print 'ibch, iwch, iach', i, ibch,iwch,iach ch = list(ibch) ch.sort() if WithCoverCredibility: print(' %s%s%s Best Choice %s (%.2f)' %\ (space,i+1,nstr,ch,rankingByChoosing[i][0][0])) else: print(' %s%s%s Best Choice %s' % (space,i+1,nstr,ch) ) if len(iach) > 0 and i < n-1: print(' %s Ambiguous Choice %s' % (space,list(iach))) space += ' ' space += ' ' for i in range(n): if n-i == 1: nstr='st' elif n-i == 2: nstr='nd' elif n-i == 3: nstr='rd' else: nstr='th' space = space[:-2] ibch = set(rankingByChoosing[n-i-1][0][1]) iwch = set(rankingByChoosing[n-i-1][1][1]) iach = iwch & ibch #print 'ibch, iwch, iach', i, ibch,iwch,iach ch = list(iwch) ch.sort() if len(iach) > 0 and i > 0: space = space[:-2] print(' %s Ambiguous Choice %s' % (space,list(iach))) if WithCoverCredibility: print(' %s%s%s Worst Choice %s (%.2f)' %\ (space,n-i,nstr,ch,rankingByChoosing[n-i-1][1][0])) else: print(' %s%s%s Worst Choice %s' % (space,n-i,nstr,ch) ) corr1 = self.computeBipolarCorrelation(self.computeRankingByChoosingRelation(rankingByChoosing)) print('Ordinal bipolar correlation with outranking relation: tau = %+.3f (D = %.1f)'% (corr1['correlation'],corr1['determination'])) corr2 = self.computeBipolarCorrelation(self.computeRankingByChoosingRelation(rankingByChoosing),MedianCut=True) print('Ordinal bipolar correlation with median cut outranking relation: tau = %+.3f (D = %.1f)'% (corr2['correlation'],corr2['determination']))
[docs] def showRankingByLastChoosing(self,rankingByLastChoosing=None,Debug=None): """ A show method for self.rankinByChoosing result. .. warning:: The self.computeRankingByLastChoosing(CoDual=False/True) method instantiating the self.rankingByChoosing slot is pre-required ! """ if rankingByLastChoosing is None: try: rankingByLastChoosing = self.rankingByLastChoosing['result'] except: print('Error: You must first run self.computeRankingByLastChoosing(CoDual=False(default)|True) !') return else: rankingByLastChoosing = rankingByLastChoosing['result'] print('Ranking by recursively rejecting') space = '' n = len(rankingByLastChoosing) for i in range(n): if i+1 == 1: nstr='st' elif i+1 == 2: nstr='nd' elif i+1 == 3: nstr='rd' else: nstr='th' iwch = set(rankingByLastChoosing[i][1]) if Debug: print( 'i, iwch', i, iwch) ch = list(iwch) ch.sort() if nstr == 'st': print(' Last Choice %s (%.2f)' % (ch,rankingByLastChoosing[i][0])) else: print(' %s%s%s Last Choice %s (%.2f)' % (space,i+1,nstr,ch,rankingByLastChoosing[i][0])) space += ' '
[docs] def showRankingByBestChoosing(self,rankingByBestChoosing=None): """ A show method for self.rankinByBestChoosing result. .. warning:: The self.computeRankingByBestChoosing(CoDual=False/True) method instantiating the self.rankingByBestChoosing slot is pre-required ! """ if rankingByBestChoosing is None: try: rankingByBestChoosing = self.rankingByBestChoosing['result'] except: print('Error: You must first run self.computeRankingByBestChoosing(CoDual=False(default)|True) !') return else: rankingByBestChoosing = rankingByBestChoosing['result'] print('Ranking by recursively best-choosing') space = '' n = len(rankingByBestChoosing) for i in range(n): if i+1 == 1: nstr='st' elif i+1 == 2: nstr='nd' elif i+1 == 3: nstr='rd' else: nstr='th' ibch = set(rankingByBestChoosing[i][1]) ch = list(ibch) ch.sort() print(' %s%s%s Best Choice %s (%.2f)' % (space,i+1,nstr,ch,rankingByBestChoosing[i][0])) space += ' '
[docs] def computeValuationStatistics(self,Sampling=False,Comments=False): """ Renders the mean and variance of the valuation of the non reflexive pairs. """ from math import sqrt mean = Decimal('0.0') squares = Decimal('0.0') #actions = self.actions #n = len(self.actions) n = self.order n2 = n * (n-1) n2d = Decimal(str(n2)) relation = self.relation for x,rx in relation.items(): for y,rxy in rx.items(): if x != y: mean += rxy squares += rxy*rxy mean = mean / n2d if Sampling: var = ( squares / (n2d-Decimal('1')) ) - (mean * mean) else: var = squares / n2d - (mean * mean) stdDev = sqrt(var) if Comments: print('mean: %.5f, std. dev.: %.5f' % (mean,stdDev)) return mean,stdDev
[docs] def computeRankingCorrelation(self, ranking, Debug=False): """ Renders the ordinal correlation K of a digraph instance when compared with a given linear ranking of its actions K = sum_{x != y} [ min( max(-self.relation(x,y)),other.relation(x,y), max(self.relation(x,y),-other.relation(x,y)) ] K /= sum_{x!=y} [ min(abs(self.relation(x,y),abs(other.relation(x,y)) ] .. note:: Renders a tuple with at position 0 the actual bipolar correlation index and in position 1 the minimal determination level D of self and the other relation. D = sum_{x != y} min(abs(self.relation(x,y)),abs(other.relation(x,y)) / n(n-1) where n is the number of actions considered. The correlation index with a completely indeterminate relation is by convention 0.0 at determination level 0.0 . """ selfMax = self.valuationdomain['max'] if selfMax != Decimal('1'): print("Error: self's valuationdomain must be normalized !") return n = len(ranking) corrSum = 0 determSum = 0 for i in range(n-1): x = ranking[i] for j in range(i+1,n): y = ranking[j] # x > y selfRelation = self.relation[x][y] otherRelation = selfMax corr = min( max(-selfRelation,otherRelation),\ max(selfRelation,-otherRelation) ) corrSum += corr determ = min( abs(selfRelation),abs(otherRelation) ) determSum += determ # y < x selfRelation = self.relation[y][x] otherRelation = -selfMax corr = min( max(-selfRelation,otherRelation),\ max(selfRelation,-otherRelation) ) corrSum += corr determ = min( abs(selfRelation),abs(otherRelation) ) determSum += determ if determSum > 0: correlation = float(corrSum) / float(determSum) n2 = (self.order*self.order) - self.order determination = (float(determSum) / n2) determination /= float(selfMax) return { 'correlation': correlation, 'determination': determination } else: return { 'correlation': 0.0, 'determination': 0.0 }
[docs] def computeOrderCorrelation(self, order, Debug=False): """ Renders the ordinal correlation K of a digraph instance when compared with a given linear order (from worst to best) of its actions K = sum_{x != y} [ min( max(-self.relation(x,y)),other.relation(x,y), max(self.relation(x,y),-other.relation(x,y)) ] K /= sum_{x!=y} [ min(abs(self.relation(x,y),abs(other.relation(x,y)) ] .. note:: Renders a dictionary with the key 'correlation' containing the actual bipolar correlation index and the key 'determination' containing the minimal determination level D of self and the other relation. D = sum_{x != y} min(abs(self.relation(x,y)),abs(other.relation(x,y)) / n(n-1) where n is the number of actions considered. The correlation index with a completely indeterminate relation is by convention 0.0 at determination level 0.0 . .. warning:: self must be a normalized outranking digraph instance ! """ selfMax = self.valuationdomain['max'] if selfMax != Decimal('1'): print("Error: self's valuationdomain must be normalized !") return n = len(order) corrSum = Decimal('0') determSum = Decimal('0') for i in range(n): x = order[i] for j in range(i+1,n): y = order[j] # x < y selfRelation = self.relation[x][y] otherRelation = -selfMax corr = min( max(-selfRelation,otherRelation), max(selfRelation,-otherRelation) ) corrSum += corr determ = min( abs(selfRelation),abs(otherRelation) ) determSum += determ # y > x selfRelation = self.relation[y][x] otherRelation = selfMax corr = min( max(-selfRelation,otherRelation), max(selfRelation,-otherRelation) ) corrSum += corr determ = min( abs(selfRelation),abs(otherRelation) ) determSum += determ if determSum > 0: correlation = corrSum / determSum n2 = (self.order*self.order) - self.order determination = determSum / Decimal(str(n2)) determination /= selfMax return { 'correlation': correlation, 'determination': determination } else: return { 'correlation': 0.0, 'determination': 0.0 }
[docs] def computeOrdinalCorrelationMP(self, other, MedianCut=False, Threading=False,nbrOfCPUs=None, startMethod=None, Comments=False,Debug=False): """ Multi processing version of the digraphs.computeOrdinalCorrelation() method. .. note:: The relation filtering and the MedinaCut option are not implemented in the MP version. """ from multiprocessing import cpu_count from copy import copy,deepcopy from itertools import product if self.valuationdomain['min'] != Decimal('-1') or\ self.valuationdomain['max'] != Decimal('1'): print('Error: the digraph instance self must be normalized - self.recodeValuation(-1,1) -first !') return None else: g = self actionsList = [x for x in g.actions] n = g.order n2 = (n*(n-1)) Med = g.valuationdomain['med'] if not isinstance(other,(dict)): #if Debug: # print('inputting a Digraph instance') if other.valuationdomain['min'] != Decimal('-1') or\ other.valuationdomain['max'] != Decimal('1'): print('Error: the digraph instance other must be normalized - other.recodeValuation(-1,1) -first !') return None otherRelation = other.relation else: otherRelation = other #if Debug: # print(otherRelation) correlation = Decimal('0') determination = Decimal('0') if Threading and cpu_count() > 4: from pickle import Pickler,dumps, loads, load from io import BytesIO #from multiprocessing import Process, Lock,\ # active_children, cpu_count import multiprocessing as mp if startMethod is None: startMethod = 'spawn' mpctx = mp.get_context(startMethod) Process = mpctx.Process active_children = mpctx.active_children cpu_count = mpctx.cpu_count # class myThread(Process): # def __init__(self, threadID,TempDirName,Debug): # Process.__init__(self) # self.threadID = threadID # self.workingDirectory = tempDirName # self.Debug = Debug # def run(self): # from pickle import dumps, loads # from os import chdir # from decimal import Decimal # chdir(self.workingDirectory) # #Debug=False # #if Debug: # # print("Starting working in %s on %s" % (self.workingDirectory, self.name)) # fi = open('dumpActions.py','rb') # actionsList = loads(fi.read()) # fi.close() # #if Debug: # # print(self.threadID,actionsList) # fi = open('dumpRelation.py','rb') # relation = loads(fi.read()) # fi.close() # #if Debug: # # print(self.threadID,relation) # fi = open('dumpOtherRelation.py','rb') # otherRelation = loads(fi.read()) # fi.close() # #if Debug: # # print(self.threadID,relation) # fiName = 'splitActions-'+str(self.threadID)+'.py' # fi = open(fiName,'rb') # splitActions = loads(fi.read()) # fi.close() # #if Debug: # # print(self.threadID,splitActions) # # compute partial correlation # correlation = Decimal('0') # determination = Decimal('0') # for x in splitActions: # grx = g.relation[x] # orx = otherRelation[x] # for y in actionsList: # if x != y: # correlation += min( max(-grx[y],orx[y]),\ # max(grx[y],-orx[y]) ) # determination += min( abs(grx[y]),\ # abs(orx[y]) ) # #if Debug: # # print(x,y,g.relation[x][y],otherRelation[x][y],correlation,determination) # splitCorrelation = {'correlation': correlation, # 'determination': determination} # # write partial correlation relation # foName = 'splitCorrelation-'+str(self.threadID)+'.py' # fo = open(foName,'wb') # fo.write(dumps(splitCorrelation,-1)) # fo.close() # pre-threading operations if nbrOfCPUs is None: nbrOfCPUs = cpu_count() if Debug: print('Nbr of cpus = ',nbrOfCPUs) if Comments: print('Starting correlation computation with %d threads ...' % nbrOfCPUs) from tempfile import TemporaryDirectory #with TemporaryDirectory() as tempDirName: tempDir = TemporaryDirectory() tempDirName = tempDir.name selfFileName = tempDirName +'/dumpActions.py' #if Debug: # print('temDirName, selfFileName', tempDirName,selfFileName) fo = open(selfFileName,'wb') pd = dumps(actionsList,-1) fo.write(pd) fo.close() selfFileName = tempDirName +'/dumpRelation.py' #if Debug: # print('temDirName, selfFileName', tempDirName,selfFileName) fo = open(selfFileName,'wb') pd = dumps(self.relation,-1) fo.write(pd) fo.close() selfFileName = tempDirName +'/dumpOtherRelation.py' #if Debug: # print('temDirName, selfFileName', tempDirName,selfFileName) fo = open(selfFileName,'wb') pd = dumps(otherRelation,-1) fo.write(pd) fo.close() nit = n//nbrOfCPUs nbrOfJobs = nbrOfCPUs if nit*nbrOfCPUs < 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) i = 0 actions2Split = actionsList actionsRemain = set(actions2Split) for jb in range(nbrOfJobs): if Comments: print('Thread = %d/%d' % (jb+1,nbrOfJobs),end=" ") splitActions=[] for k in range(nit): if jb < (nbrOfJobs -1) and i < n: splitActions.append(actions2Split[i]) else: splitActions = list(actionsRemain) i += 1 #if Debug: # print(len(splitActions)) # print(splitActions) actionsRemain = actionsRemain - set(splitActions) #if Debug: # print(actionsRemain) foName = tempDirName+'/splitActions-'+str(jb)+'.py' fo = open(foName,'wb') spa = dumps(splitActions,-1) fo.write(spa) fo.close() splitThread = _myThread2(target=jb,args=(tempDirName,Debug)) splitThread.start() while active_children() != []: pass # post threading operations if Comments: print('Exiting computing threads') for jb in range(nbrOfJobs): #if Debug: # print('Post job-%d/%d processing' % (jb+1,nbrOfJobs)) # print('job',jb) fiName = tempDirName+'/splitCorrelation-'+str(jb)+'.py' fi = open(fiName,'rb') splitCorrelation = loads(fi.read()) #if Debug: # print('splitCorrelation',splitCorrelation) fi.close() correlation += splitCorrelation['correlation'] determination += splitCorrelation['determination'] else: # no Threading if Debug: print('No threading !') for x in g.actions: grx = g.relation[x] orx = otherRelation[x] for y in (g.actions): if x != y: corr = min( max(-grx[y],orx[y]),\ max(grx[y],-orx[y]) ) correlation += corr determination += min( abs(grx[y]),\ abs(orx[y]) ) #if Debug: # print(x,y,g.relation[x][y],otherRelation[x][y],correlation,determination) if determination > Decimal('0.0'): correlation /= determination return { 'correlation': correlation, 'determination': determination / Decimal(str(n2)) } else: return {'correlation': Decimal('0.0'), 'determination': determination}
[docs] def computeOrdinalCorrelation(self, other, MedianCut=False, filterRelation=None, Debug=False): """ Renders the bipolar correlation K of a self.relation when compared with a given compatible (same actions set)) digraph or a [-1,1] valued compatible relation (same actions set). If MedianCut=True, the correlation is computed on the median polarized relations. If filterRelation is not None, the correlation is computed on the partial domain corresponding to the determined part of the filter relation. .. warning:: Notice that the 'other' relation and/or the 'filterRelation', the case given, must both be normalized, ie [-1,1]-valued ! K = sum_{x != y} [ min( max(-self.relation[x][y]),other.relation[x][y]), max(self.relation[x][y],-other.relation[x][y]) ] K /= sum_{x!=y} [ min(abs(self.relation[x][y]),abs(other.relation[x][y])) ] .. note:: Renders a tuple with at position 0 the actual bipolar correlation index and in position 1 the minimal determination level D of self and the other relation. D = sum_{x != y} min(abs(self.relation[x][y]),abs(other.relation[x][y])) / n(n-1) where n is the number of actions considered. The correlation index with a completely indeterminate relation is by convention 0.0 at determination level 0.0 . """ from copy import copy,deepcopy g = deepcopy(self) g.recodeValuation(-1,1) actions = g.actions Med = g.valuationdomain['med'] if MedianCut: g = PolarisedDigraph(g,level=Decimal('0.0'),KeepValues=False,StrictCut=True) if not isinstance(other,(dict)): if Debug: print('inputting a Digraph instance') otherg = deepcopy(other) otherg.recodeValuation(-1,1) if MedianCut: otherg = PolarisedDigraph(otherg,level=Decimal('0.0'),KeepValues=False,StrictCut=True) otherRelation = otherg.relation else: otherRelation = deepcopy(other) if MedianCut: for x in actions: rx = otherRelation[x] for y in actions: if x == y: rx[y] = Decimal('0.0') else: if rx[y] > Med: rx[y] = Decimal('1.0') elif rx[y] < Med: rx[y] = Decimal('-1.0') else: rx[y] = Decimal('0.0') correlation = Decimal('0.0') determination = Decimal('0.0') if filterRelation is None: n = len(actions) n2 = (n*(n-1)) for x in actions: grx = g.relation[x] orx = otherRelation[x] for y in actions: if x != y: corr = min( max(-grx[y],orx[y]), max(grx[y],-orx[y]) ) correlation += corr determination += min( abs(grx[y]),abs(orx[y]) ) if Debug: print(x,y,grx[y],orx[y],correlation,determination) else: n = len(actions) n2 = (n*(n-1)) for x in actions: for y in actions: if x != y: if filterRelation[x][y] != Med: corr = min( max(-g.relation[x][y],otherRelation[x][y]), max(g.relation[x][y],-otherRelation[x][y]) ) correlation += corr determination += min( abs(g.relation[x][y]),abs(otherRelation[x][y]) ) #determination += abs(corr) if Debug: print(x,y,g.relation[x][y],otherRelation[x][y],filterRelation[x][y],correlation,determination) if determination > Decimal('0.0'): correlation /= determination return { 'MedianCut':MedianCut, 'correlation': correlation, 'determination': determination / Decimal(str(n2)) } else: return {'MedianCut':MedianCut, 'correlation': Decimal('0.0'), 'determination': determination}
[docs] def computeBipolarCorrelation(self, other, MedianCut=False, filterRelation=None, Debug=False): """ obsolete: dummy replacement for Digraph.computeOrdinalCorrelation method """ return self.computeOrdinalCorrelation(other= other,MedianCut=MedianCut, filterRelation=filterRelation,Debug=Debug)
[docs] def showCorrelation(self,corr=None,ndigits=3): """ Renders the valued ordinal correlation index, the crisp Kendall tau index and their epistemic determination degree. """ if corr is not None: print('Correlation indexes:') print( (' Crisp ordinal correlation : %%+.%df' % ndigits) % corr['correlation']) print( (' Epistemic determination : %%.%df' % ndigits) % corr['determination']) print( (' Bipolar-valued equivalence : %%+.%df' % ndigits) % (corr['correlation']*corr['determination']) ) else: print('Error: a computed correlation result is required !!!')
[docs] def computeKemenyIndex(self, otherRelation): """ renders the Kemeny index of the self.relation compared with a given crisp valued relation of a compatible other digraph (same nodes or actions). """ KemenyIndex = 0.0 actions = self.actions for x in dict.keys(actions): srx = self.relation[x] orx = otherRelation[x] for y in dict.keys(actions): if x != y: if orx[y] > Decimal('0'): KemenyIndex += float(srx[y]) elif orx[y] < Decimal('0'): KemenyIndex -= float(srx[y]) return KemenyIndex
[docs] def flatChoice(self,ch,Debug=False): """ Converts set or list ch recursively to a flat list of items. """ result = [] for x in ch: if Debug: print(x) if isinstance(x,frozenset): for y in self.flatChoice(x,Debug): result.append(y) else: result.append(x) if Debug: print(result) return result
[docs] def convertValuationToDecimal(self): """ Convert the float valuation limits to Decimals. """ self.valuationdomain['min'] = Decimal(str(self.valuationdomain['min'])) self.valuationdomain['med'] = Decimal(str(self.valuationdomain['med'])) self.valuationdomain['max'] = Decimal(str(self.valuationdomain['max']))
[docs] def convertRelationToDecimal(self): """ Converts the float valued self.relation in a decimal valued one. """ actions = self.actions relation = {} for x in actions: relation[x] = {} rx = relation[x] srx = self.relation[x] for y in actions: rx[y] = Decimal(str(srx[y])) self.relation = relation
#return relation
[docs] def bipolarKCorrelation(self, digraph,Debug=False): """ Renders the bipolar Kendall correlation between two bipolar valued digraphs computed from the average valuation of the XORDigraph(self,digraph) instance. .. warning:: Obsolete! Is replaced by the self.computeBipolarCorrelation(other) Digraph method """ xor = XORDigraph(self,digraph,Debug) if Debug: xor.showRelationTable() actions = self.actions n = len(actions) xor.recodeValuation(-1.0,1.0) kDistance = Decimal("0.0") for x in dict.keys(actions): for y in dict.keys(actions): if x != y: kDistance += xor.relation[x][y] kDistance = Decimal(str(kDistance)) / Decimal(str((n * (n-1)))) # the negation of the kDistance, i.e. -kDistance gives # the bipolar extended Kendall tau correlation return -kDistance
[docs] def crispKDistance(self, digraph,Debug=False): """ Renders the crisp Kendall distance between two bipolar valued digraphs. .. warning:: Obsolete! Is replaced by the self.computeBipolarCorrelation(other, MedianCut=True) Digraph method """ xor = XORDigraph(self,digraph,Debug) if Debug: xor.showRelationTable() actions = self.actions n = len(actions) k2Distance = xor.computeSize() k2Distance = Decimal(str(k2Distance)) / Decimal(str((n * (n-1)))) return k2Distance
[docs] def bipolarKDistance(self, digraph,Debug=False): """ Renders the bipolar crisp Kendall distance between two bipolar valued digraphs. .. warning:: Obsolete! Is replaced by the self.computeBipolarCorrelation(other, MedianCut=True) Digraph method """ xor = XORDigraph(self,digraph,Debug) if Debug: xor.showRelationTable() #actions = [x for x in self.actions] n = len(self.actions) k2Distance = xor.computeCoSize() - xor.computeSize() k2Distance = Decimal(str(k2Distance)) / Decimal(str((n * (n-1)))) return k2Distance
[docs] def computeWeakCondorcetWinners(self): """ Wrapper for weakCondorcetWinners(). """ return self.weakCondorcetWinners()
[docs] def computeWeakCondorcetLosers(self): """ Wrapper for weakCondorcetLosers(). """ return self.weakCondorcetLosers()
[docs] def weakCondorcetWinners(self): """ Renders the set of decision actions x such that self.relation[x][y] >= self.valuationdomain['med'] for all y != x. """ #actions = self.actions relation = self.relation Med = self.valuationdomain['med'] wCW = [] for x,rx in relation.items(): #rx = relation[x] Winner = True for y,rxy in rx.items(): if x != y: if rx[y] < Med: Winner = False break if Winner: wCW.append(x) try: wCW.sort() except: pass return wCW
[docs] def weakCondorcetLosers(self): """ Renders the set of decision actions x such that self.relation[x][y] <= self.valuationdomain['med'] for all y != x. """ #actions = self.actions relation = self.relation Med = self.valuationdomain['med'] wCL = [] for x,rx in relation.items(): #rx = relation[x] Loser = True for y,rxy in rx.items(): if x != y: if rx[y] > Med: Loser = False break if Loser: wCL.append(x) try: wCL.sort() except: pass return wCL
[docs] def computeCondorcetWinners(self): """ Wrapper for condorcetWinners(). """ return self.condorcetWinners()
[docs] def computeCondorcetLosers(self): """ Wrapper for condorcetLosers(). """ return self.condorcetLosers()
[docs] def condorcetWinners(self): """ Renders the set of decision actions x such that self.relation[x][y] > self.valuationdomain['med'] for all y != x. """ #actions = self.actions relation = self.relation Med = self.valuationdomain['med'] CW = [] for x,rx in relation.items(): #rx = relation[x] Winner = True for y,rxy in rx.items(): if x != y: if rxy <= Med: Winner = False break if Winner: CW.append(x) try: CW.sort() except: pass return CW
[docs] def condorcetLosers(self): """ Renders the set of decision actions x such that self.relation[x][y] < self.valuationdomain['med'] for all y != x. """ #actions = self.actions relation = self.relation Med = self.valuationdomain['med'] CL = [] for x,rx in relation.items(): #rx = relation[x] Loser = True for y,rxy in rx.items(): if x != y: if rxy >= Med: Loser = False break if Loser: CL.append(x) try: CL.sort() except: pass return CL
[docs] def forcedBestSingleChoice(self): """ Renders the set of most determined outranking singletons in self. """ actions = self.actions relation = self.relation valuationList = [] for x in actions: for y in actions: if relation[x][y] not in valuationList: valuationList.append(relation[x][y]) valuationList.sort() print('Credibility levels:', valuationList) bestSingleChoices = set( list(dict.keys(actions)) ) i=0 while bestSingleChoices != set(): current = set(bestSingleChoices) i += 1 print('i_bestSingleChoices:', i, bestSingleChoices) print('level', valuationList[i]) for x in current: #print 'x', x rx = relation[x] notBest = False for y in actions: #print 'y', y, relation[x][y] if x != y and rx[y] < valuationList[i]: notBest = True if notBest: bestSingleChoices.remove(x) print('final bestSingleChoice:', current) print('leveal of credibility:', valuationList[i-1]) return (valuationList[i-1], current)
[docs] def computeMoreOrLessUnrelatedPairs(self): """ Renders a list of more or less unrelated pairs. """ actions = self.actions relation = self.relation Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] moreOrLessUnrelatedPairs = [] for x in actions: for y in actions: if x != y: if relation[x][y] < Med and relation[x][y] > Min: if relation[y][x] < Med and relation[y][x] > Min: if ((y,x),(relation[y][x],relation[x][y])) not in moreOrLessUnrelatedPairs: moreOrLessUnrelatedPairs.append(((x,y),(relation[x][y],relation[y][x]))) return moreOrLessUnrelatedPairs
[docs] def computeUnrelatedPairs(self): """ Renders a list of more or less unrelated pairs. """ actions = self.actions relation = self.relation Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] unrelatedPairs = [] for x in actions: for y in actions: if x != y: if relation[x][y] < Med: if relation[y][x] < Med: if ((y,x),(relation[y][x],relation[x][y])) not in unrelatedPairs: unrelatedPairs.append(((x,y),(relation[x][y],relation[y][x]))) return unrelatedPairs
[docs] def closeSymmetric(self,InSite=True): """ Produces the symmetric closure of self.relation. """ actions = set(self.actions) symRelation = {} relation = self.relation for x in relation: relx = relation[x] symRelation[x] = {} for y in relx: rely = relation[y] symRelation[x][y] = max(relx[y],rely[x]) if InSite: self.relation = symRelation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() else: return symRelation
[docs] def computeIncomparabilityDegree(self,InPercents=False,Comments=False): """ Renders the incomparability degree (Decimal), i.e. the relative number of symmetric indeterminate relations of the irreflexive part of a digraph. """ from decimal import Decimal Med = self.valuationdomain['med'] na = len(self.actions) actions = [x for x in self.actions] relation = self.relation nLinks = 0 notDetLinks = 0 for i in range(na): x = actions[i] for j in range(i+1,na): y = actions[j] nLinks += 1 if relation[x][y] == Med and relation[y][x] == Med: notDetLinks += 1 try: res = Decimal(str(notDetLinks))/Decimal(str(nLinks)) except: res = Decimal('1.0') if Comments: print('Digraph instance %s has order 0' % self.name) if InPercents: res *= Decimal('100.0') if Comments: if InPercents: print('Incomparability degree (%%) of digraph <%s>:' % self.name) print(' #links x<->y y: %d, #incomparable: %d, #comparable: %d' %\ (nLinks,notDetLinks,nLinks-notDetLinks) ) print(' (#incomparable/#links) = %.1f' %(res) ) else: print('Incomparability degree of digraph <%s>:' % self.name) print(' #links: %d, #incomparable: %d, #comparable: %d' %\ (nLinks,notDetLinks,nLinks-notDetLinks) ) print(' (#incomparable/#links) = %.3f' %(res) ) return res
[docs] def computeSymmetryDegree(self,InPercents=False,Comments=False): """ Renders the symmetry degree (Decimal) of the irreflexive part of a digraph. .. note:: Empty and indeterminate digraphs are considered to be symmetric. """ from decimal import Decimal Med = self.valuationdomain['med'] na = len(self.actions) actions = [x for x in self.actions] relation = self.relation narcs = 0 nsymArc = 0 for i in range(na): x = actions[i] for j in range(i+1,na): y = actions[j] if relation[x][y] > Med: narcs += 1 if relation[y][x] > Med: nsymArc += 1 elif relation[y][x] > Med: narcs += 1 try: res = Decimal(str(nsymArc))/Decimal(str(narcs)) except: res = Decimal('1.0') if Comments: print('Digraph instance %s is empty' % self.name) if InPercents: res *= Decimal('100.0') if Comments: if InPercents: print('Symmetry degree (%%) of digraph <%s>:' % self.name) print(' #arcs x>y: %d, #symmetric: %d, #asymmetric: %d' %\ (narcs,nsymArc,narcs-nsymArc) ) print(' (#symmetric/#arcs) = %.1f' %(res) ) else: print('Symmetry degree of digraph <%s>:' % self.name) print(' #arcs x>y: %d, #symmetric: %d, #asymmetric: %d' %\ (narcs,nsymArc,narcs-nsymArc) ) print(' (#symmetric/#arcs) = %.3f' %(res) ) return res
[docs] def isSymmetric(self,Comments=False): """ True if symmetry degree == 1.0. """ sd = self.computeSymmetryDegree() if Comments: print('Symmetry degree of graph <%s> : %.2f' %(self.name,sd)) if sd == Decimal('1.0'): return True else: return False
[docs] def computeDynamicProgrammingStages(self,source,sink,Debug=False): """ Renders the discrete stages of the optimal substructure for dynamic pogramming digrahs from a given source node to a given sink sink node. Returns a list of list of action identifyers. """ stages = [] #source = 'a01' #sink = 'a22' stages.append([source]) spl = self.computeShortestPathLengths() nstages = spl[source][sink] for i in range(1,nstages+1): if Debug: print('stage: ',i) neighbours = set() for x in stages[i-1]: nbx = self.gamma[x][0] if Debug: print(x,nbx) neighbours |= nbx nbList = list(neighbours) nbList.sort() stages.append(nbList) if Debug: print(stages) if len(stages[0]) != 1 and len(stages[-1]) != 1: print('Error: the given digraph is not a valid dynamic programming digraph') else: return stages
[docs] def computeTransitivityDegree(self, InPercents=False,Comments=False): """ Renders the transitivity degree (Decimal) of a digraph. .. note:: An empty or indeterminate digraph is considered to be transitive. """ from decimal import Decimal Med = self.valuationdomain['med'] transRel = self.closeTransitive(InSite=False) ntriples = 0 nclosed = 0 for x in self.actions: for y in self.actions: if x != y: for z in self.actions: if z != x and z != y: if transRel[x][y] > Med and\ transRel[y][z] > Med: ntriples += 1 if self.relation[x][z] > Med: nclosed += 1 if (ntriples) > 0: res = Decimal(str(nclosed))/Decimal(str(ntriples)) else: res = Decimal('1.0') if InPercents: res *= Decimal('100.0') if Comments: if InPercents: print('Transitivity degree (%%) of digraph <%s>:' % self.name) print(' #triples x>y>z: %d, #closed: %d, #open: %d' %\ (ntriples,nclosed,ntriples-nclosed) ) print(' (#closed/#triples) = %.1f' %(res) ) else: print('Transitivity degree of digraph <%s>:' % self.name) print(' #triples x>y>z: %d, #closed: %d, #open: %d' %\ (ntriples,nclosed,ntriples-nclosed) ) print(' (#closed/#triples) = %.3f' %(res) ) return res
[docs] def isTransitive(self,Comments=False): """ True if transitivity degree == 1.0. """ td = self.computeTransitivityDegree() if Comments: print('Transitivity degree of graph <%s> : %.2f' %(self.name,td)) if td == Decimal('1.0'): return True else: return False
[docs] def computeSizeTransitiveClosure(self): """ Renders the size of the transitive closure of a digraph. """ import copy Med = self.valuationdomain['med'] relation = self.closeTransitive(InSite=False) n1 = 0 for rx in relation.values(): for rxy in rx.values(): if rxy > Med: n1 += 1 return n1
[docs] def closeTransitive(self,Reverse=False,InSite=True,Comments=False): """ Produces the transitive closure of self.relation. *Parameters*: - If *Reverse* == True (False default) all transitive links are dropped, otherwise all transitive links are closed with min[r(x,y),r(y,z)]; - If *Insite* == False (True by default) the methods return a modified copy of self.relation without altering the original self.relation, otherwise self.relation is modified. """ from copy import deepcopy actions = set(self.actions) Med = self.valuationdomain['med'] Change = True i = 0 currRelation = deepcopy(self.relation) while Change: Change = False curriRelation = deepcopy(currRelation) i += 1 for x in actions: for y in actions: if x != y: for z in actions: if z != x and z != y: if min(curriRelation[y][x],curriRelation[x][z]) > Med \ and curriRelation[y][z] <= Med: currRelation[y][z] =\ min(curriRelation[y][x],curriRelation[x][z]) Change = True if Comments: print('iterations: %d' %i ) if Reverse: Change = True i = 0 while Change: Change = False i += 1 curriRelation = deepcopy(currRelation) for x in actions: for y in actions: if x != y: for z in actions: if z != x and z != y: if min(curriRelation[y][x],curriRelation[x][z]) > Med \ and curriRelation[y][z] > Med: currRelation[y][z] = -curriRelation[y][z] Change = True #relation[y][z] =\ #-(max(relation[y][z],min(relation[y][x],relation[x][z]))) if Comments: print('reverse iterations: %d' %i) if InSite: self.relation = deepcopy(currRelation) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() else: return currRelation
[docs] def convertValuation2Integer(self,InSite=True,Comments=False): """ Converts the self.relation valuation to integer values by converting the Decimals to Fractions and multiply by the least commun multiple of the fraction denominators. *Parameters*: - If *Insite* == False (True by default) the method returns a modified copy of self.relation without altering the original self.relation, otherwise self.relation and self.valuationdomain is modified. """ from copy import deepcopy from fractions import Fraction from math import lcm actions = [x for x in self.actions] n = len(actions) try: Hiv = self.valuationdomain['hasIntegerValuation'] except: self.valuationdomain['hasIntegerValuation'] = False if not self.valuationdomain['hasIntegerValuation']: t = [] for i in range(n): x = actions[i] for j in range(i+1,n): y = actions[j] t.append(Fraction(self.relation[x][y]).as_integer_ratio()[1]) # requires Python3.9+ valint = lcm(*t) # before Python3.9 uncomment below ! # from functools import reduce # valint = reduce(lambda x, y: lcm(x,y),t,t[0]) if Comments: print('Leat common multiple of denominators is ',valint) if InSite: if Comments: print('Recoding valuation to [%d,%d]' % (-valint,valint) ) self.recodeValuation(-valint,valint,ndigits=0) self.valuationdomain['hasIntegerValuation'] = True else: if Comments: print('renders the recode sel.relation to [%d,%d]' % (-valint,valint) ) integerRelation = {} for x in actions: integerRelation[x] = {} for y in actions: integerRelation[x][y] = Decimal('%.0f' % (self.relation[x][y] * Decimal(str(valint)) )) return integerRelation else: print('The relation is already integer-valued')
[docs] def isCyclic(self, Debug=False): """ checks the cyclicity of self.relation by checking for a reflexive loop in its transitive closure- .. warning:: self.relation is supposed to be irreflexive ! """ import copy Med = self.valuationdomain['med'] if Debug: print(Med) actions = set(self.actions) relation = copy.deepcopy(self.relation) isCyclic = False for x in actions: for y in actions: for z in actions: relation[y][z] = max(relation[y][z],min(relation[y][x],relation[x][z])) if Debug: for x in actions: print('x, relation[x][x]', x, relation[x][x]) for x in actions: if relation[x][x] > Med: isCyclic = True break return isCyclic
[docs] def isWeaklyComplete(self, Debug=False): """ checks the weakly completeness property of self.relation by checking for the absence of a link between two actions!! .. warning:: The reflexive links are ignored !! """ Med = self.valuationdomain['med'] if Debug: print('Med = ', Med) listActions = [x for x in self.actions] n = len(listActions) relation = self.relation isWeaklyComplete = True for i in range(n): x = listActions[i] for j in range(i+1,n): y = listActions[j] if relation[x][y] < Med and relation[y][x] < Med: isWeaklyComplete = False if Debug: print('x,y,relation[x][y],relation[y][x]',\ x, y, relation[x][y], relation[y][x]) break if not isWeaklyComplete: break return isWeaklyComplete
[docs] def isComplete(self, Debug=False): """ checks the completeness property of self.relation by checking for the absence of a link between two actions!! .. warning:: The reflexive links are ignored !! """ Med = self.valuationdomain['med'] if Debug: print('Med = ', Med) listActions = [x for x in self.actions] n = len(listActions) relation = self.relation isComplete = True for i in range(n): x = listActions[i] for j in range(i+1,n): y = listActions[j] if relation[x][y] <= Med and relation[y][x] <= Med: isComplete = False if Debug: print('x,y,relation[x][y],relation[y][x]',\ x, y, relation[x][y], relation[y][x]) break if not isComplete: break return isComplete
[docs] def isIntegerValued(self, Debug=False): """ Checks whether the decimal valuation of self is integer-valued be using the as_integer_ratio() method of a Decimal giving a tuple (numerator,denominator). If denominator == 1, the number is an integer. """ actions = self.actions relation = self.relation IntegerValued = True for x in actions: for y in actions: ratio = relation[x][y].as_integer_ratio() if Debug: print(x,y,relation[x][y],ratio) if ratio[1] != 1: IntegerValued = False if not IntegerValued: break if not IntegerValued: break return IntegerValued
[docs] def isOutrankingDigraph(self,Comments=True,Debug=False): """ Checks the outranking digraph characteristic condition (3.3). relation[x][y] + relation[y][x)[y] >= 0.0 .. warning:: The reflexive links are ignored and the valuation must be bipolar !! """ from copy import deepcopy #normalizing the valuation domain g = deepcopy(self) g.recodeValuation() if Debug: print(g.valuationdomain) Med = g.valuationdomain['med'] # if Med != Decimal('0.0'): # print("Error: the digraph's valuation must be bipolar !!") # print(self.valuationdomain) # return listActions = [x for x in g.actions] n = len(listActions) relation = g.relation IsOutrankingDigraph = True for i in range(n): x = listActions[i] for j in range(i+1,n): y = listActions[j] if Debug: print('x,y,relation[x][y],relation[y][x]',\ x, y, relation[x][y], relation[y][x]) if (relation[x][y] + relation[y][x]) < Med: IsOutrankingDigraph = False if Comments or Debug: print('x,y,relation[x][y],relation[y][x]',\ x, y, relation[x][y], relation[y][x]) print('Not a valid outranking valuation') return IsOutrankingDigraph
[docs] def isStrictOutrankingDigraph(self,Comments=True,Debug=False): """ Checks the strict outranking digraph characteristic condition (3.1). -(relation[x][y] + relation[y][x]) <= 0.0 , x != y .. warning:: The reflexive links are ignored and the valuation must be bipolar !! """ from copy import deepcopy # normalizing the characteristic values g = deepcopy(self) g.recodeValuation() if Debug: print(g.valuationdomain) Med = g.valuationdomain['med'] # if Med != Decimal('0.0'): # print("Error: the digraph's valuation must be bipolar !!") # print(self.valuationdomain) # return listActions = [x for x in self.actions] n = len(listActions) relation = g.relation IsStrictOutrankingDigraph = True for i in range(n): x = listActions[i] for j in range(i+1,n): y = listActions[j] if Debug: print('x,y,relation[x][y],relation[y][x]',\ x, y, relation[x][y], relation[y][x]) if relation[x][y] + relation[y][x] > Med: IsStrictOutrankingDigraph = False if Comments or Debug: print('x,y,relation[x][y],relation[y][x]',\ x, y, relation[x][y], relation[y][x]) print('Not a valid strict outranking valuation') return IsStrictOutrankingDigraph
[docs] def isAsymmetricIndeterminate(self, Debug=False): """ Checks the self.relation for assymmetric indeterminateness!! .. warning:: The reflexive links are ignored !! """ Med = self.valuationdomain['med'] if Debug: print('Med = ', Med) listActions = [x for x in self.actions] n = len(listActions) relation = self.relation AsymmetricIndeterminate = False for i in range(n): x = listActions[i] for j in range(i+1,n): y = listActions[j] if (relation[x][y] == Med and relation[y][x] != Med) or\ (relation[x][y] != Med and relation[y][x] == Med): AsymmetricIndeterminate = True if Debug: print('x,y,relation[x][y],relation[y][x]',\ x, y, relation[x][y], relation[y][x]) return AsymmetricIndeterminate
[docs] def automorphismGenerators(self): """ Adds automorphism group generators to the digraph instance. .. note:: Dependency: Uses the dreadnaut command from the nauty software package. See https://www3.cs.stonybrook.edu/~algorith/implement/nauty/implement.shtml On Ubuntu Linux: ...$ sudo apt-get install nauty """ import os Name = self.name self.savedre(Name) aindex = self.aindex arevindex = {} for i in aindex: arevindex[str(aindex[i])] = i print(arevindex) File0 = Name+'.dre' File1 = Name+'.auto' print('# automorphisms extraction from dre file #') print('# Using input file: ' + File0) String2 = "echo '<"+File0+' -m p >'+File1+" x' | dreadnaut" print(String2) os.system(String2) NoError = True try: f1 = open(File1,'r') except: print('The input file: ', File1,' could not be found!') print("Be sure that nauty's dreadnaut programm is available!") NoError = False if NoError: permutations = {} t = f1.readline() nl = 0 while t[:1] != '': nl += 1 if t[:1] == ' ': ts = f1.readline() while ts[:2] == ' ': suite = 1 t = t + ts ts = f1.readline() permutation = t.split() print('# permutation = '+ str(nl)+str(permutation)) permutations[str(nl)] = {} for i in range(len(permutation)): permutations[str(nl)][str(arevindex[str(i+1)])] = str(arevindex[str(permutation[i])]) t = ts else: #print '# ', t grpsize = '' for i in range(len(t)): #print t[i], if t[i] == '=': #print 'ok' #grpsize = '' for j in range(i+1,len(t)): if t[j] != ';': #print t[j] grpsize += t[j] else: break break #print eval(grpsize) #t = f1.readline break f1.close() self.reflections = {} self.permutations = permutations self.automorphismGroupSize = eval(grpsize) return True else: print('nauty software not installed') return False
[docs] def showAutomorphismGenerators(self): """ Renders the generators of the automorphism group. """ print('*---- Automorphism group generators ----') NoError = True try: reflections = self.reflections permutations = self.permutations except: print('No permutations or reflections defined yet !!') NoError = False if NoError: print('Permutations') for g in permutations: print(self.permutations[g]) print('Reflections') for g in reflections: print(self.reflexions[g]) else: print('Run self.automorphismGenerators()')
[docs] def showOrbits(self,InChoices,withListing=True): """ Prints the orbits of Choices along the automorphisms of the Digraph instance. Example Python session for computing the non isomorphic MISs from the 12-cycle graph: >>> from digraphs import * >>> c12 = CirculantDigraph(order=12,circulants=[1,-1]) >>> c12.automorphismGenerators() ... Permutations {'1': '1', '2': '12', '3': '11', '4': '10', '5': '9', '6': '8', '7': '7', '8': '6', '9': '5', '10': '4', '11': '3', '12': '2'} {'1': '2', '2': '1', '3': '12', '4': '11', '5': '10', '6': '9', '7': '8', '8': '7', '9': '6', '10': '5', '11': '4', '12': '3'} Reflections {} >>> print('grpsize = ', c12.automorphismGroupSize) grpsize = 24 >>> c12.showMIS(withListing=False) *--- Maximal independent choices ---* number of solutions: 29 cardinality distribution card.: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] freq.: [0, 0, 0, 0, 3, 24, 2, 0, 0, 0, 0, 0, 0] Results in c12.misset >>> c12.showOrbits(c12.misset,withListing=False) ... *---- Global result ---- Number of MIS: 29 Number of orbits : 4 Labelled representatives: 1: ['2','4','6','8','10','12'] 2: ['2','5','8','11'] 3: ['2','4','6','9','11'] 4: ['1','4','7','9','11'] Symmetry vector stabilizer size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, ...] frequency : [0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, ...] *Figure*: The symmetry axes of the non isomorphic MISs of the 12-cycle: .. image:: c12.png :width: 400 px :align: center :alt: The 4 non isomorphic MIS of the 12-cycle graph *Reference*: R. Bisdorff and J.L. Marichal (2008). Counting non-isomorphic maximal independent sets of the n-cycle graph. *Journal of Integer Sequences*, Vol. 11 Article 08.5.7 (`openly accessible here <https://www.cs.uwaterloo.ca/journals/JIS/VOL11/Marichal/marichal.html>`_) """ try: reflections = self.reflections permutations = self.permutations NoError = True except: print('No permutations or reflections defined yet !!') print('Run self.automorphismGenerators()') NoError = False if NoError: Choices = InChoices.copy() print('*--- Isomorphic reduction of choices') Iso = set() v = [0 for i in range(1,self.automorphismGroupSize + 1)] print('Number of choices:', len(Choices)) while Choices != set(): sCur = Choices.pop() print() print('current representative: ',sCur) print('length : ', len(sCur)) IsosCur = set([sCur]) Isos = set() while IsosCur != Isos: Isos = IsosCur.copy() IsosRes = IsosCur.copy() for s in IsosCur: for g in reflections: cur = s for a in reflections[g]: if (a[0] in cur) and a[1] not in cur: cur = cur - set([a[0]]) cur = cur | set([a[1]]) else: if a[1] in cur and a[0] not in cur: cur = cur - set([a[1]]) cur = cur | set([a[0]]) IsosRes.add(cur) IsosCur = IsosRes.copy() for s in IsosCur: for g in permutations: cur = frozenset() for x in s: cur = cur | set([permutations[g][str(x)]]) IsosRes = IsosRes | set([cur]) IsosCur = IsosRes.copy() Iso.add(sCur) niso = len(Isos) print('number of isomorph choices', niso) v[(self.automorphismGroupSize//niso)-1] += 1 if withListing: print('isormorph choices') for ch in Isos: print(list(ch)) print('Number of choices before : ', len(Choices) + 1) Choices = Choices - Isos print('Number of choices after : ', len(Choices)) print() print('*---- Global result ----') print('Number of choices: ', len(InChoices)) print('Number of orbits : ', len(Iso)) print('Labelled representatives:') for ch in Iso: print(list(ch)) print() print(' Symmetry vector') print('stabilizer size : ', list(range(1,self.automorphismGroupSize + 1))) print('frequency : ', v) self.orbits = Iso
[docs] def showOrbitsFromFile(self,InFile,withListing=True): """ Prints the orbits of Choices along the automorphisms of the digraph self by reading in the 0-1 misset file format. See the :py:func:`digraphs.Digraph.readPerrinMisset` method. """ try: reflections = self.reflections permutations = self.permutations except: print('No permutations or reflections defined yet !!') print('Run self.automorphismGenerators()') return try: fi = open(InFile,'r') except: print('File %s not found ?' % InFile) return actions = [x for x in self.actions] print('*--- Isomorphic reduction of choices') Iso = set() misset = set() v = [0 for i in range(1,self.order + 1)] while 1: line = fi.readline() if not line: break sCur = set() for i in range(len(line)): if line[i] == '1': sCur.add(actions[i]) if sCur not in misset: print('current representative: ',sCur) print('length : ', len(sCur)) IsosCur = set([frozenset(sCur)]) Isos = set() while IsosCur != Isos: Isos = IsosCur.copy() IsosRes = IsosCur.copy() for s in IsosCur: for g in reflections: cur = s for a in reflections[g]: if (a[0] in cur) and a[1] not in cur: cur = cur - set([a[0]]) cur = cur | set([a[1]]) else: if a[1] in cur and a[0] not in cur: cur = cur - set([a[1]]) cur = cur | set([a[0]]) IsosRes.add(cur) IsosCur = IsosRes.copy() for s in IsosCur: for g in permutations: cur = frozenset() for x in s: cur = cur | set([permutations[g][x]]) IsosRes = IsosRes | set([cur]) IsosCur = IsosRes.copy() Iso = Iso | set([frozenset(sCur)]) niso = len(Isos) print('number of isomorph choices', niso) v[((2*self.order)//niso)-1] += 1 if withListing: print('isormorph choices') for ch in Isos: print(list(ch)) print('Number of choices before : ', len(misset) + 1) misset = misset | Isos print('Number of choices after : ', len(misset)) fi.close() print() print('*---- Global result ----') print('Labelled representatives:') for ch in Iso: print(list(ch)) print() print('Number of choices: ', len(misset)) print('Number of orbits : ', len(Iso)) print('symmetry vector : ', list(range(1,self.order + 1))) print('frequency : ', v) self.orbits = Iso
# def _readPerrinMisset(self,file): # """ # read method for 0-1-char-coded MISs from perrinMIS.c curd.dat file. # """ # NoError = True # try: # f1 = open(file,'r') # except: # NoError = False # print('The input file: ', file,' could not be found ?') # if NoError: # actions = [x for x in self.actions] # nl = 0 # misset = set() # while 1: # line = f1.readline() # if not line: break # nl += 1 # mis = set() # for i in range(len(line)): # if ord(line[i]) == 1: # mis.add(actions[i]) # #print mis # misset = misset | set([frozenset(mis)]) # #print 'Reading ' + str(nl) + ' MISs.' # self.misset = misset
[docs] def readPerrinMisset(self,file='curd.dat'): """ read method for 0-1-char-coded MISs by default from the perrinMIS.c curd.dat result file. """ try: fi = open(file,'r') noError = True except: noError = False print('The input file: ', file,' could not be found ?') if noError: actions = [x for x in self.actions] nl = 0 misset = set() for line in fi.readlines(): if not line: break nl += 1 mis = set() for i in range(len(line)): if line[i] == '1': mis.add(actions[i]) #print mis misset = misset | set([frozenset(mis)]) fi.close() #print 'Reading ' + str(nl) + ' MISs.' self.misset = misset
[docs] def computeOrbit(self,choice,withListing=False): """ renders the set of isomorph copies of a choice following the automorphism of the digraph self """ try: reflections = self.reflections permutations = self.permutations if withListing: print('*- ----------------"') print('Compute orbit of choice: ',choice) print('follwoing automorphisms of digraph: ', self.name) print('Automorphism group size: ', self.automorphismGroupSize) print('Generators:') print('Reflections: ', reflections) print('Permutations: ', permutations) IsosCur = set([choice]) Isos = set() while IsosCur != Isos: Isos = IsosCur.copy() IsosRes = IsosCur.copy() for s in IsosCur: for g in reflections: cur = s for a in reflections[g]: if (a[0] in cur) and a[1] not in cur: cur = cur - set([a[0]]) cur = cur | set([a[1]]) else: if a[1] in cur and a[0] not in cur: cur = cur - set([a[1]]) cur = cur | set([a[0]]) IsosRes.add(cur) IsosCur = IsosRes.copy() for s in IsosCur: for g in permutations: cur = frozenset() for x in s: cur = cur | set([permutations[g][x]]) IsosRes = IsosRes | set([cur]) IsosCur = IsosRes.copy() if withListing: print('Orbit size: ', len(Isos)) print('List of isormorph choices') for ch in Isos: print(list(ch)) return Isos except: print('No permutations or reflections defined yet !!') print('Run self.automorphismGenerators()')
[docs] def showActions(self): """ presentation methods for digraphs actions """ print('*----- show digraphs actions --------------*') actionsList = [x for x in self.actions] actionsList.sort() for x in actionsList: print('key: ',x) try: print(' short name:',self.actions[x]['shortName']) except: pass print(' name: ',self.actions[x]['name']) print(' comment: ',self.actions[x]['comment']) print()
[docs] def showShort(self): """ concise presentation method for genuine digraphs. """ print('*----- show short --------------*') print('Digraph :', self.name) print('Actions :', self.actions) print('Valuation domain :', self.valuationdomain) self.components()
[docs] def showAll(self): """Detailed show method for genuine digraphs.""" print('*----- show detail -------------*') print('Digraph :', self.name) print('*---- Actions ----*') #actionsList = [x for x in self.actions] actionsList = [] for x in self.actions: if isinstance(x,frozenset): actionsList += [self.actions[x]['name']] else: actionsList += [str(x)] actionsList.sort() print(actionsList) print('*---- Characteristic valuation domain ----*') print(self.valuationdomain) self.showRelationTable() self.showComponents() gamma = self.gammaSets() notGamma = self.notGammaSets() print('Neighborhoods:') print(' Gamma :') for x in gamma: print('\'%s\': in => %s, out => %s' % (x,gamma[x][1],gamma[x][0])) print(' Not Gamma :') for x in notGamma: print('\'%s\': in => %s, out => %s' % (x,notGamma[x][1],notGamma[x][0]))
[docs] def showNeighborhoods(self): """ Lists the gamma and the notGamma function of self. """ gamma = self.gammaSets() notGamma = self.notGammaSets() print('Neighborhoods:') print(' Gamma :') for x in gamma: print('\'%s\': in => %s, out => %s' % (x,gamma[x][1],gamma[x][0])) print(' Not Gamma :') for x in notGamma: print('\'%s\': in => %s, out => %s' % (x,notGamma[x][1],notGamma[x][0]))
[docs] def showRelation(self): """ prints the relation valuation in ##.## format. """ print('* ---- Relation -----', end=' ') actionsList = [] for x in self.actions: if isinstance(x,frozenset): actionsList += [(self.actions[x]['name'],x)] else: actionsList += [(x,x)] #actionsList = [x for x in self.actions] actionsList.sort() try: hasIntegerValuation = self.valuationdomain['hasIntegerValuation'] except KeyError: hasIntegerValuation = False for x in actionsList: print() for y in actionsList: if hasIntegerValuation: print('('+str(x[0])+', '+str(y[0])+') = '+' % .2f ' % (self.relation[x[1]][y[1]])) else: print('('+str(x[0])+', '+str(y[0])+') = '+' %d ' % (self.relation[x[1]][y[1]])) print()
[docs] def showRelationMap(self,symbols=None,rankingRule="Copeland",fromIndex=None,toIndex=None,actionsList=None): """ Prints on the console, in text map format, the location of certainly validated and certainly invalidated outranking situations. By default, symbols = {'max':'┬','positive': '+', 'median': ' ', 'negative': '-', 'min': '┴'} The default ordering of the output is following the Copeland ranking rule from best to worst actions. Further available ranking rule is the 'NetFlows' net flows rule. Example:: >>> from outrankingDigraphs import * >>> t = RandomCBPerformanceTableau(numberOfActions=25,seed=1) >>> g = BipolarOutrankingDigraph(t,Normalized=True) >>> gcd = ~(-g) # strict outranking relation >>> gcd.showRelationMap(rankingRule="NetFlows") - ++++++++ +++++┬+┬┬+ - - + +++++ ++┬+┬+++┬++ ┴+ ┴ + ++ ++++┬+┬┬++┬++ - ++ - ++++-++++++┬++┬+ - - - ++- ┬- + -+┬+-┬┬ ----- - -┬┬┬-+ -┬┬ ---- --+-+++++++++++++ -- --+- --++ ++ +++-┬+-┬┬ ---- - -+-- ++--+++++ + ----- ++- --- +---++++┬ + -- -- ---+ -++-+++-+ +-++ -- --┴---+ + +-++-+ - + ---- ┴---+-- ++--++++ - + --┴┴-- --- - --+ --┬┬ ---------+--+ ----- +-┬┬ -┴---┴- ---+- + ---+++┬ + -┴┴--┴---+--- ++ -++--+++ -----┴--- ---+-+- ++---+ -┴┴--------------- --++┬ --┴---------------- --+┬ ┴--┴┴ -┴--┴┴-┴ --++ ++-+ ----┴┴--------------- - ┴┴┴----+-┴+┴---┴-+---+ ┴+ ┴┴-┴┴┴-┴- - -┴┴---┴┴+ ┬ - ----┴┴-┴-----┴┴--- - -- Ranking rule: NetFlows >>> """ if symbols is None: symbols = {'max':'┬','positive': '+', 'median': ' ', 'negative': '-', 'min': '┴'} if actionsList is None: if rankingRule == "Copeland": ranking = self.computeCopelandRanking() elif rankingRule == "NetFlows": ranking = self.computeNetFlowsRanking() # elif rankingRule == "RankedPairs": # ranking = self.computeRankedPairsRanking() else: rankingRule = "Alphabetic" ranking = [x for x in self.actions] ranking.sort() else: ranking = actionsList if fromIndex is None: fromIndex = 0 if toIndex is None: toIndex = len(ranking) relation = self.relation Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] Min = self.valuationdomain['min'] for x in ranking[fromIndex:toIndex]: rx = relation[x] pictStr = '' for y in ranking[fromIndex:toIndex]: if rx[y] == Max: pictStr += symbols['max'] elif rx[y] == Min: pictStr += symbols['min'] elif rx[y] > Med: pictStr += symbols['positive'] elif rx[y] ==Med: pictStr += symbols['median'] elif rx[y] < Med: pictStr += symbols['negative'] print(pictStr) if actionsList is None: print('Ranking rule: %s' % rankingRule) else: print('List of actions provided.')
[docs] def showRelationTable(self,Sorted=True, rankingRule=None, IntegerValues=False, actionsSubset= None, relation=None, ndigits=2, ReflexiveTerms=True, fromIndex=None, toIndex=None): """ Prints the relation valuation in actions X actions table format. Copeland and NetFlows rankings my be used. """ if actionsSubset is None: if rankingRule is None: actions = self.actions else: if rankingRule == 'Copeland': computeRanking = self.computeCopelandRanking elif rankingRule == 'NetFlows': computeRanking = self.computeNetFlowsRanking actions = computeRanking() Sorted = False else: actions = actionsSubset if relation is None: relation = self.relation print('* ---- Relation Table -----\n', end=' ') print(' S | ', end=' ') actionKeys = [x for x in actions] if fromIndex is None: fromIndex = 0 if toIndex is None: toIndex = len(actionKeys) actionsList = [] for x in actionKeys[fromIndex:toIndex]: if isinstance(x,frozenset): try: actionsList += [(actions[x]['shortName'],x)] except: actionsList += [(actions[x]['name'],x)] else: actionsList += [(str(x),x)] if Sorted: actionsList.sort() #print actionsList #actionsList.sort() try: hasIntegerValuation = self.valuationdomain['hasIntegerValuation'] except KeyError: hasIntegerValuation = IntegerValues for x in actionsList: print("'"+x[0]+"'\t ", end=' ') print('\n------|-------------------------------------------') for x in actionsList: print(" '"+x[0]+"' | ", end=' ') for y in actionsList: if x != y: if hasIntegerValuation: print('%d\t' % (relation[x[1]][y[1]]), end=' ') else: formatString = '%%2.%df\t' % ndigits print(formatString % (relation[x[1]][y[1]]), end=' ') else: if ReflexiveTerms: if hasIntegerValuation: print('%d\t' % (relation[x[1]][y[1]]), end=' ') else: formatString = '%%2.%df\t' % ndigits print(formatString % (relation[x[1]][y[1]]), end=' ') else: formatString = ' - \t' print(formatString, end=' ') print('') if hasIntegerValuation: print('Valuation domain: [%d;%+d]'% (self.valuationdomain['min'], self.valuationdomain['max'])) else: formatString = 'Valuation domain: [%%2.%df;%%2.%df]' % (ndigits,ndigits) print( formatString % (self.valuationdomain['min'], self.valuationdomain['max'])) if rankingRule is not None: print('RankingRule = %s' % rankingRule)
[docs] def showHTMLRelationMap(self,actionsList=None, rankingRule='Copeland', Colored=True, tableTitle='Relation Map', relationName='r(x S y)', symbols=['+','&middot;','&nbsp;','&#150;','&#151'], fromIndex=None, toIndex=None, htmlFileName=None, ): """ Launches a browser window with the colored relation map of self. See corresponding Digraph.showRelationMap() method. When *htmlFileName* parameter is set to a string value, a html file with that name will be stored in the current working directory. By default, a temporary file named: tmp*.html will be generated instead in the current working directory. Example:: >>> from outrankingDigraphs import * >>> t = RandomCBPerformanceTableau(numberOfActions=25,seed=1) >>> g = BipolarOutrankingDigraph(t,Normalized=True) >>> gcd = ~(-g) # strict outranking relation >>> gcd.showHTMLRelationMap(rankingRule="NetFlows") .. image:: relationMap.png :alt: Browser view of a relation map :width: 600 px :align: center """ import webbrowser if htmlFileName == None: from tempfile import NamedTemporaryFile fileName = (NamedTemporaryFile(suffix='.html',delete=False,dir='.')).name else: from os import getcwd fileName = getcwd()+'/'+htmlFileName fo = open(fileName,'w') fo.write(self._htmlRelationMap(actionsSubset=actionsList, rankingRule=rankingRule, Colored=Colored, tableTitle=tableTitle, symbols=symbols, ContentCentered=True, relationName=relationName, fromIndex=fromIndex, toIndex=toIndex)) fo.close() url = 'file://'+fileName webbrowser.open(url,new=2)
def _htmlRelationMap(self,tableTitle='Relation Map', relationName='r(x R y)', actionsSubset= None, rankingRule='Copeland', symbols=['+','&middot;','&nbsp;','-','_'], Colored=True, ContentCentered=True, fromIndex=None, toIndex=None): """ renders the relation map in actions X actions html table format. """ Med = self.valuationdomain['med'] Min = self.valuationdomain['min'] Max = self.valuationdomain['max'] # construct ranking and actionsList if actionsSubset is not None: ranking = actionsSubset else: if rankingRule == "Copeland": ranking = self.computeCopelandRanking() elif rankingRule == "NetFlows": ranking = self.computeNetFlowsRanking() # elif rankingRule == "RankedPairs": # ranking = self.computeRankedPairsRanking() else: rankingRule = "Alphabetic" ranking = [x for x in self.actions] ranking.sort() if fromIndex is None: fromIndex = 0 if toIndex is None: toIndex = len(ranking) actionsList = [] actions = self.actions for x in ranking[fromIndex:toIndex]: if isinstance(x,frozenset): try: actionsList += [(actions[x]['shortName'],x)] except KeyError: actionsList += [(actions[x]['name'],x)] else: actionsList += [(str(x),str(x))] # construct html text s = '<!DOCTYPE html><html><head>\n' s += '<meta charset="UTF-8">\n' s += '<title>%s</title>\n' % 'Digraph3 relation map' s += '<style type="text/css">\n' if ContentCentered: s += 'td {text-align: center;}\n' s += 'td.na {color: rgb(192,192,192);}\n' s += '</style>\n' s += '</head>\n<body>\n' s += '<h1>%s</h1>' % tableTitle s += '<h2>Ranking rule: %s</h2>' % rankingRule s += '<table border="0">\n' if Colored: s += '<tr bgcolor="#9acd32"><th>%s</th>\n' % relationName else: s += '<tr><th>%s</th>' % relationName for x in actionsList: if Colored: s += '<th bgcolor="#FFF79B">%s</th>\n' % (x[0]) else: s += '<th>%s</th\n>' % (x[0]) s += '</tr>\n' for x in actionsList: s += '<tr>' if Colored: s += '<th bgcolor="#FFF79B">%s</th>\n' % (x[0]) else: s += '<th>%s</th>\n' % (x[0]) for y in actionsList: if Colored: if self.relation[x[1]][y[1]] == Max: s += '<td bgcolor="#66ff66"><b>%s</b></td>\n' % symbols[0] elif self.relation[x[1]][y[1]] > Med: s += '<td bgcolor="#ddffdd">%s</td>' % symbols[1] elif self.relation[x[1]][y[1]] == Min: s += '<td bgcolor="#ff6666"><b>%s</b></td\n>' % symbols[4] elif self.relation[x[1]][y[1]] < Med: s += '<td bgcolor="#ffdddd">%s</td>\n' % symbols[3] else: #s += '<td bgcolor="#ffffff">%s</td>\n' % symbols[2] s += '<td class="na">%s</td>\n' % symbols[2] else: if self.relation[x[1]][y[1]] == Max: s += '<td><b>%s</b></td>\n' % symbols[0] elif self.relation[x[1]][y[1]] > Med: s += '<td>%s</td>\n' % symbols[1] elif self.relation[x[1]][y[1]] == Min: s += '<td><b>%s</b></td>\n' % symbols[4] elif self.relation[x[1]][y[1]] < Med: s += '<td>%s</td>\n' % symbols[3] else: s += '<td>%s</td>\n' % symbols[2] s += '</tr>' s += '</table>\n' # legend s += '<span style="font-size: 75%">\n' s += '<table border="1"><tr><th colspan="2"><i>Semantics</i></th></tr>\n' if Colored: s += '<tr><td bgcolor="#66ff66" align="center">%s</td><td>certainly valid</td></tr>\n' % symbols[0] s += '<tr><td bgcolor="#ddffdd" align="center">%s</td><td>valid</td></tr>\n' % symbols[1] s += '<tr><td>%s</td><td>indeterminate</td></tr>\n' % symbols[2] s += '<tr><td bgcolor="#ffdddd" align="center">%s</td><td>invalid</td></tr>\n' % symbols[3] s += '<tr><td bgcolor="#ff6666" align="center">%s</td><td>certainly invalid</td></tr>\n' % symbols[4] s += '</table>\n' else: s += '<tr><td align="center">%s</td><td>certainly valid</td></tr>\n' % symbols[0] s += '<tr><td align="center">%s</td><td>valid</td></tr>\n' % symbols[1] s += '<tr><td align="center">%s</td><td>indeterminate</td></tr>\n' % symbols[2] s += '<tr><td align="center">%s</td><td>invalid</td></tr>\n' % symbols[3] s += '<tr><td align="center">%s</td><td>certainly invalid</td></tr>\n' % symbols[4] s += '</table>\n' s += '</span>\n' # html footer s += '</body>\n' s += '</html>\n' return s
[docs] def showHTMLRelationHeatmap(self,actionsList=None, rankingRule='NetFlows', colorLevels=7, tableTitle='Relation Heatmap', relationName='r(x S y)', ndigits=2, fromIndex=None, toIndex=None, htmlFileName=None, ): """ Launches a browser window with the colored relation map of self. See corresponding :py:class:`~digraphs.Digraph.showHTMLRelationMap` method. The *colorLevels* parameter may be set to 3, 5, 7 (default) or 9. When the *actionsList* parameter is *None* (default), the digraphs actions list may be ranked with the *rankingRule* parameter set to the 'Copeland' (default) or to the 'Netlows' ranking rule. When the *htmlFileName* parameter is set to a string value 'xxx', a html file named 'xxx.html' will be generated in the current working directory. Otherwise, a temporary file named 'tmp*.html' will be generated there. Example:: >>> from outrankingDigraphs import * >>> t = RandomCBPerformanceTableau(numberOfActions=25,seed=1) >>> g = BipolarOutrankingDigraph(t,ndigits=2) >>> gcd = ~(-g) # strict outranking relation >>> gcd.showHTMLRelationHeatmap(colorLevels=7,ndigits=2) .. image:: relationHeatmap.png :alt: Browser view of a relation map :width: 600 px :align: center """ import webbrowser if htmlFileName == None: from tempfile import NamedTemporaryFile fileName = (NamedTemporaryFile(suffix='.html',delete=False,dir='.')).name else: from os import getcwd fileName = getcwd()+'/'+htmlFileName fo = open(fileName,'w') fo.write(self._htmlRelationHeatmap(actionsSubset=actionsList, rankingRule=rankingRule, colorLevels=colorLevels, tableTitle=tableTitle, ndigits=ndigits, ContentCentered=True, relationName=relationName, fromIndex=fromIndex, toIndex=toIndex)) fo.close() url = 'file://'+fileName webbrowser.open(url,new=2)
def _htmlRelationHeatmap(self,tableTitle='Relation Heatmap', relationName='r(x R y)', actionsSubset= None, rankingRule='Copeland', colorLevels=7, ContentCentered=True, ndigits=2, fromIndex=None, toIndex=None, Debug=False): """ renders the relation map in actions X actions html table format. """ Med = self.valuationdomain['med'] Min = self.valuationdomain['min'] Max = self.valuationdomain['max'] # set colo palette brewerRdYlGn9Colors = [(Decimal('0.125'),'"#D53E4F"'), (Decimal('0.25'),'"#F46D43"'), (Decimal('0.375'),'"#FDAE61"'), (Decimal('0.499999'),'"#FEE08B"'), (Decimal('0.5'),'"#FFFFFF"'), (Decimal('0.625'),'"#D9EF8B"'), (Decimal('0.75'),'"#A6D96A"'), (Decimal('0.875'),'"#65BD63"'), (Decimal('1.000'),'"#1A9850"')] # _brewerRdYlGn7Colors = [ # (Decimal('0.1429'),'"#F46D43"'), # (Decimal('0.2857'),'"#FDAE61"'), # (Decimal('0.4286'),'"#FEE08B"'), # (Decimal('0.5714'),'"#FFFFBF"'), # (Decimal('0.7143'),'"#D9EF8B"'), # (Decimal('0.8571'),'"#A6D96A"'), # (Decimal('1.0000'),'"#65BD63"') # ] brewerRdYlGn7Colors = [ (Decimal('0.166666'),'"#F46D43"'), (Decimal('0.333333'),'"#FDAE61"'), (Decimal('0.499999'),'"#FEE08B"'), (Decimal('0.5'),'"#FFFFFF"'), (Decimal('0.666666'),'"#D9EF8B"'), (Decimal('0.833333'),'"#A6D96A"'), (Decimal('1.0000'),'"#65BD63"') ] brewerRdYlGn5Colors = [ (Decimal('0.25'),'"#FDAE61"'), (Decimal('0.499999'),'"#FEE08B"'), (Decimal('0.5'),'"#FFFFFF"'), (Decimal('0.75'),'"#D9EF8B"'), (Decimal('1.0'),'"#A6D96A"') ] brewerRdYlGn3Colors = [ (Decimal('0.49999'),'"#FEE08B"'), (Decimal('0.5'),'"#FFFFFF"'), (Decimal('1.0'),'"#D9EF8B"'), ] if colorLevels is None: colorLevels = 5 if colorLevels == 7: colorPalette = brewerRdYlGn7Colors elif colorLevels == 9: colorPalette = brewerRdYlGn9Colors elif colorLevels == 5: colorPalette = brewerRdYlGn5Colors elif colorLevels == 3: colorPalette = brewerRdYlGn3Colors else: colorPalette = brewerRdYlGn5Colors nc = len(colorPalette) if Debug: print(colorPalette) backGroundColor = '"#FFFFFF"' #naColor = '"#FFFFFF"' columnHeaderColor = '"#CCFFFF"' rowHeaderColor = '"#FFFFFF"' # construct ranking and actionsList if actionsSubset is not None: ranking = actionsSubset rankingRule = None else: if rankingRule == "Copeland": ranking = self.computeCopelandRanking() elif rankingRule == "NetFlows": ranking = self.computeNetFlowsRanking() elif rankingRule == 'Kohler': ranking = self.computeKohlerRanking() else: rankingRule = None ranking = [x for x in self.actions] #ranking.sort() if fromIndex is None: fromIndex = 0 if toIndex is None: toIndex = len(ranking) actionsList = [] actions = self.actions for x in ranking[fromIndex:toIndex]: if isinstance(x,frozenset): try: actionsList += [(actions[x]['shortName'],x)] except KeyError: actionsList += [(actions[x]['name'],x)] else: actionsList += [(str(x),str(x))] if Debug: print(actionsList) # compute relation characteristic colors amplitude = Max - Min relationColor = {} for x in actions: relationColor[x] = {} for y in actions: rxy = self.relation[x][y] - Min xych = rxy/amplitude for cl in range(nc): if xych <= colorPalette[cl][0]: relationColor[x][y] = colorPalette[cl][1] break else: relationColor[x][y] = colorPalette[-1][1] if Debug: print(x,y,rxy,xych,relationColor[x][y]) # construct html text html = '<!DOCTYPE html><html><head>\n' html += '<meta charset="UTF-8">\n' html += '<title>%s</title>\n' % 'Digraph3 relation map' html += '<style type="text/css">\n' if ContentCentered: html += 'td {text-align: center;}\n' html += 'td.na {color: rgb(192,192,192);}\n' html += '</style>\n' html += '</head>\n<body>\n' html += '<h1>%s</h1>' % tableTitle if rankingRule is not None: html += '<h2>Ranking rule: %s</h2>' % rankingRule html += '<table border="0">\n' html += '<tr bgcolor="#9acd32"><th>%s</th>\n' % relationName for x in actionsList: html += '<th bgcolor="#FFF79B">%s</th>\n' % (x[0]) html += '</tr>\n' for x in actionsList: xKey = x[1] html += '<tr>' html += '<th bgcolor="#FFF79B">%s</th>\n' % (x[0]) for y in actionsList: yKey = y[1] if Debug: print(x,y,self.relation[xKey][yKey],relationColor[xKey][yKey]) if self.relation[xKey][yKey] != Med: formatString = '<td bgcolor=%s align="right">%%+ .%df</td>' \ % (relationColor[xKey][yKey],ndigits) else: formatString = '<td align="right">%%+ .%df</td>' % (ndigits) html += formatString % (self.relation[xKey][yKey]) html += '</tr>' html += '</table>\n' # legend html += '<table style="background-color:%s;">\n' % (backGroundColor) html += '<tr bgcolor=%s><td>Valuation domain</td>' % (backGroundColor) formatString = '<td>[%% .%df;</td><td>%% .%df]</td></tr>' \ % (ndigits,ndigits) html += formatString % (Min,Max) html += '</table>\n' html += '<b>Color legend: </b>\n' html += '<table style="background-color:%s;" border="1">\n' % (backGroundColor) html += '<tr bgcolor=%s><td>Levels</td>' % (backGroundColor) #mnc = (nc+1)//2 for col in range(0,nc): # if col == (mnc-1): # html += '<td>&nbsp;%.2f</td>' % Decimal('0') if col == 0: formatString = '<td bgcolor=%%s>[%%+ .%df;%%+ .%df]</td>' \ % (ndigits,ndigits) html += formatString \ % ( colorPalette[col][1], (Decimal('-1.0')*Max), ( ((colorPalette[col][0]*Decimal('2'))-Decimal('1'))*Max) ) else: formatString = '<td bgcolor=%%s>&nbsp;%%+ .%df]</td>' % ndigits html += formatString \ % ( colorPalette[col][1], ( ((colorPalette[col][0]*Decimal('2'))-Decimal('1'))*Max) ) html += '</tr>\n' html += '</table>\n' html += '</body>\n' html += '</html>\n' return html
[docs] def showHTMLRelationTable(self,actionsList=None, relation=None, IntegerValues=False, ndigits=2, Colored=True, tableTitle='Valued Adjacency Matrix', relationName='r(x S y)', ReflexiveTerms=False, htmlFileName=None, fromIndex=None, toIndex=None): """ Launches a browser window with the colored relation table of self. """ import webbrowser if htmlFileName == None: from tempfile import NamedTemporaryFile fileName = (NamedTemporaryFile(suffix='.html', delete=False,dir='.')).name else: from os import getcwd fileName = getcwd()+'/'+htmlFileName #fileName = tempDir+'/relationTable.html' fo = open(fileName,'w') fo.write(self._htmlRelationTable(actionsSubset=actionsList, relation=relation, isColored=Colored, ndigits=ndigits, hasIntegerValues=IntegerValues, tableTitle=tableTitle, relationName=relationName, ReflexiveTerms=ReflexiveTerms, fromIndex=fromIndex, toIndex=toIndex)) fo.close() url = 'file://'+fileName webbrowser.open(url,new=2)
def _htmlRelationTable(self,tableTitle='Valued Relation Table', relation=None, relationName='r(x R y)', ndigits=2, hasIntegerValues=False, actionsSubset= None, isColored=False, ReflexiveTerms=False, fromIndex=None, toIndex=None): """ renders the relation valuation in actions X actions html table format. """ Med = self.valuationdomain['med'] Min = self.valuationdomain['min'] Max = self.valuationdomain['max'] if actionsSubset is None: actions = self.actions else: actions = actionsSubset if relation is None: relation = self.relation s = '' s += '<h1>%s</h1>' % tableTitle s += '<table border="1">' if isColored: s += '<tr bgcolor="#9acd32"><th>%s</th>' % relationName else: s += '<tr><th>%s</th>' % relationName actionKeys = [x for x in actions] if fromIndex is None: fromIndex = 0 if toIndex is None: toIndex = len(actionKeys) actionsList = [] for x in actionKeys[fromIndex:toIndex]: if isinstance(x,frozenset): try: actionsList += [(actions[x]['shortName'],x)] except: actionsList += [(actions[x]['name'],x)] else: actionsList += [(str(x),x)] ## if actionsSubset is None: ## actionsList.sort() #print actionsList #actionsList.sort() if not hasIntegerValues: try: hasIntegerValuation = self.valuationdomain['hasIntegerValuation'] except KeyError: hasIntegerValuation = hasIntegerValues self.valuationdomain['hasIntegerValuation'] = hasIntegerValuation else: hasIntegerValuation = hasIntegerValues self.valuationdomain['hasIntegerValuation'] = hasIntegerValuation for x in actionsList: if isColored: s += '<th bgcolor="#FFF79B">%s</th>' % (x[0]) else: s += '<th>%s</th>' % (x[0]) s += '</tr>' for x in actionsList: s += '<tr>' if isColored: s += '<th bgcolor="#FFF79B">%s</th>' % (x[0]) else: s += '<th>%s</th>' % (x[0]) for y in actionsList: if x == y: if ReflexiveTerms: if hasIntegerValuation: if isColored: if relation[x[1]][y[1]] > Med: s += '<td bgcolor="#ddffdd" align="right">%d</td>' % (relation[x[1]][y[1]]) elif relation[x[1]][y[1]] < Med: s += '<td bgcolor="#ffddff" align="right">%d</td>' % (relation[x[1]][y[1]]) else: s += '<td bgcolor="#dddddd" align="right" >%d</td>' % (relation[x[1]][y[1]]) else: s += '<td>%d</td>' % (relation[x[1]][y[1]]) else: ndigitsFormat = '%%2.%df' % ndigits if isColored: if relation[x[1]][y[1]] > Med: formatStr = '<td bgcolor="#ddffdd" align="right">%s</td>' % ndigitsFormat s += formatStr % (relation[x[1]][y[1]]) elif relation[x[1]][y[1]] < Med: formatStr = '<td bgcolor="#ffddff" align="right">%s</td>' % ndigitsFormat s += formatStr % (relation[x[1]][y[1]]) else: formatStr = '<td bgcolor="#dddddd" align="right">%s</td>' % ndigitsFormat s += formatStr % (relation[x[1]][y[1]]) else: formatStr = '<td>%s</td>' % ndigitsFormat s += formatStr % (relation[x[1]][y[1]]) else: s += '<td bgcolor="#eeeeee" align="center"> &ndash; </td>' else: if hasIntegerValuation: if isColored: if relation[x[1]][y[1]] > Med: s += '<td bgcolor="#ddffdd" align="right">%d</td>' % (relation[x[1]][y[1]]) elif relation[x[1]][y[1]] < Med: s += '<td bgcolor="#ffddff" align="right">%d</td>' % (relation[x[1]][y[1]]) else: s += '<td bgcolor="#dddddd" align="right" >%d</td>' % (relation[x[1]][y[1]]) else: s += '<td>%d</td>' % (relation[x[1]][y[1]]) else: ndigitsFormat = '%%2.%df' % ndigits if isColored: if relation[x[1]][y[1]] > Med: formatStr = '<td bgcolor="#ddffdd" align="right">%s</td>' % ndigitsFormat s += formatStr % (relation[x[1]][y[1]]) elif relation[x[1]][y[1]] < Med: formatStr = '<td bgcolor="#ffddff" align="right">%s</td>' % ndigitsFormat s += formatStr % (relation[x[1]][y[1]]) else: formatStr = '<td bgcolor="#dddddd" align="right">%s</td>' % ndigitsFormat s += formatStr % (relation[x[1]][y[1]]) else: formatStr = '<td>%s</td>' % ndigitsFormat s += formatStr % (relation[x[1]][y[1]]) s += '</tr>' s += '</table>' if hasIntegerValuation: s += '<p>Valuation domain: [%d; %+d]</p>' % (Min,Max) else: s += '<p>Valuation domain: [%.2f; %+.2f]</p>' % (Min,Max) return s
[docs] def showdre(self): """ Shows relation in nauty format. """ print('*----- show dre -------------*') actions = [x for x in self.actions] aindex = {} i = 1 print('Actions index:') for x in actions: print(i,': ', str(x)) aindex[x] = i i += 1 Med = self.valuationdomain['med'] relation = self.relation n = len(actions) print('n='+str(n)+' $=1 d g') for x in actions: res = str(aindex[x]) + ': ' for y in actions: if relation[x][y] > Med: res = res + str(aindex[y]) + ' ' res = res + ';' print(res)
[docs] def exportPrincipalImage(self, plotFileName=None, pictureFormat="pdf", bgcolor='cornsilk', fontcolor='red3', fontsize='0.75', Reduced=False, Colwise=False, tempDir='.', Comments=False): """ Export as PDF (default) the principal projection of the valued relation using the three principal eigen vectors. Implemeted picture formats are: 'pdf' (default), 'png', 'jpeg' and 'xfig'. The background color is set by default to 'cornsilk'. Font size and color are set by default to 'red3', resp. '0.75'. When *Reduced==True*, the valued relation characeteristics are centered and reduced. When *Colwise==True*, the column vectors of the adjacency table are used for the principal projection, otherwise the rows (default) are used. Has no incidence when the *Digraph* instance *self* is symmetric. .. warning:: The method, writing and reading temporary files: tempCol.r and rotationCol.csv, resp. tempRow.r and rotationRow.csv, by default in the working directory (./), is hence not safe for multiprocessing programs, unless a temporary directory *tempDir* is provided. """ if Comments: print('*---- export 3dplot of format %s -----' % (pictureFormat)) if tempDir is None: tempDir = '.' import os,time if plotFileName is None: plotFileName = "%s/%s" % (tempDir,self.name) else: plotFileName = "%s/%s" % (tempDir,plotFileName) if Colwise: plotFileName += "_Colwise" else: plotFileName += "_Rowwise" self.saveCSV('%s/exportTemp' % tempDir,Diagonal=True) if Colwise: fo = open('%s/tempCol.r' % tempDir,'w') else: fo = open('%s/tempRow.r' % tempDir,'w') fo.write("x = read.csv('%s/exportTemp.csv',row.names=1)\n" % tempDir) if Colwise: fo.write("x = t(x)\n") if Reduced: fo.write("x = (x-colMeans(x))/(sapply(x,sd)*sqrt(length(t(x))))\n") else: fo.write("x = (x-colMeans(x))\n") fo.write("X = as.matrix(x)\n") fo.write("A = X %*% t(X)\n") fo.write("E = eigen(A, symmetric=TRUE)\n") fo.write("P = E$values * t(E$vectors)\n") if Colwise: fo.write("write.csv(t(P),'%s/rotationCol.csv',row.names=F)\n" % tempDir) else: fo.write("write.csv(t(P),'%s/rotationRow.csv',row.names=F)\n" % tempDir) if pictureFormat is None: # no principal image is required fo.close() else: fo.write("valprop = round(E$values/sum(E$values),digits=3)\n") fo.write("pcaRes = list(x=X,eig=E,a=A,P=P,val=valprop)\n") fo.write("val = pcaRes$val\n") fo.write("nval = length(val)\n") if pictureFormat == "png": if bgcolor is None: fo.write('png("%s.png",width=480,height=480\n'\ % (plotFileName) ) else: fo.write('png("%s.png",width=480,height=480,bg="%s")\n'\ % (plotFileName,bgcolor) ) elif pictureFormat == "jpeg": if bgcolor is None: fo.write('jpeg("%s.jpg",width=480,height=480\n'\ % (plotFileName) ) else: fo.write('jpeg("%s.png",width=480,height=480,bg="%s")\n'\ % (plotFileName,bgcolor) ) elif pictureFormat == "xfig": if bgcolor is None: fo.write('xfig("%s.fig",width=480,height=480\n'\ % (plotFileName) ) else: fo.write('xfig("%s.figg",width=480,height=480,bg="%s")\n'\ % (plotFileName,bgcolor) ) elif pictureFormat == "pdf": if bgcolor is None: fo.write('pdf("%s.pdf",width=6,height=6,title="PCA of relation valuation")\n' % (plotFileName) ) else: fo.write('pdf("%s.pdf",width=6,height=6,bg="%s",title="PCA of relation valuation")\n' % (plotFileName,bgcolor) ) else: print('Error: Plotting device %s not defined !' % (pictureFormat)) return fo.write("par(mfrow=c(2,2))\n") fo.write("a1 = 1\n") fo.write("a2 = 2\n") fo.write("a3 = 3\n") fo.write('plot(pcaRes$P[a1,],pcaRes$P[a2,],"n",xlab=paste("axis 1:",val[a1]*100,"%"),ylab=paste("axis 2:",val[a2]*100,"%"),asp=1)\n') fo.write('text(pcaRes$P[a1,],pcaRes$P[a2,],rownames(pcaRes$x),col="%s",cex=%s)\n' % (fontcolor,fontsize) ) fo.write('title(sub=paste("Total inertia:",(val[1]+val[2])*100,"%"),main="Factors 1 and 2",col.sub="black")\n') fo.write('abline(h=0,v=0,lty=2,col="gray")\n') fo.write('plot(pcaRes$P[a3,],pcaRes$P[a2,],"n",xlab=paste("axis 3:",val[a3]*100,"%"),ylab=paste("axis 2:",val[a2]*100,"%"),asp=1)\n') fo.write('text(pcaRes$P[a3,],pcaRes$P[a2,],rownames(pcaRes$x),col="%s",cex=%s)\n' % (fontcolor,fontsize) ) fo.write('title(sub=paste("Total inertia:",(val[3]+val[2])*100,"%"),main="Factors 3 and 2",col.sub="black")\n') fo.write('abline(h=0,v=0,lty=2,col="gray")\n') fo.write('plot(pcaRes$P[a1,],pcaRes$P[a3,],"n",xlab=paste("axis 1:",val[a1]*100,"%"),ylab=paste("axis 3:",val[a3]*100,"%"),asp=1)\n') fo.write('text(pcaRes$P[a1,],pcaRes$P[a3,],rownames(pcaRes$x),col="%s",cex=%s)\n' % (fontcolor,fontsize) ) fo.write('title(sub=paste("Total inertia:",(val[1]+val[3])*100,"%"),main="Factors 1 and 3",col.sub="black")\n') fo.write('abline(h=0,v=0,lty=2,col="gray")\n') fo.write('barplot(val[a1:nval]*100,names.arg=a1:nval,main="Axis inertia (in %)",col="orangered")\n') fo.write('dev.off()\n') fo.close() # fo.write("par(mfrow=c(2,2))\n") # fo.write("a1 = 1\n") # fo.write("a2 = 2\n") # fo.write("a3 = 3\n") # fo.write('plot(pcaRes$P[a1,],pcaRes$P[a2,],"n",xlab=paste("axis 1:",val[a1]*100,"%"),ylab=paste("axis 2:",val[a2]*100,"%"),asp=1)\n') # fo.write("text(pcaRes$P[a1,],pcaRes$P[a2,],rownames(pcaRes$x),cex=0.75)\n") # fo.write('abline(h=0,lty=2,col="gray")\n') # fo.write('abline(v=0,lty=2,col="gray")\n') # fo.write('plot(pcaRes$P[a3,],pcaRes$P[a2,],"n",xlab=paste("axis 3:",val[a3]*100,"%"),ylab=paste("axis 2:",val[a2]*100,"%"),asp=1)\n') # fo.write('text(pcaRes$P[a3,],pcaRes$P[a2,],rownames(pcaRes$x),cex=0.75)\n') # fo.write('abline(h=0,lty=2,col="gray")\n') # fo.write('abline(v=0,lty=2,col="gray")\n') # fo.write('plot(pcaRes$P[a1,],pcaRes$P[a3,],"n",xlab=paste("axis 1:",val[a1]*100,"%"),ylab=paste("axis 3:",val[a3]*100,"%"),asp=1)\n') # fo.write('text(pcaRes$P[a1,],pcaRes$P[a3,],rownames(pcaRes$x),cex=0.75)\n') # fo.write('abline(h=0,v=0,lty=2,col="gray")\n') # fo.write('barplot(val[a1:nval]*100,names.arg=a1:nval,main="Axis inertia (in %)",col="orangered")\n') # fo.write('dev.off()\n') # fo.close() if Comments: if Colwise: os.system('(cd %s;env R -q --vanilla --verbose < tempCol.r 2>&1)' % tempDir) else: os.system('(cd %s;env R -q --vanilla --verbose < tempRow.r 2>&1)' % tempDir ) else: if Colwise: os.system('(cd %s;env R -q --vanilla < tempCol.r > /dev/null 2> /dev/null)' % tempDir) else: os.system('(cd %s;env R -q --vanilla < tempRow.r > /dev/null 2> /dev/null)' % tempDir) time.sleep(3) if pictureFormat is not None and Comments: print('See %s.%s ! ' % (plotFileName,pictureFormat))
[docs] def exportGraphViz(self,fileName=None,actionsSubset=None, bestChoice=set(),worstChoice=set(), firstChoice=set(),lastChoice=set(), Comments=True,graphType='png', pictureFormat=None,graphSize='7,7', relation=None,bgcolor='cornsilk'): """ export GraphViz dot file for graph drawing filtering. """ import os if Comments: print('*---- exporting a dot file for GraphViz tools ---------*') if actionsSubset is None: actionkeys = [x for x in self.actions] else: actionkeys = [x for x in actionsSubset] n = len(actionkeys) if relation is None: relation = self.relation Med = self.valuationdomain['med'] i = 0 if fileName is None: name = self.name else: name = fileName if pictureFormat is not None: graphType = pictureFormat dotName = name+'.dot' if Comments: print('Exporting to '+dotName) if firstChoice != set(): bestChoice = firstChoice if bestChoice != set(): rankBestString = '{rank=max; ' if lastChoice != set(): worstChoice = lastChoice if worstChoice != set(): rankWorstString = '{rank=min; ' fo = open(dotName,'w') fo.write('digraph G {\n') if bgcolor is not None: fo.write('graph [ bgcolor = %s, fontname = "Helvetica-Oblique",\n fontsize = 12,\n label = "' % (bgcolor)) else: fo.write('graph [ fontname = "Helvetica-Oblique",\n fontsize = 12,\n label = "') fo.write('\\nDigraph3 (graphviz), R. Bisdorff, 2020", size="') fo.write(graphSize),fo.write('"];\n') for i in range(n): try: nodeName = self.actions[actionkeys[i]]['shortName'] except: try: nodeName = self.actions[actionkeys[i]]['name'] except: nodeName = str(actionkeys[i]) node = 'n'+str(i+1)+' [shape = "circle", label = "' +nodeName+'"' if actionkeys[i] in bestChoice: node += ', style = "filled", color = gold];\n' rankBestString += 'n'+str(i+1)+' ' elif actionkeys[i] in worstChoice: node += ', style = "filled", color = lightblue];\n' rankWorstString += 'n'+str(i+1)+' ' else: node += '];\n' fo.write(node) if bestChoice != set(): rankBestString += '}\n' if worstChoice != set(): rankWorstString += '}\n' ## for i in range(n): ## edge = 'n'+str(i+1) ## for j in range(n): ## if i != j and relation[actions[i]][actions[j]] > Med: ## edge0 = edge+'-> n'+str(j+1)+';\n' ## fo.write(edge0) ## j += 1 ## i += 1 for i in range(n): for j in range(i+1, n): edge = 'n'+str(i+1) if relation[actionkeys[i]][actionkeys[j]] > Med and relation[actionkeys[j]][actionkeys[i]] > Med: edge0 = edge+'-> n'+str(j+1)+' [dir=both,style="setlinewidth(2)",color=black, arrowhead=normal, arrowtail=normal] ;\n' fo.write(edge0) elif relation[actionkeys[i]][actionkeys[j]] > Med and relation[actionkeys[j]][actionkeys[i]] == Med: edge0 = edge+'-> n'+str(j+1)+' [dir=both, color=black, arrowhead=normal, arrowtail=empty] ;\n' fo.write(edge0) elif relation[actionkeys[i]][actionkeys[j]] == Med and relation[actionkeys[j]][actionkeys[i]] > Med: edge0 = edge+'-> n'+str(j+1)+' [dir=both, color=black, arrowtail=normal, arrowhead=empty] ;\n' fo.write(edge0) elif relation[actionkeys[i]][actionkeys[j]] == Med and relation[actionkeys[j]][actionkeys[i]] == Med: edge0 = edge+'-> n'+str(j+1)+' [dir=both, color=grey, arrowhead=empty, arrowtail=empty] ;\n' fo.write(edge0) elif relation[actionkeys[i]][actionkeys[j]] > Med and relation[actionkeys[j]][actionkeys[i]] < Med: edge0 = edge+'-> n'+str(j+1)+' [dir=forward, color=black] ;\n' fo.write(edge0) elif relation[actionkeys[i]][actionkeys[j]] == Med and relation[actionkeys[j]][actionkeys[i]] < Med: edge0 = edge+'-> n'+str(j+1)+' [dir=forward, color=grey, arrowhead=empty] ;\n' fo.write(edge0) elif relation[actionkeys[i]][actionkeys[j]] < Med and relation[actionkeys[j]][actionkeys[i]] > Med: edge0 = edge+'-> n'+str(j+1)+' [dir=back, color=black] ;\n' fo.write(edge0) elif relation[actionkeys[i]][actionkeys[j]] < Med and relation[actionkeys[j]][actionkeys[i]] == Med: edge0 = edge+'-> n'+str(j+1)+' [dir=back, color=grey, arrowtail=empty] ;\n' fo.write(edge0) if bestChoice != set(): fo.write(rankBestString) if worstChoice != set(): fo.write(rankWorstString) fo.write('}\n') fo.close() if type(self) == CirculantDigraph: commandString = 'circo -T'+graphType+' '+dotName+' -o '+name+'.' + graphType # elif type(self) == RandomTree: # commandString = 'neato -T'+graphType+' '+dotName+' -o '+name+'.' + graphType else: commandString = 'dot -Grankdir=BT -T'+graphType+' ' +dotName+' -o '+name+'.'+graphType #commandString = 'dot -T'+graphType+' ' +dotName+' -o '+name+'.'+graphType if Comments: print(commandString) try: os.system(commandString) except: if Comments: print('graphViz tools not avalaible! Please check installation.')
# def _exportD3(self, fileName="index", Comments=True): # """ # This function was designed and implemented by Gary Cornelius, 2014 for his bachelor thesis at the University of Luxembourg. # The thesis document with more explanations can be found in the literature/Cornelius directory of a Digraph3 working copy. . # *Parameters*: # * fileName, name of the generated html file, default = None (graph name as defined in python); # * Comments, True = default; # The idea of the project was to find a way that allows you to easily get details about certain nodes or edges of a directed graph in a dynamic format. # Therefore this function allows you to export a html file together with all the needed libraries, including the # D3 Library which we use for graph generation and the physics between nodes, which attracts or pushes nodes away from each other. # Features of our graph include i.e. : # * A way to only inspect a node and it's neighbours # * Dynamic draging and freezing of the graph # * Export of a newly created general graph # You can find the list of fututres in the Section below which is arranged according to the graph type. # *If the graph is an outrankingdigraphs*: # * Nodes can be dragged and only the name and comment can be edited. # * Edges can be inspected but not edited for this purpose a special json array containing all possible pairwiseComparisions is generated. # *If the graph is a general graph*: # * Nodes can be dragged, added, removed and edited. # * Edges can be added, removed, inverted and edited. But edges cannot be inspected. # * The pairwiseComparisions key leads to an empty array {}. # In both cases, undefined edges can be hidden and reappear after a simple reload.(right click - reload) # *The generated files*: # * d3.v3.js, contains the D3 Data-driven Documents source code, containing one small addition that we made in order to be able to easyly import links with a different formatself. # * digraph3lib.js, contains our library. This file contains everything that we need from import of an XMCDA2 file, visualization of the graph to export of the changed graph. # * d3export.json, usually named after the python graph name followed by a ticket number if the file is already present. It is the JSON file that is exported with the format "{"xmcda2": "some xml","pairwiseComparisions":"{"a01": "some html",...}"}. # *Example 1*: # #. python3 session: # >>> from digraphs import RandomValuationDigraph # >>> dg = RandomValuationDigraph(order=5,Normalized=True) # >>> dg.exportD3() # or # >> dg.showInteractiveGraph() # #. index.html: # * Main Screen: # .. image:: randomvaluation_d3_main.png # * Inspect function: # .. image:: randomvaluation_d3_inspect.png # .. note:: # If you want to use the automatic load in Chrome, try using the command: "python -m SimpleHTTPServer" # and then access the index.html via "http://0.0.0.0:8000/index.html". # In order to load the CSS an active internet connection is needed! # """ # import os # import json # import urllib # import htmlmodel,json # if Comments: # print('*---- exporting all needed files ---------*') # if fileName == "index": # fileName = self.name # file=fileName+".html" # dst_dir=os.getcwd() # basename = os.path.basename(file) # head, tail = os.path.splitext(basename) # dst_file = os.path.join(dst_dir, basename) # # rename if necessary # count = 0 # print(dst_file) # while os.path.exists(dst_file): # count += 1 # dst_file = os.path.join(dst_dir, '%s-%d%s' % (head, count, tail)) # actionkeys = [x for x in self.actions] # n = len(actionkeys) # relation = self.relation # Med = self.valuationdomain['med'] # pageName="" # fw = open("digraph3lib.js",'w') # fw.write(htmlmodel.javascript()) # fw.close() # if Comments: # print("File: digraph3lib.js saved!") # fw = open("d3.v3.js",'w') # fw.write(htmlmodel.d3export()) # fw.close() # if Comments: # print("File: d3.v3.js saved!") # pairwise={} # try: # for x in self.actions: # pairwise[x]={} # for x in actionkeys: # for y in actionkeys: # if(not(x == y)): # pairwise[x][y] = str(self.showPairwiseComparison(x,y,isReturningHTML=True)) # except: # pairwise={} # d3export={} # if(pairwise): # temp = "outranking_"+fileName # self.saveXMCDA2(fileName=temp+"-"+str(count)) # else: # temp = "general_"+fileName # self.saveXMCDA2(fileName=temp+"-"+str(count)) # with open(temp+"-"+str(count)+".xmcda2","r") as myFile: # data=myFile.read().replace("\n","") # try: # os.remove(temp+"-"+str(count)+".xmcda2") # except OSError: # pass # d3export["xmcda2"]= str(data) # d3export["pairwiseComparisions"] = json.dumps(pairwise) # if(count==0): # fw = open(temp+".json","w") # if Comments: # print("File: "+temp+".json saved!") # else: # fw = open(temp+"-"+str(count)+".json","w") # if Comments: # print("File:"+temp+"-"+str(count)+".json saved!") # fw.write(json.dumps(d3export)) # fw.close() # if(count==0): # fw = open(fileName+".html","w") # fw.write(htmlmodel.htmlmodel(jsonName=temp+".json")) # pageName=fileName+".html" # if Comments: # print("File: "+fileName+".html generated!") # else: # fw = open(fileName+"-"+str(count)+".html",'w') # fw.write(htmlmodel.htmlmodel(jsonName=temp+"-"+str(count)+".json")) # pageName=fileName+"-"+str(count)+".html" # if Comments: # print("File: "+fileName+"-"+str(count)+".html generated!") # fw.close() # if Comments: # print('*---- export done ---------*') # return pageName # def _showInteractiveGraph(self): # ''' # Save the graph and all needed files for the visualization of an interactive graph generated by the exportD3() function. # For best experience make sure to use Firefox, because other browser restrict the loading of local files. # ''' # import os,webbrowser # newTab=2 # url = "file://"+os.getcwd()+"/%s" % self.exportD3() # webbrowser=webbrowser.get("firefox") # webbrowser.open(url,new=newTab)
[docs] def savedre(self,fileName='temp'): """ save digraph in nauty format. """ print('*----- saving digraph in nauty dre format -------------*') actions = [x for x in self.actions] Name = fileName+'.dre' aindex = {} i = 1 print('Actions index:') for x in actions: print(i,': ', str(x)) aindex[x] = i i += 1 Med = self.valuationdomain['med'] relation = self.relation n = len(actions) fo = open(Name,'w') fo.write('n='+str(n)+' $=1 d g\n') for x in actions: res = str(aindex[x]) + ': ' for y in actions: if relation[x][y] > Med: res = res + str(aindex[y]) + ' ' res = res + ';\n' fo.write(res) fo.close() self.aindex = aindex.copy()
def _saveXML(self,fileName='temp',category='general',subcategory='general',author='digraphs Module (RB)',reference='saved from Python'): """ save digraph in XML format. obsolete and deprecated. """ print('*----- saving digraph in XML format -------------*') actions = [x for x in self.actions] nameExt = fileName+'.xml' fo = open(nameExt,'w') fo.write('<?xml version="1.0" encoding="UTF-8"?>\n') fo.write('<?xml-stylesheet type="text/xsl" href="digraphs.xsl"?>\n') fo.write('<!DOCTYPE digraph SYSTEM "digraphs.dtd">\n') fo.write('<digraph ') fo.write('category="' + category+'" subcategory="'+subcategory+'">\n') fo.write('<header>\n') fo.write('<name>') fo.write(nameExt) fo.write('</name>\n') fo.write('<author>') fo.write(author) fo.write('</author>\n') fo.write('<reference>') fo.write(reference) fo.write('</reference>\n') fo.write('</header>') actions = self.actions fo.write('<nodes>\n') for x in actions: fo.write('<node>') fo.write(str(x)) fo.write('</node>\n') fo.write('</nodes>\n') Max = self.valuationdomain['max'] Min = self.valuationdomain['min'] fo.write('<valuationdomain>\n') fo.write('<min>') fo.write(str(Min)) fo.write('</min>\n') fo.write('<max>') fo.write(str(Max)) fo.write('</max>\n') fo.write('</valuationdomain>\n') fo.write('<relation>\n') relation = self.relation for x in actions: for y in actions: fo.write('<arc>\n') fo.write('<i>') fo.write(str(x)) fo.write('</i>\n') fo.write('<t>') fo.write(str(y)) fo.write('</t>\n') fo.write('<v>') fo.write(str(relation[x][y])) fo.write('</v>\n') fo.write('</arc>\n') fo.write('</relation>\n') fo.write('</digraph>\n') fo.close() print('File: ' + nameExt + ' saved !') def _saveXMCDA(self,fileName='temp',relationName='R',category='random',subcategory='valued',author='digraphs Module (RB)',reference='saved from Python',valuationType='standard',servingD3=False): """ save digraph in XMCDA format. Obsolete and deprecated. """ print('*----- saving digraph in XML format -------------*') actions = [x for x in self.actions] nameExt = fileName+'.xmcda' fo = open(nameExt,'w') fo.write('<?xml version="1.0" encoding="UTF-8"?>\n') if servingD3: fo.write('<!-- ?xml-stylesheet type="text/xsl" href="xmcdaDefault.xsl"? -->\n') else: fo.write('<?xml-stylesheet type="text/xsl" href="xmcdaDefault.xsl"?>\n') fo.write(str('<xmcda:XMCDA xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.decision-deck.org/2008/UMCDA-ML-1.0 umcda-ml-1.0.xsd" xmlns:xmcda="http://www.decision-deck.org/2008/UMCDA-ML-1.0">\n')) # write description fo.write('<caseReference>\n') fo.write('<title>Valued Digraph in XMCDA format</title>\n') fo.write('<id>%s</id>\n' % (fileName) ) fo.write('<name>%s</name>\n' % (self.name) ) fo.write('<type>root</type>\n') fo.write('<user>%s</user>\n' % (author) ) fo.write('<version>%s</version>\n' % (reference) ) fo.write('</caseReference>\n') # write nodes actionsList = [x for x in self.actions] actionsList.sort() na = len(actionsList) actions = self.actions fo.write('<alternatives>\n') fo.write('<description>\n') fo.write('<title>%s</title>\n' % ('List of Alternatives')) fo.write('<type>%s</type>\n' % ('alternatives')) fo.write('<comment>Potential decision actions.</comment>\n') fo.write('</description>\n') for i in range(na): fo.write('<alternative id="%s">\n' % (actionsList[i])) fo.write('<description>\n') fo.write('<name>') try: fo.write(str(actions[actionsList[i]]['name'])) except: fo.write('nameless') fo.write('</name>\n') fo.write('<comment>') try: fo.write(str(actions[actionsList[i]]['comment'])) except: fo.write('No comment') fo.write('</comment>\n') fo.write('</description>\n') fo.write('<alternativeType>potential</alternativeType>\n') fo.write('</alternative>\n') fo.write('</alternatives>\n') # write valued binary Relation fo.write('<relationOnAlternatives>\n') fo.write('<description>\n') fo.write('<title>%s</title>\n' % ('Valued Binary Relation')) fo.write('<name>%s</name>\n' % (relationName) ) fo.write('<type>%s</type>\n' % ('valuedBinaryRelation')) fo.write('<comment>%s %s Digraph</comment>\n' % (category,subcategory) ) fo.write('</description>\n') fo.write('<valuationDomain>\n') fo.write('<description>\n') fo.write('<subTitle>%s</subTitle>\n' % ('Valuation Domain')) fo.write('</description>\n') fo.write('<valuationType>%s</valuationType>\n' % (valuationType) ) Max = self.valuationdomain['max'] Min = self.valuationdomain['min'] fo.write('<minimum><real>%2.2f</real></minimum>\n' % (Min)) fo.write('<maximum><real>%2.2f</real></maximum>\n' % (Max)) fo.write('</valuationDomain>\n') fo.write('<arcs>\n') fo.write('<description>\n') fo.write('<subTitle>%s</subTitle>\n' % ('Valued Adjacency Table')) try: category = self.category subcategory = self.subcategory except: pass fo.write('<comment>%s %s Digraph</comment>\n' % (category,subcategory) ) fo.write('</description>\n') relation = self.relation for x in actions: for y in actions: fo.write('<arc>\n') fo.write('<from><alternativeID>') fo.write(str(x)) fo.write('</alternativeID></from>\n') fo.write('<to><alternativeID>') fo.write(str(y)) fo.write('</alternativeID></to>\n') fo.write('<value><real>%2.2f' % (relation[x][y]) ) fo.write('</real></value>\n') fo.write('</arc>\n') fo.write('</arcs>\n') fo.write('</relationOnAlternatives>\n') fo.write('</xmcda:XMCDA>\n') fo.close() print('File: ' + nameExt + ' saved !')
[docs] def saveXMCDA2(self,fileName='temp',fileExt='xmcda2', Comments=True,relationName='R',relationType='binary', category='random',subcategory='valued', author='digraphs Module (RB)',reference='saved from Python', valuationType='standard',digits=2,servingD3=False): """ save digraph in XMCDA 2.0 format. Deprecated now. """ if Comments: print('*----- saving digraph in XML format -------------*') actions = [x for x in self.actions] nameExt = fileName+"."+fileExt fo = open(nameExt,'w') fo.write('<?xml version="1.0" encoding="UTF-8"?>\n') if servingD3: fo.write('<!-- ?xml-stylesheet type="text/xsl" href="xmcda2Rubis.xsl"? -->\n') else: fo.write('<?xml-stylesheet type="text/xsl" href="xmcdaXSL.xsl"?>\n') fo.write(str('<xmcda:XMCDA xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.decision-deck.org/2009/UMCDA-2.0.0 file:../XMCDA-2.0.0.xsd" xmlns:xmcda="http://www.decision-deck.org/2009/XMCDA-2.0.0">\n')) # write description fo.write('<projectReference id="%s" name="%s">\n' % (fileName,self.name)) fo.write('<title>Stored Digraph in XMCDA-2.0 format</title>\n') #fo.write('<id>%s</id>\n' % (fileName) ) #fo.write('<name>%s</name>\n' % (self.name) ) #fo.write('<type>root</type>\n') fo.write('<user>%s</user>\n' % (author) ) fo.write('<version>%s</version>\n' % (reference) ) fo.write('</projectReference>\n') # write nodes actionsList = [x for x in self.actions] actionsList.sort() na = len(actionsList) actions = self.actions fo.write('<alternatives mcdaConcept="Digraph nodes">\n') fo.write('<description>\n') fo.write('<title>%s</title>\n' % ('Nodes of the digraph')) #fo.write('<type>%s</type>\n' % ('alternatives')) fo.write('<comment>Set of nodes of the digraph.</comment>\n') fo.write('</description>\n') for i in range(na): try: alternativeName = str(actions[actionsList[i]]['name']) except: alternativeName = 'nameless' fo.write('<alternative id="%s" name="%s">\n' % (actionsList[i],alternativeName)) fo.write('<description>\n') fo.write('<comment>') try: fo.write(str(actions[actionsList[i]]['comment'])) except: fo.write('No comment') fo.write('</comment>\n') fo.write('</description>\n') fo.write('<type>real</type>\n') fo.write('<active>true</active>\n') fo.write('<reference>false</reference>\n') fo.write('</alternative>\n') fo.write('</alternatives>\n') # write valued binary Relation fo.write('<alternativesComparisons id="1" name="%s">\n' % (relationName)) fo.write('<description>\n') fo.write('<title>%s</title>\n' % ('Randomly Valued Binary Relation')) #fo.write('<name>%s</name>\n' % (relationName) ) #fo.write('<type>%s</type>\n' % ('valuedBinaryRelation')) fo.write('<comment>%s %s Digraph</comment>\n' % (category,subcategory) ) fo.write('</description>\n') fo.write('<valuation name="valuationDomain">\n') fo.write('<description>\n') fo.write('<subTitle>%s</subTitle>\n' % ('Valuation Domain')) fo.write('</description>\n') fo.write('<quantitative>') Max = self.valuationdomain['max'] Min = self.valuationdomain['min'] if valuationType == 'integer': fo.write('<minimum><integer>%d</integer></minimum>\n' % (Min)) fo.write('<maximum><integer>%d</integer></maximum>\n' % (Max)) else: formatString = '%%2.%df' % (digits) fo.write('<minimum><real>') fo.write(formatString % (Min)) fo.write('</real></minimum>\n') fo.write('<maximum><real>') fo.write(formatString % (Max)) fo.write('</real></maximum>\n') fo.write('</quantitative>\n') fo.write('</valuation>\n') fo.write('<comparisonType>%s</comparisonType>\n' % (relationName)) fo.write('<pairs>\n') fo.write('<description>\n') fo.write('<subTitle>%s</subTitle>\n' % ('Valued Adjacency Table')) try: category = self.category subcategory = self.subcategory except: pass fo.write('<comment>%s %s Digraph</comment>\n' % (category,subcategory) ) fo.write('</description>\n') relation = self.relation for x in actions: for y in actions: fo.write('<pair>\n') fo.write('<initial><alternativeID>') fo.write(str(x)) fo.write('</alternativeID></initial>\n') fo.write('<terminal><alternativeID>') fo.write(str(y)) fo.write('</alternativeID></terminal>\n') if valuationType == 'bipolar': formatString = '%%+2.%df' % (digits) else: formatString = '%%2.%df' % (digits) fo.write('<value><real>') fo.write(formatString % (relation[x][y]) ) fo.write('</real></value>\n') fo.write('</pair>\n') fo.write('</pairs>\n') fo.write('</alternativesComparisons>\n') fo.write('</xmcda:XMCDA>\n') fo.close() if Comments: print('File: ' + nameExt + ' saved !')
[docs] def computeDensities(self,choice): """ parameter: choice in self renders the four densitiy parameters: arc density, double arc density, single arc density, absence arc density. """ actions = set(choice) relation = self.relation Med = self.valuationdomain['med'] order = float(len(actions)) d = 0.0 dd = 0.0 sd = 0.0 ad = 0.0 for x in actions: for y in actions: if x != y: if relation[x][y] > Med: d += 1.0 if relation[x][y] > Med and relation[y][x] > Med: dd += 1.0 if relation[x][y] > Med and relation[y][x] <= Med: sd += 1.0 if relation[x][y] <= Med and relation[y][x] <= Med: ad += 1.0 d = d / (order*(order-1)) dd / (order*(order-1)) sd = (2*sd) / (order*(order-1)) ad = ad / (order*(order-1)) return d,dd,sd,ad
[docs] def computeCutLevelDensities(self,choice,level): """ parameter: choice in self, robustness level renders three robust densitiy parameters: robust double arc density, robust single arc density, robust absence arc densitiy. """ actions = set(choice) relation = self.relation Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] Max = self.valuationdomain['max'] negLevel = Max - level + Min order = float(len(actions)) rdd = 0.0 rsd = 0.0 rad = 0.0 if level < Med or level >= Max: print('Error: robustness level too low or too high !!!') else: for x in actions: for y in actions: if x != y: if relation[x][y] > level and relation[y][x] > level: rdd += 1.0 if relation[x][y] > level: if relation[y][x] < negLevel: rsd += 1.0 if relation[x][y] < negLevel and relation[y][x] < negLevel: rad += 1.0 rdd = rdd / (order*(order-1)) rsd = (2*rsd) / (order*(order-1)) rad = rad / (order*(order-1)) density = {} density['double'] = rdd density['single'] = rsd density['absence'] = rad return density
[docs] def computeAllDensities(self,choice=None): """ parameter: choice in self renders six densitiy parameters: arc density, double arc density, single arc density, strict single arc density, absence arc density, strict absence arc densitiy. """ if choice is not None: actions = set(choice) else: actions = self.actions relation = self.relation Med = self.valuationdomain['med'] order = float(len(actions)) d = 0.0 dd = 0.0 sd = 0.0 ssd = 0.0 ad = 0.0 asd = 0.0 for x in actions: for y in actions: if x != y: if relation[x][y] > Med: d += 1.0 if relation[x][y] > Med and relation[y][x] > Med: dd += 1.0 if relation[x][y] > Med: if relation[y][x] < Med: ssd += 1.0 sd += 1.0 elif relation[y][x] == Med: sd += 1.0 if relation[x][y] <= Med and relation[y][x] <= Med: ad += 1.0 if relation[x][y] < Med and relation[y][x] < Med: asd += 1.0 d = d / float(order*(order-1)) dd = dd / float(order*(order-1)) sd = (2*sd) / float(order*(order-1)) ssd = (2*ssd) / float(order*(order-1)) ad = ad / float(order*(order-1)) asd = asd / float(order*(order-1)) density = {} density['arc'] = d density['double'] = dd density['single'] = sd density['strictSingle'] = ssd density['absence'] = ad density['strictAbsence'] = asd return density
[docs] def computeValuationLevels(self,choice=None, Debug=False): """ renders the symmetric closure of the apparent valuations levels of self in an increasingly ordered list. If parameter choice is given, the computation is limited to the actions of the choice. """ Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] Min = self.valuationdomain['min'] if choice is None: actions = [x for x in self.actions] else: actions = [x for x in choice] relation = self.relation levels = set([Max,Min]) for x in actions: for y in actions: levels.add(relation[x][y]) levels.add(Max - relation[x][y] + Min) ## if Debug: ## print levels levelsList = list(levels) levelsList.sort() if Debug: print('levelsList', levelsList) return levelsList
[docs] def computePrudentBetaLevel(self, Debug=False): """ computes alpha, ie the lowest valuation level, for which the bipolarly polarised digraph doesn't contain a chordless circuit. """ Med = self.valuationdomain['med'] valuationLevels= self.computeValuationLevels(Debug=Debug) if Debug: print('number of levels; %d' % len(valuationLevels)) valuationLevels.reverse() for i in range(len([x for x in valuationLevels if x > Med])): level = valuationLevels[i+1] if Debug: print('checking level: ', level) gp = PolarisedDigraph(self,level=level) if len(gp.computeChordlessCircuits()) > 0: if Debug: gp.showChordlessCircuits() print('prudent order level = %s (med = %.2f)' % (str(valuationLevels[i-1]),Med)) self.prudentBetaLevel = valuationLevels[i] return self.prudentBetaLevel self.prudentBetaLevel = Med if Debug: ## self.computeChordlessCircuits() ## self.showChordlessCircuits() print('prudent order level = %s = med' % str(Med)) return Med
[docs] def computeValuationPercentiles(self,choice, percentages, withValues=False): """ Parameters: choice and list of percentages. renders a series of quantiles of the characteristics valuation of the arcs in the digraph. """ relation = self.relation vx = [] for x in choice: for y in choice: if x != y: vx.append(relation[x][y]) vx.sort() if withValues: print('values ', vx) nv = len(vx) percentile = {} for q in percentages: kq = q*nv//100 r = (nv*q)% 100 if q == 0: percentile[q] = vx[0] elif q == 100: percentile[q] = vx[nv-1] else: percentile[q] = vx[kq-1] + (Decimal(str(r))/Decimal('100.0')) * (vx[kq]-vx[kq-1]) return percentile
[docs] def computeValuationPercentages(self,choice,percentiles,withValues=False): """ Parameters: choice and list of percentiles. renders a series of percentages of the characteristics valuation of the arcs in the digraph. """ relation = self.relation vx = [] for x in choice: for y in choice: if x != y: vx.append(relation[x][y]) vx.sort() nv = len(vx) if withValues: print('values ', vx) np = len(percentiles) rv = [0.0 for i in range(np)] for val in vx: for i in range(np): if percentiles[i] > val: rv[i] += 1.0 percentages = {} for i in range(np): percentages[percentiles[i]] = rv[i]/float(nv) return percentages
[docs] def computeAverageValuation(self): """ Computes the bipolar average correlation between self and the crisp complete digraph of same order of the irreflexive and determined arcs of the digraph """ Med = self.valuationdomain['med'] relation = self.relation averageValuation = Decimal('0.0') determined = Decimal('0.0') #actionsList = [x for x in self.actions] nbDeterm = 0 for x,rx in relation.items(): for y,rxy in rx.items(): if x != y: if rxy != Med: nbDeterm += 1 averageValuation += rxy determined += abs(rxy) return averageValuation / determined
[docs] def computeDeterminateness(self,InPercents=False): """ Computes the Kendalll distance of self with the all median-valued indeterminate digraph of order n. Return the average determination of the irreflexive part of the digraph. *determination* = sum_(x,y) { abs[ r(xRy) - Med ] } / n(n-1) If *InPercents* is True, returns the average determination in percentage of (Max - Med) difference. >>> from outrankingDigraphs import BipolarOutrankingDigraph >>> from randomPerfTabs import Random3ObjectivesPerformanceTableau >>> t = Random3ObjectivesPerformanceTableau(numberOfActions=7,numberOfCriteria=7,seed=101) >>> g = BipolarOutrankingDigraph(t,Normalized=True) >>> g *------- Object instance description ------* Instance class : BipolarOutrankingDigraph Instance name : rel_random3ObjectivesPerfTab Actions : 7 Criteria : 7 Size : 27 Determinateness (%) : 65.67 Valuation domain : [-1.00;1.00] >>> print(g.computeDeterminateness()) 0.3134920634920634920634920638 >>> print(g.computeDeterminateness(InPercents=True)) 65.67460317460317460317460320 >>> g.recodeValuation(0,1) >>> g *------- Object instance description ------* Instance class : BipolarOutrankingDigraph Instance name : rel_random3ObjectivesPerfTab Actions : 7 Criteria : 7 Size : 27 Determinateness (%) : 65.67 Valuation domain : [0.00;1.00] >>> print(g.computeDeterminateness()) 0.1567460317460317460317460318 >>> print(g.computeDeterminateness(InPercents=True)) 65.67460317460317460317460320 """ Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] Min = self.valuationdomain['min'] relation = self.relation #actions = self.actions order = self.order D = Decimal('0.0') for x,rx in relation.items(): for y,rxy in rx.items(): if x != y: D += abs(rxy - Med) if order > 1: determination = D / Decimal(str((order * (order-1)))) else: determination = D if InPercents: return ( (determination / (Max-Med)) + Decimal('1.0')) / Decimal('2.0') * Decimal('100.0') else: return determination
[docs] def showStatistics(self): """ Computes digraph statistics like order, size and arc-density. """ #import array print('*----- general statistics -------------*') nbrcomp = len(self.components()) nbrstrcomp = len(self.strongComponents()) actions = [x for x in self.actions] relation = self.relation order = len(actions) size,undeterm,arcDensity = self.sizeSubGraph(actions) self.size = size self.undeterm = undeterm density = self.computeAllDensities(actions) self.arcDensity = density['arc'] outDegrees = self.outDegreesDistribution() inDegrees = self.inDegreesDistribution() symDegrees = self.symDegreesDistribution() nbDepths = self.neighbourhoodDepthDistribution() nb = len(nbDepths) meanLength = 0.0 for i in range(nb): meanLength += i * nbDepths[i] if nbDepths[nb-1] != 0: meanLength = 'infinity' else: meanLength = float(meanLength/order) self.meanNeighbourhoodDepth = meanLength self.digraphDiameter = self.computeDiameter() self.agglomerationCoefficient,self.meanAgglomerationCoefficient = self.agglomerationDistribution() # Outranking determinateness ## Max = self.valuationdomain['max'] ## Med = self.valuationdomain['med'] ## deter = Decimal('0.0') ## for x,rx in relation.items(): ## for y,rxy in rx.items(): ## if x != y: ## # print(relation[x][y], Med) ## deter += abs(rxy - Med) ## deter /= order * (order-1) * (Max - Med) deter = self.computeDeterminateness(InPercents=True) trans = self.computeTransitivityDegree(InPercents=True) sym = self.computeSymmetryDegree(InPercents=True) # output results print('for digraph : <' + str(self.name) + '.py>') print('order :', self.order, 'nodes') print('size :', self.size, 'arcs') print('undetermined :', self.undeterm, 'arcs') print('determinateness (%%) : %.1f' % (deter)) print("arc density : %.2f" % (density['arc'])) print("double arc density : %.2f" % (density['double'])) print("single arc density : %.2f" % (density['single'])) print("absence density : %.2f" % (density['absence'])) print("strict single arc density: %.2f" % (density['strictSingle'])) print("strict absence density : %.2f" % (density['strictAbsence'])) print('components :', nbrcomp) print('strong components :', nbrstrcomp) print('transitivity degree (%%) : %.1f' % (trans) ) print('symmetry degree (%%) : %.1f' % (sym) ) print(' :', list(range(len(outDegrees)))) print('outdegrees distribution :', list(outDegrees)) print('indegrees distribution :', list(inDegrees)) print('mean outdegree : %.2f' % (self.computeMeanOutDegree())) print('mean indegree : %.2f' % (self.computeMeanInDegree())) print(' :', list(range(len(symDegrees)))) print('symmetric degrees dist. :', list(symDegrees)) print('mean symmetric degree : %.2f' % (self.computeMeanSymDegree())) outgini = self.computeConcentrationIndex(list(range(len(outDegrees))),list(outDegrees)) ingini = self.computeConcentrationIndex(list(range(len(inDegrees))),list(inDegrees)) symgini = self.computeConcentrationIndex(list(range(len(symDegrees))),list(symDegrees)) print('outdegrees concentration index : %.4f' % (outgini)) print('indegrees concentration index : %.4f' % (ingini)) print('symdegrees concentration index : %.4f' % (symgini)) listindex = list(range(order)) listindex.append('inf') print(' :', listindex) print('neighbourhood depths distribution :', list(nbDepths)) if meanLength != 'infinity': print("mean neighbourhood depth : %.2f " % (meanLength)) else: print('mean neighbourhood length :', meanLength) print('digraph diameter :', self.digraphDiameter) print('agglomeration distribution :') for i in range(order): print(actions[i], end=' ') print(": %.2f" % (self.agglomerationCoefficient[i])) print("agglomeration coefficient : %.2f" % (self.meanAgglomerationCoefficient))
[docs] def meanLength(self,Oriented=False): """ Renders the (by default non-oriented) mean neighbourhoor depth of self. !!! self.order must be set previously !!! """ nbDepths = self.neighbourhoodDepthDistribution(Oriented) nb = len(nbDepths) meanLength = 0.0 for i in range(nb): meanLength += i * nbDepths[i] if nbDepths[nb-1] != 0: meanLength = 'infinity' else: meanLength = meanLength/float(self.order) return meanLength
[docs] def meanDegree(self): """ Renders the mean degree of self. !!! self.size must be set previously !!! """ order = len(self.actions) outDegrees = self.outDegreesDistribution() inDegrees = self.inDegreesDistribution() degrees = [] nd = len(outDegrees) meanDegree = 0.0 for i in range(nd): degrees.append(outDegrees[i]+inDegrees[i]) meanDegree += i * (max(outDegrees[i],inDegrees[i])) if self.size == 0: meanDegree = 0 else: meanDegree = meanDegree/float(2 * self.order) return meanDegree
[docs] def computeMeanOutDegree(self): """ Renders the mean degree of self. !!! self.size must be set previously !!! """ order = len(self.actions) outDegrees = self.outDegreesDistribution() nd = len(outDegrees) meanOutDegree = 0.0 for i in range(nd): meanOutDegree += i * outDegrees[i] if self.size == 0: meanOutDegree = 0 else: meanOutDegree = meanOutDegree/float(self.order) return meanOutDegree
[docs] def computeMeanInDegree(self): """ Renders the mean indegree of self. !!! self.size must be set previously !!! """ order = len(self.actions) inDegrees = self.inDegreesDistribution() nd = len(inDegrees) meanInDegree = 0.0 for i in range(nd): meanInDegree += i * inDegrees[i] if self.size == 0: meanInDegree = 0 else: meanInDegree = meanInDegree/self.order return meanInDegree
[docs] def computeMedianOutDegree(self): """ Renders the median outdegree of self. !!! self.size must be set previously !!! """ order = len(self.actions) outDegrees = self.outDegreesDistribution() nd = len(outDegrees) outDegreesList = [] for d in range(nd): for x in range(outDegrees[d]): outDegreesList.append(d) outDegreesList.sort() #print 'outdegrees sorted', outDegreesList ndl = len(outDegreesList) if ndl % 2 == 0: medpos = ndl//2 medianOutDegree = outDegreesList[medpos] else: medpos0 = ndl//2 medpos1 = (ndl + 1)//2 medianOutDegree = (outDegreesList[medpos0] + outDegreesList[medpos1])/2 return medianOutDegree
[docs] def computeMedianSymDegree(self): """ Renders the median symmetric degree of self. !!! self.size must be set previously !!! """ symDegrees = self.symDegreesDistribution() nd = len(symDegrees) symDegreesList = [] for d in range(nd): for x in range(symDegrees[d]): symDegreesList.append(d) nd = len(symDegrees) symDegreesList.sort() ndl = len(symDegreesList) if ndl % 2 == 0: medpos = ndl//2 medianSymDegree = symDegreesList[medpos] else: medpos0 = ndl/2 medpos1 = (ndl + 1)//2 medianSymDegree = (symDegreesList[medpos0] + symDegreesList[medpos1])/2 return medianSymDegree
[docs] def computeMeanSymDegree(self): """ Renders the mean degree of self. !!! self.size must be set previously !!! """ order = float(len(self.actions)) symDegrees = self.symDegreesDistribution() nd = len(symDegrees) meanSymDegree = 0.0 for i in range(nd): meanSymDegree += i * symDegrees[i] if self.size == 0: meanSymDegree = 0.0 else: meanSymDegree = meanSymDegree/self.order return meanSymDegree
[docs] def computeDiameter(self, Oriented = True): """ Renders the (by default oriented) diameter of the digraph instance """ order = len(self.actions) nbDepths = self.neighbourhoodDepthDistribution(Oriented=Oriented) nbDepths.reverse() if nbDepths[0] != 0: diameter = 'infinity' else: diameter = 0 for i in range(len(nbDepths)): if nbDepths[i+1] != 0: diameter = order - (i+1) break return diameter
def _graphDetermination(self,Normalized=True): """ Output: average normalized (by default) arc determination: averageDeterm = ( sum_(x,y) [ abs( relf-relation[x][y] - Med )] / n ) / [( Max-Med ) if Normalized], where Med = self.valuationdomain['med'] and Max = self.valuationdomain['max']. """ Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] Max = self.valuationdomain['max'] determ = Decimal("0.0") for x,rx in self.relation.items(): for y,rxy in rx.items(): if rxy > Med: determ += rxy - Med else: determ += Med - rxy if Normalized: averageDeterm = (determ / Decimal(str(self.order)))/(Max-Med) else: averageDeterm = (determ / Decimal(str(self.order))) return averageDeterm
[docs] def computeSize(self): """ Renders the number of validated non reflexive arcs """ Med = self.valuationdomain['med'] #actions = [x for x in self.actions] #actions = self.actions #relation = self.relation size = 0 for x,rx in self.relation.items(): for y,rxy in rx.items(): if x != y: if rxy > Med: size += 1 return size
[docs] def computeCoSize(self): """ Renders the number of non validated non reflexive arcs """ Med = self.valuationdomain['med'] #actions = [x for x in self.actions] #relation = self.relation coSize = 0 for x,rx in self.relation.items(): for y,rxy in rx.items(): if x != y: if rxy < Med: coSize += 1 return coSize
[docs] def sizeSubGraph(self,choice): """ Output: (size, undeterm,arcDensity). Renders the arc density of the induced subgraph. """ Med = self.valuationdomain['med'] relation = self.relation order = float(len(choice)) size = 0 undeterm = 0 for x in choice: rx = relation[x] for y in choice: if x != y: if rx[y] > Med: size += 1 if rx[y] == Med: undeterm += 1 if len(choice) < 2: arcDensity = 0.0 else: arcDensity = (size * 100.0)/ (order * (order - 1 )) return size, undeterm, arcDensity
[docs] def agglomerationDistribution(self): """ Output: aggloCoeffDistribution, meanCoeff Renders the distribution of agglomeration coefficients. """ import array actions = [x for x in self.actions] order = len(actions) aggloCoeff = array.array('f', [0] * order) meanCoeff = 0.0 for i in range(order): neighborhood = self.gamma[actions[i]][0] | self.gamma[actions[i]][1] size, undeterm, aggloCoeff[i] = self.sizeSubGraph(neighborhood) meanCoeff += aggloCoeff[i] if order == 0: meanCoeff = 0.0 else: meanCoeff /= order return aggloCoeff, meanCoeff
[docs] def outDegreesDistribution(self): """ Renders the distribution of outdegrees. """ import array order = len(self.actions) outDegrees = array.array('i', [0] * (order+1)) for x in self.actions: nx = len(self.gamma[x][0]) outDegrees[nx] += 1 return outDegrees
[docs] def computeConcentrationIndexTrapez(self,X,N): """ Renders the Gini concentration index of the X serie. N contains the partial frequencies. Based on the triangles summation formula. """ n = len(X) #dg = self.outDegreesDistribution() X = list(range(10)) N = [0,0,0,0,0,0,0,0,0,0,] print('Xi ', X, N) Q = [0.0 for i in range(n)] F = [0.0 for i in range(n)] Qsum = 0.0 for i in range(n): Qsum += X[i] * N[i] print('Qsum ',Qsum) F[0] = float(X[0])/float(n) Q[0] = 0.0 for i in range(1,n,1): qi = (X[i] * N[i])/Qsum Q[i] += Q[i-1] + qi print('Q[i] i ', i, Q[i]) fi = float(N[i])/n F[i] += F[i-1] + fi print('i, F[i]', i, F) f0 = float(N[0])/float(n) gini = 1.0 - (f0*Q[0]) print('o gini ', gini) for i in range(1,n): fi = (float(N[i])/float(n)) gini -= fi * (Q[i-1] + Q[i]) print('i gini', i, gini) return gini
[docs] def computeConcentrationIndex(self,X,N): """ Renders the Gini concentration index of the X serie. N contains the partial frequencies. Based on the triangle summation formula. """ Qsum = 0.0 n = 0.0 r = len(X) for i in range(r): n += N[i] Qsum += X[i] * N[i] if Qsum != 0.0: Q = [0.0 for i in range(r)] F = [0.0 for i in range(r)] F[0] = N[0]/n Q[0] = (X[0] * N[0])/Qsum for i in range(1,r,1): qi = (X[i] * N[i])/Qsum Q[i] += Q[i-1] + qi fi = N[i]/n F[i] += F[i-1] + fi gini = 0.0 for i in range(r-1): gini += (F[i]*Q[i+1]) - (Q[i]*F[i+1]) else: gini = -1 return gini
[docs] def inDegreesDistribution(self): """ Renders the distribution of indegrees. """ import array order = len(self.actions) inDegrees = array.array('i', [0] * (order+1)) for x in self.actions: nx = len(self.gamma[x][1]) inDegrees[nx] += 1 return inDegrees
[docs] def symDegreesDistribution(self): """ Renders the distribution of symmetric degrees. """ import array order = len(self.actions) symDegrees = array.array('i', [0] * ((2*order)+1)) for x in self.actions: nx = len(self.gamma[x][0])+len(self.gamma[x][1]) symDegrees[nx] += 1 return symDegrees
[docs] def neighbourhoodDepthDistribution(self, Oriented=False): """ Renders the distribtion of neighbourhood depths. """ import array,copy actions = set(self.actions) order = len(actions) nv = order + 1 vecNeighbourhoodDepth = array.array('i', [0] * nv) for x in actions: nbx = 0 neighbx = set([x]) restactions = actions - neighbx while restactions != set() and nbx < order: nbx += 1 iterneighbx = copy.copy(neighbx) for y in iterneighbx: if Oriented: neighbx = neighbx | self.gamma[y][0] else: neighbx = neighbx | self.gamma[y][0] | self.gamma[y][1] restactions = actions - neighbx if restactions != set(): vecNeighbourhoodDepth[order] += 1 else: vecNeighbourhoodDepth[nbx] += 1 return vecNeighbourhoodDepth
[docs] def neighbourhoodCollection(self, Oriented = False, Potential = False): """ Renders the neighbourhood. """ import array,copy actions = set(self.actions) order = len(actions) if Potential: weakGamma = self.weakGammaSets() neighbourhoods = {} for x in actions: nbx = 0 neighbx = set([x]) restactions = actions - neighbx while restactions != set() and nbx < order: nbx += 1 iterneighbx = copy.copy(neighbx) for y in iterneighbx: if Potential: if Oriented: neighbx = neighbx | weakGamma[y][0] else: neighbx = neighbx | weakGamma[y][0] | weakGamma[y][1] else: if Oriented: neighbx = neighbx | self.gamma[y][0] else: neighbx = neighbx | self.gamma[y][0] | self.gamma[y][1] restactions = actions - neighbx #print 'neighbx', neighbx neighbourhoods[x]= neighbx return neighbourhoods
[docs] def strongComponents(self, setPotential = False): """ Renders the set of strong components of self. """ neighbourhoods = self.neighbourhoodCollection(Oriented = True, Potential = setPotential) strongComponents = set() for x in self.actions: componentx = set([x]) for y in neighbourhoods[x]: if x in neighbourhoods[y]: componentx = componentx | set([y]) strongComponents = strongComponents | set([frozenset(componentx)]) return strongComponents
[docs] def showMIS(self,withListing=True): """ Prints all maximal independent choices: Result in self.misset. """ import time print('*--- Maximal independent choices ---*') t0 = time.time() self.misset = set() actions = set(self.actions) n = len(actions) v = [0 for i in range(n+1)] for choice in self.MISgen(actions,frozenset()): v[len(choice)] += 1 if withListing: print(list(choice)) t1 = time.time() print('number of solutions: ', len(self.misset)) print('cardinality distribution') print('card.: ', list(range(n+1))) print('freq.: ', v) print('execution time: %.5f sec.' % (t1-t0)) print('Results in self.misset')
[docs] def showMinDom(self,withListing=True): """ Prints all minimal dominant choices: Result in self.domset. """ import time print('*--- Computing minimal dominant choices ---*') t0 = time.time() actions = set(self.actions) cover = {} for x in actions: cover[x]=self.gamma[x][1] | set([x]) dom1 = (frozenset(list(actions)),cover) #print dom1 self.minset = set() self.minhistory = set() for choice in self.minimalChoices(dom1): pass n = len(actions) v = [0 for i in range(n+1)] for choice in self.minset: v[len(choice)] += 1 if withListing: print(list(choice)) t1 = time.time() print('number of solutions: ', len(self.minset)) print('cardinality distribution') print('card.: ', list(range(n+1))) print('freq.: ', v) print('execution time: %.5f sec.' % (t1-t0)) print('iteration history: ', len(self.minhistory)) self.domset = self.minset.copy() print('Results in self.domset')
[docs] def showMinAbs(self,withListing=True): """ Prints minimal absorbent choices: Result in self.absset. """ import time print('*--- Computing minimal absorbent choices ---*') t0 = time.time() actions = set(self.actions) cover = {} for x in actions: cover[x]=self.gamma[x][0] | set([x]) abs1 = (frozenset(list(actions)),cover) print(abs1) self.minset = set() self.minhistory = set() for choice in self.minimalChoices(abs1): pass n = len(actions) v = [0 for i in range(n+1)] for choice in self.minset: v[len(choice)] += 1 if withListing: print(list(choice)) t1 = time.time() print('number of solutions: ', len(self.minset)) print('cardinality distribution') print('card.: ', list(range(n+1))) print('freq.: ', v) print('execution time: %.5f sec.' % (t1-t0)) print('iteration history: ', len(self.minhistory)) self.absset = self.minset.copy() print('Results in self.absset')
[docs] def showMaxDomIrred(self,withListing=True): """ Computing maximal +irredundant choices: Result in self.domirset. """ import time print('*--- Computing maximal +irredundant choices ---*') t0 = time.time() actions = set(self.actions) self.domirset = set() for choice in self.plusirredundant(actions): add = 1 mirsetit = self.domirset.copy() for mir in mirsetit: if mir < choice: self.domirset.remove(mir) else: if choice <= mir: add = 0 break if add == 1: self.domirset.add(frozenset(choice)) t1 = time.time() n = len(self.actions) v = [0 for i in range(n+1)] for choice in self.domirset: v[len(choice)] += 1 if withListing: print(list(choice)) print('number of solutions: ', len(self.domirset)) print('cardinality distribution') print('card.: ', list(range(n+1))) print('freq.: ', v) print('execution time: %.5f sec.' % (t1-t0)) print('Results in self.domirset')
[docs] def showMaxAbsIrred(self,withListing=True): """ Computing maximal -irredundant choices: Result in self.absirset. """ import time print('*--- Computing maximal -irredundant choices ---*') t0 = time.time() actions = set(self.actions) self.absirset = set() for choice in self.absirredundant(actions): add = 1 mirsetit = self.absirset.copy() for mir in mirsetit: if mir < choice: self.absirset.remove(mir) else: if choice <= mir: add = 0 break if add == 1: self.absirset.add(frozenset(choice)) t1 = time.time() n = len(self.actions) v = [0 for i in range(n+1)] for choice in self.absirset: v[len(choice)] += 1 if withListing: print(list(choice)) print('number of solutions: ', len(self.absirset)) print('cardinality distribution') print('card.: ', list(range(n+1))) print('freq.: ', v) print('execution time: %.5f sec.' % (t1-t0)) print('Results in self.absirset')
[docs] def showPreKernels(self,withListing=True): """ Printing dominant and absorbent preKernels: Result in self.dompreKernels and self.abspreKernels """ import time print('*--- Computing preKernels ---*') actions = set(self.actions) n = len(actions) dompreKernels = set() abspreKernels = set() t0 = time.time() for choice in self.independentChoices(self.singletons()): restactions = actions - choice[0][0] if restactions <= choice[0][1]: dompreKernels.add(choice[0][0]) if restactions <= choice[0][2]: abspreKernels.add(choice[0][0]) t1 = time.time() if withListing: print('Dominant preKernels :') for choice in dompreKernels: print(list(choice)) print(' independence : ', self.intstab(choice)) print(' dominance : ', self.domin(choice)) print(' absorbency : ', self.absorb(choice)) print(' covering : %.3f' % self.averageCoveringIndex(choice, direction='out')) print('Absorbent preKernels :') for choice in abspreKernels: print(list(choice)) print(' independence : ', self.intstab(choice)) print(' dominance : ', self.domin(choice)) print(' absorbency : ', self.absorb(choice)) print(' covered : %.3f' % self.averageCoveringIndex(choice, direction='in')) print('*----- statistics -----') print('graph name: ', self.name) print('number of solutions') print(' dominant kernels : ', len(dompreKernels)) print(' absorbent kernels: ', len(abspreKernels)) print('cardinality frequency distributions') print('cardinality : ', list(range(n+1))) v = [0 for i in range(n+1)] for ch in dompreKernels: v[len(ch)] += 1 print('dominant kernel : ',v) v = [0 for i in range(n+1)] for ch in abspreKernels: v[len(ch)] += 1 print('absorbent kernel: ',v) print('Execution time : %.5f sec.' % (t1-t0))
[docs] def computePreKernels(self): """ computing dominant and absorbent preKernels: Result in self.dompreKernels and self.abspreKernels """ actions = set(self.actions) n = len(actions) dompreKernels = set() abspreKernels = set() for choice in self.independentChoices(self.singletons()): restactions = actions - choice[0][0] if restactions <= choice[0][1]: dompreKernels.add(choice[0][0]) if restactions <= choice[0][2]: abspreKernels.add(choice[0][0]) self.dompreKernels = dompreKernels self.abspreKernels = abspreKernels
[docs] def generateDomPreKernels(self): """ Generate all dominant prekernels from independent choices generator. """ actions = set(self.actions) for item in self.independentChoices(self.singletons()): choice = item[0][0] gammaDomChoice = item[0][1] restactions = actions - choice if restactions <= gammaDomChoice: yield choice
[docs] def generateAbsPreKernels(self): """ Generate all absorbent prekernels from independent choices generator. """ actions = set(self.actions) for item in self.independentChoices(self.singletons()): choice = item[0][0] gammaAbsChoice = item[0][2] restactions = actions - choice if restactions <= gammaAbsChoice: yield choice
[docs] def components(self): """Renders the list of connected components.""" A = {} for x in self.actions: A[x] = 0 ncomp = 1 ConComp = [] for x in A: Comp = set() if A[x] == 0: A[x] = ncomp Comp = Comp | set([x]) Comp = Comp | self._collectcomps(x, A, ncomp) if len(Comp) > 0: ncomp = ncomp + 1 ConComp = ConComp + [Comp] return ConComp
[docs] def showComponents(self): """Shows the list of connected components of the digraph instance.""" print('*--- Connected Components ---*') k=1 for Comp in self.components(): component = list(Comp) component.sort() print(str(k) + ': ' + str(component)) xk = k + 1
def _collectcomps(self, x, A, ncomp): """ Internal recursive subroutine of the components method.""" Comp = set() Nx = self.gamma[x][0] | self.gamma[x][1] for y in Nx: if A[y] == 0: A[y] = ncomp Comp.add(y) Comp = Comp | self._collectcomps(y, A, ncomp) return Comp
[docs] def outDegrees(self): """ renders the median cut outdegrees """ outDegrees ={} for x in self.actions: outDegrees[x] = len(self.gamma[x][0]) return outDegrees
[docs] def inDegrees(self): """ renders the median cut indegrees """ inDegrees ={} for x in self.actions: inDegrees[x] = len(self.gamma[x][1]) return inDegrees
def _bestRanks(self): """ renders best possible ranks from indegrees account """ bestRanks = {} inDegrees = self.inDegrees() for x in self.actions: bestRanks[x] = inDegrees[x] + 1 return bestRanks def _worstRanks(self): """ renders worst possible ranks from outdegrees account """ worstRanks = {} outDegrees = self.outDegrees() for x in self.actions: worstRanks[x] = self.order - outDegrees[x] return worstRanks
[docs] def gammaSets(self): """ Renders the dictionary of neighborhoods {node: (dx,ax)} with set *dx* gathering the dominated, and set *ax* gathering the absorbed neighborhood. """ Med = self.valuationdomain['med'] actions = self.actions relation = self.relation gamma = {} for x in actions: dx = set() ax = set() rx = relation[x] for y in actions: if x != y: if rx[y] > Med: dx.add(y) if relation[y][x] > Med: ax.add(y) gamma[x] = (dx,ax) return gamma
[docs] def notGammaSets(self): """ Renders the dictionary of neighborhoods {node: (dx,ax)} with set *dx* gathering the not dominated, and set *ax* gathering the not absorbed neighborhood. """ Med = self.valuationdomain['med'] actions = self.actions relation = self.relation notGamma = {} for x in actions: dx = set() ax = set() rx = relation[x] for y in actions: if x != y: if rx[y] < Med: dx.add(y) if relation[y][x] < Med: ax.add(y) notGamma[x] = (dx,ax) return notGamma
def _gammaSets(self): """ Renders the dictionary of neighborhoods {node: (dx,ax)}""" gamma = {} for x in self.actions: dx = self.dneighbors(x) ax = self.aneighbors(x) gamma[x] = (dx,ax) return gamma
[docs] def weakGammaSets(self): """ Renders the dictionary of neighborhoods {node: (dx,ax)}""" weakGamma = {} for x in self.actions: dx = self.weakDneighbors(x) ax = self.weakAneighbors(x) weakGamma[x] = (dx,ax) return weakGamma
def _notGammaSets(self): """ Renders the dictionary of not neighborhoods {node: (dx,ax)} """ notGamma = {} for x in self.actions: dx = self.notdneighbors(x) ax = self.notaneighbors(x) notGamma[x] = (dx,ax) return notGamma
[docs] def weakDneighbors(self,node): """ Renders the set of dominated out-neighbors of a node.""" Med = self.valuationdomain['med'] nb = set() for a in self.actions: if self.relation[node][a] >= Med: nb.add(a) return nb
[docs] def dneighbors(self,node): """ Renders the set of dominated out-neighbors of a node.""" Med = self.valuationdomain['med'] nb = set() for a in self.actions: if self.relation[node][a] > Med: nb.add(a) return nb
[docs] def notdneighbors(self,node): """ Renders the set of not dominated out-neighbors of a node.""" Med = self.valuationdomain['med'] nb = set() for a in self.actions: if a != node: if self.relation[node][a] < Med: nb.add(a) return nb
[docs] def aneighbors(self,node): """ Renders the set of absorbed in-neighbors of a node.""" Med = self.valuationdomain['med'] nb = set() for a in self.actions: if self.relation[a][node] > Med: nb.add(a) return nb
[docs] def weakAneighbors(self,node): """ Renders the set of absorbed in-neighbors of a node.""" Med = self.valuationdomain['med'] nb = set() for a in self.actions: if self.relation[a][node] >= Med: nb.add(a) return nb
[docs] def notaneighbors(self,node): """ Renders the set of absorbed not in-neighbors of a node.""" Med = self.valuationdomain['med'] nb = set() for a in self.actions: ## if a != node: ## if self.relation[a][node] < Med: ## nb.add(a) if a != node: if self.relation[a][node] < Med: nb.add(a) return nb
[docs] def singletons(self): """list of singletons and neighborhoods [(singx1, +nx1, -nx1, not(+nx1 or -nx1)),.... ]""" s = [] for x in self.actions: indep = set(self.actions) - (self.gamma[x][0] | self.gamma[x][1]) s = s + [(frozenset([x]),self.gamma[x][0],self.gamma[x][1],indep)] return s
[docs] def MISgen(self,S,I): """ generator of maximal independent choices (voir Byskov 2004): * S ::= remaining nodes; * I ::= current independent choice .. note:: Inititalize: self.MISgen(self.actions.copy(),set()) """ if S == set(): add = 1 self.missetit = self.misset.copy() for mis in self.missetit: if mis < I: self.misset.remove(mis) else: if I <= mis: add = 0 break if add == 1: self.misset = self.misset | frozenset([I]) yield I else: v = S.pop() Sv = S - (self.gamma[v][0] | self.gamma[v][1]) Iv = I | set([v]) for choice in self.MISgen(Sv,Iv): yield choice for choice in self.MISgen(S,I): yield choice
[docs] def independentChoices(self,U): """ Generator for all independent choices with neighborhoods of a bipolar valued digraph: .. note:: * Initiate with U = self.singletons(). * Yields [(independent choice, domnb, absnb, indnb)]. """ if U == []: yield [(frozenset(),set(),set(),set(self.actions))] else: x = list(U.pop()) for S in self.independentChoices(U): yield S if x[0] <= S[0][3]: Sxgamdom = S[0][1] | x[1] Sxgamabs = S[0][2] | x[2] Sxindep = S[0][3] & x[3] Sxchoice = S[0][0] | x[0] Sx = [(Sxchoice,Sxgamdom,Sxgamabs,Sxindep)] yield Sx
[docs] def coveringIndex(self,choice,direction="out"): """ Renders the covering index of a given choice in a set of objects, ie the minimum number of choice members that cover each non selected object. """ from decimal import Decimal actions = set([x for x in self.actions]) nonSelected = actions - choice n = len(choice) index = n for x in nonSelected: if direction == 'out': index = min( index, len(self.gamma[x][1] & choice) ) else: index = min( index, len(self.gamma[x][0] & choice) ) if n > 0: return Decimal(str(index))/Decimal(str(n)) else: return Decimal("0.0")
[docs] def averageCoveringIndex(self,choice,direction="out"): """ Renders the average covering index of a given choice in a set of objects, ie the average number of choice members that cover each non selected object. """ from decimal import Decimal choice = set(choice) actions = set([x for x in self.actions]) nonSelected = actions - choice n = len(choice) m = len(nonSelected) index = 0 for x in nonSelected: if direction == 'out': index += len(self.gamma[x][1] & choice) else: index += len(self.gamma[x][0] & choice) if n > 0 and m > 0: return ( Decimal(str(index))/Decimal(str(m)) ) / Decimal(str(n)) elif n > 0: return Decimal("1.0") else: return Decimal("0.0")
[docs] def zoomValuation(self,zoomFactor=1.0): """ Zooms in or out, depending on the value of the zoomFactor provided, the bipolar valuation of a digraph. """ zoomFactor = Decimal(str(zoomFactor)) oldMax = self.valuationdomain['max'] oldMin = self.valuationdomain['min'] oldMed = self.valuationdomain['med'] newMin = oldMin * zoomFactor newMax = oldMax * zoomFactor newMed = oldMed * zoomFactor actions = self.actions oldRelation = self.relation newRelation = {} for x in actions: newRelation[x] = {} for y in actions: newRelation[x][y] = oldRelation[x][y] * zoomFactor # install new values in self self.valuationdomain['max'] = newMax self.valuationdomain['min'] = newMin self.valuationdomain['med'] = newMed self.relation = newRelation
[docs] def recodeValuation(self,newMin=-1.0,newMax=1.0,ndigits=4,Debug=False): """ Recodes the characteristic valuation domain according to the parameters given. *ndigits* indicates the number of decimal digits of the valuation. """ from decimal import Decimal formatString = '%%.%df' % ndigits oldMax = Decimal(formatString % self.valuationdomain['max']) oldMin = Decimal(formatString % self.valuationdomain['min']) oldMed = Decimal(formatString % self.valuationdomain['med']) try: oldPrecision = self.valuationdomain['precision'] except: oldPrecision = Decimal(formatString % 0.0) oldAmplitude = oldMax - oldMin if Debug: print(oldMin, oldMed, oldMax, oldAmplitude) newMin = Decimal(formatString % newMin) newMax = Decimal(formatString % newMax) # the normalized median is set to a strict zero value if newMin == Decimal('-1.00') and newMax == Decimal('1.00'): newMed = Decimal('0.0') else: newMed = Decimal(formatString % ((newMax + newMin)/Decimal('2.0')) ) newPrecision = oldPrecision/oldMax newAmplitude = newMax - newMin if Debug: print(newMin, newMed, newMax, newAmplitude) print('old and new precison', oldPrecision, newPrecision) actions = self.actions oldrelation = self.relation newrelation = {} for x in actions: newrelation[x] = {} nrx = newrelation[x] orx = oldrelation[x] for y in actions: if orx[y] == oldMax: nrx[y] = newMax elif orx[y] == oldMin: nrx[y] = newMin elif orx[y] == oldMed: nrx[y] = newMed else: newValue = newMin + ((orx[y] - oldMin)/oldAmplitude)*newAmplitude nrx[y] = Decimal(formatString % newValue) if Debug: print(x,y,orx[y],nrx[y]) # install new values in self self.valuationdomain['max'] = newMax self.valuationdomain['min'] = newMin self.valuationdomain['med'] = newMed self.valuationdomain['precision'] = newPrecision if ndigits == 0: self.valuationdomain['hasIntegerValuation'] = True else: self.valuationdomain['hasIntegerValuation'] = False self.relation = newrelation
[docs] def dominantChoices(self,S): """ Generates all minimal dominant choices of a bipolar valued digraph. .. note:: Initiate with S = self.actions.copy(). """ Med = self.valuationdomain['med'] add = 1 domsetit = self.domset.copy() for dom in domsetit: if S < dom: self.domset.remove(dom) else: if S >= dom: add = 0 break if add == 1: self.domset = self.domset | set([frozenset(S)]) yield S for x in S: S1 = S - set([x]) if self.domin(S1) > Med: for choice in self.dominantChoices(S1): yield choice
[docs] def minimalChoices(self,S): """ Generates all dominant or absorbent choices of a bipolar valued digraph. .. note: * Initiate with S = (actions, dict of dominant or absorbent closed neighborhoods) * See showMinDom and showMinAbs methods. """ if S[0] not in self.minhistory: self.minhistory = self.minhistory | set([frozenset(S[0])]) add = True minsetit = self.minset.copy() for minch in minsetit: if S[0] < minch: self.minset.remove(minch) else: if S[0] >= minch: add = False break if add: self.minset = self.minset | set([frozenset(S[0])]) yield S for x in S[0]: Sxchoice = S[0] - set([x]) Sx = (Sxchoice,{}) covering = True for cover in S[1]: coverx = S[1][cover] - set([x]) if coverx == set(): covering = False break Sx[1][cover] = coverx if covering: for choice in self.minimalChoices(Sx): yield choice
[docs] def absorbentChoices(self,S): """ Generates all minimal absorbent choices of a bipolar valued digraph. """ Med = self.valuationdomain['med'] add = 1 abssetit = self.absset.copy() for absch in abssetit: if S < absch: self.absset.remove(absch) else: if S >= absch: add = 0 break if add == 1: self.absset = self.absset | set([frozenset(S)]) yield S for x in S: S1 = S - set([x]) if self.absorb(S1) > Med: for choice in self.absorbentChoices(S1): yield choice
[docs] def kChoices(self,A,k): """ Renders all choices of length k from set A """ import copy if k == 0: yield set() else: while len(A) > 0: x = A.pop() Ax = copy.copy(A) k1 = k - 1 for ch in self.kChoices(Ax,k1): yield ch | set([x])
[docs] def powerset(self,U): """ Generates all subsets of a set. """ if U == set(): yield set() else: U1 = set(U) x = U1.pop() for S in self.powerset(U1): yield S yield S | set([x])
[docs] def plusirredundant(self,U): """ Generates all +irredundant choices of a digraph. """ Med = self.valuationdomain['med'] if U == set(): yield set() else: x = U.pop() for S in self.plusirredundant(U): yield S Sx = S | set([x]) if self.domirred(Sx) > Med: yield Sx
[docs] def absirredundant(self,U): """ Generates all -irredundant choices of a digraph. """ Med = self.valuationdomain['med'] if U == set(): yield set() else: x = U.pop() for S in self.absirredundant(U): yield S S1 = S | set([x]) if self.absirred(S1) > Med: Sx = S | set([x]) yield Sx
[docs] def intstab(self,choice): """ Computes the independence degree of a choice. """ Min = self.valuationdomain['min'] Max = self.valuationdomain['max'] relation = self.relation deg = Min for a in choice: for b in choice: x = relation[a][b] if x > deg and a != b: deg = x res = Max - deg + Min return res
[docs] def domin(self,choice): """ Renders the dominance degree of a choice. """ deg = self.valuationdomain['max'] Min = self.valuationdomain['min'] restactions = set(self.actions) - choice for a in restactions: dega = Min for b in choice: x = self.relation[b][a] if x > dega: dega = x if dega < deg: deg = dega return deg
[docs] def absorb(self,choice): """ Renders the absorbency degree of a choice. """ deg = self.valuationdomain['max'] Min = self.valuationdomain['min'] restactions = set(self.actions) - choice for a in restactions: dega = Min for b in choice: x = self.relation[a][b] if x > dega: dega = x if dega < deg: deg = dega return deg
[docs] def domirred(self,choice): """ Renders the crips +irredundance degree of a choice. """ Med = self.valuationdomain['med'] irred = 1 if len(choice) > 1: for x in choice: if self.domirredx(choice,x) < Med: irred = 0 break if irred == 1: return self.valuationdomain['max'] else: return self.valuationdomain['min']
[docs] def domirredval(self,choice,relation): """ Renders the valued +irredundance degree of a choice. """ #import array actions = self.actions n = len(actions) Min = Decimal(str(self.valuationdomain['min'])) Med = Decimal(str(self.valuationdomain['med'])) Max = Decimal(str(self.valuationdomain['max'])) for x in actions: relation[x][x] = Max result = Max for x in choice: nbclx = self.readabsvector(x,relation) nbclchoice = [Min for i in actions] restchoice = set(choice) restchoice.remove(x) for y in restchoice: nbcly = self.readabsvector(y,relation) nbclchoice = [max(nbclchoice[i],nbcly[i]) for i in range(n)] resultx = max([min(nbclx[i],self.contra(nbclchoice)[i]) for i in range(n)]) result = min(result, resultx) return result
[docs] def domirredx(self,choice,x): """ Renders the crips +irredundance degree of node x in a choice. """ Max = self.valuationdomain['max'] Min = self.valuationdomain['min'] nx = self.gamma[x][0] | set([x]) chx = choice - set([x]) ny = set() for y in chx: ny = ny | self.gamma[y][0] | set([y]) nxpriv = nx - ny if nxpriv == set(): return Min else: return Max
[docs] def absirredval(self,choice,relation): """ Renders the valued -irredundance degree of a choice. """ #import array actions = self.actions n = len(actions) Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] Max = self.valuationdomain['max'] for x in actions: relation[x][x] = Max result = Max for x in choice: nbclx = self.readdomvector(x,relation) nbclchoice = [Decimal(str(Min)) for i in actions] restchoice = set(choice) restchoice.remove(x) for y in restchoice: nbcly = self.readdomvector(y,relation) nbclchoice = [max(nbclchoice[i],nbcly[i]) for i in range(n)] resultx = max([min(nbclx[i],self.contra(nbclchoice)[i]) for i in range(n)]) result = min(result, resultx) return result
[docs] def absirred(self,choice): """ Renders the crips -irredundance degree of a choice. """ Med = self.valuationdomain['med'] irred = 1 if len(choice) > 1: for x in choice: if self.absirredx(choice,x) < Med: irred = 0 break if irred == 1: return self.valuationdomain['max'] else: return self.valuationdomain['min']
[docs] def absirredx(self,choice,x): """Computes the crips -irredundance degree of node x in a choice.""" Max = self.valuationdomain['max'] Min = self.valuationdomain['min'] nx = self.gamma[x][1] | set([x]) chx = choice - set([x]) ny = set() for y in chx: ny = ny | self.gamma[y][1] | set([y]) nxpriv = nx - ny if nxpriv == set(): return Min else: return Max
[docs] def save(self,fileName='tempdigraph',option=None,DecimalValuation=True,decDigits=2): """Persistent storage of a Digraph class instance in the form of a python source code file""" print('*--- Saving digraph in file: <' + fileName + '.py> ---*') actions = self.actions relation = self.relation Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] Max = self.valuationdomain['max'] fileNameExt = str(fileName)+str('.py') fo = open(fileNameExt, 'w') fo.write('# Saved digraph instance\n') if DecimalValuation: fo.write('from decimal import Decimal\n') fo.write('from collections import OrderedDict\n') fo.write('actions = OrderedDict([\n') for x in actions: fo.write('(\'' + str(x) + '\',\n') try: fo.write(str(actions[x])+'),\n') except: fo.write('{\'name\': \'%s\'}),\n' % str(x)) fo.write('])\n') try: hasIntegerValuation = self.valuationdomain['hasIntegerValuation'] except KeyError: hasIntegerValuation = not DecimalValuation if not hasIntegerValuation: fo.write('valuationdomain = {\'hasIntegerValuation\': False, \'min\': Decimal("'+str(Min)+'"),\'med\': Decimal("'+str(Med)+'"),\'max\': Decimal("'+str(Max)+'")}\n') else: fo.write('valuationdomain = {\'hasIntegerValuation\': True, \'min\': '+str(Min)+',\'med\': '+str(Med)+',\'max\': '+str(Max)+'}\n') fo.write('relation = {\n') for x in actions: fo.write('\'' + str(x) + '\': {\n') for y in actions: if not hasIntegerValuation: valueString = '\': Decimal(\'%%.%df\'),\n' % (decDigits) fo.write('\'' + str(y) + (valueString % relation[x][y])) #fo.write('\'' + str(y) + '\': Decimal("' + str(relation[x][y]) + '"),\n') else: fo.write('\'' + str(y) + '\':' + str(relation[x][y]) + ',\n') fo.write('},\n') fo.write( '}\n') if option == 'withAutomorphismGenerators': fo.write('reflections = {\n') for ga in self.reflections: fo.write('\' '+str(ga)+'\':'+str(self.reflections[ga])+',\n') fo.write('}\n') fo.write('permutations = {\n') for ga in self.permutations: fo.write("\'"+str(ga)+"\': {\n") for p in self.permutations[ga]: fo.write('\''+str(p)+'\': \''+str(self.permutations[ga][p])+'\',\n') fo.write('},\n') fo.write('}\n') fo.close()
[docs] def saveCSV(self,fileName='tempdigraph',Normalized=False, Dual=False,Converse=False,Diagonal=False,Debug=False): """Persistent storage of a Digraph class instance in the form of a csv file. """ from copy import deepcopy import csv com = '' if Normalized: com += 'normalized' if Dual and Converse: com += ' and codual' elif Dual and not Converse: com += ' and dual' elif Converse: com += ' and converse' else: if Dual and Converse: com += 'codual' elif Dual and not Converse: com += 'dual' elif Converse: com += 'converse' if Debug: print('*--- Saving %s digraph in file: %s.csv> ---*' % (com,fileName)) fileNameExt = str(fileName)+str('.csv') fo = open(fileNameExt, 'w') csvfo = csv.writer(fo,quoting=csv.QUOTE_NONNUMERIC) actionsList = [x for x in self.actions] actionsList.sort() headerText = ["d"] + actionsList if Debug: print(headerText) csvfo.writerow(headerText) dg = deepcopy(self) if Normalized: dg.recodeValuation() Min = dg.valuationdomain['min'] Med = dg.valuationdomain['med'] Max = dg.valuationdomain['max'] if Dual: dg = -dg if Converse: dg = ~dg relation = dg.relation for x in actionsList: rowText = [x] for y in actionsList: if x == y: if not Diagonal: rowText.append(float(Med)) else: rowText.append(float(relation[x][y])) else: rowText.append(float(relation[x][y])) if Debug: print(rowText) csvfo.writerow(rowText) fo.close()
## def saveDistance2MaxCSV(self,fileName='tempdigraph',Normalized=False, ## Dual=False,Converse=False,Diagonal=True,Debug=False): ## """Persistent storage of a Digraph class instance in the form of ## a csv file. """ ## from copy import deepcopy ## import csv ## com = '' ## if Normalized: ## com += 'normalized' ## if Dual and Converse: ## com += ' and codual' ## elif Dual and not Converse: ## com += ' and dual' ## elif Converse: ## com += ' and converse' ## else: ## if Dual and Converse: ## com += 'codual' ## elif Dual and not Converse: ## com += 'dual' ## elif Converse: ## com += 'converse' ## ## if Debug: ## print('*--- Saving %s digraph in file: %s.csv> ---*' % (com,fileName)) ## fileNameExt = str(fileName)+str('.csv') ## fo = open(fileNameExt, 'w') ## csvfo = csv.writer(fo,quoting=csv.QUOTE_NONNUMERIC) ## actionsList = [x for x in self.actions] ## actionsList.sort() ## headerText = ["d"] + actionsList ## if Debug: ## print(headerText) ## csvfo.writerow(headerText) ## dg = deepcopy(self) ## if Normalized: ## dg.recodeValuation(-1,1) ## Min = dg.valuationdomain['min'] ## Med = dg.valuationdomain['med'] ## Max = dg.valuationdomain['max'] ## if Dual: ## dg = -dg ## if Converse: ## dg = ~dg ## relation = dg.relation ## for x in actionsList: ## rowText = [x] ## for y in actionsList: ## if x == y: ## if not Diagonal: # minimal distance ## rowText.append(0.0) ## else: ## rowText.append(float(Max - relation[x][y])) ## else: ## rowText.append(float(Max - relation[x][y])) ## if Debug: ## print(rowText) ## csvfo.writerow(rowText) ## fo.close() #----------------
[docs] def computeShortestPathLengths(self,WeakPaths=False, Comments=False,Debug=False): """ Renders a double dictionary with the directed distances, i.e. the shortest path lengths between all self.actions. Equals *None* if there does not exist a directed path between two actions. *Source*: Claude Berge, *The Theory of Graphs*, Dover (2001) pp. 119, original in French Dunod (1958) """ Med = self.valuationdomain['med'] Min = self.valuationdomain['min'] relation = self.relation actions = [x for x in self.actions] order = self.order ri = {} for i in range(1,order): if i == 1: ri[i] = relation else: ri[i] = {} for x in actions: ri[i][x] = {} for y in actions: ri[i][x][y] = Med if x != y: accz = ri[i-1][x][y] for z in actions: #if x != z and y != z: accz = max(accz,min(ri[i-1][x][z],ri[1][z][y])) ri[i][x][y] = accz distances = {} for x in actions: distances[x] = {} for y in actions: distances[x][y] = None if x == y: distances[x][y] = 0 else: for i in range(order-1,0,-1): if WeakPaths: if ri[i][x][y] >= Med: distances[x][y] = i else: if ri[i][x][y] > Med: distances[x][y] = i if Comments: if WeakPaths: print('Shortest weak path lengths') else: print('Shortest path lengths') print('x, y, distance') for x in actions: for y in actions: print(x,y,distances[x][y]) if Debug: self.ri = ri if WeakPaths: self.shortestWeakPathLengths = distances else: self.shortestPathLengths = distances return distances
#-------------------
[docs] def computeDigraphCentres(self,WeakDistances=False,Comments=False): """ The centers of a digraph are the nodes with finite minimal shortes path lengths. The maximal neighborhood distances are stored in *self.maximalNeighborhoodDistances*. The corresponding digraph radius and diameter are stored respectively in *self.radius* and *self.diameter*. With *Comments* = True, all these results are printed out. *Source*: Claude Berge, *The Theory of Graphs*, Dover (2001) pp. 119, original in French Dunod (1958) """ if WeakDistances: try: distances = self.shortesWeakPathLengths() except: distances = self.computeShortestPathLengths(WeakPaths=True) else: try: distances = self.shortesPathLengths() except: distances = self.computeShortestPathLengths() aN = {} actions = [x for x in self.actions] order = self.order for x in actions: aN[x] = 0 for y in actions: if distances[x][y] is not None: aN[x] = max(aN[x],distances[x][y]) else: aN[x] = order if Comments: if WeakDistances: print('Maximal weak neighborhood distances') else: print('Maximal neighborhood distances') for x in actions: if aN[x] < order: print(x,aN[x]) else: print(x,None) self.maximalNeighborhoodDistances = aN minAN = order maxAN = 0 for x in actions: minAN = min(minAN,aN[x]) maxAN = max(maxAN,aN[x]) # raduis and diameter if minAN < order: self.radius = minAN else: self.radius = None if maxAN < order: self.diameter = maxAN else: self.diameter = None if Comments: print('Radius', self.radius) print('Diameter',self.diameter) centres = {} if minAN < order: for x in actions: if aN[x] == minAN: centres[x] = aN[x] if WeakDistances: if Comments: print('Digraph weak centre(s)', centres) self.weakCentres = centres else: if Comments: print('Digraph centre(s)', centres) self.centres = centres return centres
#------------------
[docs] def chordlessPaths(self,Pk,n2,Odd=False,Comments=False,Debug=False): """ New procedure from Agrum study April 2009 recursive chordless path extraction starting from path Pk = [n2, ...., n1] and ending in node n2. Optimized with marking of visited chordless P1s. """ if Comments: Debug = True n1 = Pk[-1] self.visitedArcs.add((n1,n2)) self.visitedArcs.add((n2,n1)) med = self.valuationdomain['med'] if self.relation[n1][n2] > med and self.relation[n2][n1] <= med: detectedChordlessPath = True #self.visitedArcs.add((n1,n2)) #OddFlag = True if Debug: print('len(Pk)', Pk, len(Pk), len(Pk) % 2) if Odd: if (len(Pk) % 2) != 1: OddFlag = False else: OddFlag = True else: OddFlag = True if Debug: print('OddFlag: ', OddFlag) if OddFlag: #Pk.append(n2) self.xCC.append(Pk) if Debug: print('Chordless circuit certificate -->>> ', Pk) else: detectedChordlessPath = False NBn1 = set(self.gamma[n1][0]-self.gamma[n1][1]) while NBn1 != set(): n = NBn1.pop() if (n1,n) not in self.visitedArcs and (n,n1) not in self.visitedArcs: P = list(Pk) noChord = True for x in P[:-1]: ## if x == n1: ## if self.relation[n][x] > med: ## noChord = False ## elif x == n2: if x == n2: if self.relation[x][n] > med: noChord = False break else: if self.relation[x][n] > med or self.relation[n][x] > med: noChord = False break if noChord: P.append(n) if Debug: print('P,n2',P,n2) if self.chordlessPaths(P,n2,Odd,Comments,Debug): detectedChordlessPath = True #self.visitedArcs.add((n1,n2)) if Debug: print('No further chordless path from ',n1,' to ', n2) return detectedChordlessPath
[docs] def detectChordlessPath(self,Pk,n2,Comments=False,Debug=False): """ New procedure from Agrum study April 2009 recursive chordless path extraction starting from path Pk = [n2, ...., n1] and ending in node n2. Optimized with marking of visited chordless P1s. """ if Comments: Debug = True n1 = Pk[-1] self.visitedArcs.add((n1,n2)) self.visitedArcs.add((n2,n1)) med = self.valuationdomain['med'] if self.relation[n1][n2] > med and self.relation[n2][n1] <= med: Detected = True if Debug: print('Chordless circuit certificate -->>> ', Pk) else: Detected = False NBn1 = set(self.gamma[n1][0]-self.gamma[n1][1]) while NBn1 != set(): n = NBn1.pop() if (n1,n) not in self.visitedArcs and (n,n1) not in self.visitedArcs: P = list(Pk) noChord = True for x in P[:-1]: if x == n2: if self.relation[x][n] > med: noChord = False break else: if self.relation[x][n] > med or self.relation[n][x] > med: noChord = False break if noChord: P.append(n) if Debug: print('P,n2',P,n2) Detected = self.detectChordlessPath(P,n2,Comments,Debug) if Detected: break return Detected
# def detectCppChordlessCircuits(self,Debug=False): # """ # python wrapper for the C++/Agrum based chordless circuits detection # exchange arguments with external temporary files. # Returns a boolean value # .. warning:: Deprecated !!! The pure python version is more efficient # """ # import os # from tempfile import mkstemp # fd, tempFileName = mkstemp() # fo = os.fdopen(fd,'w') # Med = self.valuationdomain['med'] # actions = [x for x in self.actions] # relation = self.relation # for i,x in enumerate(actions): # for j,y in enumerate(actions): # if i != j: # if relation[x][y] > Med: # fo.write('%d %d\n' % (i+1,j+1)) # fo.close() # resultFile = tempFileName+'.py' # if os.path.exists('/usr/bin/detectChordlessCircuits'): # os.system('/usr/bin/detectChordlessCircuits ' + tempFileName + ' ' + resultFile) # elif os.path.exists('/usr/local/bin/detectChordlessCircuits'): # os.system('/usr/local/bin/detectChordlessCircuits ' + tempFileName + ' ' + resultFile) # elif os.path.exists('/opt/local/bin/detectChordlessCircuits'): # os.system('/opt/local/bin/detectChordlessCircuits ' + tempFileName + ' ' + resultFile) # elif os.path.exists('/home/users/rbisdorff/bin/detectChordlessCircuits'): # os.system('/home/users/rbisdorff/bin/detectChordlessCircuits ' + tempFileName + ' ' + resultFile) # else: # print('Error: detectChordlessCircuits binary could not be found !!!') # argDict = {} # fi = open(resultFile,'r') # fileText = fi.read() # fi.close() # exec(compile(fileText, str(resultFile), 'exec'),argDict) # circuits = argDict['circuitsList'] # if circuits == []: # Detected = False # else: # Detected = True # if Debug: # print(resultFile) # print(argDict['circuitsList']) # if Detected: # print('A chordless circuit has been detected !') # else: # print('No chordless circuit has been detected !') # print('certificate: ', circuits) # return Detected # def computeCppInOutPipingChordlessCircuits(self,Odd=False,Debug=False): # """ # python wrapper for the C++/Agrum based chordless circuits enumeration # exchange arguments with external temporary files # Result in *self.circuitsList*. # .. warning:: Deprecated !!! The pure python version is more efficient # """ # import os # from subprocess import Popen,PIPE # if os.path.exists('/usr/bin/enumChordlessCircuitsInOutPiping'): # p = Popen(args=['/usr/bin/enumChordlessCircuitsInOutPiping'],stdin=PIPE,stdout=PIPE) # elif os.path.exists('/usr/local/bin/enumChordlessCircuitsInOutPiping'): # p = Popen(args=['/usr/local/bin/enumChordlessCircuitsInOutPiping'],stdin=PIPE,stdout=PIPE) # elif os.path.exists('/opt/local/bin/enumChordlessCircuitsInOutPiping'): # p = Popen(args=['/opt/local/bin/enumChordlessCircuitsInOutPiping'],stdin=PIPE,stdout=PIPE) # elif os.path.exists('/home/users/rbisdorff/bin/enumChordlessCircuitsInOutPiping'): # p = Popen(args=['/home/users/rbisdorff/bin/enumChordlessCircuitsInOutPiping'],stdin=PIPE,stdout=PIPE) # else: # print('Error: executable enumChordlessCircuitsInOutPiping not found !!!') # Med = self.valuationdomain['med'] # actions = [x for x in self.actions] # relation = self.relation # inputString = '' # for i,x in enumerate(actions): # for j,y in enumerate(actions): # if i != j: # if relation[x][y] > Med: # inputString += '%d %d \n' % (i+1,j+1) # circuits = eval(p.communicate(input=inputString.encode('utf-8'))[0]) # if Debug: # print(circuits) # result = [] # history = set() # for x in circuits: # # !! a circuit has a length n + 1 !! # if Odd: # r = len(x) % 2 # ## if Debug: # ## print x, r # if r != 1: # oddCircuit = [] # for ino in x[:-1]: # oddCircuit.append(actions[ino-1]) # circuitActions = [y for y in flatten(oddCircuit)] # circuitSet = frozenset(circuitActions) # if circuitSet not in history: # result.append( ( circuitActions, circuitSet ) ) # history.add(circuitSet) # #result.append( ( oddCircuit, frozenset(oddCircuit) ) ) # else: # allCircuit = [] # for ino in x[:-1]: # allCircuit.append(actions[ino-1]) # circuitActions = [y for y in flatten(allCircuit)] # circuitSet = frozenset(circuitActions) # if circuitSet not in history: # result.append( ( circuitActions, circuitSet ) ) # history.add(circuitSet) # #result.append( ( allCircuit, frozenset(allCircuit) ) ) # self.circuitsList = result # return result # def computeCppChordlessCircuits(self,Odd=False,Debug=False): # """ # python wrapper for the C++/Agrum based chordless circuits enumeration # exchange arguments with external temporary files # Result in *self.circuitsList*. # .. warning:: Deprecated !!! The pure python version is more efficient # """ # import os # from tempfile import mkstemp # from digraphsTools import flatten # fd, tempFileName = mkstemp() # fo = os.fdopen(fd,'w+b') # Med = self.valuationdomain['med'] # actions = [x for x in self.actions] # relation = self.relation # inputString = '' # for i,x in enumerate(actions): # for j,y in enumerate(actions): # if i != j: # if relation[x][y] > Med: # inputString += '%d %d \n' % (i+1,j+1) # fo.write(inputString.encode('utf-8')) # fo.close() # ## if Debug: # ## print 'see file: ', tempFileName # resultFile = tempFileName+'.py' # if os.path.exists('/usr/bin/enumChordlessCircuits'): # os.system('/usr/bin/enumChordlessCircuits ' + tempFileName + ' ' + resultFile) # elif os.path.exists('/usr/local/bin/enumChordlessCircuits'): # os.system('/usr/local/bin/enumChordlessCircuits ' + tempFileName + ' ' + resultFile) # elif os.path.exists('/opt/local/bin/enumChordlessCircuits'): # os.system('/opt/local/bin/enumChordlessCircuits ' + tempFileName + ' ' + resultFile) # elif os.path.exists('/home/users/rbisdorff/bin/enumChordlessCircuits'): # os.system('/home/users/rbisdorff/bin/enumChordlessCircuits ' + tempFileName + ' ' + resultFile) # else: # print('Error: enumChordlessCircuits binary not found !!!') # argDict = {} # fi = open(resultFile,'r') # fileText = fi.read() # fi.close # exec(compile(fileText, str(resultFile), 'exec'),argDict) # circuits = argDict['circuitsList'] # if Debug: # print(resultFile) # print(argDict['circuitsList']) # result = [] # history = set() # for x in circuits: # # !! a circuit has a length n + 1 !! # if Odd: # r = len(x) % 2 # ## if Debug: # ## print x, r # if r != 1: # oddCircuit = [] # for ino in x[:-1]: # oddCircuit.append(actions[ino-1]) # circuitActions = [y for y in flatten(oddCircuit)] # circuitSet = frozenset(circuitActions) # if circuitSet not in history: # result.append( ( circuitActions, circuitSet ) ) # history.add(circuitSet) # else: # allCircuit = [] # for ino in x[:-1]: # allCircuit.append(actions[ino-1]) # circuitActions = [y for y in flatten(allCircuit)] # circuitSet = frozenset(circuitActions) # if circuitSet not in history: # result.append( ( circuitActions, circuitSet ) ) # history.add(circuitSet) # self.circuitsList = result # return result #@timefn
[docs] def computeChordlessCircuits(self,Odd=False,Comments=False,Debug=False): """ Renders the set of all chordless circuits detected in a digraph. Result is stored in <self.circuitsList> holding a possibly empty list of tuples with at position 0 the list of adjacent actions of the circuit and at position 1 the set of actions in the stored circuit. When *Odd* is True, only chordless circuits with an odd length are collected. """ #import copy from digraphsTools import flatten if Comments: if Odd: print('*--- chordless odd circuits ---*') else: print('*--- chordless circuits ---*') actionsList = list(self.actions) self.visitedArcs = set() chordlessCircuits = [] for x in actionsList: P = [x] if Comments: print('Starting from ', x) self.xCC = [] if self.chordlessPaths(P,x,Odd,Comments,Debug): chordlessCircuits += self.xCC self.chordlessCircuits = chordlessCircuits if Comments: print('result:', len(self.chordlessCircuits), 'circuit(s)') print(self.chordlessCircuits) circuitsList = [] history = set() for x in self.chordlessCircuits: #circuitsList.append( (x,frozenset(x)) ) circuitActions = [y for y in flatten(x)] circuitSet = frozenset(circuitActions) if Comments: print('flattening', x, circuitActions) if circuitSet not in history: history.add(circuitSet) circuitsList.append( (circuitActions,circuitSet) ) self.circuitsList = circuitsList return circuitsList
[docs] def detectChordlessCircuits(self,Comments=False,Debug=False): """ Detects a chordless circuit in a digraph. Returns a Boolean """ if Comments: print('* ---- detecting a chordless circuit, the case given. ----*') actionsList = list(self.actions) self.visitedArcs = set() Detected = False for x in actionsList: P = [x] if Comments: print('Starting from ', x) self.xCC = [] if self.detectChordlessPath(P,x,Comments,Debug): Detected = True break if Comments: if Detected: print('A chordless circuit has been detected !') else: print('No chordless circuit has been detected !') return Detected
[docs] def showChordlessCircuits(self,Recompute=False): """ Show method for chordless circuits observed in a Digraph instance. If previous computation is required, stores the detected circuits in self.circuitsList attribute. """ if Recompute: print('Recomputing the chordless circuits list.') self.computeChordlessCircuits() circuitsList = self.circuitsList try: circuitsList = self.circuitsList except: self.computeChordlessCircuits() circuitsList = self.circuitsList if len(circuitsList) == 0: print('No circuits observed in this digraph.') else: print('*---- Chordless circuits ----*') print('%d circuits.' % (len(circuitsList))) for i,(circList,circSet) in enumerate(circuitsList): deg = self.circuitMinCredibility(circList) print('%d: ' % (i+1), circList, ', credibility : %.3f' % (deg))
[docs] def minimalValuationLevelForCircuitsElimination(self,Odd=True,Debug=False,Comments=False): """ renders the minimal valuation level <lambda> that eliminates all self.circuitsList stored odd chordless circuits from self. .. warning:: The <lambda> level polarised may still contain newly appearing chordless odd circuits ! """ # try: # circuitslist = self.circuitslist # except: self.computeChordlessCircuits(Odd=Odd,Comments=Debug) circuitsList = self.circuitsList Med = self.valuationdomain['med'] qualmaj = Med oddCircuitsList = [cc for cc in circuitsList if (len(cc[0])%2 == 1)] if Comments: print('Number of chordless circuits: ', len(circuitsList)) print(circuitsList) print('Number of chordless odd circuits: ', len(oddCircuitsList)) print(oddCircuitsList) for cc in circuitsList: circuit = cc[0] if Debug: print(circuit) ccqualmaj = self.circuitMinCredibility(circuit) ## n = len(circuit) ## for i in range(n-1): ## x = cc[0][i] ## y = cc[0][i+1] ## if Debug: ## print x, y, self.relation[x][y],y,x,self.relation[y][x] ## if self.relation[x][y] > Med: ## if self.relation[x][y] < ccqualmaj: ## ccqualmaj = self.relation[x][y] ## else: ## if self.relation[y][x] < Med: ## if abs(self.relation[y][x]) < ccqualmaj: ## ccqualmaj = abs(self.relation[y][x]) if Debug: print('==>>', circuit, ccqualmaj) qualmaj = max(qualmaj,ccqualmaj) if Debug or Comments: # if Odd: # print('Number of chordless odd circuits: ', len(oddCircuitsList)) # else: # print('Number of chordless circuits: ', len(circuitsList)) print('Minimal cutting level for eliminating them: %.3f' % qualmaj) return qualmaj
## def minimalValuationLevelForCircuitsEliminationOld(self,Debug=False): ## """ ## renders the minimal valuation level <lambda> that eliminates all ## self.circuitsList stored odd chordless circuits from self. ## .. warning:: ## The <lambda> level polarised may still contain newly appearing chordless odd circuits ! ## """ ## try: ## circuitslist = self.circuitslist ## except: ## self.computeChordlessCircuits(Odd=True,Comments=Debug) ## circuitsList = self.circuitsList ## Max = self.valuationdomain['max'] ## Med = self.valuationdomain['med'] ## qualmaj = Med ## for cc in circuitsList: ## ccqualmaj = Max ## if Debug: ## print cc ## circuit = cc[0] ## circuit.append(cc[0][0]) ## if Debug: ## print circuit ## n = len(circuit) ## for i in range(n-1): ## x = cc[0][i] ## y = cc[0][i+1] ## if Debug: ## print x, y, self.relation[x][y],y,x,self.relation[y][x] ## if self.relation[x][y] > Med: ## if self.relation[x][y] < ccqualmaj: ## ccqualmaj = self.relation[x][y] ## else: ## if self.relation[y][x] < Med: ## if abs(self.relation[y][x]) < ccqualmaj: ## ccqualmaj = abs(self.relation[y][x]) ## if Debug: ## print '==>>', circuit, ccqualmaj ## if ccqualmaj > qualmaj: ## qualmaj = ccqualmaj ## if Debug: ## print qualmaj ## return qualmaj
[docs] def circuitMinCredibility(self,circ): """ Renders the minimal linking credibility of a Chordless Circuit. """ actions = self.actions Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] relation = self.relation deg = Max circuit = list(circ) n = len(circuit) for i in range(n): x = circuit[i] for j in range(i+1,n): y = circuit[j] if j == i+1: deg = min(deg,max(relation[x][y],relation[y][x])) x = circuit[-1] y = circuit[0] deg = min(deg,max(relation[x][y],relation[y][x])) return deg
[docs] def circuitMaxCredibility(self,circ): """ Renders the maximal linking credibility of a Chordless Circuit. """ actions = self.actions Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] relation = self.relation deg = Min circuit = list(circ) n = len(circuit) for i in range(n): x = circuit[i] for j in range(i+1,n): y = circuit[j] if j == i+1: deg = max(deg,max(relation[x][y],relation[y][x])) x = circuit[-1] y = circuit[0] deg = max(deg,max(relation[x][y],relation[y][x])) return deg
## def circuitMinCredibilityOld(self,circuit): ## """ ## Renders the minimal linking credibility of a COC. ## """ ## actions = self.actions ## Max = self.valuationdomain['max'] ## Med = self.valuationdomain['med'] ## relation = self.relation ## deg = Max ## for x in circuit: ## for y in circuit: ## if relation[x][y] > Med: ## deg = min(deg,relation[x][y]) ## return deg
[docs] def circuitAverageCredibility(self,circ): """ Renders the average linking credibility of a Chordless Circuit. """ actions = self.actions n = len(actions) narcs = n * (n-1) relation = self.relation Min = self.valuationdomain['min'] deg = Min circuit = list(circ) for x in circuit: for y in circuit: if x != y: deg += abs(relation[x][y]) deg = deg / Decimal(str(narcs)) return deg
[docs] def circuitCredibilities(self,circuit,Debug=False): """ Renders the average linking credibilities and the minimal link of a Chordless Circuit. """ if Debug: print(circuit) actions = self.actions relation = self.relation Med = self.valuationdomain['med'] Max = self.valuationdomain['max'] Min = self.valuationdomain['min'] if Min != -Max: # the characteristic valuation is not bipolar ! maxAmplitude = abs(Max-Med) + abs(Min-Med) degP = Decimal('0') degN = Decimal('0') nParcs = 0 nNarcs = 0 minAmplitude = maxAmplitude minLink = None nc = len(circuit) for i in range(nc): x = circuit[i] #for j in range(i+1,nc): if i != nc -1: y = circuit[i+1] else: y = circuit[0] if Debug: print(x,y,end=',') degP += (relation[x][y]-Med) nParcs += 1 diffxy = abs(relation[x][y]-Med) + abs(relation[y][x]-Med) if Debug: print(relation[x][y],relation[y][x],diffxy,end=',') if minAmplitude >= diffxy: minAmplitude = diffxy minLink = (x,y) if Debug: print(minAmplitude,diffxy,minLink) degN += (relation[y][x]-Med) nNarcs += 1 else: # the characteristic valuation is bipolar ! maxAmplitude = abs(Max) + abs(Min) degP = Med degN = Med nParcs = 0 nNarcs = 0 minAmplitude = maxAmplitude minLink = None nc = len(circuit) for i in range(nc): x = circuit[i] #for j in range(i+1,nc): if i != nc -1: y = circuit[i+1] else: y = circuit[0] if Debug: print(x,y,end=',') degP += relation[x][y] nParcs += 1 diffxy = abs(relation[x][y]) + abs(relation[y][x]) if Debug: print(relation[x][y],relation[y][x],diffxy,end=',') if minAmplitude >= diffxy: minAmplitude = diffxy minLink = (x,y) if Debug: print(minLink) degN += relation[y][x] nNarcs += 1 if nParcs != 0: degP /= Decimal(str(nParcs)) if nNarcs != 0: degN /= Decimal(str(nNarcs)) if Debug: print('degP,degN,minLink',degP,degN,minLink) return degP,degN,minLink
[docs] def contra(self, v): """ Parameter: choice. Renders the negation of a choice v characteristic's vector. """ Max = Decimal(str(self.valuationdomain['max'])) Min = Decimal(str(self.valuationdomain['min'])) #print v nv = [Max - v[x] + Min for x in range(len(v))] return nv
[docs] def sharpvec(self, v, w): """ Paramaters: choice characteristic vectors. Renders the sharpest of two characteristic vectors v and w. """ sv = [self.sharp(v[x],w[x]) for x in range(len(v))] return sv
[docs] def sharp(self, x, y): """ Paramaters: choice characteristic values. Renders the sharpest of two characteristic values x and y. """ med = Decimal(str(self.valuationdomain['med'])) if x >= med and y >= med: return max(x,y) elif x <= med and y <= med: return min(x,y) else: return med
[docs] def inner_prod(self, v1, v2): """ Parameters: two choice characteristic vectors Renders the inner product of two characteristic vetors. """ res = Decimal(str(self.valuationdomain['min'])) for i in range(len(v1)): res = max(res, min(v1[i],v2[i])) return res
[docs] def matmult2(self, m, v): """ Parameters: digraph relation and choice characteristic vector matrix multiply vector by inner production """ return [self.inner_prod(r, v) for r in m]
[docs] def readdomvector(self, x,relation): """ Parameter: action x dominant out vector. """ #import array actions = self.actions vec = [relation[y][x] for y in actions] return vec
[docs] def readabsvector(self, x,relation): """ Parameter: action x absorbent in vector. """ #import array actions = self.actions vec = [relation[x][y] for y in actions] return vec
# ----- graph restrictions methods
[docs] def domkernelrestrict(self, prekernel): """ Parameter: dominant prekernel Renders dominant prekernel restricted relation. """ actions = self.actions relation = self.relation Min = Decimal(str(self.valuationdomain['min'])) Med = Decimal(str(self.valuationdomain['med'])) relation_k = {} for x in actions: relation_k[x] = {} for y in actions: #relation_k[x][y] = {} if x == y: relation_k[x][y] = Min elif x in prekernel and y in prekernel: relation_k[x][y] = relation[x][y] elif x in prekernel and relation[x][y] > Med: relation_k[x][y] = relation[x][y] elif y in prekernel and relation[x][y] < Med: relation_k[x][y] = relation[x][y] else: relation_k[x][y] = Med return relation_k
[docs] def abskernelrestrict(self, prekernel): """ Parameter: prekernel Renders absorbent prekernel restricted relation. """ actions = self.actions relation = self.relation Min = Decimal(str(self.valuationdomain['min'])) Med = Decimal(str(self.valuationdomain['med'])) relation_k = {} for x in actions: relation_k[x] = {} for y in actions: #relation_k[x][y] = {} if x == y: relation_k[x][y] = Min elif x in prekernel and y in prekernel: relation_k[x][y] = relation[x][y] elif x in prekernel and relation[x][y] < Med: relation_k[x][y] = relation[x][y] elif y in prekernel and relation[x][y] > Med: relation_k[x][y] = relation[x][y] else: relation_k[x][y] = Med return relation_k
[docs] def showRubyChoice(self,Verbose=False,Comments=True,_OldCoca=True): """ Dummy for showBestChoiceRecommendation() needed for older versions compatibility. """ self.showBestChoiceRecommendation(Verbose=Verbose,Comments=Comments,_OldCoca=_OldCoca)
[docs] def computeBestChoiceRecommendation(self,Verbose=False, Comments=False, ChoiceVector=False, CoDual=True, Debug=False, _OldCoca=False, BrokenCocs=True): """ Sets self.bestChoice, self.bestChoiceData, self.worstChoice and self.worstChoiceData with the showBestChoiceRecommendation method. First and last choices data is the following: [(0)-determ,(1)degirred,(2)degi,(3)degd,(4)dega,(5)str(choice),(6)domvec,(7)cover] self.bestChoice = self.bestChoiceData[5] self.worstChoice = self.worstChoiceData[5] """ self.showBestChoiceRecommendation(Verbose=Verbose, Comments=Comments, ChoiceVector=ChoiceVector, CoDual=CoDual, Debug=Debug, _OldCoca=_OldCoca, BrokenCocs=BrokenCocs)
[docs] def showFirstChoiceRecommendation(self,Verbose=False, Comments=True, ChoiceVector=False, CoDual=True, Debug=False, _OldCoca=False, BrokenCocs=True, ): """ Shows the RuBis first choice recommendation. .. note:: Computes by default the Rubis first choice recommendation on the corresponding strict (codual) outranking digraph. By default, with BrokenCocs=True, we brake all chordless circuits at their weakest determined ( abs(r(x>y)) + abs(r(y>x)) ) link. When BrokenCocs=False we proceed like follows: In case of chordless circuits, if supporting arcs are more credible than the reversed negating arcs, we collapse the circuits into hyper nodes. Inversely, if supporting arcs are not more credible than the reversed negating arcs, we brake the circuits on their weakest arc. Usage example: >>> from outrankingDigraphs import * >>> t = Random3ObjectivesPerformanceTableau(seed=5) >>> g = BipolarOutrankingDigraph(t) >>> g.showFirstChoiceRecommendation() *********************** RuBis First Choice Recommendation (BCR) (in decreasing order of determinateness) Credibility domain: [-100.0, 100.0] === >> potential first choices * choice : ['a04', 'a14', 'a19', 'a20'] independence : 1.19 dominance : 4.76 absorbency : -59.52 covering (%) : 75.00 determinateness (%) : 57.86 - most credible action(s) = { 'a14': 23.81, 'a19': 11.90, 'a04': 2.38, 'a20': 1.19, } === >> potential last choices * choice : ['a03', 'a12', 'a17'] independence : 4.76 dominance : -76.19 absorbency : 4.76 covering (%) : 0.00 determinateness (%) : 65.39 - most credible action(s) = { 'a03': 38.10, 'a12': 13.10, 'a17': 4.76, } Execution time: 0.024 seconds ***************************** """ from copy import deepcopy from time import time if Comments: print('***********************') #print('RuBis BCR') if Debug: print('All comments !!!') Comments = True Verbose = True t0 = time() n0 = self.order cpself = deepcopy(self) if CoDual: g = ~(-cpself) else: g = cpself if _OldCoca: _selfwcoc = CocaDigraph(g) b1 = 0 elif BrokenCocs: _selfwcoc = BrokenCocsDigraph(g) b1 = _selfwcoc.breakings else: _selfwcoc = BreakAddCocsDigraph(g) b1 = _selfwcoc.breakings n1 = _selfwcoc.order nc = n1 - n0 #self.relation_orig = deepcopy(g.relation) if b1 > 0 or nc > 0: #self.actions_orig = deepcopy(g.actions) g.actions = deepcopy(_selfwcoc.actions) g.order = len(g.actions) g.relation = deepcopy(_selfwcoc.relation) if Debug: print('List of pseudo-independent choices') print(g.actions) g.gamma = g.gammaSets() g.notGamma = g.notGammaSets() if Debug: g.showRelationTable() #self.showPreKernels() actions = set([x for x in g.actions]) if Verbose: g.showPreKernels() if Debug: print(g.dompreKernels,g.abspreKernels) g.computeGoodChoices(Comments=Debug) g.computeBadChoices(Comments=Debug) if Debug: print('first and last choices: ',g.goodChoices,g.badChoices) t1 = time() if Comments: print('Rubis first choice recommendation(s) (BCR)') print(' (in decreasing order of determinateness) ') print('Credibility domain: [%.2f,%.2f]' % (g.valuationdomain['min'],\ g.valuationdomain['max']) ) Med = g.valuationdomain['med'] bestChoice = set() bestChoiceData = None worstChoice = set() worstChoiceData = None for gch in g.goodChoices: if gch[0] >= Med: goodChoice = True for bch in g.badChoices: if gch[5] == bch[5]: #if gch[0] == bch[0]: if gch[3] == gch[4]: if Comments: print(' === >> ambiguous first choice(s)') g.showChoiceVector(gch,choiceType='good', ChoiceVector=ChoiceVector) print(' === >> ambiguous last choice(s)') g.showChoiceVector(bch,choiceType='bad', ChoiceVector=ChoiceVector) goodChoice = False if bestChoice == set(): bestChoice = gch[5] bestChoiceData = gch elif gch[4] > gch[3]: if Comments: print(' === >> outranked (first) choice ') #g.showChoiceVector(gch,choiceType='good', # ChoiceVector=ChoiceVector) g.showChoiceVector(bch,choiceType='bad', ChoiceVector=ChoiceVector) goodChoice = False else: goodChoice = True if goodChoice: if Comments: print(' === >> potential first choice(s)') g.showChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) if bestChoice == set(): bestChoice = gch[5] bestChoiceData = gch else: if Comments: print(' === >> non robust first choice(s)') g.showChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) for bch in g.badChoices: if bch[0] >= Med: badChoice = True nullChoice = False for gch in g.goodChoices: if bch[5] == gch[5]: #if gch[0] == bch[0]: if bch[3] == bch[4]: if Verbose: print(' === >> ambiguous (last) choice ') g.showChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) g.showChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) badChoice = False nullChoice = False elif bch[3] > bch[4]: if Verbose: print(' === >> outranking (last) choice ') g.showChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) #g.showChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) badChoice = False else: badChoice = True if badChoice: if Comments: print(' === >> potential last choice(s) ') g.showChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) if worstChoice == set(): worstChoice = bch[5] worstChoiceData = bch # elif nullChoice: # if Comments: # print(' === >> ambiguous choice(s)') # g.showChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) # if worstChoice == set(): # worstChoice = bch[5] # worstChoiceData = bch else: if Comments: print('=== >> non robust last choice(s)') g.showChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) if Comments: print() print('Execution time: %.3f seconds' % (t1-t0)) print('*****************************') self.bestChoice = bestChoice self.bestChoiceData = bestChoiceData self.worstChoice = worstChoice self.worstChoiceData = worstChoiceData
[docs] def showBestChoiceRecommendation(self,Verbose=False, Comments=True, ChoiceVector=False, CoDual=True, Debug=False, _OldCoca=False, BrokenCocs=True, ): """ Shows the RuBis best choice recommendation. .. note:: Computes by default the Rubis best choice recommendation on the corresponding strict (codual) outranking digraph. By default, with BrokenCocs=True, we brake all chordless circuits at their weakest determined ( abs(r(x>y)) + abs(r(y>x)) ) link. When BrokenCocs=False we proceed like follows: In case of chordless circuits, if supporting arcs are more credible than the reversed negating arcs, we collapse the circuits into hyper nodes. Inversely, if supporting arcs are not more credible than the reversed negating arcs, we brake the circuits on their weakest arc. Usage example: >>> from outrankingDigraphs import * >>> t = Random3ObjectivesPerformanceTableau(seed=5) >>> g = BipolarOutrankingDigraph(t) >>> g.showBestChoiceRecommendation() *********************** RuBis Best Choice Recommendation (BCR) (in decreasing order of determinateness) Credibility domain: [-100.0, 100.0] === >> potential first choices * choice : ['a04', 'a14', 'a19', 'a20'] independence : 1.19 dominance : 4.76 absorbency : -59.52 covering (%) : 75.00 determinateness (%) : 57.86 - most credible action(s) = { 'a14': 23.81, 'a19': 11.90, 'a04': 2.38, 'a20': 1.19, } === >> potential last choices * choice : ['a03', 'a12', 'a17'] independence : 4.76 dominance : -76.19 absorbency : 4.76 covering (%) : 0.00 determinateness (%) : 65.39 - most credible action(s) = { 'a03': 38.10, 'a12': 13.10, 'a17': 4.76, } Execution time: 0.024 seconds ***************************** """ from copy import deepcopy from time import time if Comments: print('***********************') #print('RuBis BCR') if Debug: print('All comments !!!') Comments = True Verbose = True t0 = time() n0 = self.order cpself = deepcopy(self) if CoDual: g = ~(-cpself) else: g = cpself if _OldCoca: _selfwcoc = CocaDigraph(g) b1 = 0 elif BrokenCocs: _selfwcoc = BrokenCocsDigraph(g) b1 = _selfwcoc.breakings else: _selfwcoc = BreakAddCocsDigraph(g) b1 = _selfwcoc.breakings n1 = _selfwcoc.order nc = n1 - n0 #self.relation_orig = deepcopy(g.relation) if b1 > 0 or nc > 0: #self.actions_orig = deepcopy(g.actions) g.actions = deepcopy(_selfwcoc.actions) g.order = len(g.actions) g.relation = deepcopy(_selfwcoc.relation) if Debug: print('List of pseudo-independent choices') print(g.actions) g.gamma = g.gammaSets() g.notGamma = g.notGammaSets() if Debug: g.showRelationTable() #self.showPreKernels() actions = set([x for x in g.actions]) if Verbose: g.showPreKernels() if Debug: print(g.dompreKernels,g.abspreKernels) g.computeGoodChoices(Comments=Debug) g.computeBadChoices(Comments=Debug) if Debug: print('first and last choices: ',g.goodChoices,g.badChoices) t1 = time() if Comments: print('Rubis best choice recommendation(s) (BCR)') print(' (in decreasing order of determinateness) ') print('Credibility domain: [%.2f,%.2f]' % (g.valuationdomain['min'],\ g.valuationdomain['max']) ) Med = g.valuationdomain['med'] bestChoice = set() bestChoiceData = None worstChoice = set() worstChoiceData = None for gch in g.goodChoices: if gch[0] >= Med: goodChoice = True for bch in g.badChoices: if gch[5] == bch[5]: #if gch[0] == bch[0]: if gch[3] == gch[4]: if Comments: print(' === >> ambiguous first choice(s)') g.showChoiceVector(gch,choiceType='good', ChoiceVector=ChoiceVector) print(' === >> ambiguous last choice(s)') g.showChoiceVector(bch,choiceType='bad', ChoiceVector=ChoiceVector) goodChoice = False if bestChoice == set(): bestChoice = gch[5] bestChoiceData = gch elif gch[4] > gch[3]: if Comments: print(' === >> outranked (first) choice ') #g.showChoiceVector(gch,choiceType='good', # ChoiceVector=ChoiceVector) g.showChoiceVector(bch,choiceType='bad', ChoiceVector=ChoiceVector) goodChoice = False else: goodChoice = True if goodChoice: if Comments: print(' === >> potential first choice(s)') g.showChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) if bestChoice == set(): bestChoice = gch[5] bestChoiceData = gch else: if Comments: print(' === >> non robust first choice(s)') g.showChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) for bch in g.badChoices: if bch[0] >= Med: badChoice = True nullChoice = False for gch in g.goodChoices: if bch[5] == gch[5]: #if gch[0] == bch[0]: if bch[3] == bch[4]: if Verbose: print(' === >> ambiguous (last) choice ') g.showChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) g.showChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) badChoice = False nullChoice = False elif bch[3] > bch[4]: if Verbose: print(' === >> outranking (last) choice ') g.showChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) #g.showChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) badChoice = False else: badChoice = True if badChoice: if Comments: print(' === >> potential last choice(s) ') g.showChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) if worstChoice == set(): worstChoice = bch[5] worstChoiceData = bch # elif nullChoice: # if Comments: # print(' === >> ambiguous choice(s)') # g.showChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) # if worstChoice == set(): # worstChoice = bch[5] # worstChoiceData = bch else: if Comments: print('=== >> non robust first choice(s)') g.showChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) if Comments: print() print('Execution time: %.3f seconds' % (t1-t0)) print('*****************************') self.bestChoice = bestChoice self.bestChoiceData = bestChoiceData self.worstChoice = worstChoice self.worstChoiceData = worstChoiceData
[docs] def showRubisBestChoiceRecommendation(self,**kwargs): """ Dummy for backward portable showBestChoiceRecommendation(). """ self.showBestChoiceRecommendation(**kwargs)
############# def showHTMLBestChoiceRecommendation(self,pageTitle=None, ChoiceVector=False, CoDual=True, Debug=False, _OldCoca=False, BrokenCocs=True, #Cpp=False, htmlFileName=None, ): import webbrowser if htmlFileName == None: from tempfile import NamedTemporaryFile fileName = (NamedTemporaryFile(suffix='.html',delete=False,dir='.')).name else: from os import getcwd fileName = getcwd()+'/'+htmlFileName fo = open(fileName,'w') fo.write(self._htmlBestChoiceRecommendation( pageTitle=pageTitle, ChoiceVector=ChoiceVector, CoDual=CoDual, Debug=Debug, _OldCoca=_OldCoca, BrokenCocs=BrokenCocs)) fo.close() url = 'file://'+fileName webbrowser.open(url,new=2) def _htmlBestChoiceRecommendation(self,pageTitle=None, ChoiceVector=False, ContentCentered=True, CoDual=True, Debug=False, _OldCoca=False, BrokenCocs=True, ): """ Renders the RuBis best choice recommendation in a browser window. .. note:: Computes by default the Rubis best choice recommendation on the corresponding strict (codual) outranking digraph. In case of chordless circuits, if supporting arcs are more credible than the reversed negating arcs, we collapse the circuits into hyper nodes. Inversely, if supporting arcs are not more credible than the reversed negating arcs, we brake the circuits on their weakest arc. Usage example: >>> from outrankingDigraphs import * >>> t = Random3ObjectivesPerformanceTableau(seed=5) >>> g = BipolarOutrankingDigraph(t) >>> g.showHTMLBestChoiceRecommendation() """ from copy import deepcopy from time import time if Debug: Comments = True else: Comments = False # construct html text html = '<!DOCTYPE html><html><head>\n' html += '<meta charset="UTF-8">\n' if pageTitle is None: pageTitle = 'Best Choice Recommendation' html += '<title>%s</title>\n' % pageTitle html += '<style type="text/css">\n' if ContentCentered: html += 'td {text-align: center;}\n' html += 'td.na {color: rgb(192,192,192);}\n' html += '</style>\n' html += '</head>\n<body>\n' html += '<h1>%s</h1>' % pageTitle html += '<h3>Outranking digraph: %s</h3>' % self.name #print('RuBis BCR') t0 = time() n0 = self.order cpself = deepcopy(self) if CoDual: g = ~(-cpself) else: g = cpself if _OldCoca: _selfwcoc = CocaDigraph(g) b1 = 0 elif BrokenCocs: #print('passed here!') _selfwcoc = BrokenCocsDigraph(g) b1 = _selfwcoc.breakings else: _selfwcoc = BreakAddCocsDigraph(g) b1 = _selfwcoc.breakings n1 = _selfwcoc.order nc = n1 - n0 #self.relation_orig = deepcopy(g.relation) if b1 > 0 or nc > 0: #self.actions_orig = deepcopy(g.actions) g.actions = deepcopy(_selfwcoc.actions) g.order = len(g.actions) g.relation = deepcopy(_selfwcoc.relation) if Debug: print('List of pseudo-independent choices') print(g.actions) g.gamma = g.gammaSets() g.notGamma = g.notGammaSets() if Debug: g.showRelationTable() #self.showPreKernels() actions = set([x for x in g.actions]) if Debug: g.showPreKernels() print(g.dompreKernels,g.abspreKernels) g.computeGoodChoices(Comments=Debug) g.computeBadChoices(Comments=Debug) if Debug: print('first and last choices: ',g.goodChoices,g.badChoices) t1 = time() if Debug: print('Rubis best choice recommendation(s) (BCR)') print(' (in decreasing order of determinateness) ') print('Credibility domain: [%.2f,%.2f]' %\ (g.valuationdomain['min'],\ g.valuationdomain['max']) ) html += '<p>Rubis best choice recommendation(s) (BCR)</br>\n' html += ' (in decreasing order of determinateness)</br>\n' html += 'Credibility domain: [%.2f,%.2f]</p>\n' %\ (g.valuationdomain['min'],\ g.valuationdomain['max']) Med = g.valuationdomain['med'] bestChoice = set() worstChoice = set() for gch in g.goodChoices: if gch[0] >= Med: goodChoice = True for bch in g.badChoices: if gch[5] == bch[5]: #if gch[0] == bch[0]: if gch[3] == gch[4]: if Debug: print(' === >> ambigous (first) choice ') g.showChoiceVector(gch,choiceType='good', ChoiceVector=ChoiceVector) g.showChoiceVector(bch,choiceType='bad', ChoiceVector=ChoiceVector) html += '<h3>Ambiguous (first) choice(s)</h3>\n' html += g.htmlChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) goodChoice = False if bestChoice == set(): bestChoice = gch[5] bestChoiceData = gch elif gch[4] > gch[3]: if Debug: print(' === >> outranked (first) choice ') g.showChoiceVector(gch,choiceType='good', ChoiceVector=ChoiceVector) g.showChoiceVector(bch,choiceType='bad', ChoiceVector=ChoiceVector) html += '<h3>Outranked (first) choice(s)</h3>\n' html += g.htmlChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) goodChoice = False if bestChoice == set(): bestChoice = gch[5] bestChoiceData = gch goodChoice = False else: goodChoice = True if goodChoice: if Debug: print(' === >> potential first choice(s)') html += '<h3>Potential first choice(s)</h3>\n' html += g.htmlChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) if bestChoice == set(): bestChoice = gch[5] else: if Debug: print(' === >> non robust first choice(s)') html += '<h3>Non robust first choice(s)</h3>\n' html += g.htmlChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) for bch in g.badChoices: if bch[0] >= Med: badChoice = True nullChoice = False for gch in g.goodChoices: if bch[5] == gch[5]: #if gch[0] == bch[0]: if bch[3] == bch[4]: if Debug: print(' === >> ambiguous (last) choice ') g.showChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) g.showChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) html += '<h3>Ambiguous (last) choice(s)</h3>\n' html += g.htmlChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) badChoice = False nullChoice = False elif bch[3] > bch[4]: if Debug: print(' === >> outranking (last) choice ') g.showChoiceVector(gch,choiceType='good',ChoiceVector=ChoiceVector) g.showChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) html += '<h3>Outranking (last) choice(s)</h3>\n' html += g.htmlChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) badChoice = False else: badChoice = True if badChoice: if Debug: print(' === >> potential worst choice(s) ') html += '<h3>Potential worst choice(s)</h3>\n ' html += g.htmlChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) if worstChoice == set(): worstChoice = bch[5] # elif nullChoice: # if Debug: # print(' === >> ambiguous choice(s)') # html += '<h3>Ambiguous choice(s)</h3>\n' # html += g.htmlChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) # if worstChoice == set(): # worstChoice = bch[5] else: if Debug: print('=== >> non robust last choice(s)') html += '<h3>Non robust lasst choice(s)</h3>\n' html += g.htmlChoiceVector(bch,choiceType='bad',ChoiceVector=ChoiceVector) if Debug: print() print('Execution time: %.3f seconds' % (t1-t0)) print('*****************************') html += '<p>Execution time: %.3f seconds</p>\n' % (t1-t0) # html footer html += '</body>\n' html += '</html>\n' return html self.bestChoice = bestChoice self.worstChoice = worstChoice #if nc > 0 or b1 > 0: ## self.actions = deepcopy(self.actions_orig) ## self.relation = deepcopy(self.relation_orig) ## self.order = len(self.actions) ## self.gamma = self.gammaSets() ## self.notGamma = self.notGammaSets()
[docs] def computeRubyChoice(self,Comments=False,_OldCoca=False): """ dummy for computeRubisChoice() old versions compatibility. """ self.computeRubisChoice(Comments=Comments,_OldCoca=_OldCoca)
[docs] def computeRubisChoice(self, Comments=False,_OldCoca=False,BrokenCocs=True, Threading=False,nbrOfCPUs=1): """ Renders self.strictGoodChoices, self.nullChoices self.strictBadChoices, self.nonRobustChoices. .. warning:: Changes in site the outranking digraph by adding or braking chordless odd outranking circuits. """ #print('Passing here',_OldCoca) if Comments: from time import time t0 = time() # save original actions and relation from copy import deepcopy try: self.actions_orig = deepcopy(self.actions_orig) except: self.actions_orig = deepcopy(self.actions) #self.actions_orig = actions_orig self.relation_orig = deepcopy(self.relation) # computing Coca if Comments: print('*--- computing the COCA digraph --*') if _OldCoca: _selfwcoc = CocaDigraph(self,Comments=Comments) self.breakings = 0 elif BrokenCocs: _selfwcoc = BrokenCocsDigraph(self,Comments=Comments, Threading=Threading,nbrOfCPUs=nbrOfCPUs) self.breakings = _selfwcoc.breakings else: _selfwcoc = BreakAddCocsDigraph(self,Comments=Comments, Threading=Threading,nbrOfCPUs=nbrOfCPUs) self.breakings = _selfwcoc.breakings if Comments: print('Execution time: %.3f seconds' % (time()-t0)) _selfwcoc.showPreKernels() # transferring coca actions and relation self.actions = deepcopy(_selfwcoc.actions) self.order = len(self.actions) self.relation = deepcopy(_selfwcoc.relation) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() # computing good and bad choices actions = set([x for x in self.actions]) self.dompreKernels = set() self.abspreKernels = set() for choice in self.independentChoices(self.singletons()): restactions = actions - choice[0][0] if restactions <= choice[0][1]: self.dompreKernels.add(choice[0][0]) if restactions <= choice[0][2]: self.abspreKernels.add(choice[0][0]) self.computeGoodChoices(Comments=Comments) self.computeBadChoices(Comments=Comments) # sorting out the strict choices self.strictGoodChoices = set() self.nullChoices = set() self.strictBadChoices = set() self.nonRobustChoices = set() for gch in self.goodChoices: if gch[0] <= 0: goodChoice = True for bch in self.badChoices: if gch[5] == bch[5]: if gch[3] == gch[4]: goodChoice = False self.nullChoices.add(frozenset(gch[5])) elif gch[4] > gch[3]: goodChoice = False self.strictBadChoices.add(frozenset(bch[5])) else: goodChoice = True if goodChoice: self.strictGoodChoices.add(frozenset(gch[5])) else: self.nonRobustChoices.add(frozenset(gch[5])) if Comments: self.showGoodChoices() self.showBadChoices() #n1 = _selfwcoc.order #nc = n1 - n0 #if nc > 0 or b1 > 0: #self.actions = self.actions_orig #self.relation = self.relation_orig self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
def _computeRubisChoice(self,Comments=False,_OldCoca=False): """ Renders self.strictGoodChoices, self.nullChoices self.strictBadChoices, self.nonRobustChoices. .. warning:: Changes in site the outranking digraph by adding or braking chordless odd outranking circuits. """ #print('Passing here',_OldCoca) import copy,time if Comments: print('*--- computing the COCA digraph --*') n0 = self.order t0 = time.time() _selfwcoc = CocaDigraph(self,Comments=Comments) #b1 = _selfwcoc.brakings t1 = time.time() if Comments: print('Execution time: %.3f seconds' % (t1-t0)) _selfwcoc.showPreKernels() try: self.actions_orig = copy.deepcopy(self.actions_orig) except: self.actions_orig = copy.deepcopy(self.actions) #self.actions_orig = actions_orig self.relation_orig = copy.deepcopy(self.relation) self.actions = _selfwcoc.actions self.order = len(self.actions) self.relation = _selfwcoc.relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() actions = set([x for x in self.actions]) self.dompreKernels = set() self.abspreKernels = set() for choice in self.independentChoices(self.singletons()): restactions = actions - choice[0][0] if restactions <= choice[0][1]: self.dompreKernels.add(choice[0][0]) if restactions <= choice[0][2]: self.abspreKernels.add(choice[0][0]) self.computeGoodChoices(Comments=Comments) self.computeBadChoices(Comments=Comments) self.strictGoodChoices = set() self.nullChoices = set() self.strictBadChoices = set() self.nonRobustChoices = set() for gch in self.goodChoices: if gch[0] >= 0: goodChoice = True for bch in self.badChoices: if gch[5] == bch[5]: if gch[3] == gch[4]: goodChoice = False self.nullChoices.add(frozenset(gch[5])) elif gch[4] > gch[3]: goodChoice = False self.strictBadChoices.add(frozenset(bch[5])) else: goodChoice = True if goodChoice: self.strictGoodChoices.add(frozenset(gch[5])) else: self.nonRobustChoices.add(frozenset(gch[5])) if Comments: self.showGoodChoices() self.showBadChoices() #n1 = _selfwcoc.order #nc = n1 - n0 #if nc > 0 or b1 > 0: #self.actions = self.actions_orig #self.relation = self.relation_orig self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] def computeGoodChoiceVector(self,ker,Comments=False): """ | Computing Characteristic values for dominant pre-kernels | using the von Neumann dual fixoint equation """ import copy from operator import itemgetter temp = copy.deepcopy(self) Max = Decimal(str(temp.valuationdomain['max'])) Min = Decimal(str(temp.valuationdomain['min'])) Med = Decimal(str(temp.valuationdomain['med'])) actions = [x for x in temp.actions] #actions.sort() relation = temp.relation if Comments: print('--> kernel:', ker) choice = [y for y in ker] ## degi = temp.intstab(ker) ## dega = temp.absorb(ker) ## degd = temp.domin(ker) ## degirred = temp.domirredval(ker,relation) ## degmd = min(degi,degd) ## cover = temp.averageCoveringIndex(ker) relation_k = temp.domkernelrestrict(choice) n = len(actions) #vec1_a = array.array('f', [Max] * n) vec1_a = [Max for i in range(n)] #vec0_a = array.array('f', [Min] * n) vec0_a = [Min for i in range(n)] mat = [temp.readdomvector(x,relation_k) for x in actions] veclowa = vec0_a vechigha = vec1_a if Comments: print('initial low vector :',veclowa) print('initial high vector :', vechigha) it = 1 while veclowa != vechigha and it < 2*n*n: veclowb = temp.matmult2(mat,veclowa) vechighb = temp.matmult2(mat,vechigha) veclow = temp.contra(vechighb) vechigh = temp.contra(veclowb) if veclow == veclowa and vechigh == vechigha : break veclowa = veclow vechigha = vechigh if Comments: print(it, 'th low vector :',veclowa) print(it, 'th high vector :',vechigha) it += 1 if Comments: print('final low vector :', veclowa) print('final high vector :', vechigha) print('#iterations :', it) domvec = temp.sharpvec(veclowa,vechigha) determ = temp.determinateness(domvec) goodChoiceVector = [] for i in range(n): goodChoiceVector.append((domvec[i],str(actions[i]))) goodChoiceVector.sort(reverse=True,key=itemgetter(0)) if Comments: print('Choice vector for dominant pre-kernel: %s' % str(ker)) for i,item in enumerate(goodChoiceVector): print('%s: %+.2f' % (item[1],item[0]) ) else: return goodChoiceVector
[docs] def computeKernelVector(self,kernel,Initial=True,Comments=False): """ | Computing Characteristic values for dominant pre-kernels | using the von Neumann dual fixpoint equation """ import copy from operator import itemgetter temp = copy.deepcopy(self) Max = Decimal(str(temp.valuationdomain['max'])) Min = Decimal(str(temp.valuationdomain['min'])) Med = Decimal(str(temp.valuationdomain['med'])) actions = [x for x in temp.actions] #actions.sort() relation = temp.relation ker = set(kernel) if Comments: if Initial: print('--> Initial kernel:', ker) else: print('--> Terminal kernel:', ker) choice = [x for x in ker] if Initial: relation_k = temp.domkernelrestrict(choice) else: relation_k = temp.abskernelrestrict(choice) n = len(actions) #vec1_a = array.array('f', [Max] * n) vec1_a = [Max for i in range(n)] #vec0_a = array.array('f', [Min] * n) vec0_a = [Min for i in range(n)] if Initial: mat = [temp.readdomvector(x,relation_k) for x in actions] else: mat = [temp.readabsvector(x,relation_k) for x in actions] veclowa = vec0_a vechigha = vec1_a if Comments: print('initial low vector :',veclowa) print('initial high vector :', vechigha) it = 1 while veclowa != vechigha and it < 2*n*n: veclowb = temp.matmult2(mat,veclowa) vechighb = temp.matmult2(mat,vechigha) veclow = temp.contra(vechighb) vechigh = temp.contra(veclowb) if veclow == veclowa and vechigh == vechigha : break veclowa = veclow vechigha = vechigh if Comments: print(it, 'th low vector :',veclowa) print(it, 'th high vector :',vechigha) it += 1 if Comments: print('final low vector :', veclowa) print('final high vector :', vechigha) print('#iterations :', it) domvec = temp.sharpvec(veclowa,vechigha) determ = temp.determinateness(domvec) choiceVector = [] for i in range(n): choiceVector.append((domvec[i],str(actions[i]))) choiceVector.sort(reverse=True,key=itemgetter(0)) if Comments: if Initial: print('Choice vector for initial pre-kernel: %s' % str(ker)) else: print('Choice vector for terminal pre-kernel: %s' % str(ker)) for i,item in enumerate(choiceVector): print('%s: %+.2f' % (item[1],item[0]) ) else: return choiceVector
[docs] def computeGoodChoices(self,Comments=False): """ Computes characteristic values for potentially good choices. ..note:: Return a tuple with following content: [(0)-determ,(1)degirred,(2)degi,(3)degd,(4)dega,(5)str(choice),(6)domvec,(7)cover] """ import copy from operator import itemgetter temp = copy.deepcopy(self) Max = Decimal(str(temp.valuationdomain['max'])) Min = Decimal(str(temp.valuationdomain['min'])) Med = Decimal(str(temp.valuationdomain['med'])) actions = [x for x in temp.actions] relation = temp.relation domChoices = [] if 'dompreKernels' not in dir(temp): if Comments: temp.showPreKernels() else: temp.computePreKernels() for ker in temp.dompreKernels: if Comments: print('--> kernel:', ker) choice = [y for y in ker] #choice.sort() degi = temp.intstab(ker) dega = temp.absorb(ker) degd = temp.domin(ker) degirred = temp.domirredval(ker,relation) degmd = min(degi,degd) cover = temp.averageCoveringIndex(ker) relation_k = temp.domkernelrestrict(choice) n = len(actions) #vec1_a = array.array('f', [Max] * n) vec1_a = [Max for i in range(n)] #vec0_a = array.array('f', [Min] * n) vec0_a = [Min for i in range(n)] mat = [temp.readdomvector(x,relation_k) for x in actions] veclowa = vec0_a vechigha = vec1_a if Comments: print('initial veclow',veclowa) print('initial vechigh', vechigha) it = 1 while veclowa != vechigha and it < 2*n*n: veclowb = temp.matmult2(mat,veclowa) vechighb = temp.matmult2(mat,vechigha) veclow = temp.contra(vechighb) vechigh = temp.contra(veclowb) if veclow == veclowa and vechigh == vechigha : break veclowa = veclow vechigha = vechigh if Comments: print(it, 'th veclow :',veclowa) print(it, 'th vechigh :',vechigha) it += 1 if Comments: print('final veclow :', veclowa) print('final vechigh :', vechigha) print('#iterations :', it) #domvec = temp.sharpvec(veclowa,vechigha) domvecsharp = temp.sharpvec(veclowa,vechigha) if Comments: print('final result ;',domvecsharp) domvec = [(domvecsharp[i],str(actions[i])) for i in range(n)] domvec.sort(reverse=True) determ = temp.determinateness(domvec) domChoices.append([determ,degirred,degi,degd,-dega,str(choice),domvec,cover]) domChoicesSort = sorted(domChoices,key=itemgetter(0,7,3,4),reverse=True) goodChoicesDic = {} for ch in domChoicesSort: ch[4] = -ch[4] ch[5] = eval(ch[5]) goodChoicesDic[frozenset(ch[5])] = {'determ':ch[0], 'degirred':ch[1], 'degi':ch[2], 'degd':ch[3], 'dega':ch[4], 'cover':ch[7], 'bpv':ch[6]} self.goodChoices = domChoicesSort return goodChoicesDic
[docs] def computeGoodPirlotChoices(self,Comments=False): """ Characteristic values for potentially good choices using the Pirlot fixpoint algorithm. """ Max = self.valuationdomain['max'] Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] actions = list(self.actions.keys()) n = len(actions) relation = self.relation domChoicesSort = [] if 'dompreKernels' not in dir(self): self.computePreKernels() for ker in self.dompreKernels: if Comments: print('--> kernel:', ker) choice = [y for y in ker] #choice.sort() degi = self.intstab(ker) dega = self.absorb(ker) degd = self.domin(ker) degirred = self.domirredval(ker,relation) degmd = min(degi,degd) #vecmed = array.array('f', [Med] * n) #vecsol0 = array.array('f', [Min] * n) vecmed = [Med for i in range(n)] vecsol0 = [Min for i in range(n)] for x in range(n): if actions[x] in choice: vecsol0[x] = Max if Comments: print('initial solution', vecsol0) mat0 = [self.readdomvector(x,relation) for x in actions] mat = self.irreflex(mat0) vecsol = vecsol0 vecsolfin = vecmed it = 0 while it < 2*n*n: vecsolfin = self.matmult2(mat,vecsol) if Comments: print(it, 'th vecsol :',vecsol) veccur = self.contra(vecsolfin) if vecsol == veccur : break vecsol = veccur it += 1 if Comments: print('Final Solution=', vecsol) determ = self.determinateness(vecsol) domChoicesSort.append([-determ,degirred,degi,degd,dega,str(choice),vecsol]) domChoicesSort.sort() for ch in domChoicesSort: ch[5] = eval(ch[5]) self.goodChoices = domChoicesSort
[docs] def computeBadPirlotChoices(self,Comments=False): """ Characteristic values for potentially bad choices using the Pirlot's fixpoint algorithm. """ import array Max = self.valuationdomain['max'] Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] actions = list(self.actions.keys()) n = len(actions) relation = self.relation absChoicesSort = [] if 'abspreKernels' not in dir(self): self.computePreKernels() for ker in self.abspreKernels: if Comments: print('--> kernel:', ker) choice = [y for y in ker] #choice.sort() degi = self.intstab(ker) dega = self.absorb(ker) degd = self.domin(ker) degirred = self.absirredval(ker,relation) degmd = min(degi,degd) #vecmed = array.array('f', [Med] * n) #vecsol0 = array.array('f', [Min] * n) vecmed = [Med for i in range(n)] vecsol0 = [Min for i in range(n)] for x in range(n): if actions[x] in choice: vecsol0[x] = Max if Comments: print('initial solution', vecsol0) mat0 = [self.readabsvector(x,relation) for x in actions] mat = self.irreflex(mat0) vecsol = vecsol0 vecsolfin = vecmed it = 0 while it < 2*n*n: vecsolfin = self.matmult2(mat,vecsol) if Comments: print(it, 'th vesol :',vecsol) veccur = self.contra(vecsolfin) if vecsol == veccur : break vecsol = veccur it += 1 if Comments: print('Final Solution=', vecsol) determ = self.determinateness(vecsol) absChoicesSort.append([-determ,degirred,degi,degd,dega,str(choice),vecsol]) absChoicesSort.sort() for ch in absChoicesSort: ch[5] = eval(ch[5]) self.badChoices = absChoicesSort
[docs] def determinateness(self,vec,inPercent = True): """ Renders the determinateness of a characteristic vector *vec* = [(r(x),x),(r(y),y), ...] of length *n* in valuationdomain [Min,Med,Max]: *result* = sum_x( abs(r(x)-Med) ) / ( n*(Max-Med) ) If inPercent, *result* shifted (+1) and reduced (/2) to [0,1] range. """ Min = Decimal(str(self.valuationdomain['min'])) Max = Decimal(str(self.valuationdomain['max'])) Med = Decimal(str(self.valuationdomain['med'])) result = Decimal('0.0') n = len(vec) for i in range(n): try: result += abs(vec[i][0]-Med) except: result += abs(vec[i]-Med) #print result result /= n*(Max-Med) #print result if inPercent: return (result + Decimal('1.0'))/Decimal('2.0') else: return result*(Max-Med)
[docs] def computeBadChoices(self,Comments=False): """ Computes characteristic values for potentially bad choices. .. note:: Returns a tuple with following content: [(0)-determ,(1)degirred,(2)degi,(3)degd,(4)dega,(5)str(choice),(6)absvec] """ import copy from operator import itemgetter temp = copy.deepcopy(self) Max = Decimal(str(temp.valuationdomain['max'])) Min = Decimal(str(temp.valuationdomain['min'])) Med = Decimal(str(temp.valuationdomain['med'])) actions = [x for x in temp.actions] relation = temp.relation absChoices = [] if 'abspreKernels' not in dir(temp): temp.computePreKernels() for ker in temp.abspreKernels: if Comments: print('--> kernel:', ker) choice = [y for y in ker] #choice.sort() degi = temp.intstab(ker) dega = temp.absorb(ker) degd = temp.domin(ker) degirred = temp.absirredval(ker,relation) cover = temp.averageCoveringIndex(ker,direction="in") relation_k = temp.abskernelrestrict(choice) n = len(actions) #vec1_a = array.array('f', [Max] * n) #vec0_a = array.array('f', [Min] * n) vec1_a = [Max for i in range(n)] vec0_a = [Min for i in range(n)] mat = [temp.readabsvector(x,relation_k) for x in actions] veclowa = vec0_a vechigha = vec1_a if Comments: print('initial veclowa',veclowa) print('initial vechigha', vechigha) it = 1 while veclowa != vechigha and it < 2*n*n: veclowb = temp.matmult2(mat,veclowa) vechighb = temp.matmult2(mat,vechigha) veclow = temp.contra(vechighb) vechigh = temp.contra(veclowb) if veclow == veclowa and vechigh == vechigha : break veclowa = veclow vechigha = vechigh if Comments: print(it, 'th veclowa :',veclowa) print(it, 'th vechigha :',vechigha) it += 1 if Comments: print('final veclowa',veclowa) print('final vechigha', vechigha) absvecsharp = temp.sharpvec(veclowa,vechigha) absvec = [(absvecsharp[i],str(actions[i])) for i in range(n)] absvec.sort(reverse=True) determ = temp.determinateness(absvec) absChoices.append([determ,degirred,degi,-degd,dega,str(choice),absvec,cover]) absChoicesSort = sorted(absChoices,key=itemgetter(0,7,4,3),reverse=True) #absChoicesSort.sort() ## absChoicesSort.sort(reverse=True, key=itemgetter(7)) ## for ch in absChoicesSort: ## ch[5] = eval(ch[5]) ## self.badChoices = absChoicesSort badChoicesDic = {} for ch in absChoicesSort: ch[3] = -ch[3] ch[5] = eval(ch[5]) badChoicesDic[frozenset(ch[5])] = {'determ':ch[0], 'degirred':ch[1], 'degi':ch[2], 'degd':ch[3], 'dega':ch[4], 'cover':ch[7], 'bpv':ch[6]} self.badChoices = absChoicesSort return badChoicesDic
[docs] def htmlChoiceVector(self,ch,ChoiceVector=True,choiceType="good"): """ Show procedure for annotated bipolar choices. """ from digraphsTools import flatten from operator import itemgetter actions = [x for x in self.actions] Med = self.valuationdomain['med'] determ = ch[0] degirred = ch[1] degi = ch[2] degd = ch[3] dega = ch[4] choice = [x for x in flatten(ch[5])] choice.sort() vec = ch[6] vec.sort(reverse=True, key=itemgetter(0)) html = '<p>Choice : <b>%s</b><br/>\n ' % str(choice) #html += ' +-irredundancy : %.2f<br/>\n' % (degirred) html += ' independence : %.2f<br/>\n' % (degi) html += ' dominance : %.2f<br/>\n' % (degd) html += ' absorbency : %.2f<br/>\n' % (dega) if choiceType == "good": html += ' covering (%%): %.2f<br/>\n' %\ ( self.averageCoveringIndex(choice,direction='out') * Decimal('100') ) elif choiceType == "bad": html += ' covered (%%) : %.2f<br/>\n' %\ ( self.averageCoveringIndex(choice,direction='in') * Decimal('100') ) else: html += ' covering (%%): %.2f<br/>\n' %\ ( self.averageCoveringIndex(choice) * Decimal('100') ) html += ' determinateness (in %%) : %.2f</p>\n' % (determ*Decimal('100.0')) if ChoiceVector: html += '<p> - characteristic vector = {\n' for i in range(len(actions)): try: choice = [x for x in eval(vec[i][1])] choice.sort() html += '\'%s\': %.2f,\n ' % (str(choice),vec[i][0]) except: html += '\'%s\': %.2f,\n ' % (vec[i][1],vec[i][0]) html +='}</p>\n' ## elif determ > Decimal('0'): else: html += ' - most credible action(s) = {\n' for i in range(len(actions)): if vec[i][0] > Med: try: choice = [x for x in eval(vec[i][1])] choice.sort() html += '\'%s\': %.2f,\n' % (str(choice),vec[i][0]) except: html += '\'%s\': %.2f,\n' % (vec[i][1],vec[i][0]) html += '}</p>\n' return html
[docs] def showChoiceVector(self,ch,choiceType="good",ChoiceVector=True): """ Show procedure for annotated bipolar choices. """ from digraphsTools import flatten from operator import itemgetter actions = [x for x in self.actions] Med = self.valuationdomain['med'] determ = ch[0] degirred = ch[1] degi = ch[2] degd = ch[3] dega = ch[4] choice = [x for x in flatten(ch[5])] choice.sort() vec = ch[6] vec.sort(reverse=True,key=itemgetter(0)) print('* choice : ' + str(choice)) #print(' +-irredundancy : %.2f' % (degirred)) print(' independence : %.2f' % (degi)) print(' dominance : %.2f' % (degd)) print(' absorbency : %.2f' % (dega)) if choiceType == "good": print(' covering (%)' + ' : %.2f' %\ ( self.averageCoveringIndex(choice,direction='out') * Decimal('100') )) elif choiceType == "bad": print(' covered (%) ' + ' : %.2f' %\ ( self.averageCoveringIndex(choice,direction='in') * Decimal('100') )) else: print(' covering (%)' + ' : %.2f' %\ ( self.averageCoveringIndex(choice) * Decimal('100') )) print(" determinateness (%)", end=' ') print(': %.2f' % (determ*Decimal('100.0'))) if ChoiceVector: print(' - characteristic vector = {', end=' ') for i in range(len(actions)): #print('\'%s\': %.2f,' % (str(actions[i]),vec[i]), end=' ') try: choice = [x for x in eval(vec[i][1])] choice.sort() print('\'%s\': %.2f' % (str(choice),vec[i][0]), end=', ') except: print('\'%s\': %.2f' % (vec[i][1],vec[i][0]), end=', ') #print('\'%s\': %.2f,' % (vec[i][1],vec[i][0]), end=' ') print('}') ## elif determ > Decimal('0'): else: print(' - most credible action(s) = {', end=' ') for i in range(len(actions)): if vec[i][0] > Med: try: choice = [x for x in eval(vec[i][1])] choice.sort() print('\'%s\': %.2f' % (str(choice),vec[i][0]), end=', ') except: print('\'%s\': %.2f' % (vec[i][1],vec[i][0]), end=', ') print('}') print()
[docs] def showGoodChoices(self,Recompute=True): """ Characteristic values for potentially good choices. """ import array,copy from operator import itemgetter temp = copy.deepcopy(self) Max = temp.valuationdomain['max'] Min = temp.valuationdomain['min'] Med = temp.valuationdomain['med'] actions = [x for x in temp.actions] n = len(actions) relation = temp.relation print('*** Potentially good choices ***') print(' valuationdomain', temp.valuationdomain) domChoices = [] if 'dompreKernels' not in dir(temp) or Recompute: temp.computePreKernels() for ker in temp.dompreKernels: choice = [str(y) for y in ker] choice.sort() degi = temp.intstab(ker) dega = temp.absorb(ker) degd = temp.domin(ker) degirred = temp.domirredval(ker,relation) degmd = min(degi,degd) cover = temp.averageCoveringIndex(ker,direction="out") domChoices.append([degmd,degirred,degi,degd,-dega,str(choice),cover]) domChoicesSort = sorted(domChoices,key=itemgetter(0,6,3,4),reverse=True) print('domChoicesSort', domChoicesSort) for ch in domChoicesSort: choice = ch[5] degirred = ch[1] degi = ch[2] degd = ch[3] dega = -ch[4] print('* choice : ' + str(choice)) #print(' +irredundance : %.2f' % (degirred)) print(' independence : %.2f' % (degi)) print(' dominance : %.2f' % (degd)) print(' absorbency : %.2f' % (dega)) relation_k = temp.domkernelrestrict(eval(choice)) vec1_a = [Max for i in range(n)] vec0_a = [Min for i in range(n)] mat = [temp.readdomvector(x,relation_k) for x in actions] veclowa = vec0_a vechigha = vec1_a it = 1 while veclowa != vechigha and it < 2*n*n: veclowb = temp.matmult2(mat,veclowa) vechighb = temp.matmult2(mat,vechigha) veclow = temp.contra(vechighb) vechigh = temp.contra(veclowb) if veclow == veclowa and vechigh == vechigha : break veclowa = veclow vechigha = vechigh it += 1 print(' + characteristic vector = [', end=' ') bestvec = temp.sharpvec(veclowa,vechigha) for i in range(len(actions)): print('\'' + str(actions[i]) + '\': ',bestvec[i], ' ', end=' ') print(']') print()
[docs] def irreflex(self,mat): """ Puts diagonal entries of mat to valuationdomain['min'] """ Min = self.valuationdomain['min'] n = len(mat[0]) for i in range(n): mat[i][i] = Min return mat
[docs] def showBadChoices(self,Recompute=True): """ Characteristic values for potentially bad choices. """ import copy from operator import itemgetter temp = copy.deepcopy(self) Max = temp.valuationdomain['max'] Min = temp.valuationdomain['min'] Med = temp.valuationdomain['med'] actions = [x for x in temp.actions] #actions.sort() relation = temp.relation print('*** Potentially bad choices ***') print(' valuationdomain', temp.valuationdomain) absChoices = [] if 'abspreKernels' not in dir(temp) or Recompute: temp.computePreKernels() for ker in temp.abspreKernels: choice = [str(y) for y in ker] choice.sort() degi = temp.intstab(ker) dega = temp.absorb(ker) degd = temp.domin(ker) degirred = temp.absirredval(ker,relation) degmd = min(degi,dega) cover = temp.averageCoveringIndex(ker,direction="in") absChoices.append((degmd,degirred,degi,-degd,dega,str(choice),cover)) absChoicesSort = sorted(absChoices,key=itemgetter(0,6,4,3),reverse=True) print('absChoicesSort', absChoicesSort) absChoicesSort.sort() for ch in absChoicesSort: choice = ch[5] degirred = ch[1] degi = ch[2] degd = -ch[3] dega = ch[4] print('* choice : ' + str(choice)) #print(' -irredundance : %.2f' % (degirred)) print(' independence : %.2f' % (degi)) print(' dominance : %.2f' % (degd)) print(' absorbency : %.2f' % (dega)) relation_k = temp.abskernelrestrict(eval(choice)) n = len(actions) vec1_a = [Max for i in range(n)] vec0_a = [Min for i in range(n)] mat = [temp.readabsvector(x,relation_k) for x in actions] veclowa = vec0_a vechigha = vec1_a it = 1 while veclowa != vechigha and it < 2*n*n: veclowb = temp.matmult2(mat,veclowa) vechighb = temp.matmult2(mat,vechigha) veclow = temp.contra(vechighb) vechigh = temp.contra(veclowb) if veclow == veclowa and vechigh == vechigha : break veclowa = veclow vechigha = vechigh it += 1 print(' - characteristic vector = [', end=' ') bestvec = temp.sharpvec(veclowa,vechigha) for i in range(len(actions)): print('\'' + str(actions[i]) + '\': ',bestvec[i], ' ', end=' ') print(']') print()
[docs] def showMIS_AH(self,withListing=True): """ Prints all MIS using the Hertz method. Result saved in self.hertzmisset. """ import sys,random,copy,time relationBackup = copy.copy(self.relation) relation = {} V = set(self.actions) for x in V: relation[x] = {} for y in V: relation[x][y] = max(self.relation[x][y],self.relation[y][x]) self.relation = relation gamma = self.gammaSets() notGamma = self.notGammaSets() t0 = time.time() # MIS extraction print('*-----------------------------------*') print("* Python implementation of Hertz's *") print('* algorithm for generating all MIS *') print('* R.B. version 7(6)-25-Apr-06 *') print('*-----------------------------------*') print('*---- MIS extraction ----') t0 = time.time() # initialize MIS extraction hertzmisset = set() # global MIS collector set to empty # compute inital MIS n = len(V) S = set() # initial MIS gammaS = set() # initial MIS neighborhood while (S | gammaS) != V: i = random.choice(list(V-S-gammaS)) S.add(i) gammaS = gammaS | gamma[i][0] if withListing: print('===>>> Initial solution : ', list(S)) hertzmisset = hertzmisset | set([frozenset(S)]) # initialize all variables R = V - S ns = len(S) n = self.order i = 0 P = [set() for x in range(n)] M = [set() for x in range(n)] NR = [set() for x in range(n)] Q = [set() for x in range(n)] v = [0 for x in range(n)] NON_R = set() OUI_R = set() NON_S = set() OUI_S = set() hist = 0 # core of the algorithm while i >= 0: hist += 1 # Part 1 for k in [x for x in NON_R if gamma[x][0] & (OUI_R | OUI_S) == set() and len(gamma[x][0] - NON_R - NON_S) == 1]: sr = gamma[k][0]-NON_R-NON_S if sr != set(): if sr < R: P[i] = P[i] | sr OUI_R = OUI_R | sr r = sr.pop() NR[i] = NR[i] | (gamma[r][0] & (R-NON_R)) NON_R = NON_R | (gamma[r][0] & R) M[i] = M[i] | (gamma[r][0] & (S-NON_S)) NON_S = NON_S | (gamma[r][0] & S) test = True for vr in (R - OUI_R): if gamma[vr][0] & ((S - NON_S) | OUI_R) == set(): test = False break if test == True: hertzmisset = hertzmisset | set([frozenset((S - NON_S) | OUI_R)]) else: Q[i] = Q[i] | sr OUI_S = OUI_S | sr r = sr.pop() NR[i] = NR[i] | (gamma[r][0] & (R-NON_R)) NON_R = NON_R | (gamma[r][0] & R) # Part 2 if (NON_R | OUI_R) != R: i +=1 setPart2v = R - NON_R - OUI_R v[i] = setPart2v.pop() P[i] = set([v[i]]) OUI_R = OUI_R | P[i] M[i] = gamma[v[i]][0] & (S - NON_S) NON_S = NON_S | M[i] NR[i] = gamma[v[i]][0] & (R - NON_R) NON_R = NON_R | NR[i] test = True for vr in (R - OUI_R): if gamma[vr][0] & ((S - NON_S) | OUI_R) == set(): test = False break if test == True: hertzmisset = hertzmisset | set([frozenset((S - NON_S) | OUI_R)]) else: i -= 1 NR[i] = NR[i] | set([v[i+1]]) NON_R = (NON_R - NR[i+1]) | set([v[i+1]]) NON_S = NON_S - M[i+1] OUI_R = OUI_R - P[i+1] OUI_S = OUI_S - Q[i+1] Q[i+1] = set() # end of algorithm t1 = time.time() if withListing: print('*---- results ----*') v = [0 for i in range(n+1)] mislen = set() for ch in hertzmisset: v[len(ch)] += 1 if withListing: print(list(ch)) mislen = mislen | set([len(ch)]) print('*---- statistics ----*') print('mis lengths : ', list(range(self.order+1))) print('frequency : ', v) mislenlist = list(mislen) mislenlist.sort() print('mis lengths : ', mislenlist) print('mis solutions : ', len(hertzmisset)) print('execution time : %.5f sec.' % (t1 - t0)) print('iteration history: ', hist) print('result in self.hertzmisset') # store global results self.mislen = mislenlist self.hertzmisset = hertzmisset # restore original relation and neigborhooods self.relation = relationBackup self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] def showMIS_RB(self,withListing=True): """ Prints all MIS using the Bisdorff method. Result saved in self.newmisset. """ import sys,random,copy,time relationBackup = copy.copy(self.relation) relation = {} V = set(self.actions) for x in V: relation[x] = {} for y in V: relation[x][y] = max(self.relation[x][y],self.relation[y][x]) self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() t0 = time.time() # initialize MIS extraction newmisset = set() # global MIS collector set to empty # compute inital MIS initmisset = [] # initial MISs collector n = len(V) S = set() # initial MIS gammaS = set() # initial MIS neighborhood while (S | gammaS) != V: i = random.choice(list(V-S-gammaS)) S.add(i) gammaS = gammaS | self.gamma[i][0] newmisset = newmisset | set([frozenset(S)]) initmisset.append((len(S),frozenset(S))) R = V - S # remaining unused nodes # find disjoint further inital MISs while R != set(): S = set() # current MIS candidate gammaS = set() # current MIS neighborhood Cr = R # current remaining unused nodes while Cr != set() and S | gammaS != V: # independence condition i = Cr.pop() S.add(i) gammaS = gammaS | self.gamma[i][0] Cr = R - S - gammaS if S | gammaS == V: # cover condition newmisset = newmisset | set([frozenset(S)]) initmisset.append((len(S),frozenset(S))) R = R - S # further remaining unused nodes print('\ninital MISs ') # sorting MIS by increasing length initmisset.sort() i = 0 for mis in initmisset: print(list(mis[1]), mis[0]) i +=1 # MIS extraction print('*---- MIS extraction ----') mislen = set() # list of MIS length observed S = initmisset[i-1][1] # start with largest init MIS print('===>>> Initial solution : ', list(S)) ns = len(S) print('--> iteration 0 ') mislen = mislen | set([ns]) upmis = set() # independent choices as MIS candidates R = V - S for r in R: # add a node from outside S Sr = (S - self.gamma[r][0]) | set([r]) # Sr independent upmis = upmis | set([frozenset(Sr)]) iter = 0 uphistory = set() while upmis != set(): iter += 1 # next iteration init print('--> up iteration: ', iter) upmisiter = copy.copy(upmis) print('potential choices: ', len(upmisiter)) uphistory = uphistory | upmis upmis = set() # up movement for Sch in upmisiter: gammaSch = set() for x in Sch: gammaSch = gammaSch | self.gamma[x][0] while (Sch | gammaSch) != V: i = random.choice(list(V-Sch-gammaSch)) Sch = Sch | set([i]) gammaSch = gammaSch | self.gamma[i][0] if Sch not in newmisset: mislen = mislen | set([len(Sch)]) newmisset = newmisset | set([frozenset(Sch)]) Rch = V - Sch for r in Rch: # add a node from outside S Srch = (Sch - self.gamma[r][0]) | set([r]) # Sr independent if Srch not in uphistory: upmis = upmis | set([frozenset(Srch)]) t1 = time.time() print('*---- results ----*') v = [0 for i in range(n+1)] for ch in newmisset: v[len(ch)] += 1 if withListing: print(list(ch)) print('*---- statistics ----*') print('mis lengths : ', list(range(self.order+1))) print('frequency : ', v) mislenlist = list(mislen) mislenlist.sort() print('mis lengths : ', mislenlist) print('mis solutions : ', len(newmisset)) print('execution time: %.5f sec.' % (t1 - t0)) print('up-history : ', len(uphistory)) print('result in self.newmisset') print('*-----------------------------------*') print("* Python implementation of Hertz's *") print('* algorithm for generating all MIS *') print('* R.B. version Ronda April 2006 *') print('*-----------------------------------*') # store global results self.mislen = mislenlist self.newmisset = newmisset # restore original relation and neigborhooods self.relation = relationBackup self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] def showMIS_UD(self,withListing=True): """ Prints all MIS using the Hertz-Bisdorff method. Result saved in self.newmisset. """ import sys,random,copy,time sys.setrecursionlimit(15000) self.relationBackup = copy.copy(self.relation) relation = {} V = set(self.actions) for x in V: relation[x] = {} for y in V: relation[x][y] = max(self.relation[x][y],self.relation[y][x]) self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.showAll() t0 = time.time() # initilaize MIS extraction self.newmisset = set() # global MIS collector set to empty # compute inital MIS initmisset = [] # initial MISs collector n = len(V) S = set() # initial MIS gammaS = set() # initial MIS neighborhood V = set(self.actions) while (S | gammaS) != V: i = random.choice(list(V-S-gammaS)) S.add(i) gammaS = gammaS | self.gamma[i][0] self.newmisset = self.newmisset | set([frozenset(S)]) initmisset.append((len(S),frozenset(S))) R = V - S # remaining unused nodes # find disjoint further inital MISs while R != set(): S = set() # current MIS candidate gammaS = set() # current MIS neighborhood Cr = R # current remaining unused nodes while Cr != set() and S | gammaS != V: # independence condition i = Cr.pop() S.add(i) gammaS = gammaS | self.gamma[i][0] Cr = R - S - gammaS if S | gammaS == V: # cover condition self.newmisset = self.newmisset | set([frozenset(S)]) initmisset.append((len(S),frozenset(S))) R = R - S # further remaining unused nodes print('\ninital MISs ') # sorting MIS by increasing length initmisset.sort() for mis in initmisset: print(list(mis[1]), mis[0]) print('*---- same mises with up and down potentials ----') self.upmis = set() # independent choices as MIS candidates self.downmis = set() # covering choices as MIS candidates self.mislen = set() # list of MIS length observed for mis in initmisset: S = mis[1] print('===>>> Inital solution : ', list(S)) print('--> compute MIS of same size ', len(S)) s = 0 ns = mis[0] self.mislen = self.mislen | set([ns]) V = set(self.actions) self.computeupdown1(s,S) # compute all MIS of length ns iter = 0 while self.upmis != set() or self.downmis != set(): iter += 1 # next iteration init print('\n--> up iteration: ', iter) self.upmisiter = self.upmis.copy() self.upmis = set() self.downmisiter = self.downmis.copy() self.downmis = set() V = set(self.actions) # up movement for Sup in self.upmisiter: Sch = Sup[0] gammaSch = Sup[1] while (Sch | gammaSch) != V: i = random.choice(list(V-Sch-gammaSch)) Sch = Sch | set([i]) gammaSch = gammaSch | self.gamma[i][0] if Sch not in self.newmisset: self.mislen = self.mislen | set([len(Sch)]) self.newmisset = self.newmisset | set([frozenset(Sch)]) s = 0 self.computeupdown1(s,Sch) # down movement print('\n* <<< down potentials in', iter) for Sch in self.downmisiter: for v in Sch: if self.gamma[v][0] & Sch != set(): Sch = Sch - set([v]) gammaSch = set() for x in Sch: gammaSch = gammaSch | self.gamma[x][0] if gammaSch & Sch == set(): if Sch | gammaSch == V: if Sch not in self.newmisset: self.mislen = self.mislen | set([len(Sch)]) self.newmisset = self.newmisset | set([frozenset(Sch)]) self.computeupdown1(s,Sch) else: if Sch | gammaSch == V: self.downmis = self.downmis | set([frozenset(Sch)]) print('solutions: ', len(self.newmisset)) t1 = time.time() print('*---- results ----*') v = [0 for i in range(n+1)] for ch in self.newmisset: v[len(ch)] += 1 if withListing: print(list(ch)) print('*---- statistics ----*') print('mis lengths : ', list(range(self.order+1))) print('frequency : ', v) print('mis lengths : ', list(self.mislen)) print('mis solutions : ', len(self.newmisset)) print('execution time: %.5f sec.' % (t1 - t0)) print('result in self.newmisset') print('*-----------------------------------*') print("* Python implementation of Hertz's *") print('* algorithm for generating all MIS *') print('* R.B. April 2006 *') print('*-----------------------------------*') # restore original relation and neigborhooods self.relation = self.relationBackup self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] def computeupdown1(self, s, S): """ Help method for show_MIS_HB2 method. fills self.newmisset, self.upmis, self.downmis. """ Min = self.valuationdomain['min'] V = set(self.actions) R = V - S print(s, end=' ') # recursion depth: traces the discovery of MISs s += 1 for v in S: # choose a leaving node Sv = S - set([v]) gammaSv = set() # recompute neighborhood for sv in Sv: gammaSv = gammaSv | self.gamma[sv][0] for r in R: # add a node from outside S Svr = Sv | set([r]) gammaSvr = gammaSv | self.gamma[r][0] if gammaSvr & Svr == set(): # if independent if gammaSvr | Svr == V: # and if covering if Svr not in self.newmisset: #print 'MIS ', Svr self.newmisset = self.newmisset | set([frozenset(Svr)]) self.computeupdown1(s,Svr) else: # indep. and not covering #print 'up ->',Svr self.upmis = self.upmis | set([(frozenset(Svr),frozenset(gammaSvr))]) elif Svr | gammaSvr == V: # covering but not independent #print 'down->',Svr self.downmis = self.downmis | set([frozenset(Svr)])
[docs] def showMIS_HB2(self,withListing=True): """ Prints all MIS using the Hertz-Bisdorff method. Result saved in self.newmisset. """ import sys,random,copy,time sys.setrecursionlimit(15000) self.relationBackup = copy.copy(self.relation) relation = {} V = set(self.actions) for x in V: relation[x] = {} for y in V: relation[x][y] = max(self.relation[x][y],self.relation[y][x]) self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.showAll() t0 = time.time() # initilaize MIS extraction self.newmisset = set() # global MIS collector set to empty # compute inital MIS initmisset = [] # initial MISs collector n = len(V) S = set() # initial MIS gammaS = set() # initial MIS neighborhood V = set(self.actions) while (S | gammaS) != V: i = random.choice(list(V-S-gammaS)) S.add(i) gammaS = gammaS | self.gamma[i][0] self.newmisset = self.newmisset | set([frozenset(S)]) initmisset.append((len(S),frozenset(S))) R = V - S # remaining unused nodes # find disjoint further inital MISs while R != set(): S = set() # current MIS candidate gammaS = set() # current MIS neighborhood Cr = R # current remaining unused nodes while Cr != set() and S | gammaS != V: # independence condition i = Cr.pop() S.add(i) gammaS = gammaS | self.gamma[i][0] Cr = R - S - gammaS if S | gammaS == V: # cover condition self.newmisset = self.newmisset | set([frozenset(S)]) initmisset.append((len(S),frozenset(S))) R = R - S # further remaining unused nodes print('\ninital MISs ') # sorting MIS by increasing length initmisset.sort() for mis in initmisset: print(list(mis[1]), mis[0]) print('*---- same mises with up and down potentials ----') self.upmis = set() # independent choices as MIS candidates self.uphistory = set() self.downmis = set() # covering choices as MIS candidates self.downhistory = set() self.mislen = set() # list of MIS length observed for mis in initmisset: S = mis[1] print('===>>> Inital solution : ', list(S)) print('--> compute MIS of same size ', len(S)) s = 0 ns = mis[0] self.mislen = self.mislen | set([ns]) V = set(self.actions) self.computeupdown2(s,S) # starting from S compute all MIS of same length iter = 0 while self.upmis != set() or self.downmis != set(): iter += 1 # next iteration init self.upmisiter = self.upmis.copy() self.uphistory = self.uphistory | self.upmis self.upmis = set() self.downmisiter = self.downmis.copy() self.downhistory = self.downhistory | self.downmis self.downmis = set() V = set(self.actions) # up movement print('\n >>> up iteration: ', iter) for Sup in self.upmisiter: Sch = Sup[0] gammaSch = Sup[1] while (Sch | gammaSch) != V: i = random.choice(list(V-Sch-gammaSch)) Sch = Sch | set([i]) gammaSch = gammaSch | self.gamma[i][0] if Sch not in self.newmisset: self.mislen = self.mislen | set([len(Sch)]) self.newmisset = self.newmisset | set([frozenset(Sch)]) s = 0 self.computeupdown2(s,Sch) # down movement print('\n* <<< down potentials in', iter) for Sch in self.downmisiter: for v in Sch: if self.gamma[v][0] & Sch != set(): Sch = Sch - set([v]) gammaSch = set() for x in Sch: gammaSch = gammaSch | self.gamma[x][0] if gammaSch & Sch == set(): if Sch | gammaSch == V and Sch not in self.newmisset: self.mislen = self.mislen | set([len(Sch)]) self.newmisset = self.newmisset | set([frozenset(Sch)]) s = 0 self.computeupdown2(s,Sch) #elif Sch not in self.uphistory: #print 'up ->',Sch #self.upmis = self.upmis | set([(frozenset(Sch),frozenset(gammaSch))]) else: if Sch | gammaSch == V and Sch not in self.downhistory: self.downmis = self.downmis | set([frozenset(Sch)]) print('solutions: ', len(self.newmisset)) t1 = time.time() print('*---- results ----*') v = [0 for i in range(n+1)] for ch in self.newmisset: v[len(ch)] += 1 if withListing: print(list(ch)) print('*---- statistics ----*') print('mis lengths : ', list(range(self.order+1))) print('frequency : ', v) print('mis lengths : ', list(self.mislen)) print('mis solutions : ', len(self.newmisset)) print('execution time: %.5f sec.' % (t1 - t0)) print('result in self.newmisset') print('*-----------------------------------*') print("* Python implementation of Hertz's *") print('* algorithm for generating all MIS *') print('* R.B. April 2006 *') print('*-----------------------------------*') # restore original relation and neigborhooods self.relation = self.relationBackup self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] def computeupdown2irred(self, s, S): """ Help method for show_MIS_HB1 method. Fills self.newmisset, self.upmis, self.downmis. """ #import copy newmis = set([frozenset(S)]) V = set(self.actions) s = 0 while newmis != set(): self.newmisset = self.newmisset | newmis S = newmis.pop() R = V - S print(s, end=' ') # recursion depth: traces the discovery of MISs s += 1 #Siter = copy.copy(S) for v in S: # choose a leaving node Sv = S - set([v]) gammaSv = set() # recompute Sv neighborhood for sv in Sv: gammaSv = gammaSv | self.gamma[sv][0] privgammav = (self.gamma[v][0] | set([v])) - (gammaSv | Sv) for r in R: # add a node from outside S if privgammav <= self.gamma[r][0]: # covering Svr = Sv | set([r]) if self.gamma[r][0] & Sv == set(): # independent if Svr not in self.newmisset: print('new MIS ', Svr) newmis = newmis | set([frozenset(Svr)]) else: if Svr not in self.downhistory: print('down->',Svr) self.downmis = self.downmis | set([frozenset(Svr)]) else: if self.gamma[r][0] & Sv == set(): # independent Svr = Sv | set([r]) if Svr not in self.uphistory: print('up ->',Svr) gammaSvr = gammaSv | self.gamma[r][0] self.upmis = self.upmis | set([(frozenset(Svr),frozenset(gammaSvr))])
[docs] def computeupdown2(self, s, S): """ Help method for show_MIS_HB1 method. Fills self.newmisset, self.upmis, self.downmis. """ #import copy newmis = set([frozenset(S)]) V = set(self.actions) s = 0 while newmis != set(): self.newmisset = self.newmisset | newmis S = newmis.pop() R = V - S print(s, end=' ') # recursion depth: traces the discovery of MISs s += 1 #Siter = copy.copy(S) for v in S: # choose a leaving node Sv = S - set([v]) gammaSv = set() # recompute neighborhood for sv in Sv: gammaSv = gammaSv | self.gamma[sv][0] #privgammav = (self.gamma[v][0] | set([v])) - gammaSv for r in R: # add a node from outside S Svr = Sv | set([r]) gammaSvr = gammaSv | self.gamma[r][0] if gammaSvr & Svr == set(): # if independent if gammaSvr | Svr == V: # and if covering if Svr not in self.newmisset: #print 'new MIS ', Svr newmis = newmis | set([frozenset(Svr)]) else: # indep. and not covering if Svr not in self.uphistory: #print 'up ->',Svr self.upmis = self.upmis | set([(frozenset(Svr),frozenset(gammaSvr))]) elif Svr | gammaSvr == V: # covering but not independent if Svr not in self.downhistory: #print 'down->',Svr self.downmis = self.downmis | set([frozenset(Svr)])
[docs] def computeODistance(self,op2,comments=False): """ renders the squared normalized distance of two digraph valuations. .. note:: op2 = digraphs of same order as self. """ import math,copy op1 = copy.deepcopy(self) Debug = False if comments: print('* --- compute O Distance of two digraphs ---- *') ODistance = 0.0 actionsList1 = [x for x in op1.actions] actionsList2 = [x for x in op2.actions] if len(actionsList1) != len(actionsList2): ODistance = None if comments: print('actionsList1', actionsList1) print('actionsList2', actionsList2) print('Error: Actions sets are not comaptible ?') print(' ODistance = None !!!') else: Minop1 = op1.valuationdomain['min'] Maxop1 = op1.valuationdomain['max'] Minop2 = op2.valuationdomain['min'] Maxop2 = op2.valuationdomain['max'] op1.recodeValuation(-1.0,1.0) op2.recodeValuation(-1.0,1.0) n = len(actionsList1) for i in range(n): for j in range(i+1,n): ODistance += math.pow(op1.relation[actionsList1[i]][actionsList1[j]] - op2.relation[actionsList2[i]][actionsList2[j]],2) if Debug: print('==>>', end=' ') print(actionsList1[i],actionsList1[j], 'op1 =', end=' ') print(op1.relation[actionsList1[i]][actionsList1[j]]) print(actionsList2[i],actionsList2[j], 'op2 =', end=' ') print(op2.relation[actionsList2[i]][actionsList2[j]]) print('ODistance +=', math.pow(op1.relation[actionsList1[i]][actionsList1[j]] - op2.relation[actionsList2[i]][actionsList2[j]],2)) if comments: print('ODistance between',op1.name, ' and') print(op2.name, '=', ODistance) op1.recodeValuation(Minop1,Maxop1) op2.recodeValuation(Minop1,Maxop2) return ODistance
[docs] def computeSingletonRanking(self,Comments = False, Debug = False): """ Renders the sorted bipolar net determinatation of outrankingness minus outrankedness credibilities of all singleton choices. res = ((netdet,singleton,dom,absorb)+) """ import copy from operator import itemgetter valuationdomain = copy.deepcopy(self.valuationdomain) self.recodeValuation(0.0,100.0) sigs = [x[0] for x in self.singletons()] res = [] for i in range(len(sigs)): if Debug: print(sigs[i], self.domin(sigs[i]) - self.absorb(sigs[i])) res.append((self.domin(sigs[i]) - self.absorb(sigs[i]),sigs[i],self.domin(sigs[i]),self.absorb(sigs[i]))) res.sort(reverse=True, key=itemgetter(0)) if Comments: for x in res: print("{%s} : %.3f " % ( [y for y in x[1]][0], (float(x[0]) + 100.0)/2.0 )) if Debug: print(res) self.recodeValuation(valuationdomain['min'],valuationdomain['max']) return res
[docs] def showSingletonRanking(self,Comments = True, Debug = False): """ Calls self.computeSingletonRanking(comments=True,Debug = False). Renders and prints the sorted bipolar net determinatation of outrankingness minus outrankedness credibilities of all singleton choices. res = ((netdet,sigleton,dom,absorb)+) """ res = self.computeSingletonRanking(Comments,Debug) return res
# def omax(self,L, Debug=False): # """ # Epistemic **disjunction** for bipolar outranking characteristics # computation: Med is the valuation domain median and L is a list of # r-valued statement characteristics. # With **positive** arguments, omax operates a **max**, # with **negative** arguments, a **min**. # The mixture of **both positive and negative** arguments results in # an **indeterminate** value. # Likewise to a mean, the *omax* operator is not associative. We therefore first assemble all positive, negative and null terms # and operate omax on the three assembled arguments. # """ # Med = self.valuationdomain['med'] # terms = list(L) # termsPlus = [] # termsMinus = [] # termsNuls = [] # for i in range(len(terms)): # if terms[i] > Med: # termsPlus.append(terms[i]) # elif terms[i] < Med: # termsMinus.append(terms[i]) # else: # termsNuls.append(terms[i]) # if Debug: # print('terms', terms) # print('termsPlus',termsPlus) # print('termsMinus', termsMinus) # print('termsNuls', termsNuls) # np = len(termsPlus) # nm = len(termsMinus) # if np > 0 and nm == 0: # return max(termsPlus) # elif nm > 0 and np == 0: # return min(termsMinus) # else: # return Med # def omin(self,L, Debug=False): # """ # Epistemic **conjunction** of a list L of bipolar outranking characteristics. # Med is the given valuation domain median. # With **positive** arguments, omax operates a **min**, # with **negative** arguments, a **max**. # The mixture of both **positive and negative** arguments results # in an **indeterminate** value. # Likewise to a mean, the *omin* operator is not associative. We therefore first assemble all positive, negative and null terms # and operate omin on the three assembled arguments. # """ # Med = self.valuationdomain['med'] # terms = list(L) # termsPlus = [] # termsMinus = [] # termsNuls = [] # for i in range(len(terms)): # if terms[i] >= Med: # termsPlus.append(terms[i]) # elif terms[i] <= Med: # termsMinus.append(terms[i]) # else: # termsNuls.append(terms[i]) # if Debug: # print('terms', terms) # print('termsPlus',termsPlus) # print('termsMinus', termsMinus) # print('termsNuls', termsNuls) # np = len(termsPlus) # nm = len(termsMinus) # if np > 0: # if nm > 0: # return Med # else: # return min(termsPlus) # else: # if nm > 0: # return max(termsMinus) # else: # return Med def _computeNetFlowsRankingDict(self,Stored=True,Debug=False): """ Tenders an ordered dictionary of the actions (from best to worst) following the net flows ranking rule with rank and net flow attributes. """ from operator import itemgetter relation = self.relation actionsList = [x for x in self.actions] netFlows = [] Med = self.valuationdomain['med'] if Med == Decimal('0'): for x,rx in relation.items(): xnetflows = sum(rx[y] - relation[y][x]\ for y in actionsList) ## if Debug: ## print('netflow for %s = %.2f' % (x, xnetflows)) netFlows.append((-xnetflows,x)) # reversed sorting with keeping the actions natural ordering else: Max = self.valuationdomain['max'] Min = self.valuationdomain['min'] for x,rx in relation.items(): xnetflows = sum(rx[y] + (Max - relation[y][x] + Min)\ for y in actions) ## if Debug: ## print('netflow for %s = %.2f' % (x, xnetflows)) netFlows.append((-xnetflows,x)) # reversed sorting with keeping the actions natural ordering netFlows.sort() ## if Debug: ## print(netFlows) netFlowsRanking = OrderedDict() for k,item in enumerate(netFlows): netFlowsRanking[item[1]] = {'rank':k,'netFlow':-item[0]} if Stored: self.netFlowsRankingDict = netFlowsRanking return netFlowsRanking def _computeNetFlowsRanking(self,Stored=True,Debug=False): """ Tenders an ordered dictionary of the actions (from best to worst) following the net flows ranking rule with rank and net flow attributes. """ from operator import itemgetter relation = self.relation actionsList = [x for x in self.actions] incNetFlowsScores = [] decNetFlowsScores = [] #Med = self.valuationdomain['med'] #if Med == Decimal('0'): for x in actionsList: xnetFlows = 0 for y in actionsList: xnetFlows += relation[x][y] - relation[y][x] incNetFlowsScores.append((xnetFlows,x)) decNetFlowsScores.append((xnetFlows,x)) if Debug: print(incNetFlowsScores) incNetFlowsScores.sort(key=itemgetter(0)) decNetFlowsScores.sort(key=itemgetter(0),reverse=True) self.incNetFlowsScores = incNetFlowsScores self.decNetFlowsScores = decNetFlowsScores netFlowsRanking = [x[1] for x in decNetFlowsScores] netFlowsOrder = [x[1] for x in incNetFlowsScores] self.netFlowsRanking = netFlowsRanking self.netFlowsOrder = netFlowsOrder
[docs] def computeNetFlowsRankingDict(self): """ Renders an ordered list (from best to worst) of the actions following the net flows ranking rule. """ try: return list(self.netFlowsRankingDict.keys()) except AttributeError: netFlowsRankingDict = self._computeNetFlowsRankingDict() return list(netFlowsRankingDict.keys())
[docs] def computeNetFlowsRanking(self): """ Renders an ordered list (from best to worst) of the actions following the net flows ranking rule. """ try: return self.netFlowsRanking except: self._computeNetFlowsRanking() return self.netFlowsRanking
# try: # return list(self.netFlowsRankingDict.keys()) # except AttributeError: # netFlowsRankingDict = self._computeNetFlowsRankingDict() # return list(netFlowsRankingDict.keys())
[docs] def computeNetFlowsOrder(self): """ Renders an ordered list (from best to worst) of the actions following the net flows ranking rule. """ try: return self.netFlowsOrder except: self._computeNetFlowsRanking() return self.netFlowsOrder
# try: # return list(self.netFlowsRankingDict.keys()) # except AttributeError: # netFlowsRankingDict = self._computeNetFlowsRankingDict() # return list(netFlowsRankingDict.keys())
[docs] def computeNetFlowsOrderDict(self): """ Renders an ordered list (from worst to best) of the actions following the net flows ranking rule. """ try: return list(reversed(list(self.netFlowsRankingDict.keys()))) except AttributeError: netFlowsRankingDict = self._computeNetFlowsRankingDict() return list(reversed(list(netFlowsRankingDict.keys())))
def _computeKohlerRankingDict(self,Debug=False): """ Renders a ranking from the best to the worst of the actions following Kohler's rule as an ordered dictionary with rank and majorityMargin attributes. """ Max = self.valuationdomain['max'] actionsList = [x for x in self.actions] relation = self.relation rank = OrderedDict() k = 1 while actionsList != []: maximin = [] for x in actionsList: xmin = Max for y in actionsList: if x != y: if relation[x][y] < xmin: xmin = relation[x][y] if Debug: print('x, xmin', x, xmin) maximin.append((xmin,x)) maximin.sort() if Debug: print(maximin, maximin[-1][1]) rank[maximin[-1][1]] = {'rank':k,'majorityMargin':maximin[-1][0]} actionsList.remove(maximin[-1][1]) k += 1 if Debug: print('actionsList', actionsList) if Debug: print(rank) return rank
[docs] def computeKohlerOrder(self): """ Renders an ordering (worst to best) of the actions following Kohler's rule. """ ranking = self._computeKohlerRankingDict() res = [x for x in ranking] return list(reversed(res))
[docs] def computeKohlerRanking(self): """ Renders a ranking (best to worst) of the actions following Kohler's rule. """ ranking = self._computeKohlerRankingDict() return [x for x in ranking]
def _computeArrowRaynaudRankingDict(self,Debug=False): """ Renders a ranking of the actions following Arrow&Raynaud's rule. """ Min = self.valuationdomain['min'] actionsList = [x for x in self.actions] n = len(actionsList) relation = self.relation rank = OrderedDict() k = 1 while actionsList != []: minimax = [] for x in actionsList: xmax = Min for y in actionsList: if x != y: if relation[x][y] > xmax: xmax = relation[x][y] if Debug: print('x, xmax', x, xmax) minimax.append((xmax,x)) minimax.sort() if Debug: print(minimax, minimax[0][1]) rank[minimax[0][1]] = {'rank':n-k+1,'majorityMargin':minimax[0][0]} actionsList.remove(minimax[0][1]) k += 1 if Debug: print('actionsList', actionsList) if Debug: print(rank) return rank
[docs] def computeArrowRaynaudOrder(self): """ Renders a linear ordering from worst to best of the actions following Arrow&Raynaud's rule. """ ranking = self._computeArrowRaynaudRankingDict() return [x for x in ranking]
[docs] def computeArrowRaynaudRanking(self): """ renders a linear ranking from best to worst of the actions following Arrow&Raynaud's rule. """ ranking = self._computeArrowRaynaudRankingDict() res = [x for x in ranking] return list(reversed(res))
def _computeCopelandRanking(self): """ Renders a linear ranking from best to worst of the actions following Copelands's rule. """ from operator import itemgetter c = PolarisedDigraph(self) cRelation = c.relation actionsList = [x for x in self.actions] incCopelandScores = [] decCopelandScores = [] for x in actionsList: copelandScore = Decimal('0') for y in actionsList: copelandScore += cRelation[x][y] - cRelation[y][x] #actions[x]['score'] = copelandScore incCopelandScores.append((copelandScore,x)) decCopelandScores.append((copelandScore,x)) # reversed sorting with keeping the actions initial ordering # in case of ties incCopelandScores.sort(key=itemgetter(0)) decCopelandScores.sort(key=itemgetter(0),reverse=True) self.incCopelandScores = incCopelandScores self.decCopelandScores = decCopelandScores ## gamma = self.gamma ## copelandScores = [] ## for x in self.actions: ## copelandScore = len(gamma[x][1]) - len(gamma[x][0]) ## copelandScores.append((copelandScore,x)) ## # reversed sorting with keeping the actions initial ordering ## # in case of ties ## copelandScores.sort() copelandRanking = [x[1] for x in decCopelandScores] copelandOrder = [x[1] for x in incCopelandScores] self.copelandRanking = copelandRanking self.copelandOrder = copelandOrder
[docs] def computeCopelandRanking(self): """ renders a linear ranking from best to worst of the actions following Arrow&Raynaud's rule. """ try: ranking = self.copelandRanking except: self._computeCopelandRanking() ranking = self.copelandRanking return ranking
[docs] def computeCopelandOrder(self): """ renders a linear ordering from worst to best of the actions following Arrow&Raynaud's rule. """ try: ordering = self.copelandOrder except: self._computeCopelandRanking() ordering = self.copelandOrder return ordering
## def _computeRankedPairsOrder(self,Cpp=False,Debug=False): ## """ ## Renders an actions ordering from the worst to the best obtained from the ## ranked pairs rule. ## """ ## relation = self.relation ## #actions = self.actions ## actions = [x for x in self.actions] ## actions.sort() ## ## n = len(actions) ## ## listPairs = [] ## for x,rx in relation.items(): ## for y,rxy in rx.items(): ## if x != y: ## listPairs.append((rxy,(x,y),x,y)) ## listPairs.sort(reverse=False) ## if Debug: ## print(listPairs) ## ## g = IndeterminateDigraph(order=n) ## g.actions = self.actions ## g.valuationdomain = {'min':Decimal('-1'), 'med': Decimal('0'), 'max': Decimal('1')} ## Min = g.valuationdomain['min'] ## Max = g.valuationdomain['max'] ## Med = g.valuationdomain['med'] ## g.relation = {} ## for x in g.actions: ## g.relation[x] = {} ## grx = g.relation[x] ## for y in g.actions: ## grx[y] = Med ## ## rankedPairs = [x[1] for x in listPairs] ## for pair in rankedPairs: ## if Debug: ## print('next pair: ', pair) ## x = pair[0] ## y = pair[1] ## grxy = g.relation[x][y] ## gryx = g.relation[y][x] ## if grxy == Min and gryx == Min: ## grxy = Max ## g.gamma = g.gammaSets() ## g.notGamma = g.notGammaSets() ## if Cpp: ## circ = g.computeCppChordlessCircuits() ## else: ## circ = g.computeChordlessCircuits() ## if len(circ) != 0: ## if Debug: ## print(circ) ## grxy = Min #### else: #### if Debug: #### print('added: (%s,%s) characteristic: %.2f' %\ #### (x,y, self.relation[x][y])) ## ## g.gamma = g.gammaSets() ## ## outdegrees = [] ## for x in g.actions: ## outdegrees.append((len(g.gamma[x][0]),x)) ## outdegrees.sort(reverse=True) ## ## rankedPairsOrder = [] ## for x in outdegrees: ## rankedPairsOrder.append(x[1]) ## if Debug: ## print('Ranked Pairs Order = ', rankedPairsOrder) ## return rankedPairsOrder ## ## def _computeRankedPairsRanking(self): ## """ ## Renders an actions ordering from the best to the worst obtained from the ## ranked pairs rule. ## """ ## ordering = self.computeRankedPairsOrder() ## return list(reversed(ordering)) # def computeMedianRanking(self, # orderLimit=7,Threading=False,nbrOfCPUs=1, # Comments=False,Debug=False): # """ # Renders a ranking from best to worst of the actions with maximal marginal correlation and minimal marginal correlation amplitude. # .. note:: # Returns a triple: (medianRanking (from best to worst), # maxMarginalCorrelation, minMarginalCorrelationAmplitude) # """ # from digraphsTools import all_perms # from math import factorial # Min = self.valuationdomain['min'] # relation = self.relation # actions = [x for x in self.actions] # n = len(actions) # if n > orderLimit: # return None # maxMarginalCorrelation = Decimal('-1.0') # minMarginalCorrelationAmplitude = Decimal('2.0') # s = 1 # maxs = factorial(n) # maximalRankings = [] # for a in all_perms(list(actions)): # s += 1 # if Comments: # print('%d/%d' % (s,maxs)) # kcurr = self.computeRankingConsensusQuality(a,Threading=Threading,nbrOfCPUs=nbrOfCPUs) # ## kcurr = sum((relation[a[i]][a[j]] - relation[a[j]][a[i]])\ # ## for i in range(n) for j in range(i+1,n)) # ## for i in range(n): # ## for j in range(i+1,n): # ## kcurr += relation[a[i]][a[j]] - relation[a[j]][a[i]] # if Debug: # print(kcurr) # kcurrAmp = kcurr[0][0][0] - kcurr[0][-1][0] # if Debug: # print(s, a, kcurrAmp) # if kcurr[1] > maxMarginalCorrelation: # maxMarginalCorrelation = kcurr[1] # minMarginalCorrelationAmplitude = kcurrAmp # medianRanking = list(a) # maximalRankings = [medianRanking] # if Debug: # print(maximalRankings) # elif kcurr[1] == maxMarginalCorrelation: # if kcurrAmp < minMarginalCorrelationAmplitude: # maxMarginalCorrelation = kcurr[1] # minMarginalCorrelationAmplitude = kcurrAmp # medianRanking = list(a) # maximalRankings = [medianRanking] # if Debug: # print(maximalRankings) # elif kcurrAmp == minMarginalCorrelationAmplitude: # minMarginalCorrelationAmplitude = kcurrAmp # maximalRankings.append(list(a)) # if Debug: # print(maximalRankings) # self.maximalRankings = maximalRankings # self.maxMarginalCorrelation = maxMarginalCorrelation # self.minMarginalCorrelationAmplitude = minMarginalCorrelationAmplitude # if Debug: # print('First optimal median ranking = ', maximalRankings[0]) # print('Optimal maxMarginalCorrelation = ', maxMarginalCorrelation) # print('Optimal minMarginalCorrelationAmplitude = ', minMarginalCorrelationAmplitude) # print('# of permutations = ', s) # #kemenyOrder.reverse() # return maximalRankings[0], maxMarginalCorrelation, minMarginalCorrelationAmplitude
[docs] def computeKemenyRanking(self, orderLimit=7, seed=None, sampleSize=1000, Debug=False): """ Renders a ranking from best to worst of the actions with maximal Kemeny index. .. note:: Returns a tuple: kemenyRanking (from best to worst), kemenyIndex. """ from random import seed, shuffle from digraphsTools import all_perms Min = self.valuationdomain['min'] relation = self.relation actions = [x for x in self.actions] n = len(actions) ## Exact computation of a Kemeny order ## respecting a maximum of marginal majority margins if n > orderLimit: return None kemenyIndex = Decimal(str(n)) * Decimal(str(n)) * Min s = 1 maximalRankings = [] for a in all_perms(list(actions)): kcurr = Decimal('0.0') s += 1 kcurr = sum((relation[a[i]][a[j]] - relation[a[j]][a[i]])\ for i in range(n) for j in range(i+1,n)) ## for i in range(n): ## for j in range(i+1,n): ## kcurr += relation[a[i]][a[j]] - relation[a[j]][a[i]] if Debug: print(s, a, kcurr) if kcurr > kemenyIndex: kemenyIndex = kcurr kemenyRanking = list(a) maximalRankings = [kemenyRanking] if Debug: print(maximalRankings) elif kcurr == kemenyIndex: maximalRankings.append(list(a)) if Debug: print(maximalRankings) self.maximalRankings = maximalRankings self.kemenyIndex = kemenyIndex if Debug: print('Exact Kemeny Orders = ', kemenyRanking) print('Exact Kemeny Index = ', kemenyIndex) print('# of permutations = ', s) #kemenyOrder.reverse() return kemenyRanking, kemenyIndex
[docs] def computeKemenyOrder(self,orderLimit=7,Debug=False): """ Renders a ordering from worst to best of the actions with maximal Kemeny index. Return a tuple: kemenyOrder (from worst to best), kemenyIndex """ try: ranking = list(self.maximalRankings[0]) ranking.reverse() except AttributeError: self.computeKemenyRanking(orderLimit=orderLimit,Debug=Debug) ranking = list(self.maximalRankings[0]) ranking.reverse() return ranking, self.kemenyIndex
[docs] def computePrincipalScores(self, plotFileName=None, Colwise=False, imageType=None, tempDir=None, bgcolor='cornsilk', Comments=False, Debug=False): """ Renders a ordered list of the first principal eigenvector of the covariance of the valued outdegrees of self. .. note:: The method, relying on writing and reading temporary files by default in a temporary directory is threading and multiprocessing safe ! (see Digraph.exportPrincipalImage method) """ from csv import reader from operator import itemgetter from tempfile import TemporaryDirectory from decimal import Decimal tempd = TemporaryDirectory(dir=tempDir) tempDirName = tempd.name self.exportPrincipalImage(Colwise=Colwise,Comments=Comments, pictureFormat=imageType, plotFileName=plotFileName, bgcolor=bgcolor, tempDir=tempDirName, ) if Colwise: fi = open('%s/rotationCol.csv' % tempDirName,'r') else: fi = open('%s/rotationRow.csv' % tempDirName,'r') csvReader = reader(fi) R = [x for x in csvReader] fi.close() listActions = [x for x in self.actions] listActions.sort() principalScores = [(Decimal(R[i+1][0]),listActions[i])\ for i in range(len(listActions))] principalScores.sort(reverse=True,key=itemgetter(0)) if Debug: print(principalScores) else: tempd.cleanup() return principalScores
[docs] def computePrincipalRanking(self,Colwise=False,Comments=False): """ Rendesr a ranking from best to worst of the decision actions. """ principalScores = self.computePrincipalScores(Colwise=Colwise) if Comments: for x in principalScores: print('%s: %.3f' % (x[1],x[0])) principalRanking = [x[1] for x in principalScores] corr = self.computeRankingCorrelation(principalRanking) print(corr) if corr['correlation'] > 0.0: return principalRanking else: return list(reversed([x[1] for x in principalScores]))
[docs] def computePrincipalOrder(self,Colwise=False,Comments=False): """ Rendesr an ordering from wrost to best of the decision actions. """ principalScores = self.computePrincipalScores(Colwise=Colwise) if Comments: for x in principalScores: print('%s: %.3f' % (x[1],x[0])) principalOrder = [x[1] for x in principalScores] corr = self.computeOrderCorrelation(principalOrder) print(corr) if corr['correlation'] > 0.0: return principalOrder else: return list(reversed([x[1] for x in principalScores]))
[docs] def computeSlaterRanking(self,isProbabilistic=False, seed=None, sampleSize=1000, Debug=False): """ Renders a ranking of the actions with minimal Slater index. Return a tuple: slaterOrder, slaterIndex """ from random import seed, shuffle try: from math import copysign CopySign = True except: CopySign = False from digraphsTools import all_perms Min = self.valuationdomain['min'] relationOrig = self.relation minOrig = self.valuationdomain['min'] maxOrig = self.valuationdomain['max'] self.recodeValuation(-1,1) relation = self.relation actions = [x for x in self.actions] n = len(actions) ## Monte Carlo computation of a Slater order if isProbabilistic: if seed is not None: seed = seed a = list(actions) slaterIndex = -(n*n) slaterRanking = list(a) sampleSize = sampleSize for s in range(sampleSize): shuffle(a) kcurr = 0 for i in range(n): for j in range(i+1,n): if CopySign: kcurr += copysign(1,relation[a[i]][a[j]]) - copysign(1,relation[a[j]][a[i]]) else: if relation[a[i]][a[j]] > 0: kcurr += 1 elif relation[a[i]][a[j]] < 0: kcurr -= 1 if relation[a[j]][a[i]] > 0: kcurr -= 1 elif relation[a[j]][a[i]] < 0: kcurr += 1 if kcurr > slaterIndex: slaterIndex = kcurr slaterRanking = list(a) if Debug: print(s, slaterIndex) if Debug: print('Probabilistic Slater Order = ', slaterRanking) print('Probabilistic Slater Index = ', slaterIndex) print('with samplesize : ', sampleSize) ## Exact computation of a Slater order ## respecting a maximum of marginal majority margins else: slaterIndex = -(n*n) slaterOrder = list(actions) s = 0 for a in all_perms(slaterOrder): kcurr = 0 s += 1 for i in range(n): ai = a[i] rai = relation[a[i]] for j in range(i+1,n): aj = a[j] if CopySign: kcurr += copysign(1,rai[aj]) -\ copysign(1,relation[aj][ai]) else: if rai[aj] > 0: kcurr += 1 elif rai[aj] < 0: kcurr -= 1 if relation[aj][ai] > 0: kcurr -= 1 elif relation[aj][ai] < 0: kcurr += 1 #kcurr += copysign(1,relation[a[i]][a[j]]) - copysign(1,relation[a[j]][a[i]]) if Debug: print(s, a, kcurr) if kcurr >= slaterIndex: slaterIndex = kcurr slaterRanking = list(a) if Debug: print(s, slaterRanking, slaterIndex) if Debug: print('Exact Slater Order = ', slaterRanking) print('Exact Slater Index = ', slaterIndex) print('# of permutations = ', s) self.recodeValuation(minOrig,maxOrig) return slaterRanking, slaterIndex
[docs] def computeSlaterOrder(self,isProbabilistic=False, seed=None,sampleSize=1000,Debug=False): """ Reversed return from computeSlaterRanking method. """ slaterOrder,slaterIndex = self.computeSlaterRanking(isProbabilistic=isProbabilistic, seed=seed, sampleSize=sampleSize, Debug=Debug) slaterOrder.reverse() return slaterOrder,slaterIndex
[docs] def computeValuedRankingRelation(self,ranking): """ Renders the valued relation characteristics compatible with the given linar ranking. Discordant charcateristics are set to the indeterminate value. """ Med = self.valuationdomain['med'] valuedRankingRelation = {} actionsList = [x for x in self.actions] for x in actionsList: valuedRankingRelation[x] = {} vrrx = valuedRankingRelation[x] xi = ranking.index(x) for y in actionsList: yj = ranking.index(y) if xi < yj: vrrx[y] = max(Med,self.relation[x][y]) elif xi == yj: vrrx[y] = Med else: vrrx[y] = min(Med,self.relation[x][y]) return valuedRankingRelation
[docs] def computePairwiseClusterComparison(self, K1, K2, Debug=False): """ Computes the pairwise cluster comparison credibility vector from bipolar-valued digraph g. with K1 and K2 disjoint lists of action keys from g actions disctionary. Returns the dictionary {'I': Decimal(),'P+':Decimal(),'P-':Decimal(),'R' :Decimal()} where one and only one item is strictly positive. """ from decimal import Decimal relation = self.relation n = Decimal(str(len(K1)*len(K2))) if Debug: print('K1 = ', K1, ', K2 = ', K2, ', n = ', n) rK1SK2 = Decimal('0') rK2SK1 = Decimal('0') for x in K1: rx = relation[x] for y in K2: rK1SK2 += rx[y] rK2SK1 += relation[y][x] if Debug: print('r(K1 >= K2) = ', rK1SK2/n, ' r(K2 >= K1) = ', rK2SK1/n) rK1IK2 = min(rK1SK2,rK2SK1)/n rK1PK2 = min(rK1SK2,-rK2SK1)/n rK2PK1 = min(-rK1SK2,rK2SK1)/n rK1RK2 = min(-rK1SK2,-rK2SK1)/n if Debug: print('r(K1 = K2) = %.2f' % rK1IK2) print('r(K1 > K2) = %.2f' % rK1PK2) print('r(K1 < K2) = %.2f' % rK2PK1) print('r(K1 ? K2) = %.2f' % rK1RK2) return {'I': rK1IK2, 'P+': rK1PK2, 'P-' :rK2PK1, 'R' : rK1RK2 }
# ------ CoverDigraph construction
[docs] class CoDualDigraph(Digraph): """ Instantiates the associated codual -converse of the negation- from a deep copy of a given Digraph instance called *other*. .. note:: Instantiates *self* as other.__class__ ! And, deepcopies, the case given, the other.description, the other.criteria and the other.evaluation dictionaries into self. """ def __init__(self,other,Debug=False): from copy import deepcopy self.__class__ = other.__class__ self.name = 'codual-'+other.name att = [a for a in other.__dict__] att.remove('name') att.remove('relation') for a in att: self.__dict__[a] = deepcopy(other.__dict__[a]) Max = self.valuationdomain['max'] Min = self.valuationdomain['min'] relation = {} for x in self.actions: relation[x] = {} rx = relation[x] for y in self.actions: rx[y] = Max - other.relation[y][x] + Min self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
# ------ Cover construction
[docs] class CoverDigraph(Digraph): """ Instantiates the associated cover relation -immediate neighbours- from a deep copy of a given Digraph called *other*. The Hasse diagram for instance is the cover relation of a transitive digraph. .. note:: Instantiates as other.__class__ ! Copies the case given the other.description, the other.criteria and the other.evaluation dictionaries into self. """ def __init__(self,other, Debug=False): from copy import deepcopy self.__class__ = other.__class__ self.name = 'cover-'+other.name self.name = 'codual-'+other.name att = [a for a in other.__dict__] att.remove('name') att.remove('relation') for a in att: self.__dict__[a] = deepcopy(other.__dict__[a]) # try: # self.description = deepcopy(other.description) # except AttributeError: # pass # try: # self.criteria = deepcopy(other.criteria) # except AttributeError: # pass # try: # self.evaluation = deepcopy(other.evaluation) # except AttributeError: # pass # self.actions = deepcopy(other.actions) # self.order = len(self.actions) # self.valuationdomain = deepcopy(other.valuationdomain) #actionsList = list(self.actions) #Max = Decimal('2')*other.valuationdomain['max'] #Med = Decimal('0') #Min = -Max Min = self.valuationdomain['min'] Med = self.valuationdomain['med'] Max = self.valuationdomain['max'] relation = {} for x in self.actions: relation[x] = {} rx = relation[x] orx = other.relation[x] for y in self.actions: ory = other.relation[y] if y == x: rx[y] = Med else: coverXY = Max for z in self.actions: if z != x and z != y: coverz = max(orx[z],(Max-ory[z]+Min)) coverXY = min(coverXY,coverz) ## if Debug: ## print(x,y,z,other.relation[x][z],(Max-other.relation[y][z]+Min),coverz,coverXY) rx[y] = min(orx[y],coverXY) self.relation = relation #self.recodeValuation(other.valuationdomain['min'],other.valuationdomain['max']) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
# ------ Converse construction
[docs] class ConverseDigraph(Digraph): """ Instantiates the associated converse or reciprocal version from a deep copy of a given Digraph called other. Instantiates as other.__class__ ! Deep copies, the case given, the description, the criteria and the evaluation dictionaries into self. """ def __init__(self,other): from copy import deepcopy self.__class__ = other.__class__ att = [a for a in other.__dict__] att.remove('name') att.remove('relation') for a in att: self.__dict__[a] = deepcopy(other.__dict__[a]) self.name = 'converse-'+other.name relation = {} for x in self.actions: relation[x] = {} rx = relation[x] for y in self.actions: rx[y] = other.relation[y][x] self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] class FusionDigraph(Digraph): """ Instantiates the epistemic fusion of two given Digraph instances called dg1 and dg2. Parameter: * operator := "o-max (default)" | "o-min | o-average" : symmetrix disjunctive, resp. conjunctive, resp. avarage fusion operator. * weights := [a,b]: if None weights = [1,1] """ def __init__(self,dg1,dg2,operator="o-max",weights=None): from copy import deepcopy from digraphsTools import omin, omax self.name = 'fusion-'+dg1.name+'-'+dg2.name self.actions = deepcopy(dg1.actions) self.order = len(dg1.actions) self.valuationdomain = deepcopy(dg1.valuationdomain) #actionsList = list(self.actions) #max = self.valuationdomain['max'] Med = self.valuationdomain['med'] fusionRelation = {} for x in self.actions: fusionRelation[x] = {} fx = fusionRelation[x] dg1x = dg1.relation[x] dg2x = dg2.relation[x] for y in self.actions: if x == y: fx[y] = Med else: if operator == "o-max": fx[y] = omax(Med,(dg1x[y],dg2x[y])) elif operator == "o-average": fx[y] = symmetricAverage(Med,(dg1x[y],dg2x[y]),weights) elif operator == "o-min": fx[y] = omin(Med,(dg1x[y],dg2x[y])) else: print('!! Error: wrong fusion operator: %s' % operator) print('operator := "o-max (default)" | "o-min" | "o-average"') return self.relation = fusionRelation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] class FusionLDigraph(Digraph): """ Instantiates the epistemic fusion a list L of Digraph instances. Parameter: * operator := "o-max" (default) | "o-min" | "o-average: epistemic disjunctive, conjunctive or symmetric average fusion. * weights := [a,b, ...]: len(weights) matching len(L). If None, weights = [1 for i in range(len(L))]. """ def __init__(self,L,operator="o-max",weights=None): from copy import deepcopy self.name = 'fusion-'+L[0].name self.actions = deepcopy(L[0].actions) self.order = len(L[0].actions) self.valuationdomain = deepcopy(L[0].valuationdomain) #actionsList = list(self.actions) #Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] fusionRelation = {} for x in self.actions: fusionRelation[x] = {} fx = fusionRelation[x] #gx = g.relation[x] for y in self.actions: args = [g.relation[x][y] for g in L] if operator == "o-min": fx[y] = omin(Med,args) elif operator == "o-max": fx[y] = omax(Med,args) elif operator == "o-average": fx[y] = symmetricAverage(Med,args,weights) else: print('Error: invalid epistemic fusion operator %s' % operator) print('operator := "o-max (default)" | "o-min" | "o-average"') return self.relation = fusionRelation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
# -------- Graph Border and Inner
[docs] class GraphBorder(Digraph): """ Instantiates the partial digraph induced by its border, i.e. be the union of its initial and terminal kernels. """ def __init__(self,other,Debug=False): from copy import deepcopy if Debug: Digraph.exportGraphViz(other,other.name) self.__dict__ = deepcopy(other.__dict__) other.computePreKernels() if Debug: print('other.domprekernsls', other.dompreKernels) domBorderActions = set() for k in other.dompreKernels: domBorderActions |= k if Debug: print('other.absprekernels',other.abspreKernels) absBorderActions = set() for k in other.abspreKernels: absBorderActions |= k borderActions = domBorderActions | absBorderActions if Debug: print('border actions', borderActions) self.name = other.name + '_border' borderRelation = {} for x in self.actions: borderRelation[x] = {} #innerRelation[x] = {} for y in self.actions: if x in borderActions or y in borderActions: borderRelation[x][y] = self.relation[x][y] else: borderRelation[x][y] = self.valuationdomain['med'] self.relation = borderRelation if Debug: Digraph.exportGraphViz(self,'border',bestChoice=domBorderActions,worstChoice=absBorderActions)
[docs] class GraphInner(Digraph): """ Instantiates the partial digraph induced by the complement of its border, i.e. the nodes not included in the union of its initial and terminal kernels. """ def __init__(self,other,Debug=False): from copy import deepcopy if Debug: Digraph.exportGraphViz(other,other.name) self.__dict__ = deepcopy(other.__dict__) other.computePreKernels() if Debug: print('other.domprekernsls', other.dompreKernels) domBorderActions = set() for k in other.dompreKernels: domBorderActions |= k if Debug: print('other.absprekernels',other.abspreKernels) absBorderActions = set() for k in other.abspreKernels: absBorderActions |= k borderActions = domBorderActions | absBorderActions if Debug: print('border actions', borderActions) self.name = other.name + '_inner' innerRelation = {} for x in self.actions: innerRelation[x] = {} for y in self.actions: if x in borderActions or y in borderActions: innerRelation[x][y] = self.valuationdomain['med'] else: innerRelation[x][y] = self.relation[x][y] self.relation = innerRelation if Debug: Digraph.exportGraphViz(self,'inner',bestChoice=domBorderActions,worstChoice=absBorderActions)
# ------ Preorder construction class _Preorder(Digraph): """ Instantiates the associated preorder from a given Digraph called other. Instantiates as other.__class__ ! Copies the case given the description, the criteria and the evaluation dictionary into self. """ def __init__(self,other,direction="best",ranking=None): from copy import deepcopy self.__class__ = other.__class__ self.name = 'preorder-'+other.name self.name = 'codual-'+other.name att = [a for a in other.__dict__] att.remove('name') att.remove('relation') for a in att: self.__dict__[a] = deepcopy(other.__dict__[a]) # try: # self.description = deepcopy(other.description) # except AttributeError: # pass # try: # self.criteria = deepcopy(other.criteria) # except AttributeError: # pass # try: # self.evaluation = deepcopy(other.evaluation) # except AttributeError: # pass # self.actions = deepcopy(other.actions) # self.order = len(self.actions) # self.valuationdomain = deepcopy(other.valuationdomain) actionsList = [x for x in self.actions] Max = self.valuationdomain['max'] Min = self.valuationdomain['min'] relation = {} for x in self.actions: relation[x] = {} rx = relation[x] for y in self.actions: rx[y] = None if ranking is None: if direction == 'best': rank = other._bestRanks() else: rank = other._worstRanks() else: rank = ranking for i in range(self.order): x = actionsList[i] for j in range(i, self.order): y = actionsList[j] if rank[x] < rank[y]: relation[x][y] = Max relation[y][x] = Min elif rank[x] > rank[y]: relation[x][y] = Min relation[y][x] = Max else: relation[x][y] = Max relation[y][x] = Max self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() # ------- XOR construction
[docs] class XORDigraph(Digraph): """ Instantiates the XOR digraph of two bipolar digraphs d1 and d2 of same order. """ def __init__(self,d1,d2,Debug = False): from copy import deepcopy self.name = 'XORDigraph' if d1.order != d2.order: if Debug: print("XORDigraph init ERROR:\n the input digraphs are not of the same order !") return None self.order = d1.order self.actions = deepcopy(d1.actions) #actions = [x for x in self.actions] Mind1 = d1.valuationdomain['min'] Maxd1 = d1.valuationdomain['max'] Mind2 = d2.valuationdomain['min'] Maxd2 = d2.valuationdomain['max'] if (Mind1 != Mind2) or (Maxd1 != Maxd2): if Debug: print('!!! valuation recoding required !!!') print(d1.name,d1.valuationdomain) print(d2.name,d2.valuationdomain) d1.recodeValuation(-1.0,1.0) d2.recodeValuation(-1.0,1.0) Recoded = True else: Recoded = False xorRelation = {} for x in self.actions: xorRelation[x] = {} xorx = xorRelation[x] d1x = d1.relation[x] d2x = d2.relation[x] for y in self.actions: xorx[y] = max( min(d1x[y],-d2x[y]), min(d2x[y],-d1x[y]) ) ## if Debug: ## print(x,y,d1.relation[x][y],d2.relation[x][y],xorRelation[x][y]) self.relation = xorRelation if Recoded: self.valuationdomain = {'min': Decimal("-1.0"), 'med': Decimal("0.0"), 'max': Decimal("1.0")} d1.recodeValuation(Mind1,Maxd1) d2.recodeValuation(Mind2,Maxd2) else: self.valuationdomain = dict(d1.valuationdomain.items()) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] class EquivalenceDigraph(Digraph): """ Instantiates the logical equivalence digraph of two bipolar digraphs d1 and d2 of same order. Returns None if d1 and d2 are of different order """ def __init__(self,d1,d2,Debug = False): from copy import deepcopy self.name = 'EquivDigraph' if d1.order != d2.order: print("EquivDigraph init ERROR:\n the input digraphs are not of the same order !") return None self.order = d1.order self.actions = deepcopy(d1.actions) #actions = [x for x in self.actions] Mind1 = d1.valuationdomain['min'] Maxd1 = d1.valuationdomain['max'] Mind2 = d2.valuationdomain['min'] Maxd2 = d2.valuationdomain['max'] if (Mind1 != Decimal("-1.0")) or (Maxd1 != Decimal("1.0")): ## if Debug: ## print('!!! d1 valuation recoding required !!!') ## print(d1.name,d1.valuationdomain) d1.recodeValuation(-1.0,1.0) RecodedD1 = True else: RecodedD1 = False if (Mind2 != Decimal("-1.0")) or (Maxd2 != Decimal("1.0")): ## if Debug: ## print('!!! d2 valuation recoding required !!!') ## print(d2.name,d2.valuationdomain) d2.recodeValuation(-1.0,1.0) RecodedD2 = True else: RecodedD2 = False equivRelation = {} for x in self.actions: equivRelation[x] = {} eqvx = equivRelation[x] d1x = d1.relation[x] d2x = d2.relation[x] for y in self.actions: eqvx[y] = min( max(-d1x[y],d2x[y]), max(-d2x[y],d1x[y]) ) ## if Debug and x == y: ## print(x,y,d1.relation[x][y],d2.relation[x][y],equivRelation[x][y]) self.relation = equivRelation self.valuationdomain = {'min': Decimal("-1.0"), 'med': Decimal("0.0"), 'max': Decimal("1.0")} if RecodedD1: d1.recodeValuation(Mind1,Maxd1) if RecodedD2: d2.recodeValuation(Mind2,Maxd2) self.correlation = self.computeCorrelation() self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] def computeCorrelation(self): """ Renders a dictionary with slots: 'correlation' (tau) and 'determination' (d), representing the ordinal correlation between the two digraphs *d1* and *d2* given as arguments to the EquivalenceDigraph constructor. See the corresponding advanced topic in the Digraph3 documentation. """ corr = Decimal('0') dterm = Decimal('0') #actions = [x for x in self.actions] #actions = self.actions relation = self.relation for x,rx in relation.items(): for y,rxy in rx.items(): if x != y: corr += rxy dterm += abs(rxy) n = self.order * (self.order-1) if dterm > Decimal('0'): tau = float(corr)/float(dterm) else: tau = 0.0 if n > 0: d = float(dterm)/float(n) else: d = 0.0 return {'correlation': tau, 'determination': d}
# ------- Specialisations of the Digraph class ----------- #class RandomDigraph(randomDigraphs.RandomDigraph): # """ # dummy # """ # class _RandomDigraph(Digraph): # """ # .. warning:: # *Obsolete version!* Will be removed in the future. Instead, use # the new :py:class:`randomDigraphs.RandomDigraph` constructor. # """ # def __init__(self,order=9,arcProbability=0.5,hasIntegerValuation=True, Bipolar=False): # arcProbability = Decimal(str(arcProbability)) # if arcProbability > Decimal("1.0"): # print('Error: arc probability too high !!') # elif arcProbability < Decimal("0.0"): # print('Error: arc probability too low !!') # else: # import copy # from random import random # g = EmptyDigraph(order=order, valuationdomain=(0.0,1.0)) # self.actions = copy.copy(g.actions) # self.valuationdomain = copy.copy(g.valuationdomain) # self.valuationdomain['hasIntegerValuation'] = hasIntegerValuation # self.relation = {} # for x in g.actions: # self.relation[x] = {} # for y in g.actions: # if x == y: # self.relation[x][y] = self.valuationdomain['min'] # else: # if random() <= arcProbability: # self.relation[x][y] = self.valuationdomain['max'] # else: # self.relation[x][y] = self.valuationdomain['min'] # self.order = order # self.name = 'randomDigraph' # self.gamma = self.gammaSets() # self.notGamma = self.notGammaSets() # #class RandomValuationDigraph(randomDigraphs.RandomValuationDigraph): # # """ # # dummy # # """ # class _RandomValuationDigraph(Digraph): # """ # .. warning:: # *Obsolete version!* Will be removed in the future. Instead, use # the new :py:class:`randomDigraphs.RandomValuationDigraph` constructor. # """ # def __init__(self,order=9, ndigits=2, Normalized=False, hasIntegerValuation=False): # import random # self.name = 'randomValuationDigraph' # self.order = order # actionlist = list(range(order+1)) # actionlist.remove(0) # actions = [] # for x in actionlist: # actions.append(str(x)) # self.actions = actions # precision = pow(10,ndigits) # if hasIntegerValuation: # self.valuationdomain = {'min':-precision, 'med':0, 'max':precision} # else: # if Normalized: # self.valuationdomain = {'min':Decimal('-1.0'), 'med':Decimal('0.0'), 'max':Decimal('1.0')} # else: # self.valuationdomain = {'min':Decimal('0'), 'med':Decimal('0.5'), 'max':Decimal('1.0')} # self.valuationdomain['hasIntegerValuation'] = hasIntegerValuation # random.seed() # relation = {} # for x in actions: # relation[x] = {} # for y in actions: # if x == y: # relation[x][y] = self.valuationdomain['med'] # else: # if hasIntegerValuation: # relation[x][y] = (2*random.randrange(start=0,stop=precision)) - precision # elif Normalized: # relation[x][y] = (Decimal(str(round(float(random.randrange(start=0,stop=precision))/precision,ndigits))) * Decimal('2.0')) - Decimal('1.0') # else: # relation[x][y] = Decimal(str(round(float(random.randrange(start=0,stop=precision))/precision,ndigits))) # self.relation = relation # self.gamma = self.gammaSets() # self.notGamma = self.notGammaSets() # class _RandomWeakTournament(Digraph): # """ # .. warning:: # *Obsolete version!* Will be removed in the future. Instead, use # the new :py:class:`randomDigraphs.RandomWeakTournament` constructor. # """ # def __init__(self,order=10,ndigits=2,hasIntegerValuation=False,weaknessDegree=0.25,Comments=False): # import random # from decimal import Decimal # self.name = 'randomWeakTournament' # self.order = order # actionlist = list(range(order+1)) # actionlist.remove(0) # actions = [] # for x in actionlist: # actions.append(str(x)) # self.actions = actions # random.seed() # Max = pow(10,ndigits) # Min = - Max # Med = 0 # precision = Max # dPrecision = Decimal(str(precision)) # if hasIntegerValuation: # self.valuationdomain = {'hasIntegerValuation':True, 'min':Decimal(str(Min)), 'med':Decimal('0'), 'max':Decimal(str(Max))} # else: # self.valuationdomain = {'hasIntegerValuation':False, 'min':Decimal('-1.0'), 'med':Decimal('0.0'), 'max':Decimal('1.0')} # relation = {} # for x in actions: # relation[x] = {} # for y in actions: # relation[x][y] = self.valuationdomain['med'] # actionsList = [x for x in actions] # random.shuffle(actionsList) # weaknessDegree = Decimal(str(weaknessDegree)) # forwardDegree = (Decimal('1.0') - weaknessDegree)/Decimal('2') # #print actionsList # n = len(actionsList) # for i in range(n): # for j in range(i,n): # #print i,j # if i == j: # #print actionsList[i],actionsList[j] # relation[actionsList[i]][actionsList[j]] = self.valuationdomain['med'] # else: # u = Decimal(str(random.randint(0,precision)))/dPrecision # u1 = Decimal(str(random.randint(0,precision))) # u2 = Decimal(str(random.randint(0,precision))) # if u < weaknessDegree: # i = j # if hasIntegerValuation: # randeval1 = u1 # randeval2 = u2 # else: # randeval1 = u1/dPrecision # randeval2 = u2/dPrecision # elif u < forwardDegree: # i > j # if hasIntegerValuation: # randeval1 = u1 # randeval2 = Min + u2 # else: # randeval1 = u1/dPrecision # randeval2 = (Min + u2)/dPrecision # else: # j > i # if hasIntegerValuation: # randeval1 = Min + u1 # randeval2 = u2 # else: # randeval1 = (Min + u1)/dPrecision # randeval2 = u2/dPrecision # if hasIntegerValuation: # relation[actionsList[i]][actionsList[j]] = Decimal(str(randeval1)) # relation[actionsList[j]][actionsList[i]] = Decimal(str(randeval2)) # else: # relation[actionsList[i]][actionsList[j]] = Decimal(str(round(randeval1,ndigits))) # relation[actionsList[j]][actionsList[i]] = Decimal(str(round(randeval2,ndigits))) # self.relation = relation # self.gamma = self.gammaSets() # self.notGamma = self.notGammaSets() # if Comments: # print(self.order*(self.order-1), self.computeRelationalStructure()) # class _RandomTournament(Digraph): # """ # .. warning:: # *Obsolete version!* Will be removed in the future. Instead, use # the new :py:class:`randomDigraphs.RandomTournament` constructor. # """ # def __init__(self,order=10,ndigits=2,isCrisp=True,valuationDomain=None): # import random # from decimal import Decimal # self.name = 'randomTournament' # self.order = order # actionlist = list(range(order+1)) # actionlist.remove(0) # actions = [] # for x in actionlist: # actions.append(str(x)) # self.actions = actions # if valuationDomain is None: # self.valuationdomain = {'min':Decimal('-1.0'), 'med':Decimal('0.0'), 'max':Decimal('1.0')} # else: # self.valuationdomain = valuationDomain # valuationRange = self.valuationdomain['max'] - self.valuationdomain['min'] # relation = {} # for x in actions: # relation[x] = {} # for y in actions: # relation[x][y] = Decimal('0.0') # random.seed() # precision = pow(10,ndigits) # actionsList = [x for x in actions] # #print actionsList # n = len(actionsList) # for i in range(n): # for j in range(i,n): # #print i,j # if i == j: # #print actionsList[i],actionsList[j] # relation[actionsList[i]][actionsList[j]] = self.valuationdomain['med'] # else: # u = random.randint(0,precision) # if isCrisp: # if u < Decimal(str(precision))/Decimal('2'): # relation[actionsList[i]][actionsList[j]] = self.valuationdomain['min'] # relation[actionsList[j]][actionsList[i]] = self.valuationdomain['max'] # else: # relation[actionsList[i]][actionsList[j]] = self.valuationdomain['max'] # relation[actionsList[j]][actionsList[i]] = self.valuationdomain['min'] # else: # randeval = self.valuationdomain['min'] + Decimal(str(u))/Decimal(str(precision))*valuationRange # valuation = Decimal(str(round(randeval,ndigits))) # relation[actionsList[i]][actionsList[j]] = valuation # relation[actionsList[j]][actionsList[i]] = self.valuationdomain['max'] - valuation + self.valuationdomain['min'] # self.relation = relation # self.gamma = self.gammaSets() # self.notGamma = self.notGammaSets() # class _RandomFixedSizeDigraph(Digraph): # """ # .. warning:: # *Obsolete version!* Will be removed in the future. Instead, use # the new :py:class:`randomDigraphs.RandomFixedSizeDigraph` constructor. # """ # def __init__(self,order=7,size=14): # import random,copy # # check feasability # r = (order * order) - order # if size > r : # print('Graph not feasable (1) !!') # else: # self.name = 'randomFixedSize' # self.order = order # actionlist = list(range(order+1)) # actionlist.remove(0) # actions = [] # for x in actionlist: # actions.append(str(x)) # self.actions = actions # self.valuationdomain = {'min':Decimal('-1.0'), 'med':Decimal('0.0'), 'max':Decimal('1.0')} # Min = self.valuationdomain['min'] # Max = self.valuationdomain['max'] # random.seed() # allarcs = [] # relation = {} # for x in actions: # relation[x] = {} # for y in actions: # relation[x][y] = Min # if x != y: # allarcs.append((x,y)) # for i in range(size): # arc = random.choice(allarcs) # relation[arc[0]][arc[1]] = Max # allarcs.remove(arc) # self.relation = relation.copy() # self.gamma = self.gammaSets() # self.notGamma = self.notGammaSets() # class _RandomFixedDegreeSequenceDigraph(Digraph): # """ # .. warning:: # *Obsolete version!* Will be removed in the future. Instead, use # the new :py:class:`randomDigraphs.RandomFixedDegreeSequenceDigraph` constructor. # """ # def __init__(self,order=7,degreeSequence=[3,3,2,2,1,1,0]): # import random,copy # # check feasability # degree = max(degreeSequence) # if degree >= order: # print('!!! Graph not feasable (1) !!!') # print('Maximum degree > order !!!') # else: # sumdegrees = 0 # for i in range(order): # sumdegrees += degreeSequence[i] # r = sumdegrees % 2 # if r == 1: # print('!!! Graph not feasable (1) !!!') # print('Odd sum of degrees : ',sumdegrees,'!!') # else: # self.name = 'randomFixedDegreeSequence' # self.order = order # actionlist = list(range(order+1)) # actionlist.remove(0) # actions = [] # for x in actionlist: # actions.append(str(x)) # self.actions = actions # self.valuationdomain = {'min':Decimal('-1.0'), 'med':Decimal('0.0'), 'max':Decimal('1.0')} # Min = self.valuationdomain['min'] # Max = self.valuationdomain['max'] # relation = {} # for x in actions: # relation[x] = {} # for y in actions: # relation[x][y] = Min # random.seed() # # create a random pairing # feasable = 0 # s = 0 # while feasable == 0 and s < 100: # s += 1 # edges = [] # cells = [] # degreeseq = {} # i = 0 # for x in actions: # degreeseq[x] = degreeSequence[i] # cells.append((x,degree)) # i += 1 # while len(cells) > 1: # cell = random.choice(cells) # cells.remove(cell) # xc = cell[0] # edgescur = [] # copycells = copy.copy(cells) # while degreeseq[xc] > 0 and len(copycells) > 0: # other = random.choice(copycells) # copycells.remove(other) # edgescur.append((xc,other[0])) # degreeseq[other[0]] -= 1 # degreeseq[xc] -= 1 # edges += edgescur # for c in cells: # if degreeseq[c[0]] == 0: # cells.remove(c) # feasable = 1 # for x in actions: # if degreeseq[x] != 0: # feasable = 0 # break # if feasable == 0: # print('Graph not feasable (2) !!') # else: # for edge in edges: # relation[edge[0]][edge[1]] = Max # relation[edge[1]][edge[0]] = Max # self.relation = relation.copy() # self.gamma = self.gammaSets() # class RandomTree(Digraph): # """ # .. warning:: # *Obsolete version!* Will be removed in the future. Instead, use # the new :py:class:`graphs.RandomTree` constructor. # """ # def __init__(self,numberOfNodes=5, ndigits=2, hasIntegerValuation=True): # from random import choice # from decimal import Decimal # self.name = 'randomTree' # self.order = numberOfNodes # actions = {} # nodes = [str(x+1) for x in range(numberOfNodes)] # for x in nodes: # actions[x] = {'name': 'node %s' % x} # self.actions = actions # print(actions) # precision = pow(10,ndigits) # if hasIntegerValuation: # self.valuationdomain = {'min':-precision, 'med':0, 'max':precision} # else: # self.valuationdomain = {'min':Decimal('-1.0'), 'med':Decimal('0.0'), 'max':Decimal('1.0')} # self.valuationdomain['hasIntegerValuation'] = hasIntegerValuation # # init relation dictionary # relation = {} # nodeKeys = [x for x in actions] # print(nodeKeys) # for x in nodeKeys: # relation[x] = {} # for y in nodeKeys: # relation[x][y] = self.valuationdomain['min'] # nodes = [x for x in range(len(nodeKeys))] # pruefer = [] # for i in range(len(nodeKeys)-2): # pruefer.append(choice(nodes)) # print(pruefer) # pairs = self.prufer_to_tree(pruefer) # for (i,j) in pairs: # relation[str(i+1)][str(j+1)] = Decimal('1.0') # relation[str(j+1)][str(i+1)] = Decimal('1.0') # self.relation = relation # self.gamma = self.gammaSets() # self.notGamma = self.notGammaSets() # def prufer_to_tree(self,a): # tree = [] # T = list(range(0, len(a)+2)) # print(T) # # the degree of each node is how many times it appears # # in the sequence # deg = [1]*len(T) # print(deg) # for i in a: deg[i] += 1 # # for each node label i in a, find the first node j with degree 1 and add # # the edge (j, i) to the tree # for i in a: # for j in T: # if deg[j] == 1: # tree.append((i,j)) # # decrement the degrees of i and j # deg[i] -= 1 # deg[j] -= 1 # break # last = [x for x in T if deg[x] == 1] # tree.append((last[0],last[1])) # return tree class _RandomRegularDigraph(Digraph): """ .. warning:: *Obsolete version!* Will be removed in the future. Instead, use the new :py:class:`randomDigraphs.RandomRegularDigraph` constructor. """ def __init__(self,order=7,degree=2): import random,copy # check feasability r = (order * degree) % 2 if degree >= order or r == 1: print('Graph not feasable (1) !!') else: self.name = 'randomRegular' self.order = order actionlist = list(range(order+1)) actionlist.remove(0) actions = [] for x in actionlist: actions.append(str(x)) self.actions = actions self.valuationdomain = {'min':Decimal('-1.0'), 'med':Decimal('0.0'), 'max':Decimal('1.0')} random.seed() # create a random pairing feasable = 0 s = 0 while feasable == 0 and s < 100: s += 1 edges = [] cells = [] degreeseq = {} for x in actions: degreeseq[x] = degree cells.append((x,degree)) while len(cells) > 1: cell = random.choice(cells) cells.remove(cell) xc = cell[0] edgescur = [] copycells = copy.copy(cells) while degreeseq[xc] > 0 and len(copycells) > 0: other = random.choice(copycells) copycells.remove(other) edgescur.append((xc,other[0])) degreeseq[other[0]] -= 1 degreeseq[xc] -= 1 edges += edgescur for c in cells: if degreeseq[c[0]] == 0: cells.remove(c) feasable = 1 for x in actions: if degreeseq[x] != 0: feasable = 0 break if feasable == 0: print('Graph not feasable (2) !!') else: relation = {} for x in actions: relation[x] = {} for y in actions: relation[x][y] = self.valuationdomain['min'] for edge in edges: relation[edge[0]][edge[1]] = Decimal('1.0') relation[edge[1]][edge[0]] = Decimal('1.0') self.relation = relation.copy() self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.componentslist = self.components()
[docs] class EmptyDigraph(Digraph): """ Parameters: order > 0 (default=5); valuationdomain =(Min,Max). Specialization of the general Digraph class for generating temporary empty graphs of given order in {-1,0,1}. """ def __init__(self,order=5,valuationdomain = (-1.0,1.0)): import sys,array,copy from collections import OrderedDict self.name = 'empty' self.order = order actionlist = list(range(order+1)) actionlist.remove(0) actions = OrderedDict() for x in actionlist: actions[str(x)] = {'name':str(x)} self.actions = actions Min = Decimal(str((valuationdomain[0]))) Max = Decimal(str((valuationdomain[1]))) Med = (Max + Min)/Decimal('2') self.valuationdomain = {'min':Min,'med':Med,'max':Max} relation = {} for x in actions: relation[x] = {} for y in actions: relation[x][y] = Min self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] class IndeterminateDigraph(Digraph): """ Parameters: order > 0; valuationdomain =(Min,Max). Specialization of the general Digraph class for generating temporary empty graphs of order 5 in {-1,0,1}. """ def __init__(self,other=None,nodes=None, order=5,valuationdomain = (-1,1)): import sys,array,copy from collections import OrderedDict self.name = 'indeterminate' if other is not None: self.__class__ = other.__class__ self.order = other.order actions = other.actions self.valuationdomain = dict(other.valuationdomain.items()) Med = self.valuationdomain['med'] else: if nodes is not None: actions = OrderedDict() for x in nodes: actions[str(x)] = {'name':str(x)} else: self.order = order actionlist = list(range(order+1)) actionlist.remove(0) actions = OrderedDict() for x in actionlist: actions[str(x)] = {'name':str(x)} Min = Decimal(str(valuationdomain[0])) Max = Decimal(str(valuationdomain[1])) Med = (Max + Min)/Decimal('2') self.valuationdomain = {'min':Min,'med':Med,'max':Max} self.actions = actions relation = {} for x in actions: relation[x] = {} for y in actions: relation[x][y] = Med self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] class CirculantDigraph(Digraph): """ Specialization of the general Digraph class for generating temporary circulant digraphs. Parameters: | order > 0; | valuationdomain ={'min':m, 'max':M}; | circulant connections = list of positive and/or negative circular shifts of value 1 to n. Default instantiation C_7: | order = 7, | valuationdomain = {'min':-1.0,'max':1.0}, | circulants = [-1,1]. Example session:: >>> from digraphs import CirculantDigraph >>> c8 = CirculantDigraph(order=8,circulants=[1,3]) >>> c8.exportGraphViz('c8') *---- exporting a dot file for GraphViz tools ---------* Exporting to c8.dot circo -Tpng c8.dot -o c8.png # see below the graphviz drawing >>> c8.showChordlessCircuits() No circuits yet computed. Run computeChordlessCircuits()! >>> c8.computeChordlessCircuits() ... >>> c8.showChordlessCircuits() *---- Chordless circuits ----* ['1', '4', '7', '8'] , credibility : 1.0 ['1', '4', '5', '6'] , credibility : 1.0 ['1', '4', '5', '8'] , credibility : 1.0 ['1', '2', '3', '6'] , credibility : 1.0 ['1', '2', '5', '6'] , credibility : 1.0 ['1', '2', '5', '8'] , credibility : 1.0 ['2', '3', '6', '7'] , credibility : 1.0 ['2', '3', '4', '7'] , credibility : 1.0 ['2', '5', '6', '7'] , credibility : 1.0 ['3', '6', '7', '8'] , credibility : 1.0 ['3', '4', '7', '8'] , credibility : 1.0 ['3', '4', '5', '8'] , credibility : 1.0 12 circuits. >>> .. image:: c8.png :alt: circulant [1,3] digraph :width: 300 px :align: center """ def __init__(self,order=7, valuationdomain = {'min':Decimal('-1.0'),'max':Decimal('1.0')}, circulants = [-1,1],IndeterminateInnerPart=False): import sys,array,copy from collections import OrderedDict self.name = 'c'+str(order) self.order = order self.circulants = circulants actionlist = list(range(1,order+1)) actions = OrderedDict() for x in actionlist: actions[str(x)] = {'name': str(x)} self.actions = actions Min = Decimal(str(valuationdomain['min'])) Max = Decimal(str(valuationdomain['max'])) Med = (Max + Min)/Decimal('2') self.valuationdomain = {'min':Min,'med':Med,'max':Max} arcs = [] # circulant arcs for x in actionlist: for y in circulants: r = (x + y) % order if r == 0: arcs.append((str(x), str(order))) else: arcs.append((str(x), str(r))) relation = {} # instantiate relation for x in actions: relation[x] = {} for y in actions: if IndeterminateInnerPart: relation[x][y] = Med else: relation[x][y] = Min for x in actions: for y in actions: if (x,y) in arcs: relation[x][y] = Max if (y,x) not in arcs: relation[y][x] = Min self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] def showShort(self): print('*----- show short --------------*') print('Circulant graph : ', self.name) print('Order : ', self.order) print('Circulants : ', self.circulants)
[docs] class KneserDigraph(Digraph): """ Specialization of the general Digraph class for generating temporary Kneser digraphs Parameters: | n > 0; n > j > 0; | valuationdomain ={'min':m, 'max':M}. Default instantiation as Petersen graph: n = 5, j = 2, valuationdomain = {'min':-1.0,'max':1.0}. """ def __init__(self,n=5,j=2,valuationdomain = {'min':-1.0,'max':1.0}): import sys,array,copy self.name = 'kneser-'+str(n)+'-'+str(j) self.n = n self.j = j na = list(range(n+1)) na.remove(0) ob = set() for x in na: ob.add(str(x)) obActions = [] for x in self.kChoices(ob,j): obActions.append(frozenset(x)) order = len(obActions) self.order = order actions = [] for i in range(order): actions.append(str(i+1)) self.actions = actions Min = Decimal(str(valuationdomain['min'])) Max = Decimal(str(valuationdomain['max'])) Med = (Max + Min)/Decimal('2') self.valuationdomain = {'min':Min,'med':Med,'max':Max} aindex = {} for i in range(order): aindex[actions[i]]=obActions[i] relation = {} # instantiate relation for x in actions: relation[x] = {} for y in actions: if aindex[x] & aindex[y] == set(): relation[x][y] = Max else: relation[x][y] = Min self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] def showShort(self): print('*----- show short --------------*') print('Kneser graph : ', self.name) print('n : ', self.n) print('j : ', self.j) print('order : ', self.order)
[docs] class GridDigraph(Digraph): """ Specialization of the general Digraph class for generating temporary Grid digraphs of dimension n times m. Parameters: n,m > 0; valuationdomain ={'min':m, 'max':M}. Default instantiation (5 times 5 Grid Digraph): n = 5, m=5, valuationdomain = {'min':-1.0,'max':1.0}. Randomly orientable with hasRandomOrientation=True (default=False). """ def __init__(self,n=5,m=5,valuationdomain = {'min':-1.0,'max':1.0}, hasRandomOrientation=False,hasMedianSplitOrientation=False): import sys,array,copy self.name = 'grid-'+str(n)+'-'+str(m) self.n = n self.m = m na = list(range(n+1)) na.remove(0) ma = list(range(m+1)) ma.remove(0) actions = [] gridNodes={} for x in na: for y in ma: action = str(x)+'-'+str(y) gridNodes[action]=(x,y) actions.append(action) order = len(actions) self.order = order self.actions = actions self.gridNodes = gridNodes Min = Decimal(str(valuationdomain['min'])) Max = Decimal(str(valuationdomain['max'])) Med = (Max + Min)/Decimal('2') self.valuationdomain = {'min':Min,'med':Med,'max':Max} relation = {} # instantiate relation for x in actions: relation[x] = {} for y in actions: if gridNodes[x][1] == gridNodes[y][1]: if gridNodes[x][0] == gridNodes[y][0]-1 : relation[x][y] = Max elif gridNodes[x][0] == gridNodes[y][0]+1: relation[x][y] = Max else: relation[x][y] = Min elif gridNodes[x][0] == gridNodes[y][0]: if gridNodes[x][1] == gridNodes[y][1]-1: relation[x][y] = Max elif gridNodes[x][1] == gridNodes[y][1]+1: relation[x][y] = Max else: relation[x][y] = Min else: relation[x][y] = Min if hasRandomOrientation: import random random.seed() for x in actions: relation[x] = {} for y in actions: if gridNodes[x][1] == gridNodes[y][1]: if gridNodes[x][0] == gridNodes[y][0]-1 : if random.random() > 0.5: relation[x][y] = Max relation[y][x] = Min else: relation[x][y] = Min relation[y][x] = Max elif gridNodes[x][0] == gridNodes[y][0]+1: if random.random() > 0.5: relation[x][y] = Max relation[y][x] = Min else: relation[x][y] = Min relation[y][x] = Max else: relation[x][y] = Min elif gridNodes[x][0] == gridNodes[y][0]: if gridNodes[x][1] == gridNodes[y][1]-1: if random.random() > 0.5: relation[x][y] = Max relation[y][x] = Min else: relation[x][y] = Min relation[y][x] = Max elif gridNodes[x][1] == gridNodes[y][1]+1: if random.random() > 0.5: relation[x][y] = Max relation[y][x] = Min else: relation[x][y] = Min relation[y][x] = Max else: relation[x][y] = Min else: relation[x][y] = Min elif hasMedianSplitOrientation: for x in actions: relation[x] = {} for y in actions: if gridNodes[x][1] == gridNodes[y][1]: if gridNodes[x][0] == gridNodes[y][0]-1 : if gridNodes[y][1] <= gridNodes[x][0]: relation[x][y] = Max relation[y][x] = Min else: relation[x][y] = Min relation[y][x] = Max elif gridNodes[x][0] == gridNodes[y][0]+1: if gridNodes[y][1] >= gridNodes[x][0]: relation[x][y] = Min relation[y][x] = Max else: relation[x][y] = Max relation[y][x] = Min else: relation[x][y] = Min elif gridNodes[x][0] == gridNodes[y][0]: if gridNodes[x][1] == gridNodes[y][1]-1: if gridNodes[y][1] >= gridNodes[x][0]: relation[x][y] = Min relation[y][x] = Max else: relation[x][y] = Max relation[y][x] = Min elif gridNodes[x][1] == gridNodes[y][1]+1: if gridNodes[y][1] >= gridNodes[x][0]: relation[x][y] = Min relation[y][x] = Max else: relation[x][y] = Max relation[y][x] = Min else: relation[x][y] = Min else: relation[x][y] = Min self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] def showShort(self): print('*----- show short --------------*') print('Grid graph : ', self.name) print('n : ', self.n) print('m : ', self.m) print('order : ', self.order)
[docs] class CompleteDigraph(Digraph): """ Specialization of the general Digraph class for generating temporary complete graphs of order 5 in {-1,0,1} by default. Parameters: order > 0; valuationdomain=(Min,Max). """ def __init__(self,order=5,valuationdomain = (-1.0,1.0)): import sys,array,copy self.name = 'complete' self.order = order actionlist = list(range(order+1)) actionlist.remove(0) actions = {} for x in actionlist: actions[str(x)] = {'name':str(x)} self.actions = actions Max = Decimal(str((valuationdomain[1]))) Min = Decimal(str((valuationdomain[0]))) Med = (Max + Min)/Decimal('2') self.valuationdomain = {'min':Min,'med':Med,'max':Max} relation = {} for x in actions: relation[x] = {} for y in actions: if x == y: relation[x][y] = Min else: relation[x][y] = Max self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] class RedhefferDigraph(Digraph): """ Specialization of the general Digraph class for generating temporary Redheffer digraphs. https://en.wikipedia.org/wiki/Redheffer_matrix Parameters: order > 0; valuationdomain=(Min,Max). """ ############### helper functions def __init__(self,order=5,valuationdomain = (-1.0,1.0)): import sys,array,copy self.name = 'Redheffer' self.order = order actionlist = list(range(order+1)) actionlist.remove(0) actions = {} for x in actionlist: actions[x] = {'name':str(x)} self.actions = actions Max = Decimal(str((valuationdomain[1]))) Min = Decimal(str((valuationdomain[0]))) Med = (Max + Min)/Decimal('2') self.valuationdomain = {'min':Min,'med':Med,'max':Max} relation = {} for x in actions: relation[x] = {} for y in actions: ## if x == y: ## relation[x][y] = Min if x == 1 or y == 1: relation[x][y] = Max elif x <= y and (y%x) == 0: relation[x][y] = Max else: relation[x][y] = Min self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] class PolarisedDigraph(Digraph): """ Renders the polarised valuation of a Digraph class instance: *Parameters*: * If level = None, a default strict 50% cut level (0 in a normalized [-1,+1] valuation domain) is used. * If KeepValues = False, the polarisation results in a crisp {-1,0,1}-valued result. * If AlphaCut = True a genuine one-sided True-oriented cut is operated. * If StrictCut = True, the cut level value is excluded resulting in an open polarised valuation domain. By default the polarised valuation domain is closed and the complementary indeterminate domain is open. """ def __init__(self,digraph=None,level=None,KeepValues=True,AlphaCut=False,StrictCut=False): from copy import deepcopy if digraph is None: from randomDigraphs import RandomValuationDigraph digraph = RandomValuationDigraph() self.valuationdomain = deepcopy(digraph.valuationdomain) Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] if level is None: #level = Max - (Max - Med)*Decimal('0.5') level = Med StrictCut = True KeepValues = False else: level = Decimal(str(level)) self.name = 'cut_' + str(level)+ '_' + str(digraph.name) self.actions = deepcopy(digraph.actions) if AlphaCut: self.relation = self._constructAlphaCutRelation(digraph.relation, level=level, StrictCut=StrictCut) else: self.relation = self._constructBetaCutRelation(digraph.relation, level=level, KeepValues=KeepValues, StrictCut=StrictCut) self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() def _constructBetaCutRelation(self,relationin, level, KeepValues=True,AlphaCut=False,StrictCut=False): """ Parameters: relation and cut level. Flags: KeepValues (True), AlphaCut(False, unilateral cut), StrictCut (False) Renders the polarised relation. """ Debug = False if Debug: print('Level, KeepValues,AlphaCut', level, KeepValues,AlphaCut) # determine the cut level Min = self.valuationdomain['min'] Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] level = Decimal(str(level)) compLevel = Max - level + Min if level < Med: print('Cut Level :', level, 'too low !!!') print(self.valuationdomain) print('Original relation not changed !!!') return relationin elif level > Max: print('Cut Level :', level, 'too high !!!') print(self.valuationdomain) print('Original relation not changed !!!') return relationin # change to a normalized [-1,0,1] valuation domain if KeepValues == False: Min = Decimal('-1') Max = Decimal('1') Med = Decimal('0') self.valuationdomain['min'] = Min self.valuationdomain['max'] = Max self.valuationdomain['med'] = Med # construct polarised relation actions = self.actions relationout = {} for a in actions: relationout[a] = {} for b in actions: if StrictCut: if relationin[a][b] > level: if KeepValues: relationout[a][b] = relationin[a][b] else: relationout[a][b] = Max elif relationin[a][b] < compLevel: if KeepValues: relationout[a][b] = relationin[a][b] else: relationout[a][b] = Min else: relationout[a][b] = Med else: if relationin[a][b] >= level: if KeepValues: relationout[a][b] = relationin[a][b] else: relationout[a][b] = Max elif relationin[a][b] <= compLevel: if KeepValues: relationout[a][b] = relationin[a][b] else: relationout[a][b] = Min else: relationout[a][b] = Med return relationout def _constructAlphaCutRelation(self,relationin, level, KeepValues=True,AlphaCut=False,StrictCut=False): """ Parameters: relation and cut level. Renders the polarised relation. """ Debug = False if Debug: print('Level, KeepValues,AlphaCut', level, KeepValues,AlphaCut) actions = self.actions Min = self.valuationdomain['min'] Max = self.valuationdomain['max'] level = Decimal(str(level)) relationout = {} for a in actions: relationout[a] = {} for b in actions: if StrictCut: if relationin[a][b] > level: if KeepValues: relationout[a][b] = relationin[a][b] else: relationout[a][b] = Max else: if KeepValues: relationout[a][b] = Max - relationin[a][b] + Min else: relationout[a][b] = Min else: if relationin[a][b] >= level: if KeepValues: relationout[a][b] = relationin[a][b] else: relationout[a][b] = Max else: if KeepValues: relationout[a][b] = Max - relationin[a][b] + Min else: relationout[a][b] = Min return relationout
# class _MedianExtendedDigraph(Digraph): # """ # Parameters: # digraph + beta cut level between Med and Max. # .. warning:: # The class is obsolete and is replaced by the genuine # PolarisedDigraph class flagged with KeepValues=True. # """ # def __init__(self,digraph=None,Level=None): # from copy import copy,deepcopy # if digraph is None: # from randomDigraphs import RandomValuationDigraphs # digraph = RandomValuationDigraph() # self.valuationdomain = copy(digraph.valuationdomain) # Max = self.valuationdomain['max'] # Med = self.valuationdomain['med'] # if Level is None: # Level = Max - (Max - Med)*0.5 # self.name = 'cut_' + str(Level)+ '_' + str(digraph.name) # self.actions = copy(digraph.actions) # self.relation = self._constructRelation(digraph.relation, Level) # self.order = len(self.actions) # self.gamma = self.gammaSets() # self.notGamma = self.notGammaSets() # def _constructRelation(self,relationin, Level): # """ # Parameters: relation and cut level. # Renders the polarised relation. # """ # actions = self.actions # Min = self.valuationdomain['min'] # Max = self.valuationdomain['max'] # Med = self.valuationdomain['med'] # CompLevel = Max - Level + Min # if Level < Med: # print('Cut Level :', Level, 'too low !!!') # print(self.valuationdomain) # print('Original relation not changed !!!') # return relationin # else: # relationout = {} # for a in actions: # relationout[a] = {} # for b in actions: # if relationin[a][b] <= Level and relationin[a][b] >= CompLevel: # relationout[a][b] = Med # else: # relationout[a][b] = relationin[a][b] # return relationout
[docs] class DualDigraph(Digraph): """ Instantiates the dual ( = negated valuation) Digraph object from a deep copy of a given other Digraph instance. The relation constructor returns the dual (negation) of self.relation with generic formula: relationOut[a][b] = Max - self.relation[a][b] + Min, where Max (resp. Min) equals valuation maximum (resp. minimum). .. note:: In a bipolar valuation, the dual operator corresponds to a simple changing of signs. """ def __init__(self,other): from copy import deepcopy self.__class__ = other.__class__ self.name = 'dual-' + str(other.name) att = [a for a in other.__dict__] att.remove('name') for a in att: self.__dict__[a] = deepcopy(other.__dict__[a]) #self.order = len(self.actions) actions = self.actions dualRelation = {} Min = self.valuationdomain['min'] Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] for a in actions: dualRelation[a] = {} for b in actions: dualRelation[a][b] = Max - self.relation[a][b] + Min self.relation = dualRelation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
class _PreferenceDigraph(Digraph): """ Obsolete constructor. Initiates the valued difference S(a,b) - S(b,a) of a Digraph instance. """ def __init__(self,digraph): self.valuationdomain = digraph.valuationdomain Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] self.name = 'dual_' + str(digraph.name) self.actions = digraph.actions self.relation = self._constructRelation(digraph.relation) self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() def _constructRelation(self,relationIn): actions = self.actions Min = self.valuationdomain['min'] Max = self.valuationdomain['max'] Med = self.valuationdomain['med'] relationOut = {} for a in actions: relationOut[a] = {} for b in actions: relationOut[a][b] = (relationIn[a][b] - relationIn[b][a])/Decimal('2.0') return relationOut
[docs] class AsymmetricPartialDigraph(Digraph): """ Renders the asymmetric part of a Digraph instance. .. note:: - The non asymmetric and the reflexive links are all put to the median indeterminate characteristic value! - The constructor makes a deep copy of the given Digraph instance! """ def __init__(self,digraph): from copy import deepcopy self.valuationdomain = deepcopy(digraph.valuationdomain) self.name = 'asymmetric_' + str(digraph.name) self.actions = deepcopy(digraph.actions) self.relation = self._constructRelation(digraph.relation) self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() def _constructRelation(self,relationIn): """ Returns the asymmetric part of the relationIn """ actions = self.actions Med = self.valuationdomain['med'] relationOut = {} for a in actions: relationOut[a] = {} for b in actions: if a != b: if relationIn[a][b] >= Med and relationIn[b][a] <= Med: relationOut[a][b] = relationIn[a][b] elif relationIn[a][b] <= Med and relationIn[b][a] >= Med: relationOut[a][b] = relationIn[a][b] else: relationOut[a][b] = Med else: # reflexive terms are ignored relationOut[a][b] = Med return relationOut
[docs] class BipartitePartialDigraph(Digraph): """ Renders the bipartite part of a Digraph instance. .. note:: - *partA* and *partB* must be parts of the *actions* attribute of the given *Digraph* instance - The non-bipartite links are all put to the median indeterminate characteristic value - The constructor makes a deep copy of the given Digraph instance """ def __init__(self,digraph,partA,partB,Partial=True): from copy import deepcopy self.valuationdomain = deepcopy(digraph.valuationdomain) self.name = 'bipartite_' + str(digraph.name) self.actions = deepcopy(digraph.actions) self.actionsA = partA self.actionsB = partB self.relation = self._constructRelation(digraph.relation,partA,partB,Partial) self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() def _constructRelation(self,relationIn,partA,partB,Partial): """ Returns the asymmetric part of the relationIn """ #actions = self.actions Med = self.valuationdomain['med'] Min = self.valuationdomain['min'] actions = self.actions # initialize relationOut to all Med characteristics relationOut = {} for a in actions: relationOut[a] = {} for b in actions: relationOut[a][b] = Med for a in partA: for b in partB: relationOut[a][b] = relationIn[a][b] for b in partB: for a in partA: relationOut[b][a] = relationIn[b][a] if not Partial: for a in partA: for b in partA: if a != b: relationOut[a][b] = Min relationOut[b][a] = Min for a in partB: for b in partB: if a != b: relationOut[a][b] = Min relationOut[b][a] = Min return relationOut
[docs] class SymmetricPartialDigraph(Digraph): """ Renders the symmetric part of a Digraph instance. .. note:: - The not symmetric and the reflexive links are all put to the median indeterminate characteristics value!. - The constructor makes a deep copy of the given Digraph instance! """ def __init__(self,digraph): from copy import deepcopy self.valuationdomain = deepcopy(digraph.valuationdomain) self.name = 'symmetric_' + str(digraph.name) self.actions = deepcopy(digraph.actions) self.relation = self._constructRelation(digraph.relation) self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() def _constructRelation(self,relationIn): """ Returns the symmetric part of the relationIn. """ actions = self.actions Med = self.valuationdomain['med'] relationOut = {} for a in actions: relationOut[a] = {} for b in actions: if a != b: if relationIn[a][b] >= Med and relationIn[b][a] >= Med: relationOut[a][b] = relationIn[a][b] elif relationIn[a][b] <= Med and relationIn[b][a] <= Med: relationOut[a][b] = relationIn[a][b] else: relationOut[a][b] = Med ## relationOut[a][b] = min(relationIn[a][b],relationIn[b][a]) else: relationOut[a][b] = Med return relationOut
[docs] class kChoicesDigraph(Digraph): """ Specialization of general Digraph class for instantiation a digraph of all k-choices collapsed actions. Parameters: | digraph := Stored or memory resident digraph instance | k := cardinality of the choices """ def __init__(self,digraph=None,k=3): import random,sys,array from copy import deepcopy from collections import OrderedDict from outrankingDigraphs import OutrankingDigraph, RandomOutrankingDigraph, BipolarOutrankingDigraph if digraph is None: from randomDigraphs import RandomValuationDigraph digraph = RandomValuationDigraph() self.name = str(digraph.name) elif isinstance(digraph,(Digraph,OutrankingDigraph,RandomOutrankingDigraph)): self.name = deepcopy(digraph.name) self.valuationdomain = deepcopy(digraph.valuationdomain) dactions = [x for x in digraph.actions] drelation = digraph.relation actions = OrderedDict() for kChoice in Digraph.kChoices(digraph,dactions,k): cn = '_' for x in kChoice: cn += str(x) + '_' commentString = '%d-choice candidate' % (k) actions[frozenset(kChoice)] = {'name': cn, 'comment': commentString} self.actions = actions self.order = len(self.actions) self.relation = self._computeRelation(drelation) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() def _computeRelation(self,relation): """ computing the relation on kChoices """ Min = self.valuationdomain['min'] kChoices = self.actions krelation = {} for xch in kChoices: krelation[xch] = {} for ych in kChoices: krelation[xch][ych] = Min for x in (xch-ych): for y in (ych-xch): krelation[xch][ych] = max(krelation[xch][ych],relation[x][y]) return krelation
######### class _WeakCocaDigraph(Digraph): """ Parameters: Stored or memory resident digraph instance. Specialization of general Digraph class for instantiation of weak chordless odd circuits augmented digraphs. """ def __init__(self,digraph=None,comment=None): import random,sys,array,copy from outrankingDigraphs import OutrankingDigraph, RandomOutrankingDigraph, BipolarOutrankingDigraph if comment is None: silent = True else: silent = not(comment) #print 'weakcocosilent =', silent if digraph is None: from randomDigraphs import RandomValuationDigraph g = RandomValuationDigraph() self.name = str(g.name) self.actions = copy.copy(g.actions) self.valuationdomain = copy.copy(g.valuationdomain) self.relation = copy.copy(g.relation) elif isinstance(digraph,(Digraph,OutrankingDigraph,RandomOutrankingDigraph)): self.name = str(digraph.name) self.actions = copy.copy(digraph.actions) self.valuationdomain = copy.copy(digraph.valuationdomain) self.relation = copy.copy(digraph.relation) else: fileName = digraph + 'py' argDict = {} fi = open(fileName) fileText = fi.read() fi.close() exec(compile(fileText, fileName, 'exec'),argDict) self.name = digraph self.actions = argDict['actionset'] self.valuationdomain = argDict['valuationdomain'] self.relation = argDict['relation'] self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.weakGamma = self.weakGammaSets() self.weakCircuits = set() self.closureWeakChordlessOddCircuits(comment=silent) def closureWeakChordlessOddCircuits(self,comment=None): """ Closure of cdordless odd circuits extraction. """ newCircuits = None if comment is None: silent = True else: silent = not(comment) #print 'closuresilent=', silent while newCircuits != set(): coc = set(self.weakCircuits) self.weakChordlessOddCircuits(comment=silent) self.addWeakCircuits(comment=silent) newCircuits = self.weakCircuits - coc def addWeakCircuits(self,comment=None): """ Augmenting self with self.weakCircuits. """ import copy,time if comment is None: silent = True else: silent = not(comment) #print 'addweaksilent = ', silent order0 = self.order actions = set(self.actions) weakCircuits = self.weakCircuits valuationdomain = self.valuationdomain weakGamma = self.weakGamma relation = self.relation for cycle in weakCircuits: cn = '_' dcycle = set() acycle = set() for x in cycle: cn = cn + str(x) + '_' dcycle = dcycle | weakGamma[x][0] dcycle = dcycle | set([x]) acycle = acycle | weakGamma[x][1] acycle = acycle | set([x]) weakGamma[cn]=(dcycle,acycle) for x in actions: if x in cycle: dx0 = weakGamma[x][0] | set([cn]) dx1 = weakGamma[x][1] | set([cn]) weakGamma[x] = (dx0,dx1) relxcn = relation[x] relxcn[cn] = valuationdomain['max'] relation[x] = relxcn else: relxy = valuationdomain['min'] for y in cycle: relxy = max(relxy,relation[x][y]) relxcn = relation[x] relxcn[cn] = relxy relation[x] = relxcn relcycle = {} for x in actions: if x in cycle: relcycle[x] = valuationdomain['max'] else: relxy = valuationdomain['min'] for y in cycle: relxy = max(relxy,relation[y][x]) relcycle[x] = relxy relcycle[cn] = valuationdomain['min'] relation[cn] = relcycle actions = actions | set([cn]) self.actions = list(actions) self.order = len(actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.weakGamma = self.weakGammaSets() new = self.order - order0 if not silent: if self.order == order0: print(' No weak circuits added !') else: print(' ',new,' weak circuit(s) added!') def showCircuits(self): """ show methods for chordless odd circuits in CocaGraph """ print('*---- Chordless odd circuits ----*') for circ in self.weakCircuits: deg = self.circuitMinCredibility(circ) print(list(circ), ', credibility :', deg) #-------------------- class _CoceDigraph(Digraph): """ Specialization of general Digraph class for instantiation of digraphs where all chordless odd circuits are eliminated by appropriate cuts of the valuation of the arcs. .. note:: The method is only experimental and may easily lead to very sparse outranking digraphs with loads of undeterminate arcs. It is recommended to use instead the :py:class:`digraphs.BrokenCocsDigraph` class. Parameters: - digraph: Stored or memory resident digraph instance. - Piping: using OS pipes for data in- and output between Python and C++. """ def __init__(self,digraph=None,Piping=False,Comments=False,Debug=False): import random,sys,array from copy import deepcopy from outrankingDigraphs import OutrankingDigraph, RandomOutrankingDigraph, BipolarOutrankingDigraph ## if comment is None: ## silent = True ## else: ## silent = not(comment) if digraph is None: from randomDigraphs import RandomValuationDigraph g = RandomValuationDigraph() self.name = str(g.name) self.actions = g.actions self.valuationdomain = g.valuationdomain self.relation = g.relation elif isinstance(digraph,(Digraph,OutrankingDigraph,RandomOutrankingDigraph,BipolarOutrankingDigraph)): self.name = str(digraph.name) self.actions = deepcopy(digraph.actions) self.valuationdomain = deepcopy(digraph.valuationdomain) self.relation = deepcopy(digraph.relation) else: fileName = digraph + 'py' argDict = {} fi = open(fileName,'r') fileText = fi.read() fi.close() exec(compile(fileText, fileName, 'exec'),argDict) self.name = digraph self.actions = argDict['actionset'] self.valuationdomain = argDict['valuationdomain'] self.relation = argDict['relation'] self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.weakGamma = self.weakGammaSets() level,pg = self.iterateCocElimination(Comments=Comments, Debug=Debug) if pg is not None: self.name = '%s_pol_%.2f' % (self.name,level) self.relation = pg.relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.weakGamma = self.weakGammaSets() def iterateCocElimination(self,Comments=True,Debug=False): """ Eliminates all chordless odd circuits with rising valuation cut levels. Renders a tuple (level,polarisedDigraph) where level is the necessary bipolar cut level for eliminating all chordless odd circuits, and polarisedDigraph is the resulting digraph instance. Renders (None,None) if no chordless odd circuit is detected. """ from copy import deepcopy from time import time if Debug: Comments=True gcd = deepcopy(self) qualmaj0 = gcd.valuationdomain['med'] if Comments: print('Chorless odd circuits elimination') i = 0 qualmaj = gcd.minimalValuationLevelForCircuitsElimination(Debug=Debug,Comments=Comments) while qualmaj > qualmaj0: i += 1 if Comments: print('--> Iteration %d' % (i)) t0 = time() if qualmaj < gcd.valuationdomain['max']: pg = PolarisedDigraph(gcd,qualmaj, StrictCut=True, KeepValues=True) else: pg = PolarisedDigraph(gcd,qualmaj, StrictCut=False, KeepValues=True) qualmaj0 = qualmaj qualmaj = pg.minimalValuationLevelForCircuitsElimination(Debug=Debug,Comments=Comments) if i == 0: return (None,None) else: return (qualmaj0,pg) #--------------------
[docs] class BrokenCocsDigraph(Digraph): """ Specialization of general Digraph class for instantiation of chordless odd circuits broken digraphs. Parameters: - digraph: stored or memory resident digraph instance. - Piping: using OS pipes for data in- and output between Python and C++. All chordless odd circuits are broken at the weakest asymmetric link, i.e. a link :math:`(x, y)` with minimal difference between :math:`r(x S y)` and :math:`r(y S x)`. """ def __init__(self,digraph=None,Piping=False, Comments=False,Threading=False,nbrOfCPUs=1): import random,sys,array,copy from outrankingDigraphs import OutrankingDigraph,\ RandomOutrankingDigraph, BipolarOutrankingDigraph, ConfidentBipolarOutrankingDigraph ## if comment is None: ## silent = True ## else: ## silent = not(comment) if digraph is None: print('Erreur: A valid Digraph instance is required!') return elif isinstance(digraph,(Digraph,OutrankingDigraph, RandomOutrankingDigraph,BipolarOutrankingDigraph, ConfidentBipolarOutrankingDigraph)): self.name = str(digraph.name) self.actions = copy.deepcopy(digraph.actions) self.valuationdomain = copy.deepcopy(digraph.valuationdomain) try: self.valuationdomain['precision'] = digraph.valuationdomain['precision'] except: self.valuationdomain['precision'] = Decimal('0') self.relation = copy.deepcopy(digraph.relation) else: fileName = digraph + 'py' argDict = {} fi = open(fileName,'r') exec(compile(fi.read(), fileName, 'exec'),argDict) fi.close() self.name = digraph self.actions = argDict['actionset'] self.valuationdomain = argDict['valuationdomain'] self.relation = argDict['relation'] self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.weakGamma = self.weakGammaSets() self.breakChordlessOddCircuits(Piping=Piping, Comments=Comments,Threading=Threading,nbrOfCPUs=nbrOfCPUs)
[docs] def breakChordlessOddCircuits(self,Piping=False, Comments=True,Debug=False, Threading=False,nbrOfCPUs=1): """ Breaking of chordless odd circuits extraction. """ newCircuits = None self.circuitsList = [] self.brokenLinks = set() try: oldBreakings = self.breakings except: self.breakings = 0 self.newBreakings = self.order #while newCircuits != set() or self.newBreakings != 0: i = 0 while newCircuits != set(): i += 1 initialCircuits = set([x for cl,x in self.circuitsList]) self.breakCircuits(Comments=Comments) # if Cpp: # if Piping: # self.computeCppInOutPipingChordlessCircuits(Odd=True, # Debug=Debug) # else: # self.computeCppChordlessCircuits(Odd=True,Debug=Debug) if Threading: self.computeChordlessCircuitsMP(Odd=True,Comments=Debug, Threading=Threading,nbrOfCPUs=nbrOfCPUs) else: self.computeChordlessCircuits(Odd=True,Comments=Debug) newCircuits = set([x for cl,x in self.circuitsList]) if Comments: print('--->> iteration %d:', i) print('newCircuits', newCircuits)
[docs] def breakCircuits(self,Comments=False): """ Break all cricuits in self.circuits. """ import time from digraphsTools import flatten newBreakings = 0 if not(isinstance(self.actions,dict)): actions = {} for x in self.actions: actions[x] = {'name':x} else: actions = self.actions circuitsList = self.circuitsList if Comments: print('list of circuits tp break : ', circuitsList) valuationdomain = self.valuationdomain ## gamma = self.gamma relation = self.relation Med = valuationdomain['med'] currentCircuits = list(circuitsList) for (cycleList,cycle) in circuitsList: degP,degN,minLink = self.circuitCredibilities(cycleList,Debug=Comments) if Comments: print(cycleList,cycle,degP,degN,minLink) if Comments: print('Breaking:',cycleList,degP,degN) actionsSubset = [x for x in flatten(cycle)] if Comments: self.showRelationTable(actionsSubset=actionsSubset) x = minLink[0] y = minLink[1] if Comments: print('Minimal link put to doubt: ', x,y) if (x,y) not in self.brokenLinks: relation[x][y] = Med relation[y][x] = Med self.brokenLinks.add((x,y)) newBreakings += 1 currentCircuits.remove((cycleList,cycle)) self.actions = actions self.order = len(actions) self.relation = relation self.circuitsList = currentCircuits self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.weakGamma = self.weakGammaSets() self.breakings += newBreakings
[docs] def showComponents(self): """Shows the list of connected components of the digraph instance.""" print('*--- Connected Components ---*') k=1 for Comp in self.components(): component = list(Comp) #component.sort() print(str(k) + ': ' + str(component)) xk = k + 1
#--------------------
[docs] class BrokenChordlessCircuitsDigraph(Digraph): """ Specialization of general Digraph class for instantiation of chordless circuits broken digraphs. Parameters: - digraph: stored or memory resident digraph instance. - Piping: using OS pipes for data in- and output between Python and C++. All chordless odd circuits are broken at the weakest asymmetric link, i.e. a link :math:`(x, y)` with minimal difference between :math:`r(x S y)` and :math:`r(y S x)`. """ def __init__(self,digraph=None,Piping=False, Comments=False,Threading=False,nbrOfCPUs=1): import random,sys,array,copy from outrankingDigraphs import OutrankingDigraph,\ RandomOutrankingDigraph, BipolarOutrankingDigraph, ConfidentBipolarOutrankingDigraph ## if comment is None: ## silent = True ## else: ## silent = not(comment) if digraph is None: print('Erreur: A valid Digraph instance is required!') return elif isinstance(digraph,(Digraph,OutrankingDigraph,\ RandomOutrankingDigraph,BipolarOutrankingDigraph,\ ConfidentBipolarOutrankingDigraph)): self.name = str(digraph.name) self.actions = copy.deepcopy(digraph.actions) self.valuationdomain = copy.deepcopy(digraph.valuationdomain) try: self.valuationdomain['precision'] = digraph.valuationdomain['precision'] except: self.valuationdomain['precision'] = Decimal('0') self.relation = copy.deepcopy(digraph.relation) else: fileName = digraph + 'py' argDict = {} fi = open(fileName,'r') fileText = fi.read() fi.close() exec(compile(fileText, fileName, 'exec'),argDict) self.name = digraph self.actions = argDict['actionset'] self.valuationdomain = argDict['valuationdomain'] self.relation = argDict['relation'] self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.weakGamma = self.weakGammaSets() self.breakChordlessOddCircuits(Piping=Piping, Comments=Comments,Threading=Threading,nbrOfCPUs=nbrOfCPUs)
[docs] def breakChordlessOddCircuits(self,Piping=False, Comments=True,Debug=False,Threading=False,nbrOfCPUs=1): """ Breaking of chordless odd circuits extraction. """ newCircuits = None self.circuitsList = [] self.brokenLinks = set() try: oldBreakings = self.breakings except: self.breakings = 0 self.newBreakings = self.order #while newCircuits != set() or self.newBreakings != 0: i = 0 while newCircuits != set(): i += 1 initialCircuits = set([x for cl,x in self.circuitsList]) self.breakCircuits(Comments=Comments) # if Cpp: # if Piping: # self.computeCppInOutPipingChordlessCircuits(Odd=True,Debug=Debug) # else: # self.computeCppChordlessCircuits(Odd=True,Debug=Debug) if Threading: self.computeChordlessCircuitsMP(Odd=True,Comments=Debug, Threading=Threading,nbrOfCPUs=nbrOfCPUs) else: self.computeChordlessCircuits(Odd=False,Comments=Debug) newCircuits = set([x for cl,x in self.circuitsList]) if Comments: print('--->> iteration %d:', i) print('newCircuits', newCircuits)
[docs] def breakCircuits(self,Comments=False): """ Break all cricuits in self.circuits. """ import time from digraphsTools import flatten newBreakings = 0 if not(isinstance(self.actions,dict)): actions = {} for x in self.actions: actions[x] = {'name':x} else: actions = self.actions circuitsList = self.circuitsList if Comments: print('list of circuits tp break : ', circuitsList) valuationdomain = self.valuationdomain ## gamma = self.gamma relation = self.relation Med = valuationdomain['med'] currentCircuits = list(circuitsList) for (cycleList,cycle) in circuitsList: degP,degN,minLink = self.circuitCredibilities(cycleList,Debug=Comments) if Comments: print(cycleList,cycle,degP,degN,minLink) if Comments: print('Breaking:',cycleList,degP,degN) actionsSubset = [x for x in flatten(cycle)] if Comments: self.showRelationTable(actionsSubset=actionsSubset) x = minLink[0] y = minLink[1] if Comments: print('Minimal link put to doubt: ', x,y) if (x,y) not in self.brokenLinks: relation[x][y] = Med relation[y][x] = Med self.brokenLinks.add((x,y)) newBreakings += 1 currentCircuits.remove((cycleList,cycle)) self.actions = actions self.order = len(actions) self.relation = relation self.circuitsList = currentCircuits self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.weakGamma = self.weakGammaSets() self.breakings += newBreakings
[docs] def showComponents(self): """Shows the list of connected components of the digraph instance.""" print('*--- Connected Components ---*') k=1 for Comp in self.components(): component = list(Comp) #component.sort() print(str(k) + ': ' + str(component)) xk = k + 1
#--------------------
[docs] class BreakAddCocsDigraph(Digraph): """ Specialization of general Digraph class for instantiation of chordless odd circuits augmented digraphs. Parameters: - digraph: Stored or memory resident digraph instance. - Piping: using OS pipes for data in- and output between Python and C++. A chordless odd circuit is added if the cumulated credibility of the circuit supporting arcs is larger or equal to the cumulated credibility of the converse arcs. Otherwise, the circuit is broken at the weakest asymmetric link, i.e. a link (*x*, *y*) with minimal difference between r(*x* S *y*) - r(*y* S *x*). """ def __init__(self,digraph=None,Piping=False, Comments=False,Threading=False,nbrOfCPUs=1): import random,sys,array,copy from outrankingDigraphs import OutrankingDigraph, RandomOutrankingDigraph, BipolarOutrankingDigraph ## if comment is None: ## silent = True ## else: ## silent = not(comment) if digraph is None: from randomDigraphs import RandomValuationDigraph g = RandomValuationDigraph() self.name = str(g.name) self.actions = g.actions self.valuationdomain = g.valuationdomain self.relation = g.relation elif isinstance(digraph,(Digraph,OutrankingDigraph,RandomOutrankingDigraph,BipolarOutrankingDigraph)): self.name = str(digraph.name) self.actions = copy.deepcopy(digraph.actions) self.valuationdomain = copy.deepcopy(digraph.valuationdomain) try: self.valuationdomain['precision'] = digraph.valuationdomain['precision'] except: self.valuationdomain['precision'] = Decimal('0') self.relation = copy.deepcopy(digraph.relation) else: fileName = digraph + 'py' argDict = {} fi = open(fileName, 'r') fileText = fi.read() fi.close() exec(compile(fileText, fileName, 'exec'),argDict) self.name = digraph self.actions = argDict['actionset'] self.valuationdomain = argDict['valuationdomain'] self.relation = argDict['relation'] self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.weakGamma = self.weakGammaSets() self.closureChordlessOddCircuits(Piping=Piping, Comments=Comments,Threading=Threading,nbrOfCPUs=nbrOfCPUs)
[docs] def closureChordlessOddCircuits(self,Piping=False, Comments=True,Debug=False,Threading=False,nbrOfCPUs=1): """ Closure of chordless odd circuits extraction. """ newCircuits = None self.circuitsList = [] try: oldBreakings = self.breakings except: self.breakings = 0 self.newBreakings = self.order #while newCircuits != set() or self.newBrakings != 0: while newCircuits != set(): initialCircuits = set([x for cl,x in self.circuitsList]) self.addCircuits(Comments=Comments) # if Cpp: # if Piping: # self.computeCppInOutPipingChordlessCircuits(Odd=True,Debug=Debug) # else: # self.computeCppChordlessCircuits(Odd=True,Debug=Debug) if Threading: self.computeChordlessCircuitsMP(Odd=True,Comments=Debug, Threading=Threading,nbrOfCPUs=nbrOfCPUs) else: self.computeChordlessCircuits(Odd=True,Comments=Debug) currentCircuits = set([x for cl,x in self.circuitsList]) if Comments: print('initialCircuits, currentCircuits', initialCircuits, currentCircuits) newCircuits = currentCircuits - initialCircuits
[docs] def addCircuits(self,Comments=False): """ Augmenting self with self.circuits. """ import time from digraphsTools import flatten #from copy import deepcopy order0 = self.order newBreakings = 0 if not(isinstance(self.actions,dict)): actions = {} for x in self.actions: actions[x] = {'name':x} else: actions = self.actions #ListActions = [frozenset([x]) for x in actions] circuitsList = self.circuitsList if Comments: print('list of circuits: ', circuitsList) valuationdomain = self.valuationdomain gamma = self.gamma relation = self.relation Med = valuationdomain['med'] currentCircuits = list(circuitsList) for (cycleList,cycle) in circuitsList: degP,degN,minLink = self.circuitCredibilities(cycleList,Debug=Comments) if Comments: print(cycleList,cycle,degP,degN,minLink) #if degP+degN > Med: if (degP + degN) > valuationdomain['precision']: # if (degP + degN) >= valuationdomain['med']: # adds potentially more circuits if Comments: print('Adding cycle:', cycle, ' with Pdegree=',degP,' and Ndegree=',degN ) cn = '_' dcycle = set() acycle = set() for x in cycleList: if isinstance(x,frozenset): cn += actions[x]['name'] + '_' else: cn += str(x) + '_' dcycle = dcycle | gamma[x][0] dcycle = dcycle | set([x]) acycle = acycle | gamma[x][1] acycle = acycle | set([x]) gamma[cycle]=(dcycle,acycle) for x in actions: if x in cycle: dx0 = gamma[x][0] | set([cycle]) dx1 = gamma[x][1] | set([cycle]) gamma[x] = (dx0,dx1) relxcn = relation[x] #relxcn[cycle] = valuationdomain['max'] relxcn[cycle] = degP + degN relation[x] = relxcn else: relxy = valuationdomain['min'] for y in cycle: relxy = max(relxy,relation[x][y]) relxcn = relation[x] relxcn[cycle] = relxy relation[x] = relxcn relcycle = {} for x in actions: if x in cycle: #relcycle[x] = valuationdomain['max'] relcycle[x] = degP else: relxy = valuationdomain['min'] for y in cycle: relxy = max(relxy,relation[y][x]) relcycle[x] = relxy relcycle[cycle] = valuationdomain['min'] relation[cycle] = relcycle name = 'chordless odd %d-circuit' % (len(cycle)) actions[cycle] = {'name': cn, 'comment': name} if Comments: print(actions[cycle]) else: if Comments: print('Breaking:',cycleList,degP,degN) actionsSubset = [x for x in flatten(cycle)] if Comments: self.showRelationTable(actionsSubset=actionsSubset) x = minLink[0] y = minLink[1] if Comments: print('Minimal link put to doubt: ', x,y) relation[x][y] = Med relation[y][x] = Med currentCircuits.remove((cycleList,cycle)) newBreakings += 1 self.actions = actions self.order = len(actions) self.relation = relation self.circuitsList = currentCircuits self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.weakGamma = self.weakGammaSets() new = self.order - order0 if Comments: if new == 0: print(' No circuits added !') else: print(' ',new,' circuit(s) added!') self.newBreakings = newBreakings self.breakings += newBreakings if Comments: if newBreakings == 0: print(' No further circuit brakings !') else: print(' ',newBreakings,' new circuit(s) were broken')
[docs] def showCircuits(self,credibility=None,Debug=False): """ show methods for chordless odd circuits in CocaGraph """ print('*---- Chordless circuits ----*') for (circList,circSet) in self.circuitsList: if credibility == 'maximal': degM = self.circuitMaxCredibility(circList,Debug=Debug) print(circList, ', maximal credibility :', degM) elif credibility == 'minimal': degm = self.circuitMinCredibility(circList,Debug=Debug) print(circList, ', minimal credibility :', degm) elif credibility == 'average': degm = self.circuitMinCredibility(circList,Debug=Debug) print(circList, ', average credibility :', degm) else: degP,degN,minLink = self.circuitCredibilities(circList,Debug=Debug) print(circList, ', marginal credibility :', degP+degN) x = minLink[0] y = minLink[1] print('minimal link: ', minLink, self.relation[x][y],self.relation[y][x]) print('Coca graph of order %d with %d odd chordless circuits.' % (len(self.actions), len(self.circuitsList)))
#print len(aself.circuitsList),' cirduits
[docs] def showComponents(self): """Shows the list of connected components of the digraph instance.""" print('*--- Connected Components ---*') k=1 for Comp in self.components(): component = list(Comp) #component.sort() print(str(k) + ': ' + str(component)) xk = k + 1
#--------------------
[docs] class CocaDigraph(Digraph): """ Old CocaDigraph class without circuit breakings; all circuits and circuits of circuits are added as hyper-nodes. .. warning:: May sometimes give inconsistent results when an autranking digraph shows loads of chordless cuircuits. It is recommended in this case to use instead either the BrokenCocsDigraph class (preferred option) or the BreakAddCocsDigraph class. Parameters: - digraph: Stored or memory resident digraph instance. - Piping: using OS pipes for data in- and output between Python and C++. Specialization of general Digraph class for instantiation of chordless odd circuits augmented digraphs. """ def __init__(self,digraph=None,Piping=False,Comments=False): import random,sys,array,copy from outrankingDigraphs import OutrankingDigraph, RandomOutrankingDigraph, BipolarOutrankingDigraph ## if comment is None: ## silent = True ## else: ## silent = not(comment) if digraph is None: ## g = RandomValuationDigraph() ## self.name = str(g.name) ## self.actions = g.actions ## self.valuationdomain = g.valuationdomain ## self.relation = g.relation print('!!! Error: no valid digraph argument provided') elif isinstance(digraph,(Digraph,OutrankingDigraph,RandomOutrankingDigraph,BipolarOutrankingDigraph)): self.name = str(digraph.name) self.actions = copy.deepcopy(digraph.actions) self.valuationdomain = copy.deepcopy(digraph.valuationdomain) self.relation = copy.deepcopy(digraph.relation) else: fileName = digraph + 'py' argDict = {} fi = open(fileName,'r') fileText = fi.read() fi.close() exec(compile(fileText, fileName, 'exec'),argDict) self.name = digraph self.actions = argDict['actionset'] self.valuationdomain = argDict['valuationdomain'] self.relation = argDict['relation'] self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.weakGamma = self.weakGammaSets() self.closureChordlessOddCircuits(Piping=Piping,Comments=Comments)
[docs] def closureChordlessOddCircuits(self,Piping=False,Comments=False): """ Closure of chordless odd circuits extraction. """ newCircuits = None self.circuitsList = [] while newCircuits != set(): initialCircuits = set([x for cl,x in self.circuitsList]) # if Cpp: # if Piping: # self.computeCppInOutPipingChordlessCircuits(Odd=True,Debug=Comments) # else: # self.computeCppChordlessCircuits(Odd=True,Debug=Comments) # else: self.computeChordlessCircuits(Odd=True,Comments=Comments) self.addCircuits(Comments=Comments) currentCircuits = set([x for cl,x in self.circuitsList]) if Comments: print('initialCircuits, currentCircuits', initialCircuits, currentCircuits) newCircuits = currentCircuits - initialCircuits
[docs] def addCircuits(self,Comments=False): """ Augmenting self with self.circuits. """ import copy,time order0 = self.order if isinstance(self.actions,(list,set)): actions = {} for x in self.actions: actions[x] = {'name':x} else: actions = self.actions #ListActions = [frozenset([x]) for x in actions] circuitsList = self.circuitsList if Comments: print('list of circuits: ', circuitsList) valuationdomain = self.valuationdomain gamma = self.gamma relation = self.relation for (cycleList,cycle) in circuitsList: cn = '_' dcycle = set() acycle = set() for x in cycleList: if isinstance(x,frozenset): cn += actions[x]['name'] + '_' else: cn += str(x) + '_' dcycle = dcycle | gamma[x][0] dcycle = dcycle | set([x]) acycle = acycle | gamma[x][1] acycle = acycle | set([x]) gamma[cycle]=(dcycle,acycle) for x in actions: if x in cycle: dx0 = gamma[x][0] | set([cycle]) dx1 = gamma[x][1] | set([cycle]) gamma[x] = (dx0,dx1) relxcn = relation[x] relxcn[cycle] = valuationdomain['max'] #relxcn[cycle] = degP relation[x] = relxcn else: relxy = valuationdomain['min'] for y in cycle: relxy = max(relxy,relation[x][y]) relxcn = relation[x] relxcn[cycle] = relxy relation[x] = relxcn relcycle = {} for x in actions: if x in cycle: relcycle[x] = valuationdomain['max'] #relcycle[x] = degP else: relxy = valuationdomain['min'] for y in cycle: relxy = max(relxy,relation[y][x]) relcycle[x] = relxy relcycle[cycle] = valuationdomain['min'] relation[cycle] = relcycle name = 'chordless odd %d-circuit' % (len(cycle)) actions[cycle] = {'name': cn, 'comment': name} if Comments: print(actions[cycle]) #self.actions = list(actions) self.actions = actions self.order = len(actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() self.weakGamma = self.weakGammaSets() new = self.order - order0 if Comments: if self.order == order0: print(' No circuits added !') else: print(' ',new,' circuit(s) added!')
[docs] def showCircuits(self,credibility=None): """ show methods for chordless odd circuits in CocaGraph """ print('*---- Chordless circuits ----*') for (circList,circSet) in self.circuitsList: if credibility == 'maximal': degM = self.circuitMaxCredibility(circSet) print(circList, ', maximal credibility :', degM) elif credibility == 'minimal': degm = self.circuitMinCredibility(circSet) print(circList, ', minimal credibility :', degm) elif credibility == 'average': degm = self.circuitMinCredibility(circSet) print(circList, ', average credibility :', degm) else: degP,degN,minLink = self.circuitCredibilities(circSet) print(circList, ', marginal credibility :', degP+degN) print('Coca graph of order %d with %d odd chordles circuits.' % (len(self.actions), len(self.circuitsList)))
#print len(aself.circuitsList),' cirduits
[docs] def showComponents(self): """Shows the list of connected components of the digraph instance.""" print('*--- Connected Components ---*') k=1 for Comp in self.components(): component = list(Comp) #component.sort() print(str(k) + ': ' + str(component)) xk = k + 1
#--------------------- ##class CocaDigraph(_CocaDigraph): ## """ ## CocaDigraph class wrapper for testing purposes only ## """ #------------------------------------------
[docs] class StrongComponentsCollapsedDigraph(Digraph): """ Reduction of Digraph object to its strong components. """ def __init__(self,digraph=None): from copy import copy,deepcopy from collections import OrderedDict if digraph is None: print('Error: you must provide a valid digraph to the constructor!') else: self.name = digraph.name + '_Scc' self.valuationdomain = deepcopy(digraph.valuationdomain) scc = digraph.strongComponents() actions = OrderedDict() for i,strongComponent in enumerate(scc): actionShortName = 'Scc_'+str(i+1) actionKey = strongComponent actionName = '_' for x in strongComponent: actionName += str(x)+'_' actions[actionKey] = {'name': actionName, 'shortName': actionShortName, 'comment': 'collapsed strong component'} self.actions = actions relation = {} actionsList = [x for x in actions] actionsList.sort() for xsc in actionsList: relation[xsc] = {} for ysc in actionsList: relation[xsc][ysc] = self.valuationdomain['min'] for x in xsc: for y in ysc: if x == y: relation[xsc][ysc] = self.valuationdomain['med'] elif digraph.relation[x][y] > relation[xsc][ysc]: relation[xsc][ysc] = digraph.relation[x][y] self.relation = relation self.order = len(self.actions) self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] def showComponents(self): """Shows the list of connected components of the digraph instance.""" print('short', '\t', 'content') for x in self.actions: print(self.actions[x]['shortName'], '\t', self.actions[x]['name'])
#------------------------------------------------------- # ------------ XML encoded stored Digraph instances obsolete # class _XMLDigraph24(Digraph): # """ # Specialization of the general Digraph class for reading # stored XML formatted digraphs. # """ # def __init__(self,fileName='testsaveXML'): # from xml.sax import make_parser # xmlDigraph = _XMLDigraphHandler() # saxParser = make_parser() # saxParser.setContentHandler(xmlDigraph) # fileNameExt = fileName + '.xml' # fo = open(fileNameExt,'r') # saxParser.parse(fo) # self.name = xmlDigraph.name # self.category = xmlDigraph.category # self.subcategory = xmlDigraph.subcategory # self.actions = xmlDigraph.actions # self.valuationdomain = xmlDigraph.valuationdomain # Min = xmlDigraph.valuationdomain['min'] # Max = xmlDigraph.valuationdomain['max'] # Med = Min + ((Max - Min)/2.0) # self.valuationdomain['med'] = Med # self.relation = xmlDigraph.relation # self.gamma = self.gammaSets() # self.notGamma = self.notGammaSets() # def showAll(self): # if self.category == 'outranking': # Digraph.showAll(self) # self.showPreKernels() # self.showGoodChoices() # self.showBadChoices() # else: # Digraph.showAll(self) #---------------
[docs] class CriteriaCorrelationDigraph(Digraph): """ Renders the ordinal criteria correlation digraph from the given outranking digraph. If ValuedCorrelation==True, the correlation indexes represent the bipolar-valued p airwise relational equivalence between the marginal criteria outranking relation: that is tau * determination Otherwise, the valuation represents the ordinal correlation index tau If WithMedian==True, the correlation of the marginal criteria outranking with the global outranking relation, denoted 'm', is included. """ def __init__(self,outrankingDigraph, ValuedCorrelation=True, WithMedian=False): from copy import deepcopy from collections import OrderedDict if ValuedCorrelation: self.name = outrankingDigraph.name + 'valCorr' else: self.name = outrankingDigraph.name + 'corr' actions = OrderedDict() for g in outrankingDigraph.criteria: godg = outrankingDigraph.criteria[g] actions[str(g)] = {'shortName':str(g), 'name':godg['name']} if WithMedian: actions['m'] = {'shortName':'m', 'name':'outranking relation'} valuationdomain = {'min':Decimal('-1.0'),'med':Decimal('0.0'),'max':Decimal('1.0')} Min = valuationdomain['min'] Med = valuationdomain['med'] Max = valuationdomain['max'] criteriaList = [g for g in outrankingDigraph.criteria] n = len(criteriaList) corr,d = outrankingDigraph.computeCriteriaCorrelations(\ ValuedCorrelation=ValuedCorrelation) relation = {} for i in range(n): relation[criteriaList[i]] = {} for j in range(n): relation[criteriaList[i]][criteriaList[j]] =\ corr[criteriaList[i]][criteriaList[j]] if WithMedian: margCorr = outrankingDigraph.computeOutrankingConsensusQuality(\ ValuedCorrelation=ValuedCorrelation) #print(margCorr) relation['m'] = {} relation['m']['m'] = outrankingDigraph.computeDeterminateness() for item in margCorr[0]: relation[item[1]]['m'] = item[0] relation['m'][item[1]] = item[0] self.actions = actions self.order = len(actions) self.valuationdomain = valuationdomain self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() def _saveCorrelationDistancesCSV(self,fileName='tempdigraph',Normalized=False, Dual=False,Converse=False,Diagonal=True,Debug=False): """ file exchange with R for the principal image drawing""" from copy import deepcopy import csv com = '' if Normalized: com += 'normalized' if Dual and Converse: com += ' and codual' elif Dual and not Converse: com += ' and dual' elif Converse: com += ' and converse' else: if Dual and Converse: com += 'codual' elif Dual and not Converse: com += 'dual' elif Converse: com += 'converse' if Debug: print('*--- Saving %s digraph in file: %s.csv> ---*' % (com,fileName)) fileNameExt = str(fileName)+str('.csv') fo = open(fileNameExt, 'w') csvfo = csv.writer(fo,quoting=csv.QUOTE_NONNUMERIC) actionsList = [x for x in self.actions] actionsList.sort() headerText = ["d"] + actionsList if Debug: print(headerText) csvfo.writerow(headerText) dg = deepcopy(self) if Normalized: dg.recodeValuation(-1,1) Min = dg.valuationdomain['min'] Med = dg.valuationdomain['med'] Max = dg.valuationdomain['max'] if Dual: dg = -dg if Converse: dg = ~dg relation = dg.relation for x in actionsList: rowText = [x] for y in actionsList: if x == y: if not Diagonal: # minimal distance rowText.append(0.0) else: rowText.append(float(Max - relation[x][y])) else: rowText.append(float(Max - relation[x][y])) if Debug: print(rowText) csvfo.writerow(rowText) fo.close()
[docs] def exportPrincipalImage(self, plotFileName=None, pictureFormat="pdf", bgcolor='cornsilk', fontcolor='red3', fontsize='0.85', #_Reduced=False, #_Colwise=False, tempDir='.', Comments=False): """ Export the principal projection of the absolute correlation distances using the three principal eigen vectors. Implemeted picture formats are: 'pdf' (default), 'png', 'jpeg' and 'xfig'. The background, resp. font color is set by default to 'cornsilk', resp. 'red3'. _Colwise and _Reduced parameters are deprecated. .. warning:: The method, writing and reading temporary files: tempCol.r and rotationCol.csv, resp. tempRow.r and rotationRow.csv, by default in the working directory (./), is hence not safe for multiprocessing programs, unless a temporary directory *tempDir* is provided. """ if Comments: print('*---- export 3dplot of format %s -----' % (pictureFormat)) if tempDir is None: tempDir = '.' import os,time if plotFileName is None: plotFileName = "%s/%s" % (tempDir,self.name) else: plotFileName = "%s/%s" % (tempDir,plotFileName) self._saveCorrelationDistancesCSV('%s/exportTemp' % tempDir,Diagonal=True) #if _Colwise: #fo = open('%s/tempCol.r' % tempDir,'w') #else: fo = open('%s/tempRow.r' % tempDir,'w') fo.write("x = read.csv('%s/exportTemp.csv',row.names=1)\n" % tempDir) #if _Colwise: #fo.write("x = t(x)\n") #if _Reduced: # fo.write("x = (x-colMeans(x))/(sapply(x,sd)*sqrt(length(t(x))))\n") #else: fo.write("x = (x-colMeans(x))\n") fo.write("X = as.matrix(x)\n") fo.write("A = X %*% t(X)\n") fo.write("E = eigen(A, symmetric=TRUE)\n") fo.write("P = E$values * t(E$vectors)\n") #if _Colwise: # fo.write("write.csv(t(P),'%s/rotationCol.csv',row.names=F)\n" % tempDir) #else: fo.write("write.csv(t(P),'%s/rotationRow.csv',row.names=F)\n" % tempDir) if pictureFormat is None: # no principal image is required fo.close() else: fo.write("valprop = round(E$values/sum(E$values),digits=3)\n") fo.write("pcaRes = list(x=X,eig=E,a=A,P=P,val=valprop)\n") fo.write("val = pcaRes$val\n") fo.write("nval = length(val)\n") if pictureFormat == "png": if bgcolor is None: fo.write('png("%s.png",width=480,height=480\n'\ % (plotFileName) ) else: fo.write('png("%s.png",width=480,height=480,bg="%s")\n'\ % (plotFileName,bgcolor) ) elif pictureFormat == "jpeg": if bgcolor is None: fo.write('jpeg("%s.jpg",width=480,height=480\n'\ % (plotFileName) ) else: fo.write('jpeg("%s.png",width=480,height=480,bg="%s")\n'\ % (plotFileName,bgcolor) ) elif pictureFormat == "xfig": if bgcolor is None: fo.write('xfig("%s.fig",width=480,height=480\n'\ % (plotFileName) ) else: fo.write('xfig("%s.figg",width=480,height=480,bg="%s")\n'\ % (plotFileName,bgcolor) ) elif pictureFormat == "pdf": if bgcolor is None: fo.write('pdf("%s.pdf",width=6,height=6,title="PCA of relation valuation")\n' % (plotFileName) ) else: fo.write('pdf("%s.pdf",width=6,height=6,bg="%s",title="PCA of relation valuation")\n' % (plotFileName,bgcolor) ) else: print('Error: Plotting device %s not defined !' % (pictureFormat)) return fo.write("par(mfrow=c(2,2))\n") fo.write("a1 = 1\n") fo.write("a2 = 2\n") fo.write("a3 = 3\n") fo.write('plot(pcaRes$P[a1,],pcaRes$P[a2,],"n",xlab=paste("axis 1:",val[a1]*100,"%"),ylab=paste("axis 2:",val[a2]*100,"%"),asp=1)\n') fo.write('text(pcaRes$P[a1,],pcaRes$P[a2,],rownames(pcaRes$x),col="%s",cex=%s)\n' % (fontcolor,fontsize) ) fo.write('title(sub=paste("Total inertia:",(val[1]+val[2])*100,"%"),main="Factors 1 and 2",col.sub="black")\n') fo.write('abline(h=0,v=0,lty=2,col="gray")\n') fo.write('plot(pcaRes$P[a3,],pcaRes$P[a2,],"n",xlab=paste("axis 3:",val[a3]*100,"%"),ylab=paste("axis 2:",val[a2]*100,"%"),asp=1)\n') fo.write('text(pcaRes$P[a3,],pcaRes$P[a2,],rownames(pcaRes$x),col="%s",cex=%s)\n' % (fontcolor,fontsize) ) fo.write('title(sub=paste("Total inertia:",(val[3]+val[2])*100,"%"),main="Factors 3 and 2",col.sub="black")\n') fo.write('abline(h=0,v=0,lty=2,col="gray")\n') fo.write('plot(pcaRes$P[a1,],pcaRes$P[a3,],"n",xlab=paste("axis 1:",val[a1]*100,"%"),ylab=paste("axis 3:",val[a3]*100,"%"),asp=1)\n') fo.write('text(pcaRes$P[a1,],pcaRes$P[a3,],rownames(pcaRes$x),col="%s",cex=%s)\n' % (fontcolor,fontsize) ) fo.write('title(sub=paste("Total inertia:",(val[1]+val[3])*100,"%"),main="Factors 1 and 3",col.sub="black")\n') fo.write('abline(h=0,v=0,lty=2,col="gray")\n') fo.write('barplot(val[a1:nval]*100,names.arg=a1:nval,main="Axis inertia (in %)",col="orangered")\n') fo.write('dev.off()\n') fo.close() if Comments: #if _Colwise: # os.system('(cd %s;env R -q --vanilla --verbose < tempCol.r 2>&1)' % tempDir) #else: os.system('(cd %s;env R -q --vanilla --verbose < tempRow.r 2>&1)' % tempDir ) else: #if _Colwise: # os.system('(cd %s;env R -q --vanilla < tempCol.r > /dev/null 2> /dev/null)' % tempDir) #else: os.system('(cd %s;env R -q --vanilla < tempRow.r > /dev/null 2> /dev/null)' % tempDir) time.sleep(3) if pictureFormat is not None and Comments: print('See %s.%s ! ' % (plotFileName,pictureFormat))
#----------------
[docs] class CSVDigraph(Digraph): """ Specialization of the general Digraph class for reading stored csv formatted digraphs. Using the inbuilt module csv. Param: fileName (without the extension .csv). """ def __init__(self,fileName='temp',valuationMin=-1,valuationMax=1): from csv import reader try: fileNameExt = fileName + '.csv' fi = open(fileNameExt,'r') csvReader = reader(fi) csvText = [x for x in csvReader] except: print("Error: File %s.csv not found !!" % (fileName)) self.name = fileName self.order = len(csvText)-1 self.reference = 'CSV Digraph input method.' Min = Decimal(valuationMin) Max = Decimal(valuationMax) Med = Min + ((Max - Min)/Decimal('2.0')) valuationdomain = {} valuationdomain['min'] = Min valuationdomain['med'] = Med valuationdomain['max'] = Max self.valuationdomain = valuationdomain self.actions = [csvText[0][i] for i in range(1,self.order+1)] relation = {} for i in range(1,self.order+1): relation[csvText[i][0]] = {} for j in range(1,self.order+1): relation[csvText[i][0]][csvText[0][j]] = Decimal(csvText[i][j]) self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets()
[docs] def showAll(self): try: if self.category == 'outranking': Digraph.showAll(self) self.showPreKernels() self.showGoodChoices() self.showBadChoices() else: Digraph.showAll(self) except: Digraph.showAll(self)
#-------- class _XMCDADigraph(Digraph): """ Specialization of the general Digraph class for reading stored XMCDA formatted digraphs. Using the inbuilt module xml.etree (for Python 2.5+). Param: fileName (without the extension .xmcda). """ def __init__(self,fileName='temp'): from xml.etree import ElementTree try: fileNameExt = fileName + '.xmcda' fo = open(fileNameExt,'r') except: try: fileNameExt = fileName + '.xml' fo = open(fileNameExt,'r') except: print("Error: file %s{.xml|.xmcda} not found" % (fileName)) XMCDA = ElementTree.parse(fo).getroot() description = {} #for elem in [x for x in XMCDA.find('caseReference').getchildren()]: for elem in [x for x in XMCDA.find('caseReference')]: description[elem.tag] = elem.text self.description = description try: self.name = description['name'] except: self.name ='temp' try: self.author = description['author'] except: self.author = 'digraphs module (RB)' try: self.reference = description['comment'] except: self.reference = 'XMCDA 1.0 Digraph input method.' #Min = Decimal(XMCDA.find('relationOnAlternatives').find('valuationDomain').find('minimum').getchildren().pop().text) #Max = Decimal(XMCDA.find('relationOnAlternatives').find('valuationDomain').find('maximum').getchildren().pop().text) Min = Decimal(XMCDA.find('relationOnAlternatives').find('valuationDomain').find('minimum').pop().text) Max = Decimal(XMCDA.find('relationOnAlternatives').find('valuationDomain').find('maximum').pop().text) Med = Min + ((Max - Min)/Decimal('2.0')) valuationdomain = {} valuationdomain['min'] = Min valuationdomain['med'] = Med valuationdomain['max'] = Max self.valuationdomain = valuationdomain actions = {} for alternative in XMCDA.find('alternatives').findall('alternative'): id = alternative.attrib['id'] actions[id] = {} #for elem in [x for x in alternative.find('description').getchildren()]: for elem in [x for x in alternative.find('description')]: actions[id][elem.tag] = elem.text self.actions = actions relation = {} try: if XMCDA.find('relationOnAlternatives').find('description').find('type').text == 'outrankingDigraph': self.category = 'outranking' else: self.category = 'general' except: pass for x in actions: relation[x] = {} for arc in XMCDA.find('relationOnAlternatives').find('arcs').findall('arc'): try: relation[arc.find('from').find('alternativeID').text][arc.find('to').find('alternativeID').text] = Decimal(arc.find('value').find('real').text) except: relation[arc.find('from').find('alternativeID').text][arc.find('to').find('alternativeID').text] = Decimal(arc.find('value').find('integer').text) self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() def showAll(self): try: if self.category == 'outranking': Digraph.showAll(self) self.showPreKernels() self.showGoodChoices() self.showBadChoices() else: Digraph.showAll(self) except: Digraph.showAll(self) #------------
[docs] class XMCDA2Digraph(Digraph): """ Specialization of the general Digraph class for reading stored XMCDA-2.0 formatted digraphs. Using the inbuilt module xml.etree (for Python 2.5+). Param: fileName (without the extension .xmcda). """ def __init__(self,fileName='temp'): from xml.etree import ElementTree fileNameExt = fileName + '.xml' try: fo = open(fileNameExt,'r') except: fileNameExt = fileName + '.xmcda' try: fo = open(fileNameExt,'r') except: fileNameExt = fileName + '.xmcda2' try: fo = open(fileNameExt,'r') except: print("Error: file %s not found" % (fileNameExt)) print("file %s is being read:" % (fileNameExt)) XMCDA = ElementTree.parse(fo).getroot() try: self.name = XMCDA.attrib['name'] except: self.name ='temp' description = {} #for elem in [x for x in XMCDA.find('projectReference').getchildren()]: for elem in [x for x in XMCDA.find('projectReference')]: description[elem.tag] = elem.text self.description = description try: self.author = description['author'] except: self.author = 'digraphs module (RB)' try: self.reference = description['comment'] except: self.reference = 'XMCDA 1.0 Digraph input method.' #Min = Decimal(XMCDA.find('alternativesComparisons').find('valuation').find('quantitative').find('minimum').getchildren().pop().text) #Max = Decimal(XMCDA.find('alternativesComparisons').find('valuation').find('quantitative').find('maximum').getchildren().pop().text) Min = Decimal(XMCDA.find('alternativesComparisons').find('valuation').find('quantitative').find('minimum').find('real').text) Max = Decimal(XMCDA.find('alternativesComparisons').find('valuation').find('quantitative').find('maximum').find('real').text) Med = Min + ((Max - Min)/Decimal('2.0')) valuationdomain = {} valuationdomain['min'] = Min valuationdomain['med'] = Med valuationdomain['max'] = Max self.valuationdomain = valuationdomain actions = {} for alternative in XMCDA.find('alternatives').findall('alternative'): id = alternative.attrib['id'] actions[id] = {} actions[id]['name'] = alternative.attrib['name'] #for elem in [x for x in alternative.find('description').getchildren()]: for elem in [x for x in alternative.find('description')]: actions[id][elem.tag] = elem.text self.actions = actions self.order = len(actions) relation = {} try: if XMCDA.find('alternativesComparisons').attribute['mcdaConcept'] == 'outrankingDigraph': self.category = 'outranking' else: self.category = 'general' except: pass for x in actions: relation[x] = {} for pair in XMCDA.find('alternativesComparisons').find('pairs').findall('pair'): try: relation[pair.find('initial').find('alternativeID').text][pair.find('terminal').find('alternativeID').text] = Decimal(pair.find('value').find('real').text) except: relation[pair.find('initial').find('alternativeID').text][pair.find('terminal').find('alternativeID').text] = Decimal(pair.find('value').find('integer').text) self.relation = relation self.gamma = self.gammaSets() self.notGamma = self.notGammaSets() fo.close()
[docs] def showAll(self): try: if self.category == 'outranking': Digraph.showAll(self) self.showPreKernels() self.showGoodChoices() self.showBadChoices() else: Digraph.showAll(self) except: Digraph.showAll(self)
### replace the old outrankingDigraphs #from outrankingDigraphs import * from randomDigraphs import * ############################################# # scratch space for testing ongoing developments #----------test Digraph class ---------------- if __name__ == "__main__": print('*****************************************************') print('* Python digraphs module *') print('* $Revision: Python3.9 $ *') print('* Copyright (C) 2006-2021 Raymond Bisdorff *') print('* The module comes with ABSOLUTELY NO WARRANTY *') print('* to the extent permitted by the applicable law. *') print('* This is free software, and you are welcome to *') print('* redistribute it if it remains free software. *') print('*****************************************************') print('*-------- Testing classes and methods -------') from time import time #from digraphsTools import * from outrankingDigraphs import * from randomDigraphs import * from decimal import Decimal, getcontext t = RandomPerformanceTableau(weightDistribution="equiobjectives", numberOfActions=100,numberOfCriteria=7, missingDataProbability=0.05,seed=2) #t = CircularPerformanceTableau() #print(getcontext().prec) g = BipolarOutrankingDigraph(t,Threading=False,startMethod='spawn') print(g) #t0 = time() #print(g.computeOrdinalCorrelationMP(g,Threading=True,startMethod=None,nbrOfCPUs=None)) #print(time()-t0) #g = RandomBipolarOutrankingDigraph(10) #g.showRelationTable() #g.showRelationMap() #g.showHTMLRelationHeatmap(colorLevels=3,ndigits=1) #g.showHTMLRelationHeatmap(colorLevels=5,ndigits=2) #g.showHTMLRelationHeatmap(colorLevels=7,ndigits=3) #g.showHTMLRelationHeatmap(colorLevels=9,ndigits=4) #ranking = g.computeNetFlowsRanking() #vr = g.computeValuedRankingRelation(ranking) #g.relation = vr #g.showHTMLRelationTable(ndigits=3) #g.showHTMLRelationHeatmap(actionsList=ranking,rankingRule=None, # colorLevels=7,ndigits=3) # partA = ['a1','a2'] # partB = ['a3','a4','a5'] # bpg = BipartitePartialDigraph(g,partA,partB) # bpg.showRelationTable() # bpg1 = BipartitePartialDigraph(g,partA,partB,Partial=False) # bpg1.showRelationTable() ## gcd = ~(-g) ## #g = RandomValuationDigraph() ## Digraph.showRelationTable(g,rankingRule='Copeland') ## g.recodeValuation(ndigits=4) ## print(g.relation) ## print(g.isOutrankingDigraph(Debug=False)) ## gcd.showRelationTable(rankingRule='NetFlows') ## gcd.recodeValuation(ndigits=None) ## print(gcd.relation) ## print(gcd.isStrictOutrankingDigraph(Debug=False)) ## # g.recodeValuation(0,1) # g.showRelationTable() # print(g.isOutrankingDigraph(Debug=False)) # print((-g).isStrictOutrankingDigraph(Debug=False)) #g.showChordlessCircuits(Recompute=True) #g.isAsymmetricIndeterminate(Debug=True) #gc = BrokenChordlessCircuitsDigraph(g) #gcoc = BrokenCocsDigraph(g) #g.showObjectives() #g.showHTMLPerformanceTableau(Transposed=True) #g.recodeValuation(0.0,1.0) #cc = CriteriaCorrelation #Digraph(g,ValuedCorrelation=True,WithMedian=True) #g = ConfidentBipolarOutrankingDigraph(t) ## cc = g.computeCriteriaCorrelationDigraph(ValuedCorrelation=True,WithMedian=True) #cc.exportPrincipalImage('test',pictureFormat='pdf',fontsize='0.75',Comments=True) #Digraph.exportPrincipalImage(cc,'testa',pictureFormat='pdf',fontsize='0.75',Comments=True) #g.recodeValuation(0,2) #g.exportPrincipalImage('testd',Reduced=True,pictureFormat='pdf',fontsize='0.75',Comments=True) #g.exportPrincipalImage('testc',Colwise=True,pictureFormat='pdf',fontsize='0.75',Comments=True) #g.exportGraphViz(pictureFormat='png') ## print(g) ## g.showAttributes() ## #g.computeShortestPathLengths(Comments=True) ## g.computeDigraphCentres(Comments=True) ## g.computeDigraphCentres(WeakDistances=True,Comments=True) # g.showBestChoiceRecommendation() # g.showFirstChoiceRecommendation() #g.showRelationTable() #g.computeTransitivityDegree(Comments=True) # g.computeIncomparabilityDegree(Comments=True) # g.computeIncomparabilityDegree(InPercents=True,Comments=True) # print(g.computeCopelandOrder()) # print(g.computePrincipalOrder(Comments=True)) # print(g.computePrincipalRanking(Comments=True)) # print(g.computePrincipalOrder(Colwise=True,Comments=True)) # print(g.computePrincipalRanking(Colwise=True,Comments=True)) print('*------------------*') print('If you see this line all tests were passed successfully :-)') print('Enjoy !') print('*************************************') print('* R.B. Mar 2021 *') print('* $Revision: Python3.9.2$ *') print('*************************************') ############################# # Log record for changes: # $Log: digraphs.py,v $ # #############################