spyrrow 0.6.0__tar.gz → 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of spyrrow might be problematic. Click here for more details.

@@ -326,9 +326,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
326
326
 
327
327
  [[package]]
328
328
  name = "hermit-abi"
329
- version = "0.3.9"
329
+ version = "0.5.2"
330
330
  source = "registry+https://github.com/rust-lang/crates.io-index"
331
- checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
331
+ checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
332
332
 
333
333
  [[package]]
334
334
  name = "indoc"
@@ -359,8 +359,8 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
359
359
 
360
360
  [[package]]
361
361
  name = "jagua-rs"
362
- version = "0.6.2"
363
- source = "git+https://github.com/JeroenGar/jagua-rs.git?rev=6e47329e4fd8dfe014428bd9045db132d26d35c9#6e47329e4fd8dfe014428bd9045db132d26d35c9"
362
+ version = "0.6.3"
363
+ source = "git+https://github.com/JeroenGar/jagua-rs.git?rev=f8f18907a6a82310769eed60fa6d8c0e7aaea0d3#f8f18907a6a82310769eed60fa6d8c0e7aaea0d3"
364
364
  dependencies = [
365
365
  "anyhow",
366
366
  "document-features",
@@ -535,9 +535,9 @@ dependencies = [
535
535
 
536
536
  [[package]]
537
537
  name = "num_cpus"
538
- version = "1.16.0"
538
+ version = "1.17.0"
539
539
  source = "registry+https://github.com/rust-lang/crates.io-index"
540
- checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
540
+ checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
541
541
  dependencies = [
542
542
  "hermit-abi",
543
543
  "libc",
@@ -603,11 +603,10 @@ dependencies = [
603
603
 
604
604
  [[package]]
605
605
  name = "pyo3"
606
- version = "0.24.1"
606
+ version = "0.25.1"
607
607
  source = "registry+https://github.com/rust-lang/crates.io-index"
608
- checksum = "17da310086b068fbdcefbba30aeb3721d5bb9af8db4987d6735b2183ca567229"
608
+ checksum = "8970a78afe0628a3e3430376fc5fd76b6b45c4d43360ffd6cdd40bdde72b682a"
609
609
  dependencies = [
610
- "cfg-if",
611
610
  "indoc",
612
611
  "libc",
613
612
  "memoffset",
@@ -621,9 +620,9 @@ dependencies = [
621
620
 
622
621
  [[package]]
623
622
  name = "pyo3-build-config"
624
- version = "0.24.1"
623
+ version = "0.25.1"
625
624
  source = "registry+https://github.com/rust-lang/crates.io-index"
626
- checksum = "e27165889bd793000a098bb966adc4300c312497ea25cf7a690a9f0ac5aa5fc1"
625
+ checksum = "458eb0c55e7ece017adeba38f2248ff3ac615e53660d7c71a238d7d2a01c7598"
627
626
  dependencies = [
628
627
  "once_cell",
629
628
  "target-lexicon",
@@ -631,9 +630,9 @@ dependencies = [
631
630
 
632
631
  [[package]]
633
632
  name = "pyo3-ffi"
634
- version = "0.24.1"
633
+ version = "0.25.1"
635
634
  source = "registry+https://github.com/rust-lang/crates.io-index"
636
- checksum = "05280526e1dbf6b420062f3ef228b78c0c54ba94e157f5cb724a609d0f2faabc"
635
+ checksum = "7114fe5457c61b276ab77c5055f206295b812608083644a5c5b2640c3102565c"
637
636
  dependencies = [
638
637
  "libc",
639
638
  "pyo3-build-config",
@@ -641,9 +640,9 @@ dependencies = [
641
640
 
642
641
  [[package]]
643
642
  name = "pyo3-macros"
644
- version = "0.24.1"
643
+ version = "0.25.1"
645
644
  source = "registry+https://github.com/rust-lang/crates.io-index"
646
- checksum = "5c3ce5686aa4d3f63359a5100c62a127c9f15e8398e5fdeb5deef1fed5cd5f44"
645
+ checksum = "a8725c0a622b374d6cb051d11a0983786448f7785336139c3c94f5aa6bef7e50"
647
646
  dependencies = [
648
647
  "proc-macro2",
649
648
  "pyo3-macros-backend",
@@ -653,9 +652,9 @@ dependencies = [
653
652
 
654
653
  [[package]]
655
654
  name = "pyo3-macros-backend"
656
- version = "0.24.1"
655
+ version = "0.25.1"
657
656
  source = "registry+https://github.com/rust-lang/crates.io-index"
658
- checksum = "f4cf6faa0cbfb0ed08e89beb8103ae9724eb4750e3a78084ba4017cbe94f3855"
657
+ checksum = "4109984c22491085343c05b0dbc54ddc405c3cf7b4374fc533f5c3313a572ccc"
659
658
  dependencies = [
660
659
  "heck",
661
660
  "proc-macro2",
@@ -839,7 +838,7 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
839
838
  [[package]]
840
839
  name = "sparrow"
841
840
  version = "0.1.0"
842
- source = "git+https://github.com/JeroenGar/sparrow.git?rev=7f069e9d146f5599d7ba69a47ab4e8097c56e54f#7f069e9d146f5599d7ba69a47ab4e8097c56e54f"
841
+ source = "git+https://github.com/JeroenGar/sparrow.git?rev=8361c59e2d800e3492ae5e7f7f781c575bdde636#8361c59e2d800e3492ae5e7f7f781c575bdde636"
843
842
  dependencies = [
844
843
  "anyhow",
845
844
  "clap",
@@ -876,9 +875,10 @@ dependencies = [
876
875
 
877
876
  [[package]]
878
877
  name = "spyrrow"
879
- version = "0.6.0"
878
+ version = "0.7.0"
880
879
  dependencies = [
881
880
  "jagua-rs",
881
+ "num_cpus",
882
882
  "pyo3",
883
883
  "rand",
884
884
  "serde",
@@ -1,8 +1,9 @@
1
1
  [package]
2
2
  name = "spyrrow"
3
- version = "0.6.0"
3
+ version = "0.7.0"
4
4
  edition = "2024"
5
5
  license = "MIT"
6
+ readme = "README.md"
6
7
 
7
8
  # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8
9
  [lib]
@@ -10,9 +11,10 @@ name = "spyrrow"
10
11
  crate-type = ["cdylib"]
11
12
 
12
13
  [dependencies]
13
- jagua-rs = { git = "https://github.com/JeroenGar/jagua-rs.git", rev="6e47329e4fd8dfe014428bd9045db132d26d35c9", features = ["spp"], default-features = false}
14
- pyo3 = "0.24.0"
14
+ jagua-rs = { git = "https://github.com/JeroenGar/jagua-rs.git", rev="f8f18907a6a82310769eed60fa6d8c0e7aaea0d3", features = ["spp"]}
15
+ pyo3 = "0.25.1"
15
16
  rand = { version = "0.9.0", features = ["small_rng"] }
16
17
  serde = {version = "1.0.219", features = ["derive"]}
17
18
  serde_json = "1.0.140"
18
- sparrow = { git = "https://github.com/JeroenGar/sparrow.git",rev="7f069e9d146f5599d7ba69a47ab4e8097c56e54f",features = ["only_final_svg"], default-features = false}
19
+ sparrow = { git = "https://github.com/JeroenGar/sparrow.git",rev="8361c59e2d800e3492ae5e7f7f781c575bdde636",features = ["only_final_svg"]}
20
+ num_cpus="1.17.0"
spyrrow-0.7.0/PKG-INFO ADDED
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: spyrrow
3
+ Version: 0.7.0
4
+ Classifier: Programming Language :: Rust
5
+ Classifier: Programming Language :: Python :: Implementation :: CPython
6
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
7
+ License-File: LICENSE.txt
8
+ Author-email: Paul Durand-Lupinski <paul.durand-lupinski@reeverse-systems.com>
9
+ Requires-Python: >=3.10
10
+ Project-URL: documentation, https://spyrrow.readthedocs.io/
11
+ Project-URL: source, https://github.com/PaulDL-RS/spyrrow
@@ -0,0 +1,4 @@
1
+
2
+ # Configuration object in Python can have a lot of parameters thanks to keyword arguments
3
+ # Creating intermediary types to reduce parameter number increases complexity for the Python user
4
+ too-many-arguments-threshold=8
@@ -7,6 +7,8 @@ Public API of `spyrrow`
7
7
 
8
8
  .. autoclass:: spyrrow::StripPackingInstance
9
9
 
10
+ .. autoclass:: spyrrow::StripPackingConfig
11
+
10
12
  .. autoclass:: spyrrow::StripPackingSolution
11
13
 
12
14
  .. autoclass:: spyrrow::PlacedItem
@@ -46,7 +46,8 @@ Examples
46
46
  )
47
47
 
48
48
  instance = spyrrow.StripPackingInstance("test", strip_height=2.001, items=[rectangle1,triangle1])
49
- sol:spyrrow.StripPackingSolution = instance.solve(30)
49
+ config = spyrrow.StripPackingConfig(early_termination=False,total_computation_time=60,num_wokers=3,seed=0)
50
+ sol = instance.solve(config)
50
51
  print(sol.width)
51
52
  print(sol.density)
52
53
  print("\n")
@@ -1,4 +1,5 @@
1
- from typing import TypeAlias
1
+ from typing import TypeAlias, Optional
2
+ from datetime import timedelta
2
3
 
3
4
  Point: TypeAlias = tuple[float, float]
4
5
 
@@ -67,6 +68,61 @@ class StripPackingSolution:
67
68
  density: float
68
69
  placed_items: list[PlacedItem]
69
70
 
71
+ class StripPackingConfig:
72
+ early_termination: bool
73
+ seed: int
74
+ exploration_time: timedelta
75
+ compression_time: timedelta
76
+ quadtree_depth: int
77
+ num_wokers:Optional[int]
78
+ min_items_separation: Optional[float]
79
+
80
+ def __init__(
81
+ self,
82
+ early_termination: bool = True,
83
+ quadtree_depth: int = 4,
84
+ min_items_separation: Optional[float] = None,
85
+ total_computation_time: Optional[int] = 600,
86
+ exploration_time: Optional[int] = None,
87
+ compression_time: Optional[int] = None,
88
+ num_wokers:Optional[int]= None,
89
+ seed: Optional[int] = None,
90
+ ) -> None:
91
+ """Initializes a configuration object for the strip packing algorithm.
92
+
93
+ Either `total_computation_time`, or both `exploration_time` and
94
+ `compression_time`, must be provided. Providing all three or only
95
+ one of the latter two raises an error.
96
+
97
+ If `total_computation_time` is provided, 80% of it is allocated to
98
+ exploration and 20% to compression.
99
+
100
+ If `seed` is not provided, a random seed will be generated.
101
+
102
+ Args:
103
+ early_termination (bool, optional): Whether to allow early termination of the algorithm. Defaults to True.
104
+ quadtree_depth (int, optional): Maximum depth of the quadtree used by the collision detection engine jagua-rs.
105
+ Must be positive, common values are 3,4,5. Defaults to 4.
106
+ min_items_separation (Optional[float], optional): Minimum required distance between packed items. Defaults to None.
107
+ total_computation_time (Optional[int], optional): Total time budget in seconds.
108
+ Used if `exploration_time` and `compression_time` are not provided. Defaults to 600.
109
+ exploration_time (Optional[int], optional): Time in seconds allocated to exploration. Defaults to None.
110
+ compression_time (Optional[int], optional): Time in seconds allocated to compression. Defaults to None.
111
+ num_workers (Optional[int], optional): Number of threads used by the collision detection engine during exploration.
112
+ When set to None, detect the number of logical CPU cores on the execution plateform. Defaults to None.
113
+ seed (Optional[int], optional): Optional random seed to give reproductibility. If None, a random seed is generated. Defaults to None.
114
+
115
+ Raises:
116
+ ValueError: If the combination of time arguments is invalid.
117
+ """
118
+
119
+ def to_json_str(self)->str:
120
+ """Return a string of the JSON representation of the object
121
+
122
+ Returns:
123
+ str
124
+ """
125
+
70
126
  class StripPackingInstance:
71
127
  name: str
72
128
  strip_height: float
@@ -87,13 +143,12 @@ class StripPackingInstance:
87
143
  def to_json_str(self) -> str:
88
144
  """Return a string of the JSON representation of the object"""
89
145
 
90
- def solve(self, computation_time: int = 600) -> StripPackingSolution:
146
+ def solve(self, config: StripPackingConfig) -> StripPackingSolution:
91
147
  """
92
148
  The method to solve the instance.
93
149
 
94
150
  Args:
95
- computation_time (int): The total computation time in seconds used to find a solution.
96
- The algorithm won't exit early.Waht you input is what you get. Default is 600 s = 10 minutes.
151
+ config (StripPackingConfig): the config object to precise behavior of how to solve the strip packing instance.
97
152
 
98
153
  Returns:
99
154
  a StripPackingSolution
@@ -1,18 +1,20 @@
1
1
  use jagua_rs::io::ext_repr::{ExtItem as BaseItem, ExtSPolygon, ExtShape};
2
2
  use jagua_rs::io::import::Importer;
3
3
  use jagua_rs::probs::spp::io::ext_repr::{ExtItem, ExtSPInstance};
4
+ use num_cpus;
4
5
  use pyo3::exceptions::PyValueError;
5
6
  use pyo3::prelude::*;
6
7
  use rand::SeedableRng;
7
8
  use rand::prelude::SmallRng;
8
9
  use serde::Serialize;
9
10
  use sparrow::EPOCH;
10
- use sparrow::config::{
11
- CDE_CONFIG, COMPRESS_TIME_RATIO, EXPLORE_TIME_RATIO, MIN_ITEM_SEPARATION, SIMPL_TOLERANCE,
12
- };
13
- use sparrow::optimizer::{Terminator, optimize};
11
+ use sparrow::config::{DEFAULT_SPARROW_CONFIG, ShrinkDecayStrategy};
12
+ use sparrow::consts::{DEFAULT_FAIL_DECAY_RATIO_CMPR, DEFAULT_MAX_CONSEQ_FAILS_EXPL};
13
+ use sparrow::optimizer::optimize;
14
+ use sparrow::util::listener::DummySolListener;
15
+ use sparrow::util::terminator::BasicTerminator;
14
16
  use std::collections::HashSet;
15
- use std::fs;
17
+
16
18
  use std::time::Duration;
17
19
 
18
20
  #[pyclass(name = "Item", get_all, set_all)]
@@ -123,6 +125,100 @@ fn all_unique(strings: &[&str]) -> bool {
123
125
  strings.iter().all(|s| seen.insert(*s))
124
126
  }
125
127
 
128
+ #[pyclass(name = "StripPackingConfig", get_all, set_all)]
129
+ #[derive(Clone, Serialize)]
130
+ /// Initializes a configuration object for the strip packing algorithm.
131
+ ///
132
+ /// Either `total_computation_time`, or both `exploration_time` and
133
+ /// `compression_time`, must be provided. Providing all three or only
134
+ /// one of the latter two raises an error.
135
+ ///
136
+ /// If `total_computation_time` is provided, 80% of it is allocated to
137
+ /// exploration and 20% to compression.
138
+ ///
139
+ /// If `seed` is not provided, a random seed will be generated.
140
+ ///
141
+ /// Args:
142
+ /// early_termination (bool, optional): Whether to allow early termination of the algorithm. Defaults to True.
143
+ /// quadtree_depth (int, optional): Maximum depth of the quadtree used by the collision detection engine jagua-rs.
144
+ /// Must be positive, common values are 3,4,5. Defaults to 4.
145
+ /// min_items_separation (Optional[float], optional): Minimum required distance between packed items. Defaults to None.
146
+ /// total_computation_time (Optional[int], optional): Total time budget in seconds.
147
+ /// Used if `exploration_time` and `compression_time` are not provided. Defaults to 600.
148
+ /// exploration_time (Optional[int], optional): Time in seconds allocated to exploration. Defaults to None.
149
+ /// compression_time (Optional[int], optional): Time in seconds allocated to compression. Defaults to None.
150
+ /// num_workers (Optional[int], optional): Number of threads used by the collision detection engine during exploration.
151
+ /// When set to None, detect the number of logical CPU cores on the execution plateform. Defaults to None.
152
+ /// seed (Optional[int], optional): Optional random seed to give reproductibility. If None, a random seed is generated. Defaults to None.
153
+ ///
154
+ /// Raises:
155
+ /// ValueError: If the combination of time arguments is invalid.
156
+ struct StripPackingConfigPy {
157
+ early_termination: bool,
158
+ seed: u64,
159
+ exploration_time: Duration,
160
+ compression_time: Duration,
161
+ quadtree_depth: u8,
162
+ min_items_separation: Option<f32>,
163
+ num_wokers: usize,
164
+ }
165
+
166
+ #[pymethods]
167
+ impl StripPackingConfigPy {
168
+ #[new]
169
+ #[pyo3(signature = (early_termination=true,quadtree_depth=4,min_items_separation=None,total_computation_time=600,exploration_time=None,compression_time=None,num_wokers=None,seed=None))]
170
+ fn new(
171
+ early_termination: bool,
172
+ quadtree_depth: u8,
173
+ min_items_separation: Option<f32>,
174
+ total_computation_time: Option<u64>,
175
+ exploration_time: Option<u64>,
176
+ compression_time: Option<u64>,
177
+ num_wokers: Option<usize>,
178
+ seed: Option<u64>,
179
+ ) -> PyResult<Self> {
180
+ let (exploration_time, compression_time) = match (
181
+ total_computation_time,
182
+ exploration_time,
183
+ compression_time,
184
+ ) {
185
+ (None, Some(exploration_time), Some(compression_time)) => (
186
+ Duration::from_secs(exploration_time),
187
+ Duration::from_secs(compression_time),
188
+ ),
189
+ (Some(total_computation_time), None, None) => (
190
+ Duration::from_secs(total_computation_time).mul_f32(0.8),
191
+ Duration::from_secs(total_computation_time).mul_f32(0.2),
192
+ ),
193
+ _ => {
194
+ return Err(PyValueError::new_err(
195
+ "Either total_computation_time or both exploration_time and compression_time should be provided, not all 3 or some other combination",
196
+ ));
197
+ }
198
+ };
199
+ let seed = seed.unwrap_or_else(rand::random);
200
+ let num_wokers = num_wokers.unwrap_or_else(num_cpus::get);
201
+ Ok(Self {
202
+ early_termination,
203
+ seed,
204
+ exploration_time,
205
+ compression_time,
206
+ quadtree_depth,
207
+ num_wokers,
208
+ min_items_separation,
209
+ })
210
+ }
211
+
212
+ /// Return a string of the JSON representation of the object
213
+ ///
214
+ /// Returns:
215
+ /// str
216
+ ///
217
+ fn to_json_str(&self) -> String {
218
+ serde_json::to_string(&self).unwrap()
219
+ }
220
+ }
221
+
126
222
  #[pyclass(name = "StripPackingInstance", get_all, set_all)]
127
223
  #[derive(Clone, Serialize)]
128
224
  /// An Instance of a Strip Packing Problem.
@@ -177,7 +273,7 @@ impl StripPackingInstancePy {
177
273
  fn new(name: String, strip_height: f32, items: Vec<ItemPy>) -> PyResult<Self> {
178
274
  let item_ids: Vec<&str> = items.iter().map(|i| i.id.as_str()).collect();
179
275
  if !all_unique(&item_ids) {
180
- let error_string = format!("The item ids are not uniques: {:#?}", item_ids);
276
+ let error_string = format!("The item ids are not uniques: {item_ids:#?}");
181
277
  return Err(PyValueError::new_err(error_string));
182
278
  }
183
279
  Ok(StripPackingInstancePy {
@@ -196,7 +292,6 @@ impl StripPackingInstancePy {
196
292
  serde_json::to_string(&self).unwrap()
197
293
  }
198
294
 
199
- #[pyo3(signature = (computation_time=600))]
200
295
  /// The method to solve the instance.
201
296
  ///
202
297
  /// Args:
@@ -206,37 +301,42 @@ impl StripPackingInstancePy {
206
301
  /// Returns:
207
302
  /// a StripPackingSolution
208
303
  ///
209
- fn solve(&self, computation_time: u64, py: Python) -> StripPackingSolutionPy {
210
- // Temporary output dir for intermediary solution
211
-
212
- // let tmp = TempDir::new().expect("could not create output directory");
213
- let tmp_str = String::from("tmp");
214
- fs::create_dir_all(&tmp_str).expect("Temporary foulder should be created");
215
-
216
- // Reproductibility
217
- let seed = rand::random();
218
- let rng = SmallRng::seed_from_u64(seed);
219
-
220
- // Execution Time
221
- let (explore_dur, compress_dur) = (
222
- Duration::from_secs(computation_time).mul_f32(EXPLORE_TIME_RATIO),
223
- Duration::from_secs(computation_time).mul_f32(COMPRESS_TIME_RATIO),
224
- );
304
+ fn solve(&self, config_py: StripPackingConfigPy, py: Python) -> StripPackingSolutionPy {
305
+ let mut config = DEFAULT_SPARROW_CONFIG;
306
+ config.rng_seed = Some(config_py.seed as usize);
307
+ config.expl_cfg.time_limit = config_py.exploration_time;
308
+ config.expl_cfg.separator_config.n_workers = config_py.num_wokers;
309
+ config.cmpr_cfg.time_limit = config_py.compression_time;
310
+ let rng = SmallRng::seed_from_u64(config_py.seed);
311
+ if config_py.early_termination {
312
+ config.expl_cfg.max_conseq_failed_attempts = Some(DEFAULT_MAX_CONSEQ_FAILS_EXPL);
313
+ config.cmpr_cfg.shrink_decay =
314
+ ShrinkDecayStrategy::FailureBased(DEFAULT_FAIL_DECAY_RATIO_CMPR);
315
+ }
316
+ config.cde_config.quadtree_depth = config_py.quadtree_depth;
317
+ config.min_item_separation = config_py.min_items_separation;
225
318
 
226
319
  let ext_instance = self.clone().into();
227
- let importer = Importer::new(CDE_CONFIG, SIMPL_TOLERANCE, MIN_ITEM_SEPARATION);
320
+ let importer = Importer::new(
321
+ config.cde_config,
322
+ config.poly_simpl_tolerance,
323
+ config.min_item_separation,
324
+ );
228
325
  let instance = jagua_rs::probs::spp::io::import(&importer, &ext_instance)
229
326
  .expect("Expected a Strip Packing Problem Instance");
327
+ let mut terminator = BasicTerminator::new();
328
+
329
+ // The Python code is not concerned with intermediary solution for now
330
+ let mut dummy_exporter = DummySolListener {};
230
331
 
231
332
  py.allow_threads(move || {
232
- let terminator = Terminator::new_without_ctrlc();
233
333
  let solution = optimize(
234
334
  instance.clone(),
235
335
  rng,
236
- tmp_str.clone(),
237
- terminator,
238
- explore_dur,
239
- compress_dur,
336
+ &mut dummy_exporter,
337
+ &mut terminator,
338
+ &config.expl_cfg,
339
+ &config.cmpr_cfg,
240
340
  );
241
341
 
242
342
  let solution = jagua_rs::probs::spp::io::export(&instance, &solution, *EPOCH);
@@ -251,7 +351,7 @@ impl StripPackingInstancePy {
251
351
  translation: jpi.transformation.translation,
252
352
  })
253
353
  .collect();
254
- fs::remove_dir_all(&tmp_str).expect("Should be able to remove tmp dir");
354
+
255
355
  StripPackingSolutionPy {
256
356
  width: solution.strip_width,
257
357
  density: solution.density,
@@ -267,6 +367,7 @@ fn spyrrow(m: &Bound<'_, PyModule>) -> PyResult<()> {
267
367
  m.add_class::<ItemPy>()?;
268
368
  m.add_class::<PlacedItemPy>()?;
269
369
  m.add_class::<StripPackingInstancePy>()?;
370
+ m.add_class::<StripPackingConfigPy>()?;
270
371
  m.add_class::<StripPackingSolutionPy>()?;
271
372
  m.add("__version__", env!("CARGO_PKG_VERSION"))?;
272
373
  Ok(())
@@ -15,11 +15,30 @@ def test_basic():
15
15
  instance = spyrrow.StripPackingInstance(
16
16
  "test", strip_height=2.001, items=[rectangle1, triangle1]
17
17
  )
18
- sol = instance.solve(30)
19
- assert sol.width == pytest.approx(4,rel=0.2)
18
+ config = spyrrow.StripPackingConfig(early_termination=False,total_computation_time=60,num_wokers=3,seed=0)
19
+ sol = instance.solve(config)
20
+ assert sol.width == pytest.approx(4,rel=0.05)
21
+
22
+ def test_early_termination():
23
+ rectangle1 = spyrrow.Item(
24
+ "rectangle", [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=[0]
25
+ )
26
+ triangle1 = spyrrow.Item(
27
+ "triangle",
28
+ [(0, 0), (1, 0), (1, 1), (0, 0)],
29
+ demand=6,
30
+ allowed_orientations=[0, 90, 180, -90],
31
+ )
32
+
33
+ instance = spyrrow.StripPackingInstance(
34
+ "test", strip_height=2.001, items=[rectangle1, triangle1]
35
+ )
36
+ config = spyrrow.StripPackingConfig(early_termination=True,total_computation_time=600,num_wokers=3,seed=0)
37
+ sol = instance.solve(config)
38
+ assert sol.width == pytest.approx(4,rel=0.05)
20
39
 
21
40
  def test_2_consecutive_calls():
22
- # Test correpsonding to crash on the second consecutive call of solve method
41
+ # Test corresponding to crash on the second consecutive call of solve method
23
42
  rectangle1 = spyrrow.Item(
24
43
  "rectangle", [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=[0]
25
44
  )
@@ -33,9 +52,11 @@ def test_2_consecutive_calls():
33
52
  instance = spyrrow.StripPackingInstance(
34
53
  "test", strip_height=2.001, items=[rectangle1, triangle1]
35
54
  )
36
- sol = instance.solve(10)
37
- sol = instance.solve(30)
38
- assert sol.width == pytest.approx(4,rel=0.2)
55
+ config = spyrrow.StripPackingConfig(early_termination=False,total_computation_time=10,seed=0)
56
+ sol = instance.solve(config)
57
+ config = spyrrow.StripPackingConfig(early_termination=False,total_computation_time=30,seed=0)
58
+ sol = instance.solve(config)
59
+ assert sol.width == pytest.approx(4,rel=0.05)
39
60
 
40
61
  def test_concave_polygons():
41
62
  poly1 = spyrrow.Item("0",[(0, 0), (3, 0), (4, 1), (3, 2), (0, 2), (1, 1), (0, 0)],demand=2,allowed_orientations=[0,90,180,270])
@@ -43,7 +64,8 @@ def test_concave_polygons():
43
64
  instance = spyrrow.StripPackingInstance(
44
65
  "test", strip_height=4.001, items=[poly1, poly2]
45
66
  )
46
- sol = instance.solve(30)
67
+ config = spyrrow.StripPackingConfig(early_termination=True,total_computation_time=30,seed=0)
68
+ sol = instance.solve(config)
47
69
 
48
70
  def test_continuous_rotation():
49
71
  rectangle1 = spyrrow.Item(
@@ -59,5 +81,11 @@ def test_continuous_rotation():
59
81
  instance = spyrrow.StripPackingInstance(
60
82
  "test", strip_height=2.001, items=[rectangle1, triangle1]
61
83
  )
62
- sol = instance.solve(30)
63
- assert sol.width == pytest.approx(4,rel=0.2)
84
+ config = spyrrow.StripPackingConfig(early_termination=True,total_computation_time=90,seed=0)
85
+ sol = instance.solve(config)
86
+ print(sol.width)
87
+ assert sol.width >= 3.5
88
+ assert sol.width < 4
89
+
90
+ if __name__ == '__main__':
91
+ test_continuous_rotation()
spyrrow-0.6.0/PKG-INFO DELETED
@@ -1,68 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: spyrrow
3
- Version: 0.6.0
4
- Classifier: Programming Language :: Rust
5
- Classifier: Programming Language :: Python :: Implementation :: CPython
6
- Classifier: Programming Language :: Python :: Implementation :: PyPy
7
- License-File: LICENSE.txt
8
- Author-email: Paul Durand-Lupinski <paul.durand-lupinski@reeverse-systems.com>
9
- License: MIT
10
- Requires-Python: >=3.10
11
- Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
12
- Project-URL: documentation, https://spyrrow.readthedocs.io/
13
- Project-URL: source, https://github.com/PaulDL-RS/spyrrow
14
-
15
- # Spyrrow
16
-
17
- `spyrrow` is a Python wrapper on the Rust project [`sparrow`](https://github.com/JeroenGar/sparrow).
18
- It enables to solve 2D [Strip packing problems](https://en.wikipedia.org/wiki/Strip_packing_problem).
19
-
20
- The documentation is hosted [here](https://spyrrow.readthedocs.io/).
21
-
22
- ## Installation
23
-
24
- Spyrrow is hosted on [PyPI](https://pypi.org/project/spyrrow/).
25
-
26
- You can install with the package manager of your choice, using the PyPI package index.
27
-
28
- For example, with `pip`, the default Python package:
29
- ```bash
30
- pip install spyrrow
31
- ```
32
-
33
- ## Examples
34
- ```python
35
- import spyrrow
36
-
37
- rectangle1 = spyrrow.Item(
38
- "rectangle", [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], demand=4, allowed_orientations=[0]
39
- )
40
- triangle1 = spyrrow.Item(
41
- "triangle",
42
- [(0, 0), (1, 0), (1, 1), (0, 0)],
43
- demand=6,
44
- allowed_orientations=[0, 90, 180, -90],
45
- )
46
-
47
- instance = spyrrow.StripPackingInstance("test", strip_height=2.001, items=[rectangle1,triangle1])
48
- sol:spyrrow.StripPackingSolution = instance.solve(30)
49
- print(sol.width)
50
- print(sol.density)
51
- print("\n")
52
- for pi in sol.placed_items:
53
- print(pi.id)
54
- print(pi.rotation)
55
- print(pi.translation)
56
- print("\n")
57
- ```
58
-
59
- ## Contributing
60
-
61
- Spyrrow is open to contributions.
62
- The first target should be to reach Python open sources packages standards and practices.
63
- Second, a easier integration with the package `shapely` is envsionned.
64
-
65
- Please use GitHub issues to request features.
66
- They will be considered relative to what is already implemented in the parent library `sparrow`.
67
- If necessary, they can be forwarded to it.
68
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes