switch model_builder python from numpy to pandas
This commit is contained in:
@@ -37,7 +37,6 @@ py_test(
|
||||
"//ortools/linear_solver/testdata:large_model.mps.gz",
|
||||
"//ortools/linear_solver/testdata:maximization.mps",
|
||||
],
|
||||
python_version = "PY3",
|
||||
deps = [
|
||||
":model_builder_helper",
|
||||
":model_builder_numbers",
|
||||
@@ -65,6 +64,7 @@ py_library(
|
||||
":model_builder_helper",
|
||||
":model_builder_numbers",
|
||||
requirement("numpy"),
|
||||
requirement("pandas"),
|
||||
"//ortools/linear_solver:linear_solver_py_pb2",
|
||||
],
|
||||
)
|
||||
@@ -76,32 +76,8 @@ py_test(
|
||||
"//ortools/linear_solver/testdata:maximization.mps",
|
||||
"//ortools/linear_solver/testdata:small_model.lp",
|
||||
],
|
||||
python_version = "PY3",
|
||||
deps = [
|
||||
":model_builder",
|
||||
":model_builder_helper",
|
||||
":model_builder_numbers",
|
||||
requirement("absl-py"),
|
||||
requirement("numpy"),
|
||||
],
|
||||
)
|
||||
|
||||
py_library(
|
||||
name = "pandas_model",
|
||||
srcs = ["pandas_model.py"],
|
||||
deps = [
|
||||
":model_builder_helper",
|
||||
requirement("numpy"),
|
||||
requirement("pandas"),
|
||||
"//ortools/linear_solver:linear_solver_py_pb2",
|
||||
],
|
||||
)
|
||||
|
||||
py_test(
|
||||
name = "pandas_model_test",
|
||||
srcs = ["pandas_model_test.py"],
|
||||
deps = [
|
||||
":pandas_model",
|
||||
requirement("absl-py"),
|
||||
requirement("pandas"),
|
||||
"//ortools/linear_solver:linear_solver_py_pb2",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,7 @@
|
||||
#include "absl/log/check.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/linear_solver/linear_solver.pb.h"
|
||||
#include "ortools/linear_solver/model_exporter.h"
|
||||
#include "pybind11/eigen.h"
|
||||
@@ -177,10 +178,19 @@ PYBIND11_MODULE(model_builder_helper, m) {
|
||||
arg("mps_string"))
|
||||
.def("import_from_mps_file", &ModelBuilderHelper::ImportFromMpsFile,
|
||||
arg("mps_file"))
|
||||
#if defined(USE_LP_PARSER)
|
||||
.def("import_from_lp_string", &ModelBuilderHelper::ImportFromLpString,
|
||||
arg("lp_string"))
|
||||
.def("import_from_lp_file", &ModelBuilderHelper::ImportFromLpFile,
|
||||
arg("lp_file"))
|
||||
#else
|
||||
.def("import_from_lp_string", [](const std::string& lp_string) {
|
||||
LOG(INFO) << "Parsing LP string is not compiled in";
|
||||
})
|
||||
.def("import_from_lp_file", [](const std::string& lp_file) {
|
||||
LOG(INFO) << "Parsing LP file is not compiled in";
|
||||
})
|
||||
#endif
|
||||
.def(
|
||||
"fill_model_from_sparse_data",
|
||||
[](ModelBuilderHelper* helper,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,9 @@
|
||||
"""MIP example that solves an assignment problem."""
|
||||
# [START program]
|
||||
# [START import]
|
||||
import numpy as np
|
||||
import io
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from ortools.linear_solver.python import model_builder
|
||||
# [END import]
|
||||
@@ -24,20 +26,35 @@ from ortools.linear_solver.python import model_builder
|
||||
def main():
|
||||
# Data
|
||||
# [START data_model]
|
||||
costs = np.array(
|
||||
[
|
||||
[90, 80, 75, 70],
|
||||
[35, 85, 55, 65],
|
||||
[125, 95, 90, 95],
|
||||
[45, 110, 95, 115],
|
||||
[50, 100, 90, 100],
|
||||
]
|
||||
)
|
||||
num_workers, num_tasks = costs.shape
|
||||
data_str = """
|
||||
worker task cost
|
||||
w1 t1 90
|
||||
w1 t2 80
|
||||
w1 t3 75
|
||||
w1 t4 70
|
||||
w2 t1 35
|
||||
w2 t2 85
|
||||
w2 t3 55
|
||||
w2 t4 65
|
||||
w3 t1 125
|
||||
w3 t2 95
|
||||
w3 t3 90
|
||||
w3 t4 95
|
||||
w4 t1 45
|
||||
w4 t2 110
|
||||
w4 t3 95
|
||||
w4 t4 115
|
||||
w5 t1 50
|
||||
w5 t2 110
|
||||
w5 t3 90
|
||||
w5 t4 100
|
||||
"""
|
||||
|
||||
data = pd.read_table(io.StringIO(data_str), sep=r"\s+")
|
||||
# [END data_model]
|
||||
|
||||
# Solver
|
||||
# Create the model.
|
||||
# [START model]
|
||||
model = model_builder.ModelBuilder()
|
||||
# [END model]
|
||||
|
||||
@@ -45,25 +62,23 @@ def main():
|
||||
# [START variables]
|
||||
# x[i, j] is an array of 0-1 variables, which will be 1
|
||||
# if worker i is assigned to task j.
|
||||
x = model.new_bool_var_array(
|
||||
shape=[num_workers, num_tasks], name="x"
|
||||
) # pytype: disable=wrong-arg-types # numpy-scalars
|
||||
x = model.new_bool_var_series(name="x", index=data.index)
|
||||
# [END variables]
|
||||
|
||||
# Constraints
|
||||
# [START constraints]
|
||||
# Each worker is assigned to at most 1 task.
|
||||
for i in range(num_workers):
|
||||
model.add(np.sum(x[i, :]) <= 1)
|
||||
for unused_name, tasks in data.groupby("worker"):
|
||||
model.add(x[tasks.index].sum() <= 1)
|
||||
|
||||
# Each task is assigned to exactly one worker.
|
||||
for j in range(num_tasks):
|
||||
model.add(np.sum(x[:, j]) == 1)
|
||||
for unused_name, workers in data.groupby("task"):
|
||||
model.add(x[workers.index].sum() == 1)
|
||||
# [END constraints]
|
||||
|
||||
# Objective
|
||||
# [START objective]
|
||||
model.minimize(np.dot(x.flatten(), costs.flatten()))
|
||||
model.minimize(data.cost.dot(x))
|
||||
# [END objective]
|
||||
|
||||
# [START solve]
|
||||
@@ -79,11 +94,9 @@ def main():
|
||||
or status == model_builder.SolveStatus.FEASIBLE
|
||||
):
|
||||
print(f"Total cost = {solver.objective_value}\n")
|
||||
for i in range(num_workers):
|
||||
for j in range(num_tasks):
|
||||
# Test if x[i,j] is 1 (with tolerance for floating point arithmetic).
|
||||
if solver.value(x[i, j]) > 0.5:
|
||||
print(f"Worker {i} assigned to task {j}." + f" Cost: {costs[i][j]}")
|
||||
selected = data.loc[solver.values(x).loc[lambda x: x == 1].index]
|
||||
for unused_index, row in selected.iterrows():
|
||||
print(f"{row.task} assigned to {row.worker} with a cost of {row.cost}")
|
||||
else:
|
||||
print("No solution found.")
|
||||
# [END print_solution]
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
"""Solve a simple bin packing problem using a MIP solver."""
|
||||
# [START program]
|
||||
# [START import]
|
||||
import numpy as np
|
||||
import io
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from ortools.linear_solver.python import model_builder
|
||||
# [END import]
|
||||
@@ -25,58 +27,81 @@ from ortools.linear_solver.python import model_builder
|
||||
# [START data_model]
|
||||
def create_data_model():
|
||||
"""Create the data for the example."""
|
||||
data = {}
|
||||
weights = [48, 30, 19, 36, 36, 27, 42, 42, 36, 24, 30]
|
||||
data["weights"] = weights
|
||||
data["items"] = list(range(len(weights)))
|
||||
data["bins"] = data["items"]
|
||||
data["bin_capacity"] = 100
|
||||
return data
|
||||
|
||||
# [END data_model]
|
||||
items_str = """
|
||||
item weight
|
||||
i1 48
|
||||
i2 30
|
||||
i3 19
|
||||
i4 36
|
||||
i5 36
|
||||
i6 27
|
||||
i7 42
|
||||
i8 42
|
||||
i9 36
|
||||
i10 24
|
||||
i11 30
|
||||
"""
|
||||
|
||||
bins_str = """
|
||||
bin capacity
|
||||
b1 100
|
||||
b2 100
|
||||
b3 100
|
||||
b4 100
|
||||
b5 100
|
||||
b6 100
|
||||
b7 100
|
||||
"""
|
||||
|
||||
items = pd.read_table(io.StringIO(items_str), index_col=0, sep=r"\s+")
|
||||
bins = pd.read_table(io.StringIO(bins_str), index_col=0, sep=r"\s+")
|
||||
return items, bins
|
||||
# [END data_model]
|
||||
|
||||
|
||||
def main():
|
||||
# [START data]
|
||||
data = create_data_model()
|
||||
num_items = len(data["items"])
|
||||
num_bins = len(data["bins"])
|
||||
items, bins = create_data_model()
|
||||
# [END data]
|
||||
# [END program_part1]
|
||||
|
||||
# [START solver]
|
||||
# [START model]
|
||||
# Create the model.
|
||||
model = model_builder.ModelBuilder()
|
||||
# [END solver]
|
||||
# [END model]
|
||||
|
||||
# [START program_part2]
|
||||
# [START variables]
|
||||
# Variables
|
||||
# x[i, j] = 1 if item i is packed in bin j.
|
||||
x = model.new_bool_var_array(
|
||||
shape=[num_items, num_bins], name="x"
|
||||
) # pytype: disable=wrong-arg-types # numpy-scalars
|
||||
items_x_bins = pd.MultiIndex.from_product(
|
||||
[items.index, bins.index], names=["item", "bin"]
|
||||
)
|
||||
x = model.new_bool_var_series(name="x", index=items_x_bins)
|
||||
|
||||
# y[j] = 1 if bin j is used.
|
||||
y = model.new_bool_var_array(
|
||||
shape=[num_bins], name="y"
|
||||
) # pytype: disable=wrong-arg-types # numpy-scalars
|
||||
y = model.new_bool_var_series(name="y", index=bins.index)
|
||||
# [END variables]
|
||||
|
||||
# [START constraints]
|
||||
# Constraints
|
||||
# Each item must be in exactly one bin.
|
||||
for i in data["items"]:
|
||||
model.add(np.sum(x[i, :]) == 1)
|
||||
for unused_name, all_copies in x.groupby("item"):
|
||||
model.add(x[all_copies.index].sum() == 1)
|
||||
|
||||
# The amount packed in each bin cannot exceed its capacity.
|
||||
for j in data["bins"]:
|
||||
model.add(np.dot(x[:, j], data["weights"]) <= data["bin_capacity"] * y[j])
|
||||
for selected_bin in bins.index:
|
||||
items_in_bin = x.xs(selected_bin, level="bin")
|
||||
model.add(
|
||||
items_in_bin.dot(items.weight)
|
||||
<= bins.loc[selected_bin].capacity * y[selected_bin]
|
||||
)
|
||||
# [END constraints]
|
||||
|
||||
# [START objective]
|
||||
# Objective: minimize the number of bins used.
|
||||
model.minimize(np.sum(y))
|
||||
model.minimize(y.sum())
|
||||
# [END objective]
|
||||
|
||||
# [START solve]
|
||||
@@ -87,24 +112,23 @@ def main():
|
||||
|
||||
# [START print_solution]
|
||||
if status == model_builder.SolveStatus.OPTIMAL:
|
||||
num_bins = 0.0
|
||||
for j in data["bins"]:
|
||||
if solver.value(y[j]) == 1:
|
||||
bin_items = []
|
||||
bin_weight = 0
|
||||
for i in data["items"]:
|
||||
if solver.value(x[i, j]) > 0:
|
||||
bin_items.append(i)
|
||||
bin_weight += data["weights"][i]
|
||||
if bin_weight > 0:
|
||||
num_bins += 1
|
||||
print("Bin number", j)
|
||||
print(" Items packed:", bin_items)
|
||||
print(" Total weight:", bin_weight)
|
||||
print()
|
||||
print(f"Number of bins used = {solver.objective_value}")
|
||||
|
||||
x_values = solver.values(x)
|
||||
y_values = solver.values(y)
|
||||
active_bins = y_values.loc[lambda x: x == 1].index
|
||||
|
||||
for b in active_bins:
|
||||
print(f"Bin {b}")
|
||||
items_in_bin = x_values.xs(b, level="bin").loc[lambda x: x == 1].index
|
||||
for item in items_in_bin:
|
||||
print(f" Item {item} - weight {items.loc[item].weight}")
|
||||
print(f" Packed items weight: {items.loc[items_in_bin].sum().to_string()}")
|
||||
print()
|
||||
|
||||
print(f"Total packed weight: {items.weight.sum()}")
|
||||
print()
|
||||
print("Number of bins used:", num_bins)
|
||||
print("Time = ", solver.wall_time, " seconds")
|
||||
print(f"Time = {solver.wall_time} seconds")
|
||||
else:
|
||||
print("The problem does not have an optimal solution.")
|
||||
# [END print_solution]
|
||||
|
||||
@@ -46,6 +46,7 @@ def code_sample_py(name):
|
||||
deps = [
|
||||
requirement("absl-py"),
|
||||
requirement("numpy"),
|
||||
requirement("pandas"),
|
||||
"//ortools/linear_solver/python:model_builder",
|
||||
],
|
||||
python_version = "PY3",
|
||||
@@ -63,6 +64,7 @@ def code_sample_py(name):
|
||||
deps = [
|
||||
requirement("absl-py"),
|
||||
requirement("numpy"),
|
||||
requirement("pandas"),
|
||||
],
|
||||
python_version = "PY3",
|
||||
srcs_version = "PY3",
|
||||
|
||||
@@ -29,12 +29,8 @@ def main():
|
||||
|
||||
# [START variables]
|
||||
# Create the variables x and y.
|
||||
x = model.new_num_var(
|
||||
0.0, math.inf, "x"
|
||||
) # pytype: disable=wrong-arg-types # numpy-scalars
|
||||
y = model.new_num_var(
|
||||
0.0, math.inf, "y"
|
||||
) # pytype: disable=wrong-arg-types # numpy-scalars
|
||||
x = model.new_num_var(0.0, math.inf, "x")
|
||||
y = model.new_num_var(0.0, math.inf, "y")
|
||||
|
||||
print("Number of variables =", model.num_variables)
|
||||
# [END variables]
|
||||
|
||||
@@ -29,12 +29,8 @@ def main():
|
||||
|
||||
# [START variables]
|
||||
# x and y are integer non-negative variables.
|
||||
x = model.new_int_var(
|
||||
0.0, math.inf, "x"
|
||||
) # pytype: disable=wrong-arg-types # numpy-scalars
|
||||
y = model.new_int_var(
|
||||
0.0, math.inf, "y"
|
||||
) # pytype: disable=wrong-arg-types # numpy-scalars
|
||||
x = model.new_int_var(0.0, math.inf, "x")
|
||||
y = model.new_int_var(0.0, math.inf, "y")
|
||||
|
||||
print("Number of variables =", model.num_variables)
|
||||
# [END variables]
|
||||
|
||||
Reference in New Issue
Block a user