[SCIP] performance for scheduling problems
Brannon King
countprimes at gmail.com
Thu Jun 13 18:57:17 CEST 2024
I know it's improper to compare SCIP to commercial solvers. However,
some days I really wish that SCIP was faster for my experimentation
purposes. For scheduling problems in particular, are there SCIP
parameters that you tweak to get more performance? Are there known
cuts/heuristics that aren't coded in SCIP that address these types of
problems? I know many scheduling heuristics, but I doubt that Gurobi
is using scheduling-specific heuristics behind the scenes.
I offer this code below as proof of my performance conundrum. CPLEX
tends to be 2-3x worse than Gurobi on these 10x10s, although it's
constraint solver is faster than Gurobi. SCIP is on average 100x worse
than Gurobi.
import job_shop_lib as jsl # get this from pypi
import job_shop_lib.generators as jslg
import gurobipy as grb
import pyscipopt as scp
def as_gurobi_balas_model(instance: jsl.JobShopInstance):
model = grb.Model(instance.name)
O = M = instance.num_machines
s = model.addMVar((instance.num_jobs, O), vtype='C') # start time
for each op o on job j
cmax = model.addVar(name='c_max')
model.setObjective(cmax, grb.GRB.MINIMIZE)
bigM = 1
lookup = {m: [] for m in range(M)}
for job in instance.jobs:
for op in job: # operation, (machine, delay)
o = op.position_in_job
j = op.job_id
model.addConstr((s[j, o + 1] if o + 1 < O else cmax) -
s[j, o] >= op.duration) # op precedence
lookup[op.machine_id].append((j, o, op.duration))
bigM += op.duration
for m in range(M):
for l1, (j1, o1, d1) in enumerate(lookup[m]):
for l2, (j2, o2, d2) in enumerate(lookup[m][l1 + 1:], l1 + 1):
x = model.addVar(vtype='B') # no overlap, indicator
constraints are slower
model.addConstr(s[j2, o2] >= d1 + s[j1, o1] - bigM * (1 - x))
model.addConstr(s[j1, o1] >= d2 + s[j2, o2] - bigM * x)
return model
def as_scip_balas_model(instance: jsl.JobShopInstance):
model = scp.Model(instance.name)
O = M = instance.num_machines
s = {(j, o): model.addVar(vtype='C') for j in
range(instance.num_jobs) for o in range(O)}
cmax = model.addVar(name='c_max')
model.setObjective(cmax)
bigM = 1
lookup = {m: [] for m in range(M)}
for job in instance.jobs:
for op in job: # operation, (machine, delay)
o = op.position_in_job
j = op.job_id
model.addCons((s[j, o + 1] if o + 1 < O else cmax) - s[j,
o] >= op.duration) # op precedence
lookup[op.machine_id].append((j, o, op.duration))
bigM += op.duration
for m in range(M):
for l1, (j1, o1, d1) in enumerate(lookup[m]):
for l2, (j2, o2, d2) in enumerate(lookup[m][l1 + 1:], l1 + 1):
x = model.addVar(vtype='B') # no overlap, indicator
constraints are slower
model.addCons(s[j2, o2] >= d1 + s[j1, o1] - bigM * (1 - x))
model.addCons(s[j1, o1] >= d2 + s[j2, o2] - bigM * x)
return model
bgen = jslg.BasicGenerator(duration_range=(20, 200), seed=42,
num_jobs=10, num_machines=10)
instance1 = bgen.generate()
model_gur = as_gurobi_balas_model(instance1)
model_scp = as_scip_balas_model(instance1)
model_gur.optimize()
model_scp.optimize()
More information about the Scip
mailing list