@@ -474,7 +474,7 @@ def linear_ordering_to_path_decomposition(G, L):
474
474
# #################################################################
475
475
476
476
def pathwidth (self , k = None , certificate = False , algorithm = " BAB" , verbose = False ,
477
- max_prefix_length = 20 , max_prefix_number = 10 ** 6 ):
477
+ max_prefix_length = 20 , max_prefix_number = 10 ** 6 , *, solver = None ):
478
478
r """
479
479
Compute the pathwidth of ``self`` ( and provides a decomposition)
480
480
@@ -513,6 +513,14 @@ def pathwidth(self, k=None, certificate=False, algorithm="BAB", verbose=False,
513
513
number of stored prefixes used to prevent using too much memory. This
514
514
parameter is used only when ``algorithm=="BAB"``.
515
515
516
+ - ``solver`` -- string ( default: ``None``) ; specify a Mixed Integer Linear
517
+ Programming ( MILP) solver to be used. If set to ``None``, the default one
518
+ is used. For more information on MILP solvers and which default solver is
519
+ used, see the method :meth:`solve
520
+ <sage. numerical. mip. MixedIntegerLinearProgram. solve>` of the class
521
+ :class:`MixedIntegerLinearProgram
522
+ <sage. numerical. mip. MixedIntegerLinearProgram>`.
523
+
516
524
OUTPUT:
517
525
518
526
Return the pathwidth of ``self``. When ``k`` is specified, it returns
@@ -566,6 +574,12 @@ def pathwidth(self, k=None, certificate=False, algorithm="BAB", verbose=False,
566
574
Traceback ( most recent call last) :
567
575
...
568
576
ValueError: algorithm "SuperFast" has not been implemented yet, please contribute
577
+
578
+ Using a specific solver::
579
+
580
+ sage: g = graphs. PetersenGraph( )
581
+ sage: g. pathwidth( solver='SCIP') # optional - pyscipopt
582
+ 5
569
583
"""
570
584
from sage.graphs.graph import Graph
571
585
if not isinstance (self , Graph):
@@ -574,7 +588,8 @@ def pathwidth(self, k=None, certificate=False, algorithm="BAB", verbose=False,
574
588
pw, L = vertex_separation(self , algorithm = algorithm, verbose = verbose,
575
589
cut_off = k, upper_bound = None if k is None else (k+ 1 ),
576
590
max_prefix_length = max_prefix_length,
577
- max_prefix_number = max_prefix_number)
591
+ max_prefix_number = max_prefix_number,
592
+ solver = solver)
578
593
579
594
if k is None :
580
595
return (pw, linear_ordering_to_path_decomposition(self , L)) if certificate else pw
@@ -786,6 +801,13 @@ def vertex_separation(G, algorithm="BAB", cut_off=None, upper_bound=None, verbos
786
801
sage: print( vertex_separation( D))
787
802
( 3, [10, 11, 8, 9, 4, 5, 6, 7, 0, 1, 2, 3 ])
788
803
804
+ Using a specific MILP solver::
805
+
806
+ sage: from sage. graphs. graph_decompositions. vertex_separation import vertex_separation
807
+ sage: G = graphs. PetersenGraph( )
808
+ sage: vs, L = vertex_separation( G, algorithm="MILP", solver="SCIP") ; vs # optional - pyscipopt
809
+ 5
810
+
789
811
TESTS:
790
812
791
813
Given a wrong algorithm::
@@ -1249,6 +1271,108 @@ def width_of_path_decomposition(G, L):
1249
1271
# MILP formulation for vertex separation #
1250
1272
# #########################################
1251
1273
1274
+ def _vertex_separation_MILP_formulation (G , integrality = False , solver = None ):
1275
+ r """
1276
+ MILP formulation of the vertex separation of `G` and the optimal ordering of its vertices.
1277
+
1278
+ This MILP is an improved version of the formulation proposed in [SP2010 ]_. See the
1279
+ :mod:`module's documentation <sage. graphs. graph_decompositions. vertex_separation>` for
1280
+ more details on this MILP formulation.
1281
+
1282
+ INPUT:
1283
+
1284
+ - ``G`` -- a Graph or a DiGraph
1285
+
1286
+ - ``integrality`` -- boolean ( default: ``False``) ; specify if variables
1287
+ `x_v^ t` and `u_v^ t` must be integral or if they can be relaxed. This has
1288
+ no impact on the validity of the solution, but it is sometimes faster to
1289
+ solve the problem using binary variables only.
1290
+
1291
+ - ``solver`` -- string ( default: ``None``) ; specify a Mixed Integer Linear
1292
+ Programming ( MILP) solver to be used. If set to ``None``, the default one
1293
+ is used. For more information on MILP solvers and which default solver is
1294
+ used, see the method :meth:`solve
1295
+ <sage. numerical. mip. MixedIntegerLinearProgram. solve>` of the class
1296
+ :class:`MixedIntegerLinearProgram
1297
+ <sage. numerical. mip. MixedIntegerLinearProgram>`.
1298
+
1299
+ OUTPUT:
1300
+
1301
+ - the :class:`~sage. numerical. mip. MixedIntegerLinearProgram`
1302
+
1303
+ - :class:`sage. numerical. mip. MIPVariable` objects ``x``, ``u``, ``y``, ``z``.
1304
+
1305
+ EXAMPLES::
1306
+
1307
+ sage: from sage. graphs. graph_decompositions. vertex_separation import _vertex_separation_MILP_formulation
1308
+ sage: G = digraphs. DeBruijn( 2,3)
1309
+ sage: p, x, u, y, z = _vertex_separation_MILP_formulation( G)
1310
+ sage: p
1311
+ Mixed Integer Program ( minimization, 193 variables, 449 constraints)
1312
+ """
1313
+ from sage.graphs.graph import Graph
1314
+ from sage.graphs.digraph import DiGraph
1315
+ if not isinstance (G, Graph) and not isinstance (G, DiGraph):
1316
+ raise ValueError (" the first input parameter must be a Graph or a DiGraph" )
1317
+
1318
+ from sage.numerical.mip import MixedIntegerLinearProgram
1319
+ p = MixedIntegerLinearProgram(maximization = False , solver = solver)
1320
+
1321
+ # Declaration of variables.
1322
+ x = p.new_variable(binary = integrality, nonnegative = True )
1323
+ u = p.new_variable(binary = integrality, nonnegative = True )
1324
+ y = p.new_variable(binary = True )
1325
+ z = p.new_variable(integer = True , nonnegative = True )
1326
+
1327
+ N = G.order()
1328
+ V = list (G)
1329
+ neighbors_out = G.neighbors_out if G.is_directed() else G.neighbors
1330
+
1331
+ # (2) x[v,t] <= x[v,t+1] for all v in V, and for t:=0..N-2
1332
+ # (3) y[v,t] <= y[v,t+1] for all v in V, and for t:=0..N-2
1333
+ for v in V:
1334
+ for t in range (N - 1 ):
1335
+ p.add_constraint(x[v, t] - x[v, t + 1 ] <= 0 )
1336
+ p.add_constraint(y[v, t] - y[v, t + 1 ] <= 0 )
1337
+
1338
+ # (4) y[v,t] <= x[w,t] for all v in V, for all w in N^+(v), and for all t:=0..N-1
1339
+ for v in V:
1340
+ for w in neighbors_out(v):
1341
+ for t in range (N):
1342
+ p.add_constraint(y[v, t] - x[w, t] <= 0 )
1343
+
1344
+ # (5) sum_{v in V} y[v,t] == t+1 for t:=0..N-1
1345
+ for t in range (N):
1346
+ p.add_constraint(p.sum(y[v, t] for v in V) == t + 1 )
1347
+
1348
+ # (6) u[v,t] >= x[v,t]-y[v,t] for all v in V, and for all t:=0..N-1
1349
+ for v in V:
1350
+ for t in range (N):
1351
+ p.add_constraint(x[v, t] - y[v, t] - u[v, t] <= 0 )
1352
+
1353
+ # (7) z >= sum_{v in V} u[v,t] for all t:=0..N-1
1354
+ for t in range (N):
1355
+ p.add_constraint(p.sum(u[v, t] for v in V) - z[' z' ] <= 0 )
1356
+
1357
+ # (8)(9) 0 <= x[v,t] and u[v,t] <= 1
1358
+ if not integrality:
1359
+ for v in V:
1360
+ for t in range (N):
1361
+ p.add_constraint(x[v, t], min = 0 , max = 1 )
1362
+ p.add_constraint(u[v, t], min = 0 , max = 1 )
1363
+
1364
+ # (10) y[v,t] in {0,1}
1365
+ # already declared
1366
+
1367
+ # (11) 0 <= z <= |V|
1368
+ p.add_constraint(z[' z' ] <= N)
1369
+
1370
+ # (1) Minimize z
1371
+ p.set_objective(z[' z' ])
1372
+
1373
+ return p, x, u, y, z
1374
+
1375
+
1252
1376
@ rename_keyword (deprecation = 32222 , verbosity = ' verbose' )
1253
1377
def vertex_separation_MILP (G , integrality = False , solver = None , verbose = 0 ,
1254
1378
*, integrality_tolerance = 1e-3 ):
@@ -1341,65 +1465,11 @@ def vertex_separation_MILP(G, integrality=False, solver=None, verbose=0,
1341
1465
...
1342
1466
ValueError: the first input parameter must be a Graph or a DiGraph
1343
1467
"""
1344
- from sage.graphs.graph import Graph
1345
- from sage.graphs.digraph import DiGraph
1346
- if not isinstance (G, Graph) and not isinstance (G, DiGraph):
1347
- raise ValueError (" the first input parameter must be a Graph or a DiGraph" )
1348
-
1349
- from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException
1350
- p = MixedIntegerLinearProgram(maximization = False , solver = solver)
1351
-
1352
- # Declaration of variables.
1353
- x = p.new_variable(binary = integrality, nonnegative = True )
1354
- u = p.new_variable(binary = integrality, nonnegative = True )
1355
- y = p.new_variable(binary = True )
1356
- z = p.new_variable(integer = True , nonnegative = True )
1468
+ from sage.numerical.mip import MIPSolverException
1357
1469
1470
+ p, x, u, y, z = _vertex_separation_MILP_formulation(G, integrality = integrality, solver = solver)
1358
1471
N = G.order()
1359
1472
V = list (G)
1360
- neighbors_out = G.neighbors_out if G.is_directed() else G.neighbors
1361
-
1362
- # (2) x[v,t] <= x[v,t+1] for all v in V, and for t:=0..N-2
1363
- # (3) y[v,t] <= y[v,t+1] for all v in V, and for t:=0..N-2
1364
- for v in V:
1365
- for t in range (N - 1 ):
1366
- p.add_constraint(x[v, t] - x[v, t + 1 ] <= 0 )
1367
- p.add_constraint(y[v, t] - y[v, t + 1 ] <= 0 )
1368
-
1369
- # (4) y[v,t] <= x[w,t] for all v in V, for all w in N^+(v), and for all t:=0..N-1
1370
- for v in V:
1371
- for w in neighbors_out(v):
1372
- for t in range (N):
1373
- p.add_constraint(y[v, t] - x[w, t] <= 0 )
1374
-
1375
- # (5) sum_{v in V} y[v,t] == t+1 for t:=0..N-1
1376
- for t in range (N):
1377
- p.add_constraint(p.sum(y[v, t] for v in V) == t + 1 )
1378
-
1379
- # (6) u[v,t] >= x[v,t]-y[v,t] for all v in V, and for all t:=0..N-1
1380
- for v in V:
1381
- for t in range (N):
1382
- p.add_constraint(x[v, t] - y[v, t] - u[v, t] <= 0 )
1383
-
1384
- # (7) z >= sum_{v in V} u[v,t] for all t:=0..N-1
1385
- for t in range (N):
1386
- p.add_constraint(p.sum(u[v, t] for v in V) - z[' z' ] <= 0 )
1387
-
1388
- # (8)(9) 0 <= x[v,t] and u[v,t] <= 1
1389
- if not integrality:
1390
- for v in V:
1391
- for t in range (N):
1392
- p.add_constraint(x[v, t], min = 0 , max = 1 )
1393
- p.add_constraint(u[v, t], min = 0 , max = 1 )
1394
-
1395
- # (10) y[v,t] in {0,1}
1396
- # already declared
1397
-
1398
- # (11) 0 <= z <= |V|
1399
- p.add_constraint(z[' z' ] <= N)
1400
-
1401
- # (1) Minimize z
1402
- p.set_objective(z[' z' ])
1403
1473
1404
1474
try :
1405
1475
obj = p.solve(log = verbose)
0 commit comments