def lastTransitiveClosureTime(numNodes, transaction_list, verbose): """ Given a list of transactions which are in the form of a list of (sender, receiver, timestamp) tuples, return the list of times at which open two-paths are closed. Parameters: numNodes - number of actors (nodes) transaction_list - list of (sender, receiver, timestamp) tuples. Sender and receiver are intergers in 0..numNodes-1 and timestamps are numeric values. The list must be ordered by timestamp ascending. verbose - if True write debug output to stdout Return value: List of tuples (open_time, delta_time) where open_time is second timestamp in open two-path and and delta_time is it took open two-paths to be closed (the difference in timestamp between the closing transaction (arc) and the second (along arc) timestamp in the open two-path, BUT only if the second arc in two-path has higher timestamp than the first (i.e. we don't count a two-path that goes backward in time along the path) """ G = Digraph(numNodes) lastTime = None delta_time_list = [] for trans in transaction_list: assert (trans[TSENDER] >= 0 and trans[TSENDER] < numNodes) assert (trans[TRECEIVER] >= 0 and trans[TRECEIVER] < numNodes) assert (lastTime is None or trans[TTIME] >= lastTime) if verbose: print trans[TSENDER], '->', trans[TRECEIVER], ' at time ', trans[ TTIME], closed_2paths_v = closedTwoPaths(G, trans[TSENDER], trans[TRECEIVER]) if len(closed_2paths_v) > 0: if verbose: print 'closed', len( closed_2paths_v), 'two-paths via', closed_2paths_v path_2nd_time_list = [] for v in closed_2paths_v: path_1st_time = G.G[trans[TSENDER]][v] path_2nd_time = G.G[v][trans[TRECEIVER]] if path_2nd_time > path_1st_time: if verbose: print ' path via', v, 'is forward in time (', path_1st_time, ',', path_2nd_time, '), considering' path_2nd_time_list.append(path_2nd_time) else: if verbose: print ' path via', v, 'is backwards in time (', path_1st_time, ',', path_2nd_time, '), skipping' if len(path_2nd_time_list) > 0: path_2nd_time_max = max(path_2nd_time_list) delta_time = trans[TTIME] - path_2nd_time_max if verbose: print ' ', len( path_2nd_time_list ), 'paths considered as forward in time, max 2nd time is', path_2nd_time_max, ' appending delta_time =', delta_time delta_time_list.append((path_2nd_time_max, delta_time)) else: if verbose: print ' (no paths forward in time)' else: if verbose: print if not G.isArc(trans[TSENDER], trans[TRECEIVER]): # only insert arc if not already one there, to keep first # time on transactions, not subsequent times. G.insertArc(trans[TSENDER], trans[TRECEIVER], trans[TTIME]) lastTime = trans[TTIME] return delta_time_list
def openTwoPathOpenTimes(numNodes, transaction_list, verbose): """Given a list of transactions which are in the form of a list of (sender, receiver, timestamp) tuples, return the list of times at open two-paths are created. Parameters: numNodes - number of actors (nodes) transaction_list - list of (sender, receiver, timestamp) tuples. Sender and receiver are intergers in 0..numNodes-1 and timestamps are numeric values. The list must be ordered by timestamp ascending. verbose - if True write debug output to stdout Return value: dict { (i, j, k) : t } where (i, j, k) is an open directed two-path (note this means it is not part of a transitive triad i.e. i -> k is not present, but it may be part of a cyclic triad i.e. k -> i may be present), and t is the time the two-path was created (open). BUT only if the second arc in two-path has higher timestamp than the first (i.e. we don't count a two-path that goes backward in time along the path) """ G = Digraph(numNodes) lastTime = None pathdict = {} # dict mapping (i,j,k) two-path tuple to open time for trans in transaction_list: assert (trans[TSENDER] >= 0 and trans[TSENDER] < numNodes) assert (trans[TRECEIVER] >= 0 and trans[TRECEIVER] < numNodes) assert (lastTime is None or trans[TTIME] >= lastTime) if verbose: print trans[TSENDER], '->', trans[TRECEIVER], ' at time ', trans[ TTIME], (ulist, vlist) = openTwoPaths(G, trans[TSENDER], trans[TRECEIVER]) i = trans[TSENDER] j = trans[TRECEIVER] if len(ulist) > 0 or len(vlist) > 0: if verbose: print 'opened', len(ulist) + len(vlist), 'two-paths' for u in ulist: # u -> i -> j path_1st_time = G.G[u][i] path_2nd_time = trans[TTIME] # will be G.G[i][j] when inserted if path_2nd_time > path_1st_time: if verbose: print ' path from', u, 'is forward in time (', path_1st_time, ',', path_2nd_time, '), including' if not pathdict.has_key((u, i, j)): pathdict[(u, i, j)] = path_2nd_time else: if verbose: print ' two-path ', u, i, j, 'already present from time', pathdict[ (u, i, j)], ' not updating' else: if verbose: print ' path from', u, 'is backwards in time (', path_1st_time, ',', path_2nd_time, '), skipping' if len(vlist) > 0: if verbose: print ' ', len(vlist), ' are backward in time, skipping' for v in vlist: # i -> j -> v path_1st_time = trans[TTIME] # will be G.G[i][j] when inserted path_2nd_time = G.G[j][v] # as the transactions are ordered in time we cannot # have this a two-path ordered in time, as the 2nd is older assert (path_1st_time > path_2nd_time) else: if verbose: print # now check if the new arc i -> j would close any currently open # two-paths. If so, remove those from the dictionary of open # two paths. for v in closedTwoPaths(G, i, j): # For each v, i -> v -> j is now a two-path closed by i -> j if verbose: print ' removing ', i, v, j, ' as it is now a transitive triad' # note (i, v, j) might exist in pathdict as it was an open two-path # but NOT NECESSARILY as it might have been ignored as backward in time if pathdict.has_key((i, v, j)): pathdict.pop((i, v, j)) else: if verbose: print ' (did not exist in dict)' # add this new arc i -> j to the graph # note there is a potential inconsistency here in that this # arc might already exist in which case we update the time with # the new (later) time, however in the pathdict dictionary # we do not update times of open two-paths but keep the first # opening time. Need to decide which one really is correct. G.insertArc(trans[TSENDER], trans[TRECEIVER], trans[TTIME]) lastTime = trans[TTIME] return pathdict