Skip to content

Commit

Permalink
Add feasibility checks for rectangleguillotine solutions
Browse files Browse the repository at this point in the history
  • Loading branch information
fontanf committed Jan 4, 2025
1 parent ac336aa commit 0921b04
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 12 deletions.
35 changes: 35 additions & 0 deletions include/packingsolver/rectangleguillotine/instance.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ struct ItemType
/** Stack id to which the item type belongs. */
StackId stack_id;

/** Position of the item in the stack. */
ItemPos stack_pos;

/** Indicates if the item is oriented (i.e. cannot be rotated). */
bool oriented;

Expand Down Expand Up @@ -510,6 +513,19 @@ class Instance
const BinType& bin_type,
CutOrientation o) const;

/**
* Return the id of a defect intersecting an x-coordinate in bin type i
* with orientation o.
*
* Return -1 if there is none.
*/
inline DefectId x_intersects_defect(
Length x,
Length b,
Length t,
const BinType& bin_type,
CutOrientation o) const;

/**
* Return the id of a defect intersecting an y-coordinate in bin type i
* with orientation o.
Expand Down Expand Up @@ -807,6 +823,25 @@ DefectId Instance::y_intersects_defect(
return k_min;
}

DefectId Instance::x_intersects_defect(
Length x,
Length b,
Length t,
const BinType& bin_type,
CutOrientation o) const
{
DefectId k_min = -1;
for (const Defect& k: bin_type.defects) {
if (top(k, o) <= b || bottom(k, o) >= t)
continue;
if (left(k, o) >= x || right(k, o) <= x)
continue;
if (k_min == -1 || bottom(k, o) < bottom(bin_type.defects[k_min], o))
k_min = k.id;
}
return k_min;
}

DefectId Instance::x_intersects_defect(
Length x,
const BinType& bin_type,
Expand Down
31 changes: 31 additions & 0 deletions include/packingsolver/rectangleguillotine/solution.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,37 @@ class Solution
/** Instance. */
const Instance* instance_;

/*
* Private attributes: feasiblity
*/

/** Feasibility for the minimum waste length. */
bool minimum_waste_length_feasible_ = true;

/** Feasibility for the minimum distance between two consecutive 1-cuts. */
bool minimum_distance_1_cuts_feasible_ = true;

/** Feasibility for the maximum distance between two consecutive 1-cuts. */
bool maximum_distance_1_cuts_feasible_ = true;

/** Feasibility for the minimum distance between two consecutive 2-cuts. */
bool minimum_distance_2_cuts_feasible_ = true;

/** Feasibility for the maximum number of 2-cuts in a first-level sub-plate. */
bool maximum_number_2_cuts_feasible_ = true;

/** Feasibility for the stacks. */
bool stacks_feasible_ = true;

/** Feasibility for the defect intersections. */
bool defects_feasible_ = true;

/** Feasibility for the cut through defects. */
bool cut_through_defects_feasible_ = true;

/** Overall feasibility. */
bool feasible_ = true;

