Closed jajhall closed 5 months ago
Correct, this was by design so that we can efficiently add a batch of variables. It's not ideal for people to add many variables one-by-one - but obviously it's still possible for them to do so.
For the mats.py example, instead of:
for i in range(nvars):
h.addVar(0, inf)
h.changeColCost(i, 1)
we can simply use:
for i in range(nvars):
h.addVar(0, inf, 1)
h.update() # needed since calling low-level addRow afterwards (instead of addConstr)
or (horribly):
for i in range(nvars):
h.addVar(0, inf)
h.update()
h.changeColCost(i, 1)
That said, this also connects to #1679, and so it depends if we want the default API to use the new highspy interface, or directly access the base HiGHS class.
BTW: I believe with the new highspy, mats.py can be rewritten from:
for i in range(nvars):
h.addVar(0, inf)
h.changeColCost(i, 1)
row_nnz = 2
index = np.array([0, 1])
value = np.array([1, 1], dtype=np.double)
for i in range(nvars - 1):
index[0] = i
index[1] = i+1
h.addRow(1, inf, row_nnz, index, value)
h.run()
to:
X = [h.addVar(0, inf, 1) for _ in range(nvars)]
C = [h.addConstr(X[i] + X[i+1] >= 1) for i in range(nvars - 1)]
h.run()
mats.py
was actually my attempt to re-write
https://github.com/ERGO-Code/HiGHS/blob/highspy-modelling/examples/scip.py
when we lost the ability to build highspy
with your modelling language.
Your version is even more succinct, although (keeping within the modelling language) it would work with
X = [h.addVar(obj=1) for _ in range(nvars)] C = [h.addConstr(X[i] + X[i+1] >= 1) for i in range(nvars - 1)]
h.run()
Good point. Referring to comment in #1679, if we also support addVars/addConstrs
in highspy, we could simplify even further:
X = h.addVars(nvars, obj=1)
C = h.addConstrs(X[i] + X[i+1] >= 1 for i in range(nvars - 1))
h.run()
That highspy code would look something like the following:
def addVars(self, nvars, lb = 0, ub = kHighsInf, obj = 0, type=HighsVarType.kContinuous, name_prefix = None):
return [self.addVar(lb, ub, obj, type, None if name_prefix == None else name_prefix + str(_)) for _ in range(nvars)]
def addConstrs(self, generator, name_prefix=None):
self.update()
lower = []
upper = []
starts = [0]
indices = []
values = []
nnz = 0;
for cons in generator:
# if we have duplicate variables, add the vals together
vars,vals = zip(*[(var, sum(v[1] for v in Vals)) for var, Vals in groupby(sorted(zip(cons.vars, cons.vals)), key=itemgetter(0))])
lower.append(cons.LHS - cons.constant)
upper.append(cons.RHS - cons.constant)
indices.extend(vars)
values.extend(vals)
nnz += len(vars)
starts.append(nnz)
new_rows = len(lower)
super().addRows(new_rows, lower, upper, nnz, starts, indices, values);
cons = [highs_cons(self.numConstrs - new_rows + n, self, None if name_prefix == None else name_prefix + str(n)) for n in range(new_rows)]
self._cons.extend(cons)
return cons
Closed by #1753
Hence, if
highspy
calls are made to change model data for columns, they fail needsself.update()
call?See
mats.py