solverforge 0.2.3__tar.gz → 0.2.4__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.
Files changed (62) hide show
  1. {solverforge-0.2.3 → solverforge-0.2.4}/Cargo.lock +5 -5
  2. {solverforge-0.2.3 → solverforge-0.2.4}/Cargo.toml +1 -1
  3. {solverforge-0.2.3 → solverforge-0.2.4}/PKG-INFO +1 -1
  4. {solverforge-0.2.3 → solverforge-0.2.4}/pyproject.toml +1 -1
  5. {solverforge-0.2.3 → solverforge-0.2.4}/python/solverforge/__init__.py +6 -0
  6. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-python/python/solverforge/__init__.py +6 -0
  7. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-python/src/annotations.rs +174 -0
  8. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-python/src/lib.rs +26 -3
  9. {solverforge-0.2.3 → solverforge-0.2.4}/README.md +0 -0
  10. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/Cargo.toml +0 -0
  11. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/README.md +0 -0
  12. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/analysis/explanation.rs +0 -0
  13. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/analysis/manager.rs +0 -0
  14. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/analysis/mod.rs +0 -0
  15. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/bridge.rs +0 -0
  16. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/constraints/collectors.rs +0 -0
  17. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/constraints/constraint.rs +0 -0
  18. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/constraints/joiners.rs +0 -0
  19. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/constraints/mod.rs +0 -0
  20. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/constraints/stream.rs +0 -0
  21. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/domain/annotations.rs +0 -0
  22. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/domain/class.rs +0 -0
  23. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/domain/constraint_config.rs +0 -0
  24. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/domain/mod.rs +0 -0
  25. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/domain/model.rs +0 -0
  26. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/domain/shadow.rs +0 -0
  27. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/error.rs +0 -0
  28. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/handles.rs +0 -0
  29. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/lib.rs +0 -0
  30. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/score/bendable.rs +0 -0
  31. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/score/hard_soft.rs +0 -0
  32. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/score/hard_soft_decimal.rs +0 -0
  33. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/score/mod.rs +0 -0
  34. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/score/simple.rs +0 -0
  35. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/score/simple_decimal.rs +0 -0
  36. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/solver/builder.rs +0 -0
  37. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/solver/client.rs +0 -0
  38. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/solver/config.rs +0 -0
  39. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/solver/environment.rs +0 -0
  40. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/solver/factory.rs +0 -0
  41. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/solver/mod.rs +0 -0
  42. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/solver/request.rs +0 -0
  43. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/solver/response.rs +0 -0
  44. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/solver/termination.rs +0 -0
  45. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/traits.rs +0 -0
  46. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/value.rs +0 -0
  47. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/wasm/expr_builder.rs +0 -0
  48. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/wasm/expression.rs +0 -0
  49. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/wasm/generator.rs +0 -0
  50. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/wasm/host_functions.rs +0 -0
  51. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/wasm/memory.rs +0 -0
  52. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-core/src/wasm/mod.rs +0 -0
  53. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-python/Cargo.toml +0 -0
  54. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-python/README.md +0 -0
  55. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-python/src/bridge.rs +0 -0
  56. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-python/src/collectors.rs +0 -0
  57. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-python/src/decorators.rs +0 -0
  58. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-python/src/joiners.rs +0 -0
  59. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-python/src/lambda_analyzer.rs +0 -0
  60. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-python/src/score.rs +0 -0
  61. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-python/src/solver.rs +0 -0
  62. {solverforge-0.2.3 → solverforge-0.2.4}/solverforge-python/src/stream.rs +0 -0
