Skip to content

Commit f2dea4a

Browse files
committed
Support for limiting the number of guillotine stages
1 parent b8ff0eb commit f2dea4a

File tree

8 files changed

+52
-28
lines changed

8 files changed

+52
-28
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ The algorithm currently has support for:
2020
- [x] 90° rotation of items
2121
- [x] can handle instances with insufficient bins to produce all items
2222
- [x] configurable cost and stock quantity per bin type
23+
- [x] configurable maximum number of guillotine stages
2324

2425
# How to use
2526

@@ -82,6 +83,9 @@ If provided, the algorithm will run until the predefined number of iterations is
8283
Both `maxRRIterations` and `maxRunTime` fields are optional.
8384
The algorithm will continue execution until either, one of the termination conditions (defined in the config json) is reached, or it is manually terminated (CTRL+C).
8485

86+
The `maxStages` field (optional) can be used to limit the number stages the guillotine saw can make.
87+
A value of `"maxStages": 2` will ensure that all patterns can be cut with at most 2 rotations of the guillotine saw.
88+
8589
Configuring more than 1 thread for instances with only a single type of bin won't make much of an improvement to the end result.
8690
On the contrary, many threads will result in a reduction of iterations/s per individual thread.
8791
Which, in turn, can lead to increased runtimes to reach the same solution quality.