/*
* Private attributes: bins
*/
Expand Down
2 changes: 2 additions & 0 deletions src/rectangleguillotine/instance_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,7 @@ Instance InstanceBuilder::build()
<= item_type.stack_id) {
instance_.item_type_ids_.push_back({});
}
instance_.item_types_[item_type_id].stack_pos = instance_.item_type_ids_[item_type.stack_id].size();
for (ItemPos c = 0; c < item_type.copies; ++c)
instance_.item_type_ids_[item_type.stack_id].push_back(item_type_id);
}
Expand All @@ -741,6 +742,7 @@ Instance InstanceBuilder::build()
if (item_type.stack_id != -1)
continue;
instance_.item_types_[item_type_id].stack_id = instance_.item_type_ids_.size();
instance_.item_types_[item_type_id].stack_pos = 0;
instance_.item_type_ids_.push_back({});
for (ItemPos c = 0; c < item_type.copies; ++c)
instance_.item_type_ids_[item_type.stack_id].push_back(item_type_id);
Expand Down
169 changes: 169 additions & 0 deletions src/rectangleguillotine/solution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ void Solution::update_indicators(

width_ = 0;
height_ = 0;
Counter subplate1curr_number_of_2_cuts = 0;
Length subpalte1curr_end = -1;
for (const SolutionNode& node: bin.nodes) {
if (node.f != -1 && node.item_type_id >= 0) {
number_of_items_ += bin.copies;
Expand All @@ -73,6 +75,167 @@ void Solution::update_indicators(
width_ = node.r;
if (node.t < bin_type.rect.h && height_ < node.t)
height_ = node.t;

// Check minimum waste length.
if (node.d >= 1
&& node.item_type_id < 0) {
if ((node.r - node.l
< instance().parameters().minimum_waste_length)
|| (node.t - node.b
< instance().parameters().minimum_waste_length)) {
//std::cout << "minimum_waste_length_feasible_ = false" << std::endl;
minimum_waste_length_feasible_ = false;
feasible_ = false;
}
}

// Check minimum distance between 1-cuts.
if (node.d == 1
&& node.item_type_id == -2) {
if ((bin.first_cut_orientation == CutOrientation::Vertical
&& node.r - node.l
< instance().parameters().minimum_distance_1_cuts)
|| (bin.first_cut_orientation == CutOrientation::Horizontal
&& node.t - node.b
< instance().parameters().minimum_distance_1_cuts)) {
//std::cout << "minimum_distance_1_cuts = false" << std::endl;
minimum_distance_1_cuts_feasible_ = false;
feasible_ = false;
}
}

// Check maximum distance between 1-cuts.
if (instance().parameters().maximum_distance_1_cuts >= 0) {
if (node.d == 1
&& node.item_type_id == -2) {
if ((bin.first_cut_orientation == CutOrientation::Vertical
&& node.r - node.l
> instance().parameters().maximum_distance_1_cuts)
|| (bin.first_cut_orientation == CutOrientation::Horizontal
&& node.t - node.b
> instance().parameters().maximum_distance_1_cuts)) {
//std::cout << "maximum_distance_1_cuts = false" << std::endl;
maximum_distance_1_cuts_feasible_ = false;
feasible_ = false;
}
}
}

// Check minimum distance between 2-cuts.
if (node.d == 2
&& node.item_type_id == -2) {
if ((bin.first_cut_orientation == CutOrientation::Vertical
&& node.t - node.b
< instance().parameters().minimum_distance_2_cuts)
|| (bin.first_cut_orientation == CutOrientation::Horizontal
&& node.r - node.l
< instance().parameters().minimum_distance_2_cuts)) {
//std::cout << "minimum_distance_2_cuts = false" << std::endl;
minimum_distance_2_cuts_feasible_ = false;
feasible_ = false;
}
}

// Check maximum number of 2-cuts.
if (instance().parameters().maximum_number_2_cuts >= 0) {
if (node.d == 1) {
subpalte1curr_end = (bin.first_cut_orientation == CutOrientation::Vertical)?
node.t:
node.r;
subplate1curr_number_of_2_cuts = 0;
}
if (node.d == 2) {
if ((bin.first_cut_orientation == CutOrientation::Vertical
&& node.t != subpalte1curr_end)
|| (bin.first_cut_orientation == CutOrientation::Horizontal
&& node.r != subpalte1curr_end)) {
subplate1curr_number_of_2_cuts++;
if (subplate1curr_number_of_2_cuts
> instance().parameters().maximum_number_2_cuts) {
//std::cout << "maximum_number_2_cuts = false" << std::endl;
maximum_number_2_cuts_feasible_ = false;
feasible_ = false;
}
}
}
}

// Check stacks.
if (node.d >= 1
&& node.item_type_id >= 0) {
const ItemType& item_type = instance().item_type(node.item_type_id);
if (item_type.stack_pos > 0) {
ItemTypeId item_type_id_pred = instance().item(
item_type.stack_id,
item_type.stack_pos - 1);
const ItemType& item_type_pred = instance().item_type(item_type_id_pred);
if (item_copies(item_type_id_pred) != item_type_pred.copies) {
//std::cout << "stacks_feasible = false" << std::endl;
//std::cout << "item_type_id " << node.item_type_id
// << " stack_id " << item_type.stack_id
// << " stack_pos " << item_type.stack_pos
// << std::endl;
//std::cout << "item_type_id_pred " << item_type_id_pred
// << " stack_id " << item_type_pred.stack_id
// << " stack_pos " << item_type_pred.stack_pos
// << " copies " << item_copies(item_type_id_pred)
// << " / " << item_type_pred.copies
// << std::endl;
stacks_feasible_ = false;
feasible_ = false;
}
}
}

// Check defect intersections.
if (node.d >= 1
&& node.item_type_id >= 0) {
DefectId k = instance().rect_intersects_defect(
node.l,
node.r,
node.b,
node.t,
bin_type,
CutOrientation::Vertical);
if (k != -1) {
std::cout << "defects_feasible = false" << std::endl;
defects_feasible_ = false;
feasible_ = false;
}
}

// Check cuts through defects.
if (!instance().parameters().cut_through_defects
&& node.d >= 1) {
DefectId kl = instance().x_intersects_defect(
node.l,
node.b,
node.t,
bin_type,
CutOrientation::Vertical);
DefectId kr = instance().x_intersects_defect(
node.r,
node.b,
node.t,
bin_type,
CutOrientation::Vertical);
DefectId kb = instance().y_intersects_defect(
node.l,
node.r,
node.b,
bin_type,
CutOrientation::Vertical);
DefectId kt = instance().y_intersects_defect(
node.l,
node.r,
node.t,
bin_type,
CutOrientation::Vertical);
if (kl != -1 || kr != -1 || kb != -1 || kt != -1) {
cut_through_defects_feasible_ = false;
feasible_ = false;
}
}
}
}

Expand Down Expand Up @@ -120,6 +283,12 @@ void Solution::append(

bool Solution::operator<(const Solution& solution) const
{
// Check feasibility.
if (!solution.feasible_)
return false;
if (!feasible_)
return true;

switch (instance().objective()) {
case Objective::Default: {
if (solution.profit() < profit())
Expand Down
12 changes: 0 additions & 12 deletions src/rectangleguillotine/solution_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -493,17 +493,5 @@ Solution SolutionBuilder::build()
solution_.update_indicators(bin_pos);
}

// Check the size of nodes containing items.
// TODO

// Check min waste constraint.
// TODO

// Check consecutive cuts constraints.
// TODO

// Check stack order.
// TODO

return std::move(solution_);
}

0 comments on commit 0921b04

Please sign in to comment.