I'm using pulp to solve a particular problem which has many variables, with a mixture of 1-D, 2-D and 3-D variables. When the solution is obtained, I would like to get these variables into k-dimensional numpy arrays.
Variables:
u = pulp.LpVariable.dicts("u", (ns, ns), 0, 1, "Integer")
v = pulp.LpVariable.dicts("v", (ns, ns), 0, 1, "Integer")
w = pulp.LpVariable.dicts("w", (ns, ns, ps), 0, 1, "Integer")
x = pulp.LpVariable.dicts("x", (ns, ps), 0, 1, "Integer")
y = pulp.LpVariable.dicts("y", (ms, ps), 0, 1, "Integer")
z = pulp.LpVariable.dicts("z", (ns), parameters.hold_duration_min,
parameters.hold_duration_max, "Continuous")
varnames = {"u": u, "v": v, "w": w, "x": x, "y": y, "z": z}
Input (after solving):
problem.variables()
Output:
[u_0_1, u_0_2, u_0_3, u_0_4, u_0_5, u_0_6, u_1_2, u_1_3, u_1_4, u_1_5, u_1_6, u_2_3, u_2_4, u_2_5, u_2_6, u_3_4, u_3_5, u_3_6, u_4_5, u_4_6, u_5_6, v_0_1, v_0_2, v_0_3, v_0_4, v_0_5, v_0_6, v_1_2, v_1_3, v_1_4, v_1_5, v_1_6, v_2_3, v_2_4, v_2_5, v_2_6, v_3_4, v_3_5, v_3_6, v_4_5, v_4_6, v_5_6, w_0_1_0, w_0_1_1, w_0_1_2, w_0_1_3, w_0_1_4, w_0_1_5, w_0_1_6, w_0_2_0, w_0_2_1, w_0_2_2, w_0_2_3, w_0_2_4, w_0_2_5, w_0_2_6, w_0_3_0, w_0_3_1, w_0_3_2, w_0_3_3, w_0_3_4, w_0_3_5, w_0_3_6, w_0_4_0, w_0_4_1, w_0_4_2, w_0_4_3, w_0_4_4, w_0_4_5, w_0_4_6, w_0_5_0, w_0_5_1, w_0_5_2, w_0_5_3, w_0_5_4, w_0_5_5, w_0_5_6, w_0_6_0, w_0_6_1, w_0_6_2, w_0_6_3, w_0_6_4, w_0_6_5, w_0_6_6, w_1_0_0, w_1_0_1, w_1_0_2, w_1_0_3, w_1_0_4, w_1_0_5, w_1_0_6, w_1_2_0, w_1_2_1, w_1_2_2, w_1_2_3, w_1_2_4, w_1_2_5, w_1_2_6, w_1_3_0, w_1_3_1, w_1_3_2, w_1_3_3, w_1_3_4, w_1_3_5, w_1_3_6, w_1_4_0, w_1_4_1, w_1_4_2, w_1_4_3, w_1_4_4, w_1_4_5, w_1_4_6, w_1_5_0, w_1_5_1, w_1_5_2, w_1_5_3, w_1_5_4, w_1_5_5, w_1_5_6, w_1_6_0, w_1_6_1, w_1_6_2, w_1_6_3, w_1_6_4, w_1_6_5, w_1_6_6, w_2_0_0, w_2_0_1, w_2_0_2, w_2_0_3, w_2_0_4, w_2_0_5, w_2_0_6, w_2_1_0, w_2_1_1, w_2_1_2, w_2_1_3, w_2_1_4, w_2_1_5, w_2_1_6, w_2_3_0, w_2_3_1, w_2_3_2, w_2_3_3, w_2_3_4, w_2_3_5, w_2_3_6, w_2_4_0, w_2_4_1, w_2_4_2, w_2_4_3, w_2_4_4, w_2_4_5, w_2_4_6, w_2_5_0, w_2_5_1, w_2_5_2, w_2_5_3, w_2_5_4, w_2_5_5, w_2_5_6, w_2_6_0, w_2_6_1, w_2_6_2, w_2_6_3, w_2_6_4, w_2_6_5, w_2_6_6, w_3_0_0, w_3_0_1, w_3_0_2, w_3_0_3, w_3_0_4, w_3_0_5, w_3_0_6, w_3_1_0, w_3_1_1, w_3_1_2, w_3_1_3, w_3_1_4, w_3_1_5, w_3_1_6, w_3_2_0, w_3_2_1, w_3_2_2, w_3_2_3, w_3_2_4, w_3_2_5, w_3_2_6, w_3_4_0, w_3_4_1, w_3_4_2, w_3_4_3, w_3_4_4, w_3_4_5, w_3_4_6, w_3_5_0, w_3_5_1, w_3_5_2, w_3_5_3, w_3_5_4, w_3_5_5, w_3_5_6, w_3_6_0, w_3_6_1, w_3_6_2, w_3_6_3, w_3_6_4, w_3_6_5, w_3_6_6, w_4_0_0, w_4_0_1, w_4_0_2, w_4_0_3, w_4_0_4, w_4_0_5, w_4_0_6, w_4_1_0, w_4_1_1, w_4_1_2, w_4_1_3, w_4_1_4, w_4_1_5, w_4_1_6, w_4_2_0, w_4_2_1, w_4_2_2, w_4_2_3, w_4_2_4, w_4_2_5, w_4_2_6, w_4_3_0, w_4_3_1, w_4_3_2, w_4_3_3, w_4_3_4, w_4_3_5, w_4_3_6, w_4_5_0, w_4_5_1, w_4_5_2, w_4_5_3, w_4_5_4, w_4_5_5, w_4_5_6, w_4_6_0, w_4_6_1, w_4_6_2, w_4_6_3, w_4_6_4, w_4_6_5, w_4_6_6, w_5_0_0, w_5_0_1, w_5_0_2, w_5_0_3, w_5_0_4, w_5_0_5, w_5_0_6, w_5_1_0, w_5_1_1, w_5_1_2, w_5_1_3, w_5_1_4, w_5_1_5, w_5_1_6, w_5_2_0, w_5_2_1, w_5_2_2, w_5_2_3, w_5_2_4, w_5_2_5, w_5_2_6, w_5_3_0, w_5_3_1, w_5_3_2, w_5_3_3, w_5_3_4, w_5_3_5, w_5_3_6, w_5_4_0, w_5_4_1, w_5_4_2, w_5_4_3, w_5_4_4, w_5_4_5, w_5_4_6, w_5_6_0, w_5_6_1, w_5_6_2, w_5_6_3, w_5_6_4, w_5_6_5, w_5_6_6, w_6_0_0, w_6_0_1, w_6_0_2, w_6_0_3, w_6_0_4, w_6_0_5, w_6_0_6, w_6_1_0, w_6_1_1, w_6_1_2, w_6_1_3, w_6_1_4, w_6_1_5, w_6_1_6, w_6_2_0, w_6_2_1, w_6_2_2, w_6_2_3, w_6_2_4, w_6_2_5, w_6_2_6, w_6_3_0, w_6_3_1, w_6_3_2, w_6_3_3, w_6_3_4, w_6_3_5, w_6_3_6, w_6_4_0, w_6_4_1, w_6_4_2, w_6_4_3, w_6_4_4, w_6_4_5, w_6_4_6, w_6_5_0, w_6_5_1, w_6_5_2, w_6_5_3, w_6_5_4, w_6_5_5, w_6_5_6, x_0_0, x_0_1, x_0_2, x_0_3, x_0_4, x_0_5, x_0_6, x_1_0, x_1_1, x_1_2, x_1_3, x_1_4, x_1_5, x_1_6, x_2_0, x_2_1, x_2_2, x_2_3, x_2_4, x_2_5, x_2_6, x_3_0, x_3_1, x_3_2, x_3_3, x_3_4, x_3_5, x_3_6, x_4_0, x_4_1, x_4_2, x_4_3, x_4_4, x_4_5, x_4_6, x_5_0, x_5_1, x_5_2, x_5_3, x_5_4, x_5_5, x_5_6, x_6_0, x_6_1, x_6_2, x_6_3, x_6_4, x_6_5, x_6_6, y_0_0, y_0_1, y_0_2, y_0_3, y_0_4, y_0_5, y_0_6, y_1_0, y_1_1, y_1_2, y_1_3, y_1_4, y_1_5, y_1_6, y_2_0, y_2_1, y_2_2, y_2_3, y_2_4, y_2_5, y_2_6, y_3_0, y_3_1, y_3_2, y_3_3, y_3_4, y_3_5, y_3_6, y_4_0, y_4_1, y_4_2, y_4_3, y_4_4, y_4_5, y_4_6, y_5_0, y_5_1, y_5_2, y_5_3, y_5_4, y_5_5, y_5_6, y_6_0, y_6_1, y_6_2, y_6_3, y_6_4, y_6_5, y_6_6, y_7_0, y_7_1, y_7_2, y_7_3, y_7_4, y_7_5, y_7_6, y_8_0, y_8_1, y_8_2, y_8_3, y_8_4, y_8_5, y_8_6, z_0, z_1, z_2, z_3, z_4, z_5, z_6]
What I'm looking to generate is a dictionary of numpy arrays to hold results, e.g. results such that, e.g.:
results["x"][1][4] == pulp.value(x[1][4])
type(results["x"]) == numpy.ndarray
results["z"][2] == pulp.value(z[2])
results(["w"][6][0][2]) == pulp.value(w[6][0][2])
I've tried some things using e.g. pandas, but can't get it to work for my mix of 1-D, 2-D and 3-D arrays:
results = {}
vfunc = numpy.vectorize(lambda i: pulp.value(i))
for k, v in varnames.items():
try:
results[k] = vfunc(pandas.DataFrame(v).values)
except:
pass
...the above works for the 2D variables, but not the 1-D or 3-D ones.
EDIT: I've returned to this problem more recently on another project and have solved it far more neatly by defining a new class for multidimensional variables:
import pulp
import numpy
class MultiDimensionalLpVariable:
def __init__(self, name, dimensions, low_bound, up_bound, cat):
self.name = name
try:
self.dimensions = (*dimensions,)
except:
self.dimensions = (dimensions,)
self.low_bound = low_bound
self.up_bound = up_bound
assert cat in pulp.LpCategories, 'cat must be one of ("{}").'.format(
'", "'.join(pulp.LpCategories)
)
self.cat = cat
self.variables = self._build_variables_array()
self.values = None
def __getitem__(self, index):
return self.variables[index]
def _build_variables_array(self):
f = numpy.vectorize(self._define_variable)
return numpy.fromfunction(f, self.dimensions, dtype="int")
def _define_variable(self, *index):
name = "_".join(map(str, (self.name, *index)))
return pulp.LpVariable(name, self.low_bound, self.up_bound, self.cat)
def evaluate(self):
f = numpy.vectorize(lambda i: pulp.value(i))
self.values = f(self.variables)
I can now define a new variable as follows:
x = MultiDimensionalLpVariable("x",(10,20), 0, 1, "Binary")
Individual variables can be accessed by doing e.g.
>>> x[1,2]
x_1_2
or:
>>> x[1][2]
x_1_2
This allows constraints to be implemented in a clear syntax, e.g. the equation:
can be written as:
for n in range(N):
problem += sum([x[n][p] for p in range(P)]) == 1
This approach is generalizable to any number of dimensions.
Once the problem has been solved, the values of the variables can be obtained
>>> x.evaluate()
>>> x.values
array([[1., 0., 0., 0., 0.],
[0., 0., 1., 0., 0.],
[1., 0., 0., 0., 0.],
[0., 0., 1., 0., 0.],
[0., 0., 0., 1., 0.]])
Individual variable values can be accessed by doing e.g.
>>> x.values[1,2]
1.0
or:
>>> x.values[1][2]
1.0