src/core/entities/layout.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use generational_arena::{Arena, Index};
22
use itertools::Itertools;
3-
43
use crate::core::{cost::Cost, insertion::insertion_blueprint::InsertionBlueprint};
54
use crate::core::entities::node::Node;
65
use crate::core::insertion::node_blueprint::NodeBlueprint;
@@ -25,7 +24,7 @@ pub struct Layout<'a> {
2524
impl<'a> Layout<'a> {
2625
pub fn new(id: usize, sheettype: &'a SheetType, first_cut_orientation: Orientation) -> Self {
2726
let mut nodes = Arena::new();
28-
let top_node = Node::new(sheettype.width(), sheettype.height(), first_cut_orientation, None);
27+
let top_node = Node::new(0, sheettype.width(), sheettype.height(), first_cut_orientation, None);
2928
let top_node_i = nodes.insert(top_node);
3029

3130
let mut layout = Self {
@@ -39,7 +38,7 @@ impl<'a> Layout<'a> {
3938
};
4039

4140
//The top node cannot be modified, so we register a placeholder node to be able to insert parts
42-
let placeholder_node = Node::new(sheettype.width(), sheettype.height(), first_cut_orientation.rotate(), None);
41+
let placeholder_node = Node::new(1, sheettype.width(), sheettype.height(), first_cut_orientation.rotate(), None);
4342
layout.register_node(placeholder_node, top_node_i, true);
4443

4544
layout
@@ -75,7 +74,7 @@ impl<'a> Layout<'a> {
7574
fn implement_node_blueprint(&mut self, parent: Index, blueprint: &NodeBlueprint, instance: &'a Instance, new_nodes: &mut Vec<Index>) {
7675
let parttype = blueprint.parttype_id().map(|id| instance.get_parttype(id));
7776

78-
let node = Node::new(blueprint.width(), blueprint.height(), blueprint.next_cut_orient(), parttype);
77+
let node = Node::new(self.nodes[parent].level() + 1, blueprint.width(), blueprint.height(), blueprint.next_cut_orient(), parttype);
7978
let node_index = self.register_node(node, parent, blueprint.is_empty());
8079

8180
new_nodes.push(node_index);
@@ -138,11 +137,11 @@ impl<'a> Layout<'a> {
138137
let replacement_node = match parent_node.next_cut_orient() {
139138
Orientation::Horizontal => {
140139
let new_height = empty_node.height() + node.height();
141-
Node::new(node.width(), new_height, node.next_cut_orient(), None)
140+
Node::new(node.level(), node.width(), new_height, node.next_cut_orient(), None)
142141
}
143142
Orientation::Vertical => {
144143
let new_width = empty_node.width() + node.width();
145-
Node::new(new_width, node.height(), node.next_cut_orient(), None)
144+
Node::new(node.level(), new_width, node.height(), node.next_cut_orient(), None)
146145
}
147146
};
148147

@@ -155,7 +154,7 @@ impl<'a> Layout<'a> {
155154
let grandparent_index = parent_node.parent().expect("grandparent node needs to be present").clone();
156155

157156
//create empty parent
158-
let empty_parent_node = Node::new(parent_node.width(), parent_node.height(), parent_node.next_cut_orient(), None);
157+
let empty_parent_node = Node::new(parent_node.level(), parent_node.width(), parent_node.height(), parent_node.next_cut_orient(), None);
159158

160159
//replace
161160
self.unregister_node(parent_node_index, &mut removed_parts);
@@ -167,7 +166,7 @@ impl<'a> Layout<'a> {
167166

168167
//create empty replacement node
169168
let node = &self.nodes[node_index];
170-
let replacement_node = Node::new(node.width(), node.height(), node.next_cut_orient(), None);
169+
let replacement_node = Node::new(node.level(), node.width(), node.height(), node.next_cut_orient(), None);
171170

172171
//replace
173172
self.unregister_node(node_index, &mut removed_parts);
@@ -211,6 +210,8 @@ impl<'a> Layout<'a> {
211210
self.register_part(parttype);
212211
}
213212

213+
debug_assert!(node.level() == self.nodes[parent].level() + 1);
214+
214215
let node_index = self.nodes.insert(node);
215216

216217
//All empty nodes need to be added to the sorted empty nodes list

src/core/entities/node.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::core::rotation::Rotation;
99

1010
#[derive(Debug, Clone)]
1111
pub struct Node<'a> {
12+
level: u8,
1213
width: u64,
1314
height: u64,
1415
children: Vec<Index>,
@@ -19,8 +20,9 @@ pub struct Node<'a> {
1920

2021

2122
impl<'a> Node<'a> {
22-
pub fn new(width: u64, height: u64, next_cut_orient: Orientation, parttype: Option<&'a PartType>) -> Node<'a> {
23+
pub fn new(level: u8, width: u64, height: u64, next_cut_orient: Orientation, parttype: Option<&'a PartType>) -> Node<'a> {
2324
Node {
25+
level,
2426
width,
2527
height,
2628
children: vec![],
@@ -43,7 +45,7 @@ impl<'a> Node<'a> {
4345
self.children.remove(old_child_index);
4446
}
4547

46-
pub fn generate_insertion_node_blueprints(&self, parttype: &'a PartType, rotation: Rotation, mut insertion_replacements: Vec<Vec<NodeBlueprint>>) -> Vec<Vec<NodeBlueprint>> {
48+
pub fn generate_insertion_node_blueprints(&self, parttype: &'a PartType, rotation: Rotation, max_level: u8, mut insertion_replacements: Vec<Vec<NodeBlueprint>>) -> Vec<Vec<NodeBlueprint>> {
4749
debug_assert!(self.insertion_possible(parttype, rotation));
4850

4951
let part_size = match rotation {
@@ -101,7 +103,7 @@ impl<'a> Node<'a> {
101103
---***** ---*****
102104
*/
103105

104-
if self.next_cut_orient == Orientation::Horizontal && self.width == part_size.width() {
106+
if self.next_cut_orient == Orientation::Horizontal && self.width == part_size.width() && self.level < max_level {
105107
let mut copy = NodeBlueprint::new(self.width, self.height, None, self.next_cut_orient);
106108

107109
let remainder_height = self.height - part_size.height();
@@ -116,7 +118,7 @@ impl<'a> Node<'a> {
116118
return insertion_replacements;
117119
}
118120

119-
if self.next_cut_orient == Orientation::Vertical && self.height == part_size.height() {
121+
if self.next_cut_orient == Orientation::Vertical && self.height == part_size.height() && self.level < max_level {
120122
let mut copy = NodeBlueprint::new(self.width, self.height, None, self.next_cut_orient);
121123

122124
let remainder_width = self.width - part_size.width();
@@ -145,7 +147,7 @@ impl<'a> Node<'a> {
145147
This requires an extra available level
146148
*/
147149

148-
if self.next_cut_orient == Orientation::Horizontal {
150+
if self.next_cut_orient == Orientation::Horizontal && self.level < max_level {
149151
let remainder_width_top = self.width - part_size.width();
150152
let mut part_node_parent = NodeBlueprint::new(part_size.width(), self.height, None, self.next_cut_orient);
151153
let remainder_node_top = NodeBlueprint::new(remainder_width_top, self.height, None, self.next_cut_orient);
@@ -160,7 +162,7 @@ impl<'a> Node<'a> {
160162
insertion_replacements.push(vec![part_node_parent, remainder_node_top]);
161163
}
162164

163-
if self.next_cut_orient == Orientation::Vertical {
165+
if self.next_cut_orient == Orientation::Vertical && self.level < max_level {
164166
let remainder_height_top = self.height - part_size.height();
165167
let mut part_node_parent = NodeBlueprint::new(self.width, part_size.height(), None, self.next_cut_orient);
166168
let remainder_node_top = NodeBlueprint::new(self.width, remainder_height_top, None, self.next_cut_orient);
@@ -185,7 +187,7 @@ impl<'a> Node<'a> {
185187
186188
*/
187189

188-
if self.next_cut_orient == Orientation::Horizontal {
190+
if self.next_cut_orient == Orientation::Horizontal && self.level + 1 < max_level {
189191
let mut copy = NodeBlueprint::new(self.width, self.height, None, self.next_cut_orient);
190192

191193
let remainder_height_top = self.height - part_size.height();
@@ -205,7 +207,7 @@ impl<'a> Node<'a> {
205207
insertion_replacements.push(vec![copy]);
206208
}
207209

208-
if self.next_cut_orient == Orientation::Vertical {
210+
if self.next_cut_orient == Orientation::Vertical && self.level + 1 < max_level {
209211
let mut copy = NodeBlueprint::new(self.width, self.height, None, self.next_cut_orient);
210212

211213
let remainder_width_top = self.width - part_size.width();
@@ -225,7 +227,7 @@ impl<'a> Node<'a> {
225227

226228
insertion_replacements.push(vec![copy]);
227229
}
228-
return insertion_replacements;
230+
insertion_replacements
229231
}
230232

231233
pub fn insertion_possible(&self, parttype: &PartType, rotation: Rotation) -> bool {
@@ -274,6 +276,9 @@ impl<'a> Node<'a> {
274276
pub fn parent(&self) -> &Option<Index> {
275277
&self.parent
276278
}
279+
pub fn level(&self) -> u8 {
280+
self.level
281+
}
277282
}
278283

279284

src/core/entities/sheettype.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,18 @@ pub struct SheetType {
88
height: u64,
99
value: u64,
1010
fixed_first_cut_orientation: Option<Orientation>,
11+
max_stages: u8,
1112
}
1213

1314
impl SheetType {
14-
pub fn new(id: usize, width: u64, height: u64, value: u64, fixed_first_cut_orientation: Option<Orientation>) -> SheetType {
15+
pub fn new(id: usize, width: u64, height: u64, value: u64, fixed_first_cut_orientation: Option<Orientation>, max_stages: u8) -> SheetType {
1516
SheetType {
1617
id,
1718
width,
1819
height,
1920
value,
2021
fixed_first_cut_orientation,
22+
max_stages,
2123
}
2224
}
2325

@@ -44,6 +46,10 @@ impl SheetType {
4446
pub fn fixed_first_cut_orientation(&self) -> Option<Orientation> {
4547
self.fixed_first_cut_orientation
4648
}
49+
50+
pub fn max_stages(&self) -> u8 {
51+
self.max_stages
52+
}
4753
}
4854

4955
impl Hash for SheetType {

src/core/insertion/insertion_option.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::fmt::Debug;
22

3-
use generational_arena::{Index};
3+
use generational_arena::Index;
44
use itertools::Itertools;
55

66
use crate::core::cost::Cost;
@@ -29,20 +29,21 @@ impl<'a> InsertionOption<'a> {
2929
layout_i,
3030
original_node_i,
3131
parttype,
32-
rotation
32+
rotation,
3333
}
3434
}
3535

3636
pub fn generate_blueprints(&self, problem: &Problem) -> Vec<InsertionBlueprint<'a>> {
3737
let layout = problem.get_layout(&self.layout_i);
3838
let original_node = &layout.nodes()[self.original_node_i];
39+
let max_stages = layout.sheettype().max_stages();
3940
let node_blueprints = match self.rotation {
4041
Some(rotation) => {
41-
original_node.generate_insertion_node_blueprints(self.parttype, rotation, vec![])
42+
original_node.generate_insertion_node_blueprints(self.parttype, rotation, max_stages, vec![])
4243
}
4344
None => {
44-
let node_blueprints = original_node.generate_insertion_node_blueprints(self.parttype, Rotation::Default, vec![]);
45-
original_node.generate_insertion_node_blueprints(self.parttype, Rotation::Rotated, node_blueprints)
45+
let node_blueprints = original_node.generate_insertion_node_blueprints(self.parttype, Rotation::Default, max_stages, vec![]);
46+
original_node.generate_insertion_node_blueprints(self.parttype, Rotation::Rotated, max_stages, node_blueprints)
4647
}
4748
};
4849
let original_cost = original_node.calculate_cost();

src/io/parser.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,18 @@ pub fn generate_instance(json_instance: &mut JsonInstance, config: &Config) -> I
3939
SheetValuationMode::Cost => json_sheet.cost
4040
};
4141

42+
let max_stages = config.max_stages.unwrap_or(u8::MAX);
43+
4244
let sheettype = SheetType::new(
4345
sheet_id,
4446
json_sheet.length,
4547
json_sheet.height,
4648
sheet_value,
4749
None,
50+
max_stages
4851
);
49-
let stock = match json_sheet.stock {
50-
Some(stock) => stock,
51-
None => usize::MAX
52-
};
52+
53+
let stock = json_sheet.stock.unwrap_or(usize::MAX);
5354
sheets.push((sheettype, stock));
5455
sheet_id += 1;
5556
}

src/optimization/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub struct Config {
1515
pub rotation_allowed: bool,
1616
pub n_threads: usize,
1717
pub sheet_valuation_mode : SheetValuationMode,
18+
pub max_stages: Option<u8>,
1819
}
1920

2021
#[derive(Serialize, Deserialize)]

src/optimization/sol_collectors/global_sol_collector.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,12 @@ impl GlobalSolCollector {
129129
self.best_complete_solution = Some(solution.clone());
130130

131131
for tx_sync in &self.tx_syncs {
132-
tx_sync.send(SyncMessage::SyncMatLimit(solution.cost().material_cost)).expect("Error sending sync matlimit message");
132+
match tx_sync.send(SyncMessage::SyncMatLimit(solution.cost().material_cost)) {
133+
Ok(_) => {},
134+
Err(err) => {
135+
timed_println!("{}: {:?}", "Error syncing material limit".bright_red().bold(), err.to_string());
136+
},
137+
}
133138
}
134139
}
135140
}

0 commit comments

Comments
 (0)