use black on examples/python
This commit is contained in:
202
examples/python/knapsack_2d_sat.py
Executable file → Normal file
202
examples/python/knapsack_2d_sat.py
Executable file → Normal file
@@ -11,6 +11,7 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Solver a 2D rectangle knapsack problem.
|
||||
|
||||
This code is adapted from
|
||||
@@ -29,14 +30,16 @@ from google.protobuf import text_format
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
_OUTPUT_PROTO = flags.DEFINE_string(
|
||||
'output_proto', '', 'Output file to write the cp_model proto to.')
|
||||
_PARAMS = flags.DEFINE_string(
|
||||
'params',
|
||||
'num_search_workers:16,log_search_progress:true,max_time_in_seconds:45',
|
||||
'Sat solver parameters.',
|
||||
"output_proto", "", "Output file to write the cp_model proto to."
|
||||
)
|
||||
_PARAMS = flags.DEFINE_string(
|
||||
"params",
|
||||
"num_search_workers:16,log_search_progress:true,max_time_in_seconds:45",
|
||||
"Sat solver parameters.",
|
||||
)
|
||||
_MODEL = flags.DEFINE_string(
|
||||
"model", "rotation", "'duplicate' or 'rotation' or 'optional'"
|
||||
)
|
||||
_MODEL = flags.DEFINE_string('model', 'rotation',
|
||||
'\'duplicate\' or \'rotation\' or \'optional\'')
|
||||
|
||||
|
||||
def build_data():
|
||||
@@ -55,25 +58,25 @@ def build_data():
|
||||
k10 9 11 5 369.560 cyan
|
||||
"""
|
||||
|
||||
data = pd.read_table(io.StringIO(data), sep=r'\s+')
|
||||
print('Input data')
|
||||
data = pd.read_table(io.StringIO(data), sep=r"\s+")
|
||||
print("Input data")
|
||||
print(data)
|
||||
|
||||
max_height = 20
|
||||
max_width = 30
|
||||
|
||||
print(f'Container max_width:{max_width} max_height:{max_height}')
|
||||
print(f'#Items: {len(data.index)}')
|
||||
print(f"Container max_width:{max_width} max_height:{max_height}")
|
||||
print(f"#Items: {len(data.index)}")
|
||||
return (data, max_height, max_width)
|
||||
|
||||
|
||||
def solve_with_duplicate_items(data, max_height, max_width):
|
||||
"""Solve the problem by building 2 items (rotated or not) for each item."""
|
||||
# Derived data (expanded to individual items).
|
||||
data_widths = data['width'].to_numpy()
|
||||
data_heights = data['height'].to_numpy()
|
||||
data_availability = data['available'].to_numpy()
|
||||
data_values = data['value'].to_numpy()
|
||||
data_widths = data["width"].to_numpy()
|
||||
data_heights = data["height"].to_numpy()
|
||||
data_availability = data["available"].to_numpy()
|
||||
data_values = data["value"].to_numpy()
|
||||
|
||||
# Non duplicated items data.
|
||||
base_item_widths = np.repeat(data_widths, data_availability)
|
||||
@@ -102,21 +105,25 @@ def solve_with_duplicate_items(data, max_height, max_width):
|
||||
|
||||
for i in range(num_items):
|
||||
## Is the item used?
|
||||
is_used.append(model.NewBoolVar(f'is_used{i}'))
|
||||
is_used.append(model.NewBoolVar(f"is_used{i}"))
|
||||
|
||||
## Item coordinates.
|
||||
x_starts.append(model.NewIntVar(0, max_width, f'x_start{i}'))
|
||||
x_ends.append(model.NewIntVar(0, max_width, f'x_end{i}'))
|
||||
y_starts.append(model.NewIntVar(0, max_height, f'y_start{i}'))
|
||||
y_ends.append(model.NewIntVar(0, max_height, f'y_end{i}'))
|
||||
x_starts.append(model.NewIntVar(0, max_width, f"x_start{i}"))
|
||||
x_ends.append(model.NewIntVar(0, max_width, f"x_end{i}"))
|
||||
y_starts.append(model.NewIntVar(0, max_height, f"y_start{i}"))
|
||||
y_ends.append(model.NewIntVar(0, max_height, f"y_end{i}"))
|
||||
|
||||
## Interval variables.
|
||||
x_intervals.append(
|
||||
model.NewIntervalVar(x_starts[i], item_widths[i] * is_used[i],
|
||||
x_ends[i], f'x_interval{i}'))
|
||||
model.NewIntervalVar(
|
||||
x_starts[i], item_widths[i] * is_used[i], x_ends[i], f"x_interval{i}"
|
||||
)
|
||||
)
|
||||
y_intervals.append(
|
||||
model.NewIntervalVar(y_starts[i], item_heights[i] * is_used[i],
|
||||
y_ends[i], f'y_interval{i}'))
|
||||
model.NewIntervalVar(
|
||||
y_starts[i], item_heights[i] * is_used[i], y_ends[i], f"y_interval{i}"
|
||||
)
|
||||
)
|
||||
|
||||
# Unused boxes are fixed at (0.0).
|
||||
model.Add(x_starts[i] == 0).OnlyEnforceIf(is_used[i].Not())
|
||||
@@ -136,8 +143,8 @@ def solve_with_duplicate_items(data, max_height, max_width):
|
||||
|
||||
# Output proto to file.
|
||||
if _OUTPUT_PROTO.value:
|
||||
print(f'Writing proto to {_OUTPUT_PROTO.value}')
|
||||
with open(_OUTPUT_PROTO.value, 'w') as text_file:
|
||||
print(f"Writing proto to {_OUTPUT_PROTO.value}")
|
||||
with open(_OUTPUT_PROTO.value, "w") as text_file:
|
||||
text_file.write(str(model))
|
||||
|
||||
# Solve model.
|
||||
@@ -150,25 +157,27 @@ def solve_with_duplicate_items(data, max_height, max_width):
|
||||
# Report solution.
|
||||
if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
|
||||
used = {i for i in range(num_items) if solver.BooleanValue(is_used[i])}
|
||||
data = pd.DataFrame({
|
||||
'x_start': [solver.Value(x_starts[i]) for i in used],
|
||||
'y_start': [solver.Value(y_starts[i]) for i in used],
|
||||
'item_width': [item_widths[i] for i in used],
|
||||
'item_height': [item_heights[i] for i in used],
|
||||
'x_end': [solver.Value(x_ends[i]) for i in used],
|
||||
'y_end': [solver.Value(y_ends[i]) for i in used],
|
||||
'item_value': [item_values[i] for i in used]
|
||||
})
|
||||
data = pd.DataFrame(
|
||||
{
|
||||
"x_start": [solver.Value(x_starts[i]) for i in used],
|
||||
"y_start": [solver.Value(y_starts[i]) for i in used],
|
||||
"item_width": [item_widths[i] for i in used],
|
||||
"item_height": [item_heights[i] for i in used],
|
||||
"x_end": [solver.Value(x_ends[i]) for i in used],
|
||||
"y_end": [solver.Value(y_ends[i]) for i in used],
|
||||
"item_value": [item_values[i] for i in used],
|
||||
}
|
||||
)
|
||||
print(data)
|
||||
|
||||
|
||||
def solve_with_duplicate_optional_items(data, max_height, max_width):
|
||||
"""Solve the problem by building 2 optional items (rotated or not) for each item."""
|
||||
# Derived data (expanded to individual items).
|
||||
data_widths = data['width'].to_numpy()
|
||||
data_heights = data['height'].to_numpy()
|
||||
data_availability = data['available'].to_numpy()
|
||||
data_values = data['value'].to_numpy()
|
||||
data_widths = data["width"].to_numpy()
|
||||
data_heights = data["height"].to_numpy()
|
||||
data_availability = data["available"].to_numpy()
|
||||
data_values = data["value"].to_numpy()
|
||||
|
||||
# Non duplicated items data.
|
||||
base_item_widths = np.repeat(data_widths, data_availability)
|
||||
@@ -195,22 +204,27 @@ def solve_with_duplicate_optional_items(data, max_height, max_width):
|
||||
|
||||
for i in range(num_items):
|
||||
## Is the item used?
|
||||
is_used.append(model.NewBoolVar(f'is_used{i}'))
|
||||
is_used.append(model.NewBoolVar(f"is_used{i}"))
|
||||
|
||||
## Item coordinates.
|
||||
x_starts.append(
|
||||
model.NewIntVar(0, max_width - int(item_widths[i]), f'x_start{i}'))
|
||||
model.NewIntVar(0, max_width - int(item_widths[i]), f"x_start{i}")
|
||||
)
|
||||
y_starts.append(
|
||||
model.NewIntVar(0, max_height - int(item_heights[i]),
|
||||
f'y_start{i}'))
|
||||
model.NewIntVar(0, max_height - int(item_heights[i]), f"y_start{i}")
|
||||
)
|
||||
|
||||
## Interval variables.
|
||||
x_intervals.append(
|
||||
model.NewOptionalFixedSizeIntervalVar(x_starts[i], item_widths[i],
|
||||
is_used[i], f'x_interval{i}'))
|
||||
model.NewOptionalFixedSizeIntervalVar(
|
||||
x_starts[i], item_widths[i], is_used[i], f"x_interval{i}"
|
||||
)
|
||||
)
|
||||
y_intervals.append(
|
||||
model.NewOptionalFixedSizeIntervalVar(y_starts[i], item_heights[i],
|
||||
is_used[i], f'y_interval{i}'))
|
||||
model.NewOptionalFixedSizeIntervalVar(
|
||||
y_starts[i], item_heights[i], is_used[i], f"y_interval{i}"
|
||||
)
|
||||
)
|
||||
# Unused boxes are fixed at (0.0).
|
||||
model.Add(x_starts[i] == 0).OnlyEnforceIf(is_used[i].Not())
|
||||
model.Add(y_starts[i] == 0).OnlyEnforceIf(is_used[i].Not())
|
||||
@@ -229,8 +243,8 @@ def solve_with_duplicate_optional_items(data, max_height, max_width):
|
||||
|
||||
# Output proto to file.
|
||||
if _OUTPUT_PROTO.value:
|
||||
print(f'Writing proto to {_OUTPUT_PROTO.value}')
|
||||
with open(_OUTPUT_PROTO.value, 'w') as text_file:
|
||||
print(f"Writing proto to {_OUTPUT_PROTO.value}")
|
||||
with open(_OUTPUT_PROTO.value, "w") as text_file:
|
||||
text_file.write(str(model))
|
||||
|
||||
# Solve model.
|
||||
@@ -243,27 +257,27 @@ def solve_with_duplicate_optional_items(data, max_height, max_width):
|
||||
# Report solution.
|
||||
if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
|
||||
used = {i for i in range(num_items) if solver.BooleanValue(is_used[i])}
|
||||
data = pd.DataFrame({
|
||||
'x_start': [solver.Value(x_starts[i]) for i in used],
|
||||
'y_start': [solver.Value(y_starts[i]) for i in used],
|
||||
'item_width': [item_widths[i] for i in used],
|
||||
'item_height': [item_heights[i] for i in used],
|
||||
'x_end': [solver.Value(x_starts[i]) + item_widths[i] for i in used],
|
||||
'y_end': [
|
||||
solver.Value(y_starts[i]) + item_heights[i] for i in used
|
||||
],
|
||||
'item_value': [item_values[i] for i in used]
|
||||
})
|
||||
data = pd.DataFrame(
|
||||
{
|
||||
"x_start": [solver.Value(x_starts[i]) for i in used],
|
||||
"y_start": [solver.Value(y_starts[i]) for i in used],
|
||||
"item_width": [item_widths[i] for i in used],
|
||||
"item_height": [item_heights[i] for i in used],
|
||||
"x_end": [solver.Value(x_starts[i]) + item_widths[i] for i in used],
|
||||
"y_end": [solver.Value(y_starts[i]) + item_heights[i] for i in used],
|
||||
"item_value": [item_values[i] for i in used],
|
||||
}
|
||||
)
|
||||
print(data)
|
||||
|
||||
|
||||
def solve_with_rotations(data, max_height, max_width):
|
||||
"""Solve the problem by rotating items."""
|
||||
# Derived data (expanded to individual items).
|
||||
data_widths = data['width'].to_numpy()
|
||||
data_heights = data['height'].to_numpy()
|
||||
data_availability = data['available'].to_numpy()
|
||||
data_values = data['value'].to_numpy()
|
||||
data_widths = data["width"].to_numpy()
|
||||
data_heights = data["height"].to_numpy()
|
||||
data_availability = data["available"].to_numpy()
|
||||
data_values = data["value"].to_numpy()
|
||||
|
||||
item_widths = np.repeat(data_widths, data_availability)
|
||||
item_heights = np.repeat(data_heights, data_availability)
|
||||
@@ -287,26 +301,26 @@ def solve_with_rotations(data, max_height, max_width):
|
||||
for i in range(num_items):
|
||||
sizes = [0, int(item_widths[i]), int(item_heights[i])]
|
||||
# X coordinates.
|
||||
x_starts.append(model.NewIntVar(0, max_width, f'x_start{i}'))
|
||||
x_starts.append(model.NewIntVar(0, max_width, f"x_start{i}"))
|
||||
x_sizes.append(
|
||||
model.NewIntVarFromDomain(cp_model.Domain.FromValues(sizes),
|
||||
f'x_size{i}'))
|
||||
x_ends.append(model.NewIntVar(0, max_width, f'x_end{i}'))
|
||||
model.NewIntVarFromDomain(cp_model.Domain.FromValues(sizes), f"x_size{i}")
|
||||
)
|
||||
x_ends.append(model.NewIntVar(0, max_width, f"x_end{i}"))
|
||||
|
||||
# Y coordinates.
|
||||
y_starts.append(model.NewIntVar(0, max_height, f'y_start{i}'))
|
||||
y_starts.append(model.NewIntVar(0, max_height, f"y_start{i}"))
|
||||
y_sizes.append(
|
||||
model.NewIntVarFromDomain(cp_model.Domain.FromValues(sizes),
|
||||
f'y_size{i}'))
|
||||
y_ends.append(model.NewIntVar(0, max_height, f'y_end{i}'))
|
||||
model.NewIntVarFromDomain(cp_model.Domain.FromValues(sizes), f"y_size{i}")
|
||||
)
|
||||
y_ends.append(model.NewIntVar(0, max_height, f"y_end{i}"))
|
||||
|
||||
## Interval variables
|
||||
x_intervals.append(
|
||||
model.NewIntervalVar(x_starts[i], x_sizes[i], x_ends[i],
|
||||
f'x_interval{i}'))
|
||||
model.NewIntervalVar(x_starts[i], x_sizes[i], x_ends[i], f"x_interval{i}")
|
||||
)
|
||||
y_intervals.append(
|
||||
model.NewIntervalVar(y_starts[i], y_sizes[i], y_ends[i],
|
||||
f'y_interval{i}'))
|
||||
model.NewIntervalVar(y_starts[i], y_sizes[i], y_ends[i], f"y_interval{i}")
|
||||
)
|
||||
|
||||
# is_used[i] == True if and only if item i is selected.
|
||||
is_used = []
|
||||
@@ -315,9 +329,9 @@ def solve_with_rotations(data, max_height, max_width):
|
||||
|
||||
## for each item, decide is unselected, no_rotation, rotated.
|
||||
for i in range(num_items):
|
||||
not_selected = model.NewBoolVar(f'not_selected_{i}')
|
||||
no_rotation = model.NewBoolVar(f'no_rotation_{i}')
|
||||
rotated = model.NewBoolVar(f'rotated_{i}')
|
||||
not_selected = model.NewBoolVar(f"not_selected_{i}")
|
||||
no_rotation = model.NewBoolVar(f"no_rotation_{i}")
|
||||
rotated = model.NewBoolVar(f"rotated_{i}")
|
||||
|
||||
### Exactly one state must be chosen.
|
||||
model.AddExactlyOne(not_selected, no_rotation, rotated)
|
||||
@@ -346,8 +360,8 @@ def solve_with_rotations(data, max_height, max_width):
|
||||
|
||||
# Output proto to file.
|
||||
if _OUTPUT_PROTO.value:
|
||||
print(f'Writing proto to {_OUTPUT_PROTO.value}')
|
||||
with open(_OUTPUT_PROTO.value, 'w') as text_file:
|
||||
print(f"Writing proto to {_OUTPUT_PROTO.value}")
|
||||
with open(_OUTPUT_PROTO.value, "w") as text_file:
|
||||
text_file.write(str(model))
|
||||
|
||||
# Solve model.
|
||||
@@ -360,28 +374,30 @@ def solve_with_rotations(data, max_height, max_width):
|
||||
# Report solution.
|
||||
if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
|
||||
used = {i for i in range(num_items) if solver.BooleanValue(is_used[i])}
|
||||
data = pd.DataFrame({
|
||||
'x_start': [solver.Value(x_starts[i]) for i in used],
|
||||
'y_start': [solver.Value(y_starts[i]) for i in used],
|
||||
'item_width': [solver.Value(x_sizes[i]) for i in used],
|
||||
'item_height': [solver.Value(y_sizes[i]) for i in used],
|
||||
'x_end': [solver.Value(x_ends[i]) for i in used],
|
||||
'y_end': [solver.Value(y_ends[i]) for i in used],
|
||||
'item_value': [item_values[i] for i in used]
|
||||
})
|
||||
data = pd.DataFrame(
|
||||
{
|
||||
"x_start": [solver.Value(x_starts[i]) for i in used],
|
||||
"y_start": [solver.Value(y_starts[i]) for i in used],
|
||||
"item_width": [solver.Value(x_sizes[i]) for i in used],
|
||||
"item_height": [solver.Value(y_sizes[i]) for i in used],
|
||||
"x_end": [solver.Value(x_ends[i]) for i in used],
|
||||
"y_end": [solver.Value(y_ends[i]) for i in used],
|
||||
"item_value": [item_values[i] for i in used],
|
||||
}
|
||||
)
|
||||
print(data)
|
||||
|
||||
|
||||
def main(_):
|
||||
"""Solve the problem with all models."""
|
||||
data, max_height, max_width = build_data()
|
||||
if _MODEL.value == 'duplicate':
|
||||
if _MODEL.value == "duplicate":
|
||||
solve_with_duplicate_items(data, max_height, max_width)
|
||||
elif _MODEL.value == 'optional':
|
||||
elif _MODEL.value == "optional":
|
||||
solve_with_duplicate_optional_items(data, max_height, max_width)
|
||||
else:
|
||||
solve_with_rotations(data, max_height, max_width)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
app.run(main)
|
||||
|
||||
Reference in New Issue
Block a user