From 2baf354c56ee6fd26f619d82f81d6d7fe5d526f6 Mon Sep 17 00:00:00 2001 From: borassi Date: Fri, 15 May 2015 14:27:56 +0200 Subject: [PATCH 01/12] Renamed, set CCL+FA as default, included approximation using CCL+FA. --- src/sage/graphs/hyperbolicity.pyx | 163 ++++++++++++++---------------- 1 file changed, 77 insertions(+), 86 deletions(-) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index 66a0fc1de3a..437aac87bbd 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -58,7 +58,7 @@ Hyperbolicity hyp(a, b, c, d) \leq \min_{u,v\in\{a,b,c,d\}}2dist(u,v) This result is used to reduce the number of tested 4-tuples in the naive - implementation (called 'basic+'). + implementation (called 'basic+C' or 'basic+'). - Another upper bound on `hyp(a, b, c, d)` has been proved in [CCL12]_. It is used to design an algorithm with worse case time complexity in `O(n^4)` @@ -84,13 +84,13 @@ Hyperbolicity =& dist(c, d)\\ We obtain similarly that `hyp(a, b, c, d) \leq dist(a, b)`. Consequently, - in the implementation of the 'cuts' algorithm, we ensure that `S_1` is + in the implementation of the 'CCL' algorithm, we ensure that `S_1` is larger than `S_2` and `S_3` using an ordering of the pairs by decreasing lengths. Then, we use the best value `h` found so far to stop exploration as soon as `dist(a, b) \leq h`. The worst case time complexity of this algorithm is `O(n^4)`, but it - performs very well in practice since it cuts the search space. This + performs very well in practice since it CCL the search space. This algorithm can be turned into an approximation algorithm since at any step of its execution we maintain an upper and a lower bound. We can thus stop execution as soon as a multiplicative approximation factor or an additive @@ -109,8 +109,8 @@ Hyperbolicity `(a,b)` and `(c,d)` satisfying `\delta(G) = hyp(a, b, c, d)/2`. For instance, the `n\times m`-grid has only two far-apart pairs, and so computing its hyperbolicity is immediate once the far-apart pairs are - found. The 'cuts+' algorithm improves the 'cuts' algorithm since it uses - far-apart pairs. + found. The 'CCL+FA' or 'CCL+' algorithm improves the 'CCL' algorithm since it + uses far-apart pairs. TODO: @@ -152,6 +152,7 @@ AUTHORS: - David Coudert (2012): initial version, exact and approximate algorithm, distribution, sampling - David Coudert (2014): improved exact algorithm using far-apart pairs +- Michele Borassi (2015): cleaned the code Methods @@ -276,15 +277,12 @@ cdef inline int __hyp__(unsigned short ** distances, int a, int b, int c, int d) cdef tuple __hyperbolicity_basic_algorithm__(int N, unsigned short ** distances, - use_bounds, verbose): """ Returns **twice** the hyperbolicity of a graph, and a certificate. This method implements the basic algorithm for computing the hyperbolicity - of a graph which tests all `\binom n 4` 4-tuples of vertices. It can - optionally use a cutting rule proposed in [Soto11]_. See the module's - documentation for more details. + of a graph which tests all `\binom n 4` 4-tuples of vertices. INPUTS: @@ -292,9 +290,6 @@ cdef tuple __hyperbolicity_basic_algorithm__(int N, - ``distances`` -- path distance matrix (see the distance_all_pairs module). - - ``use_bounds`` -- (default: ``True``) is boolean. Uses a cutting rule - proposed in [Soto11]_ when set to ``True``. - - ``verbose`` -- (default: ``False``) is boolean. Set to True to display some information during execution. @@ -313,49 +308,22 @@ cdef tuple __hyperbolicity_basic_algorithm__(int N, cdef list certificate h_LB = -1 - if use_bounds: - for 0 <= a < N-3: - for a < b < N-2: - - # We use the cutting rule proposed in [Soto11]_ - if 2*distances[a][b] <= h_LB: - continue - - for b < c < N-1: - - # We use the cutting rule proposed in [Soto11]_ - if 2*distances[a][c] <= h_LB or 2*distances[b][c] <= h_LB: - continue - - for c < d < N: - - # We compute the hyperbolicity of the 4-tuple - hh = __hyp__(distances, a, b, c, d) - - # We compare the value with previously known bound - if hh > h_LB: - h_LB = hh - certificate = [a, b, c, d] - - if verbose: - print 'New lower bound:', ZZ(hh)/2 - - else: - for 0 <= a < N-3: - for a < b < N-2: - for b < c < N-1: - for c < d < N: + + for 0 <= a < N-3: + for a < b < N-2: + for b < c < N-1: + for c < d < N: - # We compute the hyperbolicity of the 4-tuple - hh = __hyp__(distances, a, b, c, d) + # We compute the hyperbolicity of the 4-tuple + hh = __hyp__(distances, a, b, c, d) - # We compare the value with previously known bound - if hh > h_LB: - h_LB = hh - certificate = [a, b, c, d] + # We compare the value with previously known bound + if hh > h_LB: + h_LB = hh + certificate = [a, b, c, d] - if verbose: - print 'New lower bound:',ZZ(hh)/2 + if verbose: + print 'New lower bound:',ZZ(hh)/2 # Last, we return the computed value and the certificate if h_LB != -1: @@ -786,7 +754,7 @@ cdef tuple __hyperbolicity__(int N, return (h, certificate, h_UB if GOTO_RETURN else h) -def hyperbolicity(G, algorithm='cuts', approximation_factor=None, additive_gap=None, verbose = False): +def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap=None, verbose = False): r""" Return the hyperbolicity of the graph or an approximation of this value. @@ -805,28 +773,27 @@ def hyperbolicity(G, algorithm='cuts', approximation_factor=None, additive_gap=N - ``G`` -- a connected Graph - - ``algorithm`` -- (default: ``'cuts'``) specifies the algorithm to use - among: + - ``algorithm`` -- (default: ``'CCL+FA'``) specifies the algorithm to use among: - ``'basic'`` is an exhaustive algorithm considering all possible 4-tuples and so have time complexity in `O(n^4)`. - - ``'basic+'`` uses a cutting rule proposed in [Soto11]_ to - significantly reduce the overall computation time of the ``'basic'`` + - ``'basic+C'`` or ``'basic+'`` uses a cutting rule proposed in [Soto11]_ + to significantly reduce the overall computation time of the ``'basic'`` algorithm. - - ``'cuts'`` is an exact algorithm proposed in [CCL12_]. It considers + - ``'CCL'`` is an exact algorithm proposed in [CCL12_]. It considers the 4-tuples in an ordering allowing to cut the search space as soon as a new lower bound is found (see the module's documentation). This algorithm can be turned into a approximation algorithm. - - ``'cuts+'`` uses the notion of far-apart pairs as proposed in + - ``'CCL+FA'`` or ``'CCL+'`` uses the notion of far-apart pairs as proposed in [Soto11]_ to significantly reduce the overall computation time of - the ``'cuts'`` algorithm. + the ``'CCL'`` algorithm. - ``'dom'`` is an approximation with additive constant four. It computes the hyperbolicity of the vertices of a dominating set of - the graph. This is sometimes slower than ``'cuts'`` and sometimes + the graph. This is sometimes slower than ``'CCL'`` and sometimes faster. Try it to know if it is interesting for you. The ``additive_gap`` and ``approximation_factor`` parameters cannot be used in combination with this method and so are ignored. @@ -836,13 +803,13 @@ def hyperbolicity(G, algorithm='cuts', approximation_factor=None, additive_gap=N soon as the ratio between the upper bound and the best found solution is less than the approximation factor. When the approximation factor is 1.0, the problem is solved optimaly. This parameter is used only when the - chosen algorithm is ``'cuts'``. + chosen algorithm is ``'CCL'`` or ``'CCL+FA'``. - ``additive_gap`` -- (default: None) When sets to a positive number, the function stop computations as soon as the difference between the upper bound and the best found solution is less than additive gap. When the gap is 0.0, the problem is solved optimaly. This parameter is used only when - the chosen algorithm is ``'cuts'``. + the chosen algorithm is ``'CCL'`` or ``'CCL+FA'``. - ``verbose`` -- (default: ``False``) is a boolean set to True to display some information during execution: new upper and lower bounds, etc. @@ -866,7 +833,7 @@ def hyperbolicity(G, algorithm='cuts', approximation_factor=None, additive_gap=N sage: from sage.graphs.hyperbolicity import hyperbolicity sage: G = graphs.GridGraph([3,3]) - sage: hyperbolicity(G,algorithm='cuts') + sage: hyperbolicity(G,algorithm='CCL') (2, [(0, 0), (0, 2), (2, 0), (2, 2)], 2) sage: hyperbolicity(G,algorithm='basic') (2, [(0, 0), (0, 2), (2, 0), (2, 2)], 2) @@ -875,29 +842,44 @@ def hyperbolicity(G, algorithm='cuts', approximation_factor=None, additive_gap=N sage: from sage.graphs.hyperbolicity import hyperbolicity sage: G = graphs.PetersenGraph() - sage: hyperbolicity(G,algorithm='cuts') + sage: hyperbolicity(G,algorithm='CCL') + (1/2, [0, 1, 2, 3], 1/2) + sage: hyperbolicity(G,algorithm='CCL+') (1/2, [0, 1, 2, 3], 1/2) - sage: hyperbolicity(G,algorithm='cuts+') + sage: hyperbolicity(G,algorithm='CCL+FA') (1/2, [0, 1, 2, 3], 1/2) sage: hyperbolicity(G,algorithm='basic') (1/2, [0, 1, 2, 3], 1/2) - sage: hyperbolicity(G,algorithm='basic+') + sage: hyperbolicity(G,algorithm='basic+C') (1/2, [0, 1, 2, 3], 1/2) sage: hyperbolicity(G,algorithm='dom') (0, [0, 2, 8, 9], 1) - Asking for an approximation:: + Asking for an approximation in a grid graph:: sage: from sage.graphs.hyperbolicity import hyperbolicity sage: G = graphs.GridGraph([2,10]) - sage: hyperbolicity(G,algorithm='cuts', approximation_factor=1.5) + sage: hyperbolicity(G,algorithm='CCL', approximation_factor=1.5) (1, [(0, 0), (0, 9), (1, 0), (1, 9)], 3/2) - sage: hyperbolicity(G,algorithm='cuts', approximation_factor=4) + sage: hyperbolicity(G,algorithm='CCL+', approximation_factor=1.5) + (1, [(0, 0), (0, 9), (1, 0), (1, 9)], 1) + sage: hyperbolicity(G,algorithm='CCL', approximation_factor=4) (1, [(0, 0), (0, 9), (1, 0), (1, 9)], 4) - sage: hyperbolicity(G,algorithm='cuts', additive_gap=2) + sage: hyperbolicity(G,algorithm='CCL', additive_gap=2) (1, [(0, 0), (0, 9), (1, 0), (1, 9)], 3) sage: hyperbolicity(G,algorithm='dom') (1, [(0, 1), (0, 9), (1, 0), (1, 8)], 5) + + Asking for an approximation in a cycle graph:: + + sage: from sage.graphs.hyperbolicity import hyperbolicity + sage: G = graphs.CycleGraph(10) + sage: hyperbolicity(G,algorithm='CCL', approximation_factor=1.5) + (2, [0, 2, 5, 7], 5/2) + sage: hyperbolicity(G,algorithm='CCL+FA', approximation_factor=1.5) + (2, [0, 2, 5, 7], 5/2) + sage: hyperbolicity(G,algorithm='CCL+FA', additive_gap=1) + (2, [0, 2, 5, 7], 5/2) Comparison of results:: @@ -906,10 +888,11 @@ def hyperbolicity(G, algorithm='cuts', approximation_factor=None, additive_gap=N ... G = graphs.RandomBarabasiAlbert(100,2) ... d1,_,_ = hyperbolicity(G,algorithm='basic') ... d4,_,_ = hyperbolicity(G,algorithm='basic+') - ... d2,_,_ = hyperbolicity(G,algorithm='cuts') - ... d3,_,_ = hyperbolicity(G,algorithm='cuts+') + ... d2,_,_ = hyperbolicity(G,algorithm='CCL') + ... d3,_,_ = hyperbolicity(G,algorithm='CCL+') + ... d5,_,_ = hyperbolicity(G,algorithm='CCL+FA') ... l3,_,u3 = hyperbolicity(G,approximation_factor=2) - ... if (not d1==d2==d3==d4) or l3>d1 or u3d1 or u3= 1.0. @@ -953,10 +936,10 @@ def hyperbolicity(G, algorithm='cuts', approximation_factor=None, additive_gap=N sage: from sage.graphs.hyperbolicity import hyperbolicity sage: G = Graph() - sage: hyperbolicity(G,algorithm='cuts', additive_gap=-1) + sage: hyperbolicity(G,algorithm='CCL', additive_gap=-1) Traceback (most recent call last): ... - ValueError: The additive gap must be >= 0 when using the 'cuts' algorithm. + ValueError: The additive gap must be >= 0 when using the 'CCL' algorithm. Asking for an unknown algorithm:: @@ -966,16 +949,24 @@ def hyperbolicity(G, algorithm='cuts', approximation_factor=None, additive_gap=N Traceback (most recent call last): ... ValueError: Algorithm 'tip top' not yet implemented. Please contribute. - """ + """ + + # Abbreviations for algorithms are expanded. + if algorithm == "CCL+": + algorithm = "CCL+FA" + + if algorithm == "basic+": + algorithm = "basic+C" + if not isinstance(G,Graph): raise ValueError("The input parameter must be a Graph.") - if not algorithm in ['basic', 'basic+', 'cuts', 'cuts+', 'dom']: + if not algorithm in ['basic', 'basic+', 'basic+C', 'CCL', 'CCL+FA', 'dom']: raise ValueError("Algorithm '%s' not yet implemented. Please contribute." %(algorithm)) if approximation_factor is None: approximation_factor = 1.0 elif approximation_factor==1.0: pass - elif algorithm=='cuts': + elif algorithm in ['CCL', 'CCL+FA']: if not approximation_factor in RR or approximation_factor < 1.0: raise ValueError("The approximation factor must be >= 1.0.") else: @@ -984,14 +975,14 @@ def hyperbolicity(G, algorithm='cuts', approximation_factor=None, additive_gap=N additive_gap = 0.0 elif additive_gap==0.0: pass - elif algorithm=='cuts': + elif algorithm in ['CCL', 'CCL+FA']: if not additive_gap in RR: raise ValueError("The additive gap must be a real positive number.") elif additive_gap < 0.0: raise ValueError("The additive gap must be >= 0 when using the '%s' algorithm." %(algorithm)) else: raise ValueError("The additive_gap is ignored when using the '%s' algorithm." %(algorithm)) - + # The hyperbolicity is defined on connected graphs if not G.is_connected(): raise ValueError("The input Graph must be connected.") @@ -1072,7 +1063,7 @@ def hyperbolicity(G, algorithm='cuts', approximation_factor=None, additive_gap=N if distances == NULL: raise MemoryError("Unable to allocate array 'distances'.") - if algorithm=='cuts+': + if algorithm == 'CCL+FA': _distances_ = sage_malloc(N * N * sizeof(unsigned short)) _far_apart_pairs_ = sage_malloc(N * N * sizeof(unsigned short)) far_apart_pairs = sage_malloc(N * sizeof(unsigned short *)) @@ -1103,7 +1094,7 @@ def hyperbolicity(G, algorithm='cuts', approximation_factor=None, additive_gap=N # We call the cython function for computing the hyperbolicity with the # required parameters. - if algorithm in ['cuts', 'cuts+']: + if algorithm in ['CCL', 'CCL+FA']: sig_on() hyp, certif, hyp_UB = __hyperbolicity__(N, distances, far_apart_pairs, D, hyp, approximation_factor, 2*additive_gap, verbose) @@ -1131,10 +1122,10 @@ def hyperbolicity(G, algorithm='cuts', approximation_factor=None, additive_gap=N sig_off() hyp_UB = min( hyp+8, D) - elif algorithm in ['basic', 'basic+']: + elif algorithm in ['basic', 'basic+C']: sig_on() hyp, certif = __hyperbolicity_basic_algorithm__(N, distances, - use_bounds=(algorithm=='basic+'), + use_bounds=(algorithm=='basic+C'), verbose=verbose) sig_off() hyp_UB = hyp From 48da1e6ddcec8122602f15ff71c29981edcf6495 Mon Sep 17 00:00:00 2001 From: borassi Date: Fri, 15 May 2015 15:45:41 +0200 Subject: [PATCH 02/12] Separated computation of far apart pairs --- src/sage/graphs/hyperbolicity.pyx | 189 +++++++++++++++--------------- 1 file changed, 95 insertions(+), 94 deletions(-) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index 437aac87bbd..0ff65c7a3b0 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -50,16 +50,6 @@ Hyperbolicity Several improvements over the naive algorithm have been proposed and are implemented in the current module. - - It is shown in [Soto11]_ that `hyp(a, b, c, d)` is upper bounded by the - smallest distance between the vertices in `\{a,b,c,d\}` multiplied by 2. - - .. MATH:: - - hyp(a, b, c, d) \leq \min_{u,v\in\{a,b,c,d\}}2dist(u,v) - - This result is used to reduce the number of tested 4-tuples in the naive - implementation (called 'basic+C' or 'basic+'). - - Another upper bound on `hyp(a, b, c, d)` has been proved in [CCL12]_. It is used to design an algorithm with worse case time complexity in `O(n^4)` but that behaves much better in practice. @@ -386,7 +376,7 @@ cdef inline distances_and_far_apart_pairs(gg, # less than MAX_UNSIGNED_SHORT vertices. raise ValueError("The graph backend contains more than {} nodes and " "we cannot compute the matrix of distances/far-apart " - "pairs on something like that !".format( -1)) + "pairs on something like that!".format( -1)) # The vertices which have already been visited cdef bitset_t seen @@ -475,10 +465,94 @@ cdef inline distances_and_far_apart_pairs(gg, sage_free(waiting_list) free_short_digraph(sd) sage_free(c_far_apart) + + + +cdef inline pair** sort_pairs(int N, int D, unsigned short ** distances, + unsigned short ** far_apart_pairs, + uint32_t * nb_p, + uint32_t * nb_pairs_of_length + ): + """ + Uses quick sort to create the list of far apart pairs, in decreasing order of + distance (see the module's documentation for the definition of far-apart pairs). + + This method assumes that the arrays distances and far_apart_pairs have length + N. If not, the result will be incorrect. + """ + # pairs_of_length[d] is the list of pairs of vertices at distance d + cdef pair ** pairs_of_length = sage_malloc(sizeof(pair *)*(D+1)) + cdef unsigned short *p_far_apart + nb_p[0] = 0; + + memset(nb_pairs_of_length, 0, (D+1) * sizeof(uint32_t)) + + if far_apart_pairs == NULL: + nb_p[0] = (N*(N-1))/2 + else: + for i from 0 <= i < N: + p_far_apart = far_apart_pairs[i] + for j from i < j < N: + if far_apart_pairs[i][j]: + nb_p[0] += 1 + + if pairs_of_length != NULL: + pairs_of_length[0] = sage_malloc(sizeof(pair) * nb_p[0]) + + # temporary variable used to fill pairs_of_length + cdef uint32_t * cpt_pairs = sage_calloc(D+1, sizeof(uint32_t)) + + if (pairs_of_length == NULL or + pairs_of_length[0] == NULL or + cpt_pairs == NULL): + if pairs_of_length != NULL: + sage_free(pairs_of_length[0]) + sage_free(nb_pairs_of_length) + sage_free(pairs_of_length) + sage_free(cpt_pairs) + raise MemoryError + + # ==> Fills nb_pairs_of_length + if far_apart_pairs == NULL: + for i from 0 <= i < N: + for j from i < j < N: + nb_pairs_of_length[ distances[i][j] ] += 1 + else: + for i from 0 <= i < N: + p_far_apart = far_apart_pairs[i] + for j from i < j < N: + if p_far_apart[j]: + nb_pairs_of_length[ distances[i][j] ] += 1 + + # ==> Defines pairs_of_length[d] for all d + for i from 1 <= i <= D: + pairs_of_length[i] = pairs_of_length[i-1] + nb_pairs_of_length[i-1] + + # ==> Fills pairs_of_length[d] for all d + if far_apart_pairs == NULL: + for i from 0 <= i < N: + for j from i+1 <= j < N: + k = distances[i][j] + if k: + pairs_of_length[ k ][ cpt_pairs[ k ] ].s = i + pairs_of_length[ k ][ cpt_pairs[ k ] ].t = j + cpt_pairs[ k ] += 1 + else: + for i from 0 <= i < N: + p_far_apart = far_apart_pairs[i] + for j from i+1 <= j < N: + if p_far_apart[j]: + k = distances[i][j] + pairs_of_length[ k ][ cpt_pairs[ k ] ].s = i + pairs_of_length[ k ][ cpt_pairs[ k ] ].t = j + cpt_pairs[ k ] += 1 + sage_free(cpt_pairs) + return pairs_of_length + ###################################################################### -# Compute the hyperbolicity using a the algorithm of [CCL12]_ +# Compute the hyperbolicity using the algorithm of [CCL12]_ ###################################################################### cdef tuple __hyperbolicity__(int N, @@ -544,8 +618,7 @@ cdef tuple __hyperbolicity__(int N, cdef int a, b, c, d, h, h_UB cdef int x, y, l1, l2, S1, S2, S3 cdef list certificate = [] - cdef unsigned short *p_far_apart - cdef int nb_p = 0 + cdef uint32_t *nb_p = sage_malloc(sizeof(uint32_t)) # The total number of pairs. # Test if the distance matrix corresponds to a connected graph, i.e., if # distances from node 0 are all less or equal to N-1. @@ -553,83 +626,21 @@ cdef tuple __hyperbolicity__(int N, if distances[0][a]>=N: raise ValueError("The input graph must be connected.") - - if far_apart_pairs == NULL: - nb_p = (N*(N-1))/2 - else: - for i from 0 <= i < N: - p_far_apart = far_apart_pairs[i] - for j from i < j < N: - if far_apart_pairs[i][j]: - nb_p += 1 - # nb_pairs_of_length[d] is the number of pairs of vertices at distance d - cdef uint32_t * nb_pairs_of_length = sage_calloc(D+1,sizeof(uint32_t)) - - # pairs_of_length[d] is the list of pairs of vertices at distance d - cdef pair ** pairs_of_length = sage_malloc(sizeof(pair *)*(D+1)) - - if pairs_of_length != NULL: - pairs_of_length[0] = sage_malloc(sizeof(pair)*nb_p) + cdef uint32_t * nb_pairs_of_length = sage_malloc((D+1) * sizeof(uint32_t)) - # temporary variable used to fill pairs_of_length - cdef uint32_t * cpt_pairs = sage_calloc(D+1,sizeof(uint32_t)) - - if (nb_pairs_of_length == NULL or - pairs_of_length == NULL or - pairs_of_length[0] == NULL or - cpt_pairs == NULL): - if pairs_of_length != NULL: - sage_free(pairs_of_length[0]) - sage_free(nb_pairs_of_length) - sage_free(pairs_of_length) - sage_free(cpt_pairs) + if (nb_pairs_of_length == NULL): raise MemoryError - # ==> Fills nb_pairs_of_length - if far_apart_pairs == NULL: - for i from 0 <= i < N: - for j from i < j < N: - nb_pairs_of_length[ distances[i][j] ] += 1 - else: - for i from 0 <= i < N: - p_far_apart = far_apart_pairs[i] - for j from i < j < N: - if p_far_apart[j]: - nb_pairs_of_length[ distances[i][j] ] += 1 - - # ==> Defines pairs_of_length[d] for all d - for i from 1 <= i <= D: - pairs_of_length[i] = pairs_of_length[i-1] + nb_pairs_of_length[i-1] - - # ==> Fills pairs_of_length[d] for all d - if far_apart_pairs == NULL: - for i from 0 <= i < N: - for j from i+1 <= j < N: - k = distances[i][j] - if k: - pairs_of_length[ k ][ cpt_pairs[ k ] ].s = i - pairs_of_length[ k ][ cpt_pairs[ k ] ].t = j - cpt_pairs[ k ] += 1 - else: - for i from 0 <= i < N: - p_far_apart = far_apart_pairs[i] - for j from i+1 <= j < N: - if p_far_apart[j]: - k = distances[i][j] - pairs_of_length[ k ][ cpt_pairs[ k ] ].s = i - pairs_of_length[ k ][ cpt_pairs[ k ] ].t = j - cpt_pairs[ k ] += 1 - - sage_free(cpt_pairs) - + cdef pair ** pairs_of_length = sort_pairs(N, D, distances, far_apart_pairs, nb_p, nb_pairs_of_length) + if verbose: print "Current 2 connected component has %d vertices and diameter %d" %(N,D) if far_apart_pairs == NULL: - print "Number of pairs: %d" %(nb_p) + print "Number of pairs: %d" %(nb_p[0]) print "Repartition of pairs:", [ (i, nb_pairs_of_length[i]) for i in range(1, D+1) if nb_pairs_of_length[i]>0] else: - print "Number of far-apart pairs: %d\t(%d pairs in total)" %(nb_p, binomial(N, 2)) + print "Number of far-apart pairs: %d\t(%d pairs in total)" %(nb_p[0], binomial(N, 2)) print "Repartition of far-apart pairs:", [ (i, nb_pairs_of_length[i]) for i in range(1, D+1) if nb_pairs_of_length[i]>0] @@ -778,10 +789,6 @@ def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap - ``'basic'`` is an exhaustive algorithm considering all possible 4-tuples and so have time complexity in `O(n^4)`. - - ``'basic+C'`` or ``'basic+'`` uses a cutting rule proposed in [Soto11]_ - to significantly reduce the overall computation time of the ``'basic'`` - algorithm. - - ``'CCL'`` is an exact algorithm proposed in [CCL12_]. It considers the 4-tuples in an ordering allowing to cut the search space as soon as a new lower bound is found (see the module's documentation). This @@ -850,8 +857,6 @@ def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap (1/2, [0, 1, 2, 3], 1/2) sage: hyperbolicity(G,algorithm='basic') (1/2, [0, 1, 2, 3], 1/2) - sage: hyperbolicity(G,algorithm='basic+C') - (1/2, [0, 1, 2, 3], 1/2) sage: hyperbolicity(G,algorithm='dom') (0, [0, 2, 8, 9], 1) @@ -954,13 +959,10 @@ def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap # Abbreviations for algorithms are expanded. if algorithm == "CCL+": algorithm = "CCL+FA" - - if algorithm == "basic+": - algorithm = "basic+C" if not isinstance(G,Graph): raise ValueError("The input parameter must be a Graph.") - if not algorithm in ['basic', 'basic+', 'basic+C', 'CCL', 'CCL+FA', 'dom']: + if not algorithm in ['basic', 'CCL', 'CCL+FA', 'dom']: raise ValueError("Algorithm '%s' not yet implemented. Please contribute." %(algorithm)) if approximation_factor is None: approximation_factor = 1.0 @@ -1122,10 +1124,9 @@ def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap sig_off() hyp_UB = min( hyp+8, D) - elif algorithm in ['basic', 'basic+C']: + elif algorithm == 'basic': sig_on() hyp, certif = __hyperbolicity_basic_algorithm__(N, distances, - use_bounds=(algorithm=='basic+C'), verbose=verbose) sig_off() hyp_UB = hyp From 39a3f55e16c9fee162318493de730364c97212b1 Mon Sep 17 00:00:00 2001 From: borassi Date: Fri, 15 May 2015 16:36:50 +0200 Subject: [PATCH 03/12] Before renewal of hyperbolicity --- src/sage/graphs/hyperbolicity.pyx | 74 ++++++++++++++++++------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index 0ff65c7a3b0..c260e3aadeb 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -468,33 +468,57 @@ cdef inline distances_and_far_apart_pairs(gg, -cdef inline pair** sort_pairs(int N, int D, unsigned short ** distances, - unsigned short ** far_apart_pairs, - uint32_t * nb_p, - uint32_t * nb_pairs_of_length - ): +cdef inline pair** sort_pairs(int N, + int D, + unsigned short ** values, + unsigned short ** to_include, + uint32_t * nb_p, + uint32_t * nb_pairs_of_length + ): """ - Uses quick sort to create the list of far apart pairs, in decreasing order of - distance (see the module's documentation for the definition of far-apart pairs). + Uses quick sort to create a list of unordered pairs (i,j), in decreasing + order of values(i,j). If to_include[i][j] = 0, the pair is ignored. We + assume N and D to be correct with respect to the arrays values and to_include. - This method assumes that the arrays distances and far_apart_pairs have length - N. If not, the result will be incorrect. + INPUT: + + - ``N`` -- the range of i and j (that is, the square root of the number + of pairs to be sorted); + + - ``D`` -- the maximum value of an element; + + - ``values`` -- the maximum value of an element; + + - ``to_include`` -- an array such that to_include[i][j] contains "1" if pair + (i,j) should be included, "0" otherwise; + + OUTPUT: + + - ``nb_p`` -- the number of pairs to be included; + + - ``nb_pairs_of_length`` -- an array containing in position k the number of + pairs (i,j) that are included and such that values[i][j] = k. """ # pairs_of_length[d] is the list of pairs of vertices at distance d cdef pair ** pairs_of_length = sage_malloc(sizeof(pair *)*(D+1)) - cdef unsigned short *p_far_apart + cdef unsigned short *p_to_include nb_p[0] = 0; + # fills nb_pairs_of_length and nb_p memset(nb_pairs_of_length, 0, (D+1) * sizeof(uint32_t)) - if far_apart_pairs == NULL: + if to_include == NULL: nb_p[0] = (N*(N-1))/2 + for i from 0 <= i < N: + for j from i < j < N: + nb_pairs_of_length[ values[i][j] ] += 1 else: for i from 0 <= i < N: - p_far_apart = far_apart_pairs[i] + p_to_include = to_include[i] for j from i < j < N: - if far_apart_pairs[i][j]: + if p_to_include[j]: nb_p[0] += 1 + nb_pairs_of_length[ values[i][j] ] += 1 if pairs_of_length != NULL: pairs_of_length[0] = sage_malloc(sizeof(pair) * nb_p[0]) @@ -512,37 +536,25 @@ cdef inline pair** sort_pairs(int N, int D, unsigned short ** distances, sage_free(cpt_pairs) raise MemoryError - # ==> Fills nb_pairs_of_length - if far_apart_pairs == NULL: - for i from 0 <= i < N: - for j from i < j < N: - nb_pairs_of_length[ distances[i][j] ] += 1 - else: - for i from 0 <= i < N: - p_far_apart = far_apart_pairs[i] - for j from i < j < N: - if p_far_apart[j]: - nb_pairs_of_length[ distances[i][j] ] += 1 - # ==> Defines pairs_of_length[d] for all d for i from 1 <= i <= D: pairs_of_length[i] = pairs_of_length[i-1] + nb_pairs_of_length[i-1] # ==> Fills pairs_of_length[d] for all d - if far_apart_pairs == NULL: + if to_include == NULL: for i from 0 <= i < N: for j from i+1 <= j < N: - k = distances[i][j] + k = values[i][j] if k: pairs_of_length[ k ][ cpt_pairs[ k ] ].s = i pairs_of_length[ k ][ cpt_pairs[ k ] ].t = j cpt_pairs[ k ] += 1 else: for i from 0 <= i < N: - p_far_apart = far_apart_pairs[i] + p_to_include = to_include[i] for j from i+1 <= j < N: - if p_far_apart[j]: - k = distances[i][j] + if p_to_include[j]: + k = values[i][j] pairs_of_length[ k ][ cpt_pairs[ k ] ].s = i pairs_of_length[ k ][ cpt_pairs[ k ] ].t = j cpt_pairs[ k ] += 1 @@ -555,7 +567,7 @@ cdef inline pair** sort_pairs(int N, int D, unsigned short ** distances, # Compute the hyperbolicity using the algorithm of [CCL12]_ ###################################################################### -cdef tuple __hyperbolicity__(int N, +cdef tuple __hyperbolicity_old__(int N, unsigned short ** distances, unsigned short ** far_apart_pairs, int D, From b7cecc1b4e41eb973d517668e2d26522af94474d Mon Sep 17 00:00:00 2001 From: borassi Date: Mon, 18 May 2015 10:46:50 +0200 Subject: [PATCH 04/12] Cleaned hyperbolicity --- src/sage/graphs/hyperbolicity.pyx | 33 +++++++++++++++++++------------ 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index c260e3aadeb..fdf6b20ea54 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -172,8 +172,8 @@ include "sage/data_structures/bitset.pxi" # Defining a pair of vertices as a C struct ctypedef struct pair: - uint16_t s - uint16_t t + uint32_t s + uint32_t t ###################################################################### @@ -468,17 +468,20 @@ cdef inline distances_and_far_apart_pairs(gg, -cdef inline pair** sort_pairs(int N, - int D, +cdef inline pair** sort_pairs(uint32_t N, + uint16_t D, unsigned short ** values, unsigned short ** to_include, uint32_t * nb_p, uint32_t * nb_pairs_of_length ): """ - Uses quick sort to create a list of unordered pairs (i,j), in decreasing + Uses quick sort to create a list of unordered pairs {i,j}, in decreasing order of values(i,j). If to_include[i][j] = 0, the pair is ignored. We - assume N and D to be correct with respect to the arrays values and to_include. + assume N and D to be correct with respect to the arrays values and to_include, + that values and to_include are symmetric (that is, + values[i][j] = values[j][i] and to_include[i][j] = to_include[j][i], and that + nb_p, nb_pairs_of_length are already allocated. INPUT: @@ -487,10 +490,11 @@ cdef inline pair** sort_pairs(int N, - ``D`` -- the maximum value of an element; - - ``values`` -- the maximum value of an element; + - ``values`` -- an array containing in position (i,j) the value of the pair + (i,j); - ``to_include`` -- an array such that to_include[i][j] contains "1" if pair - (i,j) should be included, "0" otherwise; + (i,j) should be included, "0" otherwise. If NULL, all elements are included; OUTPUT: @@ -498,6 +502,10 @@ cdef inline pair** sort_pairs(int N, - ``nb_pairs_of_length`` -- an array containing in position k the number of pairs (i,j) that are included and such that values[i][j] = k. + + - ``pairs_of_length`` -- this function returns this array, containing in + position k a pointer to the first included pair (i,j) such that + values[i][j] = k. """ # pairs_of_length[d] is the list of pairs of vertices at distance d cdef pair ** pairs_of_length = sage_malloc(sizeof(pair *)*(D+1)) @@ -567,7 +575,7 @@ cdef inline pair** sort_pairs(int N, # Compute the hyperbolicity using the algorithm of [CCL12]_ ###################################################################### -cdef tuple __hyperbolicity_old__(int N, +cdef tuple __hyperbolicity__(int N, unsigned short ** distances, unsigned short ** far_apart_pairs, int D, @@ -779,7 +787,7 @@ cdef tuple __hyperbolicity_old__(int N, def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap=None, verbose = False): r""" - Return the hyperbolicity of the graph or an approximation of this value. + Returns the hyperbolicity of the graph or an approximation of this value. The hyperbolicity of a graph has been defined by Gromov [Gromov87]_ as follows: Let `a, b, c, d` be vertices of the graph, let `S_1 = dist(a, b) + @@ -904,12 +912,11 @@ def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap sage: for i in xrange(10): # long time ... G = graphs.RandomBarabasiAlbert(100,2) ... d1,_,_ = hyperbolicity(G,algorithm='basic') - ... d4,_,_ = hyperbolicity(G,algorithm='basic+') ... d2,_,_ = hyperbolicity(G,algorithm='CCL') ... d3,_,_ = hyperbolicity(G,algorithm='CCL+') - ... d5,_,_ = hyperbolicity(G,algorithm='CCL+FA') + ... d4,_,_ = hyperbolicity(G,algorithm='CCL+FA') ... l3,_,u3 = hyperbolicity(G,approximation_factor=2) - ... if (not d1==d2==d3==d4==d5) or l3>d1 or u3d1 or u3 Date: Mon, 18 May 2015 12:48:25 +0200 Subject: [PATCH 05/12] Cleaned hyperbolicity (2) --- src/sage/graphs/hyperbolicity.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index fdf6b20ea54..a34675f8f40 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -1344,7 +1344,7 @@ def hyperbolicity_distribution(G, algorithm='sampling', sampling_size=10**6): TESTS: - Giving anythin else than a Graph:: + Giving anything else than a Graph:: sage: from sage.graphs.hyperbolicity import hyperbolicity_distribution sage: hyperbolicity_distribution([]) From 66099f5b908b90ee4c2340c6a9cba32134ebf616 Mon Sep 17 00:00:00 2001 From: borassi Date: Mon, 18 May 2015 19:49:46 +0200 Subject: [PATCH 06/12] Temp --- src/sage/graphs/hyperbolicity.pyx | 175 ++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index a34675f8f40..152084f8978 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -785,6 +785,181 @@ cdef tuple __hyperbolicity__(int N, return (h, certificate, h_UB if GOTO_RETURN else h) +###################################################################### +# Compute the hyperbolicity using the algorithm of [BCC12]_ +###################################################################### + +cdef tuple __hyper_new__(int N, + unsigned short ** distances, + unsigned short ** far_apart_pairs, + int D, + int h_LB, + float approximation_factor, + float additive_gap, + verbose = False): + """ + Return the hyperbolicity of a graph. + + This method implements the exact and the approximate algorithms proposed in + [CCL12]_. See the module's documentation for more details. + + This method assumes that the graph under consideration is connected. + + INPUTS: + + - ``N`` -- number of vertices of the graph + + - ``distances`` -- path distance matrix + + - ``far_apart_pairs`` -- 0/1 matrix of far-apart pairs. Pair ``(i,j)`` is + far-apart if ``far_apart_pairs[i][j]\neq 0``. + + - ``D`` -- diameter of the graph + + - ``h_LB`` -- lower bound on the hyperbolicity + + - ``approximation_factor`` -- When the approximation factor is set to some + value larger than 1.0, the function stop computations as soon as the ratio + between the upper bound and the best found solution is less than the + approximation factor. When the approximation factor is 1.0, the problem is + solved optimaly. + + - ``additive_gap`` -- When sets to a positive number, the function stop + computations as soon as the difference between the upper bound and the + best found solution is less than additive gap. When the gap is 0.0, the + problem is solved optimaly. + + - ``verbose`` -- (default: ``False``) is boolean set to ``True`` to display + some information during execution + + OUTPUTS: + + This function returns a tuple ( h, certificate, h_UB ), where: + + - ``h`` -- is an integer. When 4-tuples with hyperbolicity larger or equal + to `h_LB are found, h is the maximum computed value and so twice the + hyperbolicity of the graph. If no such 4-tuple is found, it returns -1. + + - ``certificate`` -- is a list of vertices. When 4-tuples with hyperbolicity + larger that h_LB are found, certificate is the list of the 4 vertices for + which the maximum value (and so the hyperbolicity of the graph) has been + computed. If no such 4-tuple is found, it returns the empty list []. + + - ``h_UB`` -- is an integer equal to the proven upper bound for `h`. When + ``h == h_UB``, the returned solution is optimal. + """ + cdef int hh # can get negative value + cdef int a, b, c, d, h, h_UB + cdef int x, y, l1, l2, S1, S2, S3 + cdef list certificate = [] + cdef uint32_t *nb_p = sage_malloc(sizeof(uint32_t)) # The total number of pairs. + + # Test if the distance matrix corresponds to a connected graph, i.e., if + # distances from node 0 are all less or equal to N-1. + for a from 0 <= a < N: + if distances[0][a]>=N: + raise ValueError("The input graph must be connected.") + + # nb_pairs_of_length[d] is the number of pairs of vertices at distance d + cdef uint32_t * nb_pairs_of_length = sage_malloc((D+1) * sizeof(uint32_t)) + + if (nb_pairs_of_length == NULL): + raise MemoryError + + cdef pair ** pairs_of_length = sort_pairs(N, D, distances, far_apart_pairs, nb_p, nb_pairs_of_length) + + if verbose: + print "Current 2 connected component has %d vertices and diameter %d" %(N,D) + if far_apart_pairs == NULL: + print "Number of pairs: %d" %(nb_p[0]) + print "Repartition of pairs:", [ (i, nb_pairs_of_length[i]) for i in range(1, D+1) if nb_pairs_of_length[i]>0] + else: + print "Number of far-apart pairs: %d\t(%d pairs in total)" %(nb_p[0], binomial(N, 2)) + print "Repartition of far-apart pairs:", [ (i, nb_pairs_of_length[i]) for i in range(1, D+1) if nb_pairs_of_length[i]>0] + + + approximation_factor = min(approximation_factor, D) + additive_gap = min(additive_gap, D) + + # We use some short-cut variables for efficiency + cdef pair * pair_ab + cdef pair * pair_cd + cdef uint32_t ** mate + cdef uint32_t * cont_mate + h_UB = D + + for pair_ab from pairs_of_length[0] + 1 <= pair_ab < pairs_of_length[D]: + a = pair_ab.s + b = pair_ab.t + + dist_a = distances[a] + dist_b = distances[b] + h_UB = dist_a[b] + + # Termination if required approximation is found + if certificate and ( (h_UB <= h*approximation_factor) or (h_UB-h <= additive_gap) ): + GOTO_RETURN = 1 + break + + # If we cannot improve further, we stop + # + # See the module's documentation for a proof that this cut is + # valid. Remember that the triples are sorted in a specific order. + if h_UB <= h: + h_UB = h + break + + for pair_cd from pairs_of_length[0] <= pair_cd < pair_ab: + c = pair_cd.s + d = pair_cd.t + S2 = dist_a[c] + dist_b[d] + S3 = dist_a[d] + dist_b[c] + + if S2 > S3: + hh = S1 - S2 + else: + hh = S1 - S3 + + if h < hh or not certificate: + # We update current bound on the hyperbolicity and the + # search space. + # + # Note that if hh==0, we first make sure that a,b,c,d are + # all distinct and are a valid certificate. + if hh > 0 or not (a == c or a == d or b == c or b == d): + h = hh + certificate = [a, b, c, d] + + if verbose: + print "New lower bound:", ZZ(hh) / 2 + + # If we cannot improve further, we stop + if l2 <= h: + GOTO_RETURN = 1 + h_UB = h + break + + # Termination if required approximation is found + if (h_UB <= h * approximation_factor) or (h_UB - h <= additive_gap): + GOTO_RETURN = 1 + break + + + + # We now free the memory + sage_free(nb_pairs_of_length) + sage_free(pairs_of_length[0]) + sage_free(pairs_of_length) + + # Last, we return the computed value and the certificate + if len(certificate) == 0: + return ( -1, [], h_UB ) + else: + # When using far-apart pairs, the loops may end before improving the + # upper-bound + return (h, certificate, h_UB if GOTO_RETURN else h) + + def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap=None, verbose = False): r""" Returns the hyperbolicity of the graph or an approximation of this value. From 49ce5317dedbf50b6e556576ff6cfd1c81a80a61 Mon Sep 17 00:00:00 2001 From: borassi Date: Tue, 19 May 2015 14:57:19 +0200 Subject: [PATCH 07/12] Revert --- src/sage/graphs/hyperbolicity.pyx | 175 ------------------------------ 1 file changed, 175 deletions(-) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index 152084f8978..a34675f8f40 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -785,181 +785,6 @@ cdef tuple __hyperbolicity__(int N, return (h, certificate, h_UB if GOTO_RETURN else h) -###################################################################### -# Compute the hyperbolicity using the algorithm of [BCC12]_ -###################################################################### - -cdef tuple __hyper_new__(int N, - unsigned short ** distances, - unsigned short ** far_apart_pairs, - int D, - int h_LB, - float approximation_factor, - float additive_gap, - verbose = False): - """ - Return the hyperbolicity of a graph. - - This method implements the exact and the approximate algorithms proposed in - [CCL12]_. See the module's documentation for more details. - - This method assumes that the graph under consideration is connected. - - INPUTS: - - - ``N`` -- number of vertices of the graph - - - ``distances`` -- path distance matrix - - - ``far_apart_pairs`` -- 0/1 matrix of far-apart pairs. Pair ``(i,j)`` is - far-apart if ``far_apart_pairs[i][j]\neq 0``. - - - ``D`` -- diameter of the graph - - - ``h_LB`` -- lower bound on the hyperbolicity - - - ``approximation_factor`` -- When the approximation factor is set to some - value larger than 1.0, the function stop computations as soon as the ratio - between the upper bound and the best found solution is less than the - approximation factor. When the approximation factor is 1.0, the problem is - solved optimaly. - - - ``additive_gap`` -- When sets to a positive number, the function stop - computations as soon as the difference between the upper bound and the - best found solution is less than additive gap. When the gap is 0.0, the - problem is solved optimaly. - - - ``verbose`` -- (default: ``False``) is boolean set to ``True`` to display - some information during execution - - OUTPUTS: - - This function returns a tuple ( h, certificate, h_UB ), where: - - - ``h`` -- is an integer. When 4-tuples with hyperbolicity larger or equal - to `h_LB are found, h is the maximum computed value and so twice the - hyperbolicity of the graph. If no such 4-tuple is found, it returns -1. - - - ``certificate`` -- is a list of vertices. When 4-tuples with hyperbolicity - larger that h_LB are found, certificate is the list of the 4 vertices for - which the maximum value (and so the hyperbolicity of the graph) has been - computed. If no such 4-tuple is found, it returns the empty list []. - - - ``h_UB`` -- is an integer equal to the proven upper bound for `h`. When - ``h == h_UB``, the returned solution is optimal. - """ - cdef int hh # can get negative value - cdef int a, b, c, d, h, h_UB - cdef int x, y, l1, l2, S1, S2, S3 - cdef list certificate = [] - cdef uint32_t *nb_p = sage_malloc(sizeof(uint32_t)) # The total number of pairs. - - # Test if the distance matrix corresponds to a connected graph, i.e., if - # distances from node 0 are all less or equal to N-1. - for a from 0 <= a < N: - if distances[0][a]>=N: - raise ValueError("The input graph must be connected.") - - # nb_pairs_of_length[d] is the number of pairs of vertices at distance d - cdef uint32_t * nb_pairs_of_length = sage_malloc((D+1) * sizeof(uint32_t)) - - if (nb_pairs_of_length == NULL): - raise MemoryError - - cdef pair ** pairs_of_length = sort_pairs(N, D, distances, far_apart_pairs, nb_p, nb_pairs_of_length) - - if verbose: - print "Current 2 connected component has %d vertices and diameter %d" %(N,D) - if far_apart_pairs == NULL: - print "Number of pairs: %d" %(nb_p[0]) - print "Repartition of pairs:", [ (i, nb_pairs_of_length[i]) for i in range(1, D+1) if nb_pairs_of_length[i]>0] - else: - print "Number of far-apart pairs: %d\t(%d pairs in total)" %(nb_p[0], binomial(N, 2)) - print "Repartition of far-apart pairs:", [ (i, nb_pairs_of_length[i]) for i in range(1, D+1) if nb_pairs_of_length[i]>0] - - - approximation_factor = min(approximation_factor, D) - additive_gap = min(additive_gap, D) - - # We use some short-cut variables for efficiency - cdef pair * pair_ab - cdef pair * pair_cd - cdef uint32_t ** mate - cdef uint32_t * cont_mate - h_UB = D - - for pair_ab from pairs_of_length[0] + 1 <= pair_ab < pairs_of_length[D]: - a = pair_ab.s - b = pair_ab.t - - dist_a = distances[a] - dist_b = distances[b] - h_UB = dist_a[b] - - # Termination if required approximation is found - if certificate and ( (h_UB <= h*approximation_factor) or (h_UB-h <= additive_gap) ): - GOTO_RETURN = 1 - break - - # If we cannot improve further, we stop - # - # See the module's documentation for a proof that this cut is - # valid. Remember that the triples are sorted in a specific order. - if h_UB <= h: - h_UB = h - break - - for pair_cd from pairs_of_length[0] <= pair_cd < pair_ab: - c = pair_cd.s - d = pair_cd.t - S2 = dist_a[c] + dist_b[d] - S3 = dist_a[d] + dist_b[c] - - if S2 > S3: - hh = S1 - S2 - else: - hh = S1 - S3 - - if h < hh or not certificate: - # We update current bound on the hyperbolicity and the - # search space. - # - # Note that if hh==0, we first make sure that a,b,c,d are - # all distinct and are a valid certificate. - if hh > 0 or not (a == c or a == d or b == c or b == d): - h = hh - certificate = [a, b, c, d] - - if verbose: - print "New lower bound:", ZZ(hh) / 2 - - # If we cannot improve further, we stop - if l2 <= h: - GOTO_RETURN = 1 - h_UB = h - break - - # Termination if required approximation is found - if (h_UB <= h * approximation_factor) or (h_UB - h <= additive_gap): - GOTO_RETURN = 1 - break - - - - # We now free the memory - sage_free(nb_pairs_of_length) - sage_free(pairs_of_length[0]) - sage_free(pairs_of_length) - - # Last, we return the computed value and the certificate - if len(certificate) == 0: - return ( -1, [], h_UB ) - else: - # When using far-apart pairs, the loops may end before improving the - # upper-bound - return (h, certificate, h_UB if GOTO_RETURN else h) - - def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap=None, verbose = False): r""" Returns the hyperbolicity of the graph or an approximation of this value. From 177c42e864833025f19de9ab4009daa1bd9b3e1b Mon Sep 17 00:00:00 2001 From: borassi Date: Tue, 19 May 2015 17:29:14 +0200 Subject: [PATCH 08/12] After David's suggestions --- src/sage/graphs/hyperbolicity.pyx | 195 +++++++++++++++++------------- 1 file changed, 111 insertions(+), 84 deletions(-) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index a34675f8f40..64dd46d5748 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -10,7 +10,7 @@ Hyperbolicity defined by .. MATH:: - +Z S_1 = dist(a, b) + dist(b, c)\\ S_2 = dist(a, c) + dist(b, d)\\ S_3 = dist(a, d) + dist(b, c)\\ @@ -25,7 +25,8 @@ Hyperbolicity \delta(G) = \frac{1}{2}\max_{a,b,c,d\in V(G)}hyp(a, b, c, d) - (note that `hyp(a, b, c, d)=0` whenever two elements among `a,b,c,d` are equal) + (note that `hyp(a, b, c, d)=0` whenever two elements among `a,b,c,d` are + equal) **Some known results** : @@ -51,8 +52,8 @@ Hyperbolicity implemented in the current module. - Another upper bound on `hyp(a, b, c, d)` has been proved in [CCL12]_. It - is used to design an algorithm with worse case time complexity in `O(n^4)` - but that behaves much better in practice. + is used to design an algorithm with worse case time complexity in + `O(n^4)` but that behaves much better in practice. Assume that `S_1 = dist(a, b) + dist(c, d)` is the largest sum among `S_1,S_2,S_3`. We have @@ -99,8 +100,8 @@ Hyperbolicity `(a,b)` and `(c,d)` satisfying `\delta(G) = hyp(a, b, c, d)/2`. For instance, the `n\times m`-grid has only two far-apart pairs, and so computing its hyperbolicity is immediate once the far-apart pairs are - found. The 'CCL+FA' or 'CCL+' algorithm improves the 'CCL' algorithm since it - uses far-apart pairs. + found. The 'CCL+FA' or 'CCL+' algorithm improves the 'CCL' algorithm + since it uses far-apart pairs. TODO: @@ -122,9 +123,9 @@ At Python level : REFERENCES: -.. [CCL12] N. Cohen, D. Coudert, and A. Lancin. Exact and approximate algorithms - for computing the hyperbolicity of large-scale graphs. Research Report - RR-8074, Sep. 2012. [``_]. +.. [CCL12] N. Cohen, D. Coudert, and A. Lancin. Exact and approximate + algorithms for computing the hyperbolicity of large-scale graphs. + Research Report RR-8074, Sep. 2012. [``_]. .. [FIV12] H. Fournier, A. Ismail, and A. Vigneron. Computing the Gromov hyperbolicity of a discrete metric space. ArXiv, Tech. Rep. arXiv:1210.3323, @@ -133,9 +134,9 @@ REFERENCES: .. [Gromov87] M. Gromov. Hyperbolic groups. Essays in Group Theory, 8:75--263, 1987. -.. [Soto11] M. A. Soto Gomez. 2011. Quelques proprietes topologiques des graphes - et applications a internet et aux reseaux. Ph.D. Dissertation. Univ. Paris - Diderot (Paris 7). +.. [Soto11] M. A. Soto Gomez. 2011. Quelques proprietes topologiques des + graphes et applications a internet et aux reseaux. Ph.D. Dissertation. Univ. + Paris Diderot (Paris 7). AUTHORS: @@ -164,6 +165,10 @@ from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR from sage.functions.other import floor from sage.data_structures.bitset import Bitset +from sage.ext.memory cimport check_allocarray, check_calloc +from sage.graphs.base.static_sparse_graph cimport short_digraph +from sage.graphs.base.static_sparse_graph cimport init_short_digraph +from sage.graphs.base.static_sparse_graph cimport free_short_digraph from libc.stdint cimport uint16_t, uint32_t, uint64_t include "sage/ext/interrupt.pxi" include "sage/ext/stdsage.pxi" @@ -188,9 +193,9 @@ def _my_subgraph(G, vertices, relabel=False, return_map=False): ignored as well as any other decoration of the graph (vertex position, etc.). - If ``relabel`` is ``True``, the vertices of the new graph are relabeled with - integers in the range '0\cdots |vertices|-1'. The relabeling map is returned - if ``return_map`` is also ``True``. + If ``relabel`` is ``True``, the vertices of the new graph are relabeled + with integers in the range '0\cdots |vertices|-1'. The relabeling map is + returned if ``return_map`` is also ``True``. TESTS: @@ -272,13 +277,15 @@ cdef tuple __hyperbolicity_basic_algorithm__(int N, Returns **twice** the hyperbolicity of a graph, and a certificate. This method implements the basic algorithm for computing the hyperbolicity - of a graph which tests all `\binom n 4` 4-tuples of vertices. + of a graph which tests all 4-tuples of vertices not satisfying a cutting + rule proposed in [Soto11]_. INPUTS: - ``N`` -- number of vertices of the graph. - - ``distances`` -- path distance matrix (see the distance_all_pairs module). + - ``distances`` -- path distance matrix (see the distance_all_pairs + module). - ``verbose`` -- (default: ``False``) is boolean. Set to True to display some information during execution. @@ -287,8 +294,9 @@ cdef tuple __hyperbolicity_basic_algorithm__(int N, This function returns a tuple ( h, certificate ), where: - - ``h`` -- the maximum computed value over all 4-tuples, and so is twice the - hyperbolicity of the graph. If no such 4-tuple is found, -1 is returned. + - ``h`` -- the maximum computed value over all 4-tuples, and so is twice + the hyperbolicity of the graph. If no such 4-tuple is found, -1 is + returned. - ``certificate`` -- 4-tuple of vertices maximizing the value `h`. If no such 4-tuple is found, the empty list [] is returned. @@ -300,20 +308,30 @@ cdef tuple __hyperbolicity_basic_algorithm__(int N, h_LB = -1 for 0 <= a < N-3: - for a < b < N-2: - for b < c < N-1: - for c < d < N: + for a < b < N-2: - # We compute the hyperbolicity of the 4-tuple - hh = __hyp__(distances, a, b, c, d) + # We use the cutting rule proposed in [Soto11]_ + if 2*distances[a][b] <= h_LB: + continue - # We compare the value with previously known bound - if hh > h_LB: - h_LB = hh - certificate = [a, b, c, d] + for b < c < N-1: - if verbose: - print 'New lower bound:',ZZ(hh)/2 + # We use the cutting rule proposed in [Soto11]_ + if 2*distances[a][c] <= h_LB or 2*distances[b][c] <= h_LB: + continue + + for c < d < N: + + # We compute the hyperbolicity of the 4-tuple + hh = __hyp__(distances, a, b, c, d) + + # We compare the value with previously known bound + if hh > h_LB: + h_LB = hh + certificate = [a, b, c, d] + + if verbose: + print 'New lower bound:', ZZ(hh)/2 # Last, we return the computed value and the certificate if h_LB != -1: @@ -348,8 +366,6 @@ def _greedy_dominating_set(H, verbose=False): # Distances and far-apart pairs ###################################################################### -from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph - cdef inline distances_and_far_apart_pairs(gg, unsigned short * distances, unsigned short * far_apart_pairs): @@ -360,7 +376,8 @@ cdef inline distances_and_far_apart_pairs(gg, This method assumes that: - - The input graph gg is connected. If not, the result will be incorrect. + - The input graph gg is connected. If not, the result will be + incorrect. - The arrays distances and far_apart_pairs have already been allocated with size `n^2`. @@ -376,15 +393,16 @@ cdef inline distances_and_far_apart_pairs(gg, # less than MAX_UNSIGNED_SHORT vertices. raise ValueError("The graph backend contains more than {} nodes and " "we cannot compute the matrix of distances/far-apart " - "pairs on something like that!".format( -1)) + "pairs on something" + "like that!".format( -1)) # The vertices which have already been visited cdef bitset_t seen bitset_init(seen, n) # The list of waiting vertices - cdef uint32_t * waiting_list = sage_malloc(n * sizeof(uint32_t)) - cdef unsigned short ** c_far_apart = sage_malloc(n * sizeof(unsigned short*)) + cdef uint32_t * waiting_list = check_allocarray(n, sizeof(uint32_t)) + cdef unsigned short ** c_far_apart = check_allocarray(n, sizeof(unsigned short*)) if waiting_list == NULL or c_far_apart == NULL: bitset_free(seen) sage_free(waiting_list) @@ -476,12 +494,12 @@ cdef inline pair** sort_pairs(uint32_t N, uint32_t * nb_pairs_of_length ): """ - Uses quick sort to create a list of unordered pairs {i,j}, in decreasing + Uses counting sort to create a list of unordered pairs {i,j}, in decreasing order of values(i,j). If to_include[i][j] = 0, the pair is ignored. We - assume N and D to be correct with respect to the arrays values and to_include, - that values and to_include are symmetric (that is, - values[i][j] = values[j][i] and to_include[i][j] = to_include[j][i], and that - nb_p, nb_pairs_of_length are already allocated. + assume N and D to be correct with respect to the arrays values and + to_include, that values and to_include are symmetric (that is, + values[i][j] = values[j][i] and to_include[i][j] = to_include[j][i], and + that nb_p, nb_pairs_of_length are already allocated. INPUT: @@ -490,25 +508,26 @@ cdef inline pair** sort_pairs(uint32_t N, - ``D`` -- the maximum value of an element; - - ``values`` -- an array containing in position (i,j) the value of the pair - (i,j); + - ``values`` -- an array containing in position (i,j) the value of the + pair (i,j); - - ``to_include`` -- an array such that to_include[i][j] contains "1" if pair - (i,j) should be included, "0" otherwise. If NULL, all elements are included; + - ``to_include`` -- an array such that to_include[i][j] contains "1" if + pair (i,j) should be included, "0" otherwise. If NULL, all elements are + included; OUTPUT: - ``nb_p`` -- the number of pairs to be included; - - ``nb_pairs_of_length`` -- an array containing in position k the number of - pairs (i,j) that are included and such that values[i][j] = k. + - ``nb_pairs_of_length`` -- an array containing in position k the number + of pairs (i,j) that are included and such that values[i][j] = k. - ``pairs_of_length`` -- this function returns this array, containing in position k a pointer to the first included pair (i,j) such that values[i][j] = k. """ # pairs_of_length[d] is the list of pairs of vertices at distance d - cdef pair ** pairs_of_length = sage_malloc(sizeof(pair *)*(D+1)) + cdef pair ** pairs_of_length = check_allocarray(D+1, sizeof(pair *)) cdef unsigned short *p_to_include nb_p[0] = 0; @@ -529,10 +548,10 @@ cdef inline pair** sort_pairs(uint32_t N, nb_pairs_of_length[ values[i][j] ] += 1 if pairs_of_length != NULL: - pairs_of_length[0] = sage_malloc(sizeof(pair) * nb_p[0]) + pairs_of_length[0] = check_allocarray(nb_p[0], sizeof(pair)) # temporary variable used to fill pairs_of_length - cdef uint32_t * cpt_pairs = sage_calloc(D+1, sizeof(uint32_t)) + cdef uint32_t * cpt_pairs = check_calloc(D+1, sizeof(uint32_t)) if (pairs_of_length == NULL or pairs_of_length[0] == NULL or @@ -605,10 +624,10 @@ cdef tuple __hyperbolicity__(int N, - ``h_LB`` -- lower bound on the hyperbolicity - ``approximation_factor`` -- When the approximation factor is set to some - value larger than 1.0, the function stop computations as soon as the ratio - between the upper bound and the best found solution is less than the - approximation factor. When the approximation factor is 1.0, the problem is - solved optimaly. + value larger than 1.0, the function stop computations as soon as the + ratio between the upper bound and the best found solution is less than + the approximation factor. When the approximation factor is 1.0, the + problem is solved optimaly. - ``additive_gap`` -- When sets to a positive number, the function stop computations as soon as the difference between the upper bound and the @@ -626,10 +645,11 @@ cdef tuple __hyperbolicity__(int N, to `h_LB are found, h is the maximum computed value and so twice the hyperbolicity of the graph. If no such 4-tuple is found, it returns -1. - - ``certificate`` -- is a list of vertices. When 4-tuples with hyperbolicity - larger that h_LB are found, certificate is the list of the 4 vertices for - which the maximum value (and so the hyperbolicity of the graph) has been - computed. If no such 4-tuple is found, it returns the empty list []. + - ``certificate`` -- is a list of vertices. When 4-tuples with + hyperbolicity larger that h_LB are found, certificate is the list of the + 4 vertices for which the maximum value (and so the hyperbolicity of the + graph) has been computed. If no such 4-tuple is found, it returns the + empty list []. - ``h_UB`` -- is an integer equal to the proven upper bound for `h`. When ``h == h_UB``, the returned solution is optimal. @@ -638,7 +658,8 @@ cdef tuple __hyperbolicity__(int N, cdef int a, b, c, d, h, h_UB cdef int x, y, l1, l2, S1, S2, S3 cdef list certificate = [] - cdef uint32_t *nb_p = sage_malloc(sizeof(uint32_t)) # The total number of pairs. + cdef uint32_t *nb_p = check_allocarray(1, sizeof(uint32_t)) + # The total number of pairs. # Test if the distance matrix corresponds to a connected graph, i.e., if # distances from node 0 are all less or equal to N-1. @@ -647,21 +668,22 @@ cdef tuple __hyperbolicity__(int N, raise ValueError("The input graph must be connected.") # nb_pairs_of_length[d] is the number of pairs of vertices at distance d - cdef uint32_t * nb_pairs_of_length = sage_malloc((D+1) * sizeof(uint32_t)) + cdef uint32_t * nb_pairs_of_length = check_allocarray(D+1, sizeof(uint32_t)) if (nb_pairs_of_length == NULL): raise MemoryError - cdef pair ** pairs_of_length = sort_pairs(N, D, distances, far_apart_pairs, nb_p, nb_pairs_of_length) + cdef pair ** pairs_of_length = sort_pairs(N, D, distances, far_apart_pairs, + nb_p, nb_pairs_of_length) if verbose: print "Current 2 connected component has %d vertices and diameter %d" %(N,D) if far_apart_pairs == NULL: print "Number of pairs: %d" %(nb_p[0]) - print "Repartition of pairs:", [ (i, nb_pairs_of_length[i]) for i in range(1, D+1) if nb_pairs_of_length[i]>0] + print "Repartition of pairs:", [(i, nb_pairs_of_length[i]) for i in range(1, D+1) if nb_pairs_of_length[i]>0] else: print "Number of far-apart pairs: %d\t(%d pairs in total)" %(nb_p[0], binomial(N, 2)) - print "Repartition of far-apart pairs:", [ (i, nb_pairs_of_length[i]) for i in range(1, D+1) if nb_pairs_of_length[i]>0] + print "Repartition of far-apart pairs:", [(i, nb_pairs_of_length[i]) for i in range(1, D+1) if nb_pairs_of_length[i]>0] approximation_factor = min(approximation_factor, D) @@ -701,7 +723,7 @@ cdef tuple __hyperbolicity__(int N, print "New upper bound:",ZZ(h_UB)/2 # Termination if required approximation is found - if certificate and ( (h_UB <= h*approximation_factor) or (h_UB-h <= additive_gap) ): + if certificate and ((h_UB <= h*approximation_factor) or (h_UB-h <= additive_gap)): GOTO_RETURN = 1 break @@ -785,7 +807,11 @@ cdef tuple __hyperbolicity__(int N, return (h, certificate, h_UB if GOTO_RETURN else h) -def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap=None, verbose = False): +def hyperbolicity(G, + algorithm='CCL+FA', + approximation_factor=None, + additive_gap=None, + verbose = False): r""" Returns the hyperbolicity of the graph or an approximation of this value. @@ -794,8 +820,8 @@ def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap dist(b, c)`, `S_2 = dist(a, c) + dist(b, d)`, and `S_3 = dist(a, d) + dist(b, c)`, and let `M_1` and `M_2` be the two largest values among `S_1`, `S_2`, and `S_3`. We have `hyp(a, b, c, d) = |M_1 - M_2|`, and the - hyperbolicity of the graph is the maximum over all possible 4-tuples `(a, b, - c, d)` divided by 2. The worst case time complexity is in `O( n^4 )`. + hyperbolicity of the graph is the maximum over all possible 4-tuples `(a,b, + c,d)` divided by 2. The worst case time complexity is in `O( n^4 )`. See the documentation of :mod:`sage.graphs.hyperbolicity` for more information. @@ -804,7 +830,8 @@ def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap - ``G`` -- a connected Graph - - ``algorithm`` -- (default: ``'CCL+FA'``) specifies the algorithm to use among: + - ``algorithm`` -- (default: ``'CCL+FA'``) specifies the algorithm to use + among: - ``'basic'`` is an exhaustive algorithm considering all possible 4-tuples and so have time complexity in `O(n^4)`. @@ -814,9 +841,9 @@ def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap as a new lower bound is found (see the module's documentation). This algorithm can be turned into a approximation algorithm. - - ``'CCL+FA'`` or ``'CCL+'`` uses the notion of far-apart pairs as proposed in - [Soto11]_ to significantly reduce the overall computation time of - the ``'CCL'`` algorithm. + - ``'CCL+FA'`` or ``'CCL+'`` uses the notion of far-apart pairs as + proposed in [Soto11]_ to significantly reduce the overall + computation time of the ``'CCL'`` algorithm. - ``'dom'`` is an approximation with additive constant four. It computes the hyperbolicity of the vertices of a dominating set of @@ -920,7 +947,8 @@ def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap ... print "That's not good!" - The hyperbolicity of a graph is the maximum value over all its biconnected components:: + The hyperbolicity of a graph is the maximum value over all its biconnected + components:: sage: from sage.graphs.hyperbolicity import hyperbolicity sage: G = graphs.PetersenGraph() * 2 @@ -963,7 +991,7 @@ def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap sage: hyperbolicity(G,algorithm='CCL', additive_gap=-1) Traceback (most recent call last): ... - ValueError: The additive gap must be >= 0 when using the 'CCL' algorithm. + ValueError: The additive gap must be a real positive number. Asking for an unknown algorithm:: @@ -991,16 +1019,15 @@ def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap if not approximation_factor in RR or approximation_factor < 1.0: raise ValueError("The approximation factor must be >= 1.0.") else: - raise ValueError("The approximation_factor is ignored when using the '%s' algorithm." %(algorithm)) + raise ValueError("The approximation_factor is ignored when using" + "the '%s' algorithm." %(algorithm)) if additive_gap is None: additive_gap = 0.0 elif additive_gap==0.0: pass elif algorithm in ['CCL', 'CCL+FA']: - if not additive_gap in RR: + if not additive_gap in RR or additive_gap < 0.0: raise ValueError("The additive gap must be a real positive number.") - elif additive_gap < 0.0: - raise ValueError("The additive gap must be >= 0 when using the '%s' algorithm." %(algorithm)) else: raise ValueError("The additive_gap is ignored when using the '%s' algorithm." %(algorithm)) @@ -1080,14 +1107,14 @@ def hyperbolicity(G, algorithm='CCL+FA', approximation_factor=None, additive_gap cdef unsigned short ** far_apart_pairs # We compute the distances and store the results in a 2D array - distances = sage_malloc(sizeof(unsigned short *)*N) + distances = check_allocarray(N, sizeof(unsigned short *)) if distances == NULL: raise MemoryError("Unable to allocate array 'distances'.") if algorithm == 'CCL+FA': - _distances_ = sage_malloc(N * N * sizeof(unsigned short)) - _far_apart_pairs_ = sage_malloc(N * N * sizeof(unsigned short)) - far_apart_pairs = sage_malloc(N * sizeof(unsigned short *)) + _distances_ = check_allocarray(N * N, sizeof(unsigned short)) + _far_apart_pairs_ = check_allocarray(N * N, sizeof(unsigned short)) + far_apart_pairs = check_allocarray(N, sizeof(unsigned short *)) if _distances_ == NULL or _far_apart_pairs_ == NULL or far_apart_pairs == NULL: sage_free(_distances_) sage_free(distances) @@ -1203,7 +1230,7 @@ cdef dict __hyperbolicity_distribution__(int N, unsigned short ** distances): # int instead of a dictionnary since it is much faster. cdef int i - cdef uint64_t * hdistr = sage_calloc(N+1,sizeof(uint64_t)) + cdef uint64_t * hdistr = check_calloc(N+1,sizeof(uint64_t)) if hdistr == NULL: raise MemoryError @@ -1266,7 +1293,7 @@ cdef dict __hyperbolicity_sampling__(int N, unsigned short ** distances, uint64_ # We initialize the table of hyperbolicity. We use an array of unsigned long # int instead of a dictionnary since it is much faster. - cdef uint64_t * hdistr = sage_calloc(N+1,sizeof(uint64_t)) + cdef uint64_t * hdistr = check_calloc(N+1,sizeof(uint64_t)) if hdistr == NULL: raise MemoryError @@ -1383,7 +1410,7 @@ def hyperbolicity_distribution(G, algorithm='sampling', sampling_size=10**6): # for faster access. H = G.relabel( inplace = False ) _distances_ = c_distances_all_pairs(H) - distances = sage_malloc(sizeof(unsigned short *)*N) + distances = check_allocarray(N, sizeof(unsigned short *)) if distances == NULL: sage_free(_distances_) raise MemoryError From 8a7ffe1d55e5efee710c4a50a6ae406674737e68 Mon Sep 17 00:00:00 2001 From: borassi Date: Tue, 19 May 2015 17:31:52 +0200 Subject: [PATCH 09/12] After David's suggestions --- src/sage/graphs/hyperbolicity.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index 64dd46d5748..4fcf833d5bc 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -271,7 +271,7 @@ cdef inline int __hyp__(unsigned short ** distances, int a, int b, int c, int d) ###################################################################### cdef tuple __hyperbolicity_basic_algorithm__(int N, - unsigned short ** distances, + unsigned short ** distances, verbose): """ Returns **twice** the hyperbolicity of a graph, and a certificate. From 4280fce704003ff53b80000e9d918c01b2a4e23b Mon Sep 17 00:00:00 2001 From: borassi Date: Wed, 20 May 2015 16:12:31 +0200 Subject: [PATCH 10/12] After second correction. --- src/sage/graphs/hyperbolicity.pyx | 37 +++++++++++++++++-------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index 4fcf833d5bc..727229cfe14 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -81,7 +81,7 @@ Z as soon as `dist(a, b) \leq h`. The worst case time complexity of this algorithm is `O(n^4)`, but it - performs very well in practice since it CCL the search space. This + performs very well in practice since it cuts the search space. This algorithm can be turned into an approximation algorithm since at any step of its execution we maintain an upper and a lower bound. We can thus stop execution as soon as a multiplicative approximation factor or an additive @@ -270,7 +270,7 @@ cdef inline int __hyp__(unsigned short ** distances, int a, int b, int c, int d) # Basic algorithm for the hyperbolicity ###################################################################### -cdef tuple __hyperbolicity_basic_algorithm__(int N, +cdef tuple hyperbolicity_basic_algorithm(int N, unsigned short ** distances, verbose): """ @@ -494,12 +494,14 @@ cdef inline pair** sort_pairs(uint32_t N, uint32_t * nb_pairs_of_length ): """ - Uses counting sort to create a list of unordered pairs {i,j}, in decreasing - order of values(i,j). If to_include[i][j] = 0, the pair is ignored. We - assume N and D to be correct with respect to the arrays values and - to_include, that values and to_include are symmetric (that is, - values[i][j] = values[j][i] and to_include[i][j] = to_include[j][i], and - that nb_p, nb_pairs_of_length are already allocated. + Returns an array of unordered pairs {i,j} in increasing order of values. + + Uses counting sort to list pairs {i,j} in increasing order of values(i,j). + If to_include[i][j] = 0, the pair is ignored. We assume N and D to be + correct with respect to the arrays values and to_include, that values and + to_include are symmetric (that is, values[i][j] = values[j][i] and + to_include[i][j] = to_include[j][i], and that nb_p, nb_pairs_of_length are + already allocated. INPUT: @@ -529,6 +531,7 @@ cdef inline pair** sort_pairs(uint32_t N, # pairs_of_length[d] is the list of pairs of vertices at distance d cdef pair ** pairs_of_length = check_allocarray(D+1, sizeof(pair *)) cdef unsigned short *p_to_include + cdef uint32_t i,j,k nb_p[0] = 0; # fills nb_pairs_of_length and nb_p @@ -594,7 +597,7 @@ cdef inline pair** sort_pairs(uint32_t N, # Compute the hyperbolicity using the algorithm of [CCL12]_ ###################################################################### -cdef tuple __hyperbolicity__(int N, +cdef tuple hyperbolicity_ccl(int N, unsigned short ** distances, unsigned short ** far_apart_pairs, int D, @@ -658,7 +661,7 @@ cdef tuple __hyperbolicity__(int N, cdef int a, b, c, d, h, h_UB cdef int x, y, l1, l2, S1, S2, S3 cdef list certificate = [] - cdef uint32_t *nb_p = check_allocarray(1, sizeof(uint32_t)) + cdef uint32_t nb_p # The total number of pairs. # Test if the distance matrix corresponds to a connected graph, i.e., if @@ -674,15 +677,15 @@ cdef tuple __hyperbolicity__(int N, raise MemoryError cdef pair ** pairs_of_length = sort_pairs(N, D, distances, far_apart_pairs, - nb_p, nb_pairs_of_length) + &nb_p, nb_pairs_of_length) if verbose: print "Current 2 connected component has %d vertices and diameter %d" %(N,D) if far_apart_pairs == NULL: - print "Number of pairs: %d" %(nb_p[0]) + print "Number of pairs: %d" %(nb_p) print "Repartition of pairs:", [(i, nb_pairs_of_length[i]) for i in range(1, D+1) if nb_pairs_of_length[i]>0] else: - print "Number of far-apart pairs: %d\t(%d pairs in total)" %(nb_p[0], binomial(N, 2)) + print "Number of far-apart pairs: %d\t(%d pairs in total)" %(nb_p, binomial(N, 2)) print "Repartition of far-apart pairs:", [(i, nb_pairs_of_length[i]) for i in range(1, D+1) if nb_pairs_of_length[i]>0] @@ -1144,7 +1147,7 @@ def hyperbolicity(G, # required parameters. if algorithm in ['CCL', 'CCL+FA']: sig_on() - hyp, certif, hyp_UB = __hyperbolicity__(N, distances, far_apart_pairs, D, hyp, + hyp, certif, hyp_UB = hyperbolicity_ccl(N, distances, far_apart_pairs, D, hyp, approximation_factor, 2*additive_gap, verbose) sig_off() @@ -1166,14 +1169,14 @@ def hyperbolicity(G, distances[i][j] = 0 distances[j][i] = 0 sig_on() - hyp, certif, hyp_UB = __hyperbolicity__(N, distances, NULL, D, hyp, 1.0, 0.0, verbose) + hyp, certif, hyp_UB = hyperbolicity_ccl(N, distances, NULL, D, hyp, 1.0, 0.0, verbose) sig_off() hyp_UB = min( hyp+8, D) elif algorithm == 'basic': sig_on() - hyp, certif = __hyperbolicity_basic_algorithm__(N, distances, - verbose=verbose) + hyp, certif = hyperbolicity_basic_algorithm(N, distances, + verbose=verbose) sig_off() hyp_UB = hyp From 4c55f5eeadc7539a443bdcec0797eff28e99da35 Mon Sep 17 00:00:00 2001 From: borassi Date: Wed, 20 May 2015 16:15:33 +0200 Subject: [PATCH 11/12] After second correction (2) --- src/sage/graphs/hyperbolicity.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index 727229cfe14..a6b8a8e3b6e 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -597,7 +597,7 @@ cdef inline pair** sort_pairs(uint32_t N, # Compute the hyperbolicity using the algorithm of [CCL12]_ ###################################################################### -cdef tuple hyperbolicity_ccl(int N, +cdef tuple hyperbolicity_CCL(int N, unsigned short ** distances, unsigned short ** far_apart_pairs, int D, @@ -1147,7 +1147,7 @@ def hyperbolicity(G, # required parameters. if algorithm in ['CCL', 'CCL+FA']: sig_on() - hyp, certif, hyp_UB = hyperbolicity_ccl(N, distances, far_apart_pairs, D, hyp, + hyp, certif, hyp_UB = hyperbolicity_CCL(N, distances, far_apart_pairs, D, hyp, approximation_factor, 2*additive_gap, verbose) sig_off() @@ -1169,7 +1169,7 @@ def hyperbolicity(G, distances[i][j] = 0 distances[j][i] = 0 sig_on() - hyp, certif, hyp_UB = hyperbolicity_ccl(N, distances, NULL, D, hyp, 1.0, 0.0, verbose) + hyp, certif, hyp_UB = hyperbolicity_CCL(N, distances, NULL, D, hyp, 1.0, 0.0, verbose) sig_off() hyp_UB = min( hyp+8, D) From 0ce54207bc9085c3128d407867e428e797d8a49c Mon Sep 17 00:00:00 2001 From: borassi Date: Sat, 23 May 2015 17:23:45 +0200 Subject: [PATCH 12/12] After correction of 'Z' --- src/sage/graphs/hyperbolicity.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index a6b8a8e3b6e..076908f6a86 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -10,7 +10,7 @@ Hyperbolicity defined by .. MATH:: -Z + S_1 = dist(a, b) + dist(b, c)\\ S_2 = dist(a, c) + dist(b, d)\\ S_3 = dist(a, d) + dist(b, c)\\