@@ -1507,7 +1507,7 @@ dependencies = [
1507
1507
 
1508
1508
  [[package]]
1509
1509
  name = "solverforge"
1510
- version = "0.2.3"
1510
+ version = "0.2.4"
1511
1511
  dependencies = [
1512
1512
  "solverforge-core",
1513
1513
  "solverforge-derive",
@@ -1516,7 +1516,7 @@ dependencies = [
1516
1516
 
1517
1517
  [[package]]
1518
1518
  name = "solverforge-core"
1519
- version = "0.2.3"
1519
+ version = "0.2.4"
1520
1520
  dependencies = [
1521
1521
  "base64",
1522
1522
  "indexmap",
@@ -1532,7 +1532,7 @@ dependencies = [
1532
1532
 
1533
1533
  [[package]]
1534
1534
  name = "solverforge-derive"
1535
- version = "0.2.3"
1535
+ version = "0.2.4"
1536
1536
  dependencies = [
1537
1537
  "proc-macro2",
1538
1538
  "quote",
@@ -1544,7 +1544,7 @@ dependencies = [
1544
1544
 
1545
1545
  [[package]]
1546
1546
  name = "solverforge-python"
1547
- version = "0.2.3"
1547
+ version = "0.2.4"
1548
1548
  dependencies = [
1549
1549
  "log",
1550
1550
  "pyo3",
@@ -1557,7 +1557,7 @@ dependencies = [
1557
1557
 
1558
1558
  [[package]]
1559
1559
  name = "solverforge-service"
1560
- version = "0.2.3"
1560
+ version = "0.2.4"
1561
1561
  dependencies = [
1562
1562
  "base64",
1563
1563
  "chrono",
@@ -3,7 +3,7 @@ members = ["solverforge-core", "solverforge-python"]
3
3
  resolver = "2"
4
4
 
5
5
  [workspace.package]
6
- version = "0.2.3"
6
+ version = "0.2.4"
7
7
  edition = "2021"
8
8
  license = "Apache-2.0"
9
9
  repository = "https://github.com/solverforge/solverforge"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: solverforge
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: 3
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "solverforge"
7
- version = "0.2.3"
7
+ version = "0.2.4"
8
8
  description = "Python bindings for SolverForge constraint solver"
9
9
  readme = "README.md"
10
10
  license = { text = "Apache-2.0" }
@@ -24,6 +24,9 @@ from solverforge._solverforge import (
24
24
  PlanningEntityCollectionProperty,
25
25
  PlanningPin,
26
26
  InverseRelationShadowVariable,
27
+ PreviousElementShadowVariable,
28
+ NextElementShadowVariable,
29
+ CascadingUpdateShadowVariable,
27
30
  # Score types
28
31
  SimpleScore,
29
32
  HardSoftScore,
@@ -81,6 +84,9 @@ __all__ = [
81
84
  "PlanningEntityCollectionProperty",
82
85
  "PlanningPin",
83
86
  "InverseRelationShadowVariable",
87
+ "PreviousElementShadowVariable",
88
+ "NextElementShadowVariable",
89
+ "CascadingUpdateShadowVariable",
84
90
  # Score types
85
91
  "SimpleScore",
86
92
  "HardSoftScore",
@@ -24,6 +24,9 @@ from solverforge._solverforge import (
24
24
  PlanningEntityCollectionProperty,
25
25
  PlanningPin,
26
26
  InverseRelationShadowVariable,
27
+ PreviousElementShadowVariable,
28
+ NextElementShadowVariable,
29
+ CascadingUpdateShadowVariable,
27
30
  # Score types
28
31
  SimpleScore,
29
32
  HardSoftScore,
@@ -81,6 +84,9 @@ __all__ = [
81
84
  "PlanningEntityCollectionProperty",
82
85
  "PlanningPin",
83
86
  "InverseRelationShadowVariable",
87
+ "PreviousElementShadowVariable",
88
+ "NextElementShadowVariable",
89
+ "CascadingUpdateShadowVariable",
84
90
  # Score types
85
91
  "SimpleScore",
86
92
  "HardSoftScore",
@@ -383,6 +383,140 @@ impl PyInverseRelationShadowVariable {
383
383
  }
384
384
  }
385
385
 
386
+ /// Marks a field as a previous element shadow variable.
387
+ ///
388
+ /// Automatically tracks the previous element in a planning list variable.
389
+ /// Used with PlanningListVariable to access the element before this one.
390
+ ///
391
+ /// # Parameters
392
+ ///
393
+ /// - `source_variable_name`: Name of the planning list variable this shadows
394
+ ///
395
+ /// # Example
396
+ ///
397
+ /// ```python
398
+ /// @planning_entity
399
+ /// class Visit:
400
+ /// previous_visit: Annotated[Optional['Visit'], PreviousElementShadowVariable(source_variable_name='visits')]
401
+ /// ```
402
+ #[pyclass(name = "PreviousElementShadowVariable")]
403
+ #[derive(Clone, Debug)]
404
+ pub struct PyPreviousElementShadowVariable {
405
+ #[pyo3(get)]
406
+ pub source_variable_name: String,
407
+ }
408
+
409
+ #[pymethods]
410
+ impl PyPreviousElementShadowVariable {
411
+ #[new]
412
+ fn new(source_variable_name: String) -> Self {
413
+ Self {
414
+ source_variable_name,
415
+ }
416
+ }
417
+
418
+ fn __repr__(&self) -> String {
419
+ format!(
420
+ "PreviousElementShadowVariable(source_variable_name='{}')",
421
+ self.source_variable_name
422
+ )
423
+ }
424
+ }
425
+
426
+ /// Marks a field as a next element shadow variable.
427
+ ///
428
+ /// Automatically tracks the next element in a planning list variable.
429
+ /// Used with PlanningListVariable to access the element after this one.
430
+ ///
431
+ /// # Parameters
432
+ ///
433
+ /// - `source_variable_name`: Name of the planning list variable this shadows
434
+ ///
435
+ /// # Example
436
+ ///
437
+ /// ```python
438
+ /// @planning_entity
439
+ /// class Visit:
440
+ /// next_visit: Annotated[Optional['Visit'], NextElementShadowVariable(source_variable_name='visits')]
441
+ /// ```
442
+ #[pyclass(name = "NextElementShadowVariable")]
443
+ #[derive(Clone, Debug)]
444
+ pub struct PyNextElementShadowVariable {
445
+ #[pyo3(get)]
446
+ pub source_variable_name: String,
447
+ }
448
+
449
+ #[pymethods]
450
+ impl PyNextElementShadowVariable {
451
+ #[new]
452
+ fn new(source_variable_name: String) -> Self {
453
+ Self {
454
+ source_variable_name,
455
+ }
456
+ }
457
+
458
+ fn __repr__(&self) -> String {
459
+ format!(
460
+ "NextElementShadowVariable(source_variable_name='{}')",
461
+ self.source_variable_name
462
+ )
463
+ }
464
+ }
465
+
466
+ /// Marks a field as a cascading update shadow variable.
467
+ ///
468
+ /// A shadow variable that is updated based on other shadow variables or planning variables,
469
+ /// with updates cascading through a chain of elements.
470
+ ///
471
+ /// # Parameters
472
+ ///
473
+ /// - `target_method_name`: Name of the method to call for computing the shadow value
474
+ /// - `source_variable_name`: Optional name of the source variable that triggers updates
475
+ ///
476
+ /// # Example
477
+ ///
478
+ /// ```python
479
+ /// @planning_entity
480
+ /// class Visit:
481
+ /// arrival_time: Annotated[datetime, CascadingUpdateShadowVariable(
482
+ /// target_method_name='calculate_arrival_time',
483
+ /// source_variable_name='visits'
484
+ /// )]
485
+ /// ```
486
+ #[pyclass(name = "CascadingUpdateShadowVariable")]
487
+ #[derive(Clone, Debug)]
488
+ pub struct PyCascadingUpdateShadowVariable {
489
+ #[pyo3(get)]
490
+ pub target_method_name: String,
491
+ #[pyo3(get)]
492
+ pub source_variable_name: Option<String>,
493
+ }
494
+
495
+ #[pymethods]
496
+ impl PyCascadingUpdateShadowVariable {
497
+ #[new]
498
+ #[pyo3(signature = (target_method_name, source_variable_name=None))]
499
+ fn new(target_method_name: String, source_variable_name: Option<String>) -> Self {
500
+ Self {
501
+ target_method_name,
502
+ source_variable_name,
503
+ }
504
+ }
505
+
506
+ fn __repr__(&self) -> String {
507
+ match &self.source_variable_name {
508
+ Some(src) => format!(
509
+ "CascadingUpdateShadowVariable(target_method_name='{}', source_variable_name='{}')",
510
+ self.target_method_name, src
511
+ ),
512
+ None => format!(
513
+ "CascadingUpdateShadowVariable(target_method_name='{}')",
514
+ self.target_method_name
515
+ ),
516
+ }
517
+ }
518
+ }
519
+
386
520
  /// Marks a field as requiring deep planning cloning.
387
521
  ///
388
522
  /// Not needed for `planning_solution` or `planning_entity` attributes because those
@@ -454,6 +588,9 @@ pub fn register_annotations(m: &Bound<'_, PyModule>) -> PyResult<()> {
454
588
  m.add_class::<PyPlanningEntityCollectionProperty>()?;
455
589
  m.add_class::<PyPlanningPin>()?;
456
590
  m.add_class::<PyInverseRelationShadowVariable>()?;
591
+ m.add_class::<PyPreviousElementShadowVariable>()?;
592
+ m.add_class::<PyNextElementShadowVariable>()?;
593
+ m.add_class::<PyCascadingUpdateShadowVariable>()?;
457
594
  m.add_class::<PyDeepPlanningClone>()?;
458
595
  m.add_function(wrap_pyfunction!(py_deep_planning_clone, m)?)?;
459
596
  Ok(())
@@ -546,6 +683,43 @@ mod tests {
546
683
  assert!(shadow.__repr__().contains("visits"));
547
684
  }
548
685
 
686
+ #[test]
687
+ fn test_previous_element_shadow_variable() {
688
+ let shadow = PyPreviousElementShadowVariable::new("visits".to_string());
689
+ assert_eq!(shadow.source_variable_name, "visits");
690
+ assert!(shadow.__repr__().contains("visits"));
691
+ assert!(shadow.__repr__().contains("PreviousElementShadowVariable"));
692
+ }
693
+
694
+ #[test]
695
+ fn test_next_element_shadow_variable() {
696
+ let shadow = PyNextElementShadowVariable::new("visits".to_string());
697
+ assert_eq!(shadow.source_variable_name, "visits");
698
+ assert!(shadow.__repr__().contains("visits"));
699
+ assert!(shadow.__repr__().contains("NextElementShadowVariable"));
700
+ }
701
+
702
+ #[test]
703
+ fn test_cascading_update_shadow_variable() {
704
+ let shadow = PyCascadingUpdateShadowVariable::new(
705
+ "calculate_arrival".to_string(),
706
+ Some("visits".to_string()),
707
+ );
708
+ assert_eq!(shadow.target_method_name, "calculate_arrival");
709
+ assert_eq!(shadow.source_variable_name, Some("visits".to_string()));
710
+ assert!(shadow.__repr__().contains("calculate_arrival"));
711
+ assert!(shadow.__repr__().contains("visits"));
712
+ }
713
+
714
+ #[test]
715
+ fn test_cascading_update_shadow_variable_no_source() {
716
+ let shadow = PyCascadingUpdateShadowVariable::new("update_time".to_string(), None);
717
+ assert_eq!(shadow.target_method_name, "update_time");
718
+ assert_eq!(shadow.source_variable_name, None);
719
+ assert!(shadow.__repr__().contains("update_time"));
720
+ assert!(!shadow.__repr__().contains("source_variable_name"));
721
+ }
722
+
549
723
  #[test]
550
724
  fn test_deep_planning_clone() {
551
725
  let clone = PyDeepPlanningClone::new();
@@ -16,9 +16,10 @@ mod solver;
16
16
  mod stream;
17
17
 
18
18
  pub use annotations::{
19
- PyDeepPlanningClone, PyInverseRelationShadowVariable, PyPlanningEntityCollectionProperty,
20
- PyPlanningEntityProperty, PyPlanningId, PyPlanningListVariable, PyPlanningPin, PyPlanningScore,
21
- PyPlanningVariable, PyProblemFactCollectionProperty, PyProblemFactProperty,
19
+ PyCascadingUpdateShadowVariable, PyDeepPlanningClone, PyInverseRelationShadowVariable,
20
+ PyNextElementShadowVariable, PyPlanningEntityCollectionProperty, PyPlanningEntityProperty,
21
+ PyPlanningId, PyPlanningListVariable, PyPlanningPin, PyPlanningScore, PyPlanningVariable,
22
+ PyPreviousElementShadowVariable, PyProblemFactCollectionProperty, PyProblemFactProperty,
22
23
  PyValueRangeProvider,
23
24
  };
24
25
  pub use bridge::{PyBridge, PythonBridge};
@@ -145,4 +146,26 @@ mod tests {
145
146
  assert_type::<PyHardSoftScore>();
146
147
  assert_type::<PyHardMediumSoftScore>();
147
148
  }
149
+
150
+ /// Verify that annotation types are accessible
151
+ #[test]
152
+ fn test_annotation_types_exported() {
153
+ fn assert_type<T>() {}
154
+
155
+ assert_type::<PyPlanningId>();
156
+ assert_type::<PyPlanningVariable>();
157
+ assert_type::<PyPlanningListVariable>();
158
+ assert_type::<PyPlanningScore>();
159
+ assert_type::<PyValueRangeProvider>();
160
+ assert_type::<PyProblemFactProperty>();
161
+ assert_type::<PyProblemFactCollectionProperty>();
162
+ assert_type::<PyPlanningEntityProperty>();
163
+ assert_type::<PyPlanningEntityCollectionProperty>();
164
+ assert_type::<PyPlanningPin>();
165
+ assert_type::<PyInverseRelationShadowVariable>();
166
+ assert_type::<PyPreviousElementShadowVariable>();
167
+ assert_type::<PyNextElementShadowVariable>();
168
+ assert_type::<PyCascadingUpdateShadowVariable>();
169
+ assert_type::<PyDeepPlanningClone>();
170
+ }
148
171
  }
File without changes