| 3256 | |
| 3257 | def edge_cut(self,s,t,value_only=True,use_edge_labels=False): |
| 3258 | r""" |
| 3259 | Returns a minimum edge cut between vertices `s` and `t` |
| 3260 | ( cf. http://en.wikipedia.org/wiki/Cut_%28graph_theory%29 ) |
| 3261 | represented by a list of edges. |
| 3262 | |
| 3263 | |
| 3264 | INPUT: |
| 3265 | |
| 3266 | - ``s`` -- Source vertex |
| 3267 | |
| 3268 | - ``t`` -- Sink vertex |
| 3269 | |
| 3270 | - ``value_only`` (boolean) |
| 3271 | |
| 3272 | - When set to ``True``, only the value of a maximal |
| 3273 | flow is returned. |
| 3274 | |
| 3275 | - When set to ``False``, a list of edges in the minimum cut is |
| 3276 | also returned. |
| 3277 | |
| 3278 | - ``use_edge_labels`` (boolean) |
| 3279 | - When set to ``True``, computes a weighted minimum cut |
| 3280 | where each edge has a weight defined by its label. ( if |
| 3281 | an edge has no label, `1` is assumed ) |
| 3282 | |
| 3283 | - when set to ``False`` (default), each edge has weight `1`. |
| 3284 | |
| 3285 | EXAMPLE: |
| 3286 | |
| 3287 | A basic application in a ``PappusGraph``:: |
| 3288 | |
| 3289 | sage: g=graphs.PappusGraph() |
| 3290 | sage: g.edge_cut(1,2,value_only=True) # optional - requires Glpk or COIN-OR/CBC |
| 3291 | 3.0 |
| 3292 | |
| 3293 | If the graph is a path with weighted edges, the edge cut between the two ends |
| 3294 | is the edge of minimum weight :: |
| 3295 | |
| 3296 | sage: g = graphs.PathGraph(15) |
| 3297 | sage: for (u,v) in g.edge_iterator(labels=None): |
| 3298 | ... g.set_edge_label(u,v,random()) |
| 3299 | sage: minimum = min([l for u,v,l in g.edge_iterator()]) |
| 3300 | sage: minimum == g.edge_cut(0,14,use_edge_labels=True) # optional - requires Glpk or COIN-OR/CBC |
| 3301 | True |
| 3302 | sage: [value,[[u,v]]] = g.edge_cut(0,14,use_edge_labels=True, value_only=False) # optional - requires Glpk or COIN-OR/CBC |
| 3303 | sage: g.edge_label(u,v) == minimum # optional - requires Glpk or COIN-OR/CBC |
| 3304 | True |
| 3305 | """ |
| 3306 | from sage.numerical.mip import MixedIntegerLinearProgram |
| 3307 | g=self |
| 3308 | p=MixedIntegerLinearProgram(maximization=False) |
| 3309 | b=p.new_variable(dim=2) |
| 3310 | v=p.new_variable() |
| 3311 | |
| 3312 | if use_edge_labels: |
| 3313 | weight=lambda x: 1 if x==None else x |
| 3314 | else: |
| 3315 | weight=lambda x: 1 |
| 3316 | |
| 3317 | # Some vertices belong to part 1, others to part 0 |
| 3318 | p.add_constraint(v[s],min=0,max=0) |
| 3319 | p.add_constraint(v[t],min=1,max=1) |
| 3320 | |
| 3321 | if g.is_directed(): |
| 3322 | # we minimize the number of edges |
| 3323 | p.set_objective(sum([weight(w)*b[x][y] for (x,y,w) in g.edges()])) |
| 3324 | |
| 3325 | # Adjacent vertices can belong to different parts only if the |
| 3326 | # edge that connects them is part of the cut |
| 3327 | [p.add_constraint(v[x]+b[x][y]-v[y],min=0,max=0) for (x,y) in g.edges(labels=None)] |
| 3328 | |
| 3329 | |
| 3330 | else: |
| 3331 | # we minimize the number of edges |
| 3332 | p.set_objective(sum([weight(w)*b[min(x,y)][max(x,y)] for (x,y,w) in g.edges()])) |
| 3333 | |
| 3334 | # Adjacent vertices can belong to different parts only if the |
| 3335 | # edge that connects them is part of the cut |
| 3336 | for (x,y) in g.edges(labels=None): |
| 3337 | p.add_constraint(v[x]+b[min(x,y)][max(x,y)]-v[y],min=0) |
| 3338 | p.add_constraint(v[y]+b[min(x,y)][max(x,y)]-v[x],min=0) |
| 3339 | |
| 3340 | p.set_binary(v) |
| 3341 | p.set_binary(b) |
| 3342 | |
| 3343 | if value_only: |
| 3344 | return p.solve(objective_only=True) |
| 3345 | else: |
| 3346 | obj=p.solve() |
| 3347 | b=p.get_values(b) |
| 3348 | if g.is_directed(): |
| 3349 | return [obj,[(x,y) for (x,y) in g.edges(labels=None) if b[x][y]==1]] |
| 3350 | else: |
| 3351 | return [obj,[(x,y) for (x,y) in g.edges(labels=None) if b[min(x,y)][max(x,y)]==1]] |
| 3352 | |
| 3353 | def vertex_cut(self,s,t,value_only=True): |
| 3354 | r""" |
| 3355 | Returns a minimum vertex cut between non-adjacent vertices `s` and `t` |
| 3356 | ( cf. http://en.wikipedia.org/wiki/Cut_%28graph_theory%29 ) |
| 3357 | represented by a list of vertices. |
| 3358 | |
| 3359 | |
| 3360 | INPUT: |
| 3361 | |
| 3362 | - ``value_only`` : When set to ``True``, only the cardinal of the |
| 3363 | minimum cut is returned |
| 3364 | |
| 3365 | EXAMPLE: |
| 3366 | |
| 3367 | A basic application in a ``PappusGraph``:: |
| 3368 | |
| 3369 | sage: g=graphs.PappusGraph() |
| 3370 | sage: g.vertex_cut(1,16,value_only=True) # optional - requires Glpk or COIN-OR/CBC |
| 3371 | 3.0 |
| 3372 | |
| 3373 | Or in a bipartite complete graph `K_{2,8}` between the first 2 |
| 3374 | vertices, in which case the minimum cut is the other set of 8 |
| 3375 | vertices :: |
| 3376 | |
| 3377 | sage: g = graphs.CompleteBipartiteGraph(2,8) |
| 3378 | sage: [value, vertices] = g.vertex_cut(0,1, value_only=False) # optional - requires Glpk or COIN-OR/CBC |
| 3379 | sage: print value # optional - requires Glpk or COIN-OR/CBC |
| 3380 | 8.0 |
| 3381 | sage: vertices == range(2,10) # optional - requires Glpk or COIN-OR/CBC |
| 3382 | True |
| 3383 | """ |
| 3384 | from sage.numerical.mip import MixedIntegerLinearProgram |
| 3385 | g=self |
| 3386 | if g.has_edge(s,t): |
| 3387 | raise ValueError, "There can be no vertex cut between adjacent vertices !" |
| 3388 | |
| 3389 | p=MixedIntegerLinearProgram(maximization=False) |
| 3390 | b=p.new_variable() |
| 3391 | v=p.new_variable() |
| 3392 | |
| 3393 | # Some vertices belong to part 1, some others to part 0 |
| 3394 | p.add_constraint(v[s],min=0,max=0) |
| 3395 | p.add_constraint(v[t],min=1,max=1) |
| 3396 | |
| 3397 | # b indicates whether the vertices belong to the cut |
| 3398 | p.add_constraint(b[s],min=0,max=0) |
| 3399 | p.add_constraint(b[t],min=0,max=0) |
| 3400 | |
| 3401 | if g.is_directed(): |
| 3402 | p.set_objective(sum([b[x] for x in g.vertices()])) |
| 3403 | # adjacent vertices belong to the same part except if one of them |
| 3404 | # belongs to the cut |
| 3405 | [p.add_constraint(v[x]+b[y]-v[y],min=0) for (x,y) in g.edges(labels=None)] |
| 3406 | |
| 3407 | else: |
| 3408 | p.set_objective(sum([b[x] for x in g.vertices()])) |
| 3409 | # adjacent vertices belong to the same part except if one of them |
| 3410 | # belongs to the cut |
| 3411 | for (x,y) in g.edges(labels=None): |
| 3412 | p.add_constraint(v[x]+b[y]-v[y],min=0) |
| 3413 | p.add_constraint(v[y]+b[x]-v[x],min=0) |
| 3414 | |
| 3415 | p.set_binary(b) |
| 3416 | p.set_binary(v) |
| 3417 | |
| 3418 | if value_only: |
| 3419 | return p.solve(objective_only=True) |
| 3420 | else: |
| 3421 | obj=p.solve() |
| 3422 | b=p.get_values(b) |
| 3423 | return [obj,[v for v in g if b[v]==1]] |