-
Notifications
You must be signed in to change notification settings - Fork 6
Fix #114: Add Knapsack model #171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
3a74983
0a8d61c
8da012e
d843c38
c189700
3d0898c
48239c5
692337e
ff649bc
1130360
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,6 @@ pub const ALIASES: &[(&str, &str)] = &[ | |
| ("3SAT", "KSatisfiability"), | ||
| ("KSAT", "KSatisfiability"), | ||
| ("TSP", "TravelingSalesman"), | ||
| ("BP", "BinPacking"), | ||
| ("CVP", "ClosestVectorProblem"), | ||
| ("MaxMatching", "MaximumMatching"), | ||
| ]; | ||
|
|
@@ -50,8 +49,9 @@ pub fn resolve_alias(input: &str) -> String { | |
| "paintshop" => "PaintShop".to_string(), | ||
| "bmf" => "BMF".to_string(), | ||
| "bicliquecover" => "BicliqueCover".to_string(), | ||
| "bp" | "binpacking" => "BinPacking".to_string(), | ||
| "binpacking" => "BinPacking".to_string(), | ||
| "cvp" | "closestvectorproblem" => "ClosestVectorProblem".to_string(), | ||
| "knapsack" => "Knapsack".to_string(), | ||
|
||
| _ => input.to_string(), // pass-through for exact names | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| //! Knapsack problem implementation. | ||
| //! | ||
| //! The 0-1 Knapsack problem asks for a subset of items that maximizes | ||
| //! total value while respecting a weight capacity constraint. | ||
|
|
||
| use crate::registry::{FieldInfo, ProblemSchemaEntry}; | ||
| use crate::traits::{OptimizationProblem, Problem}; | ||
| use crate::types::{Direction, SolutionSize}; | ||
| use serde::{Deserialize, Serialize}; | ||
|
|
||
| inventory::submit! { | ||
| ProblemSchemaEntry { | ||
| name: "Knapsack", | ||
| module_path: module_path!(), | ||
| description: "Select items to maximize total value subject to weight capacity constraint", | ||
| fields: &[ | ||
| FieldInfo { name: "weights", type_name: "Vec<i64>", description: "Item weights w_i" }, | ||
| FieldInfo { name: "values", type_name: "Vec<i64>", description: "Item values v_i" }, | ||
| FieldInfo { name: "capacity", type_name: "i64", description: "Knapsack capacity C" }, | ||
| ], | ||
| } | ||
| } | ||
|
|
||
| /// The 0-1 Knapsack problem. | ||
| /// | ||
| /// Given `n` items, each with weight `w_i` and value `v_i`, and a capacity `C`, | ||
| /// find a subset `S ⊆ {0, ..., n-1}` such that `∑_{i∈S} w_i ≤ C`, | ||
| /// maximizing `∑_{i∈S} v_i`. | ||
| /// | ||
| /// # Representation | ||
| /// | ||
| /// Each item has a binary variable: `x_i = 1` if item `i` is selected, `0` otherwise. | ||
| /// | ||
| /// # Example | ||
| /// | ||
| /// ``` | ||
| /// use problemreductions::models::misc::Knapsack; | ||
| /// use problemreductions::{Problem, Solver, BruteForce}; | ||
| /// | ||
| /// let problem = Knapsack::new(vec![2, 3, 4, 5], vec![3, 4, 5, 7], 7); | ||
| /// let solver = BruteForce::new(); | ||
| /// let solution = solver.find_best(&problem); | ||
| /// assert!(solution.is_some()); | ||
| /// ``` | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct Knapsack { | ||
| weights: Vec<i64>, | ||
| values: Vec<i64>, | ||
| capacity: i64, | ||
| } | ||
|
|
||
| impl Knapsack { | ||
| /// Create a new Knapsack instance. | ||
| /// | ||
| /// # Panics | ||
| /// Panics if `weights` and `values` have different lengths. | ||
| pub fn new(weights: Vec<i64>, values: Vec<i64>, capacity: i64) -> Self { | ||
| assert_eq!( | ||
| weights.len(), | ||
| values.len(), | ||
| "weights and values must have the same length" | ||
| ); | ||
| Self { | ||
| weights, | ||
| values, | ||
| capacity, | ||
| } | ||
| } | ||
|
|
||
| /// Returns the item weights. | ||
| pub fn weights(&self) -> &[i64] { | ||
| &self.weights | ||
| } | ||
|
|
||
| /// Returns the item values. | ||
| pub fn values(&self) -> &[i64] { | ||
| &self.values | ||
| } | ||
|
|
||
| /// Returns the knapsack capacity. | ||
| pub fn capacity(&self) -> i64 { | ||
| self.capacity | ||
| } | ||
|
|
||
| /// Returns the number of items. | ||
| pub fn num_items(&self) -> usize { | ||
| self.weights.len() | ||
| } | ||
| } | ||
|
|
||
| impl Problem for Knapsack { | ||
| const NAME: &'static str = "Knapsack"; | ||
| type Metric = SolutionSize<i64>; | ||
|
|
||
| fn variant() -> Vec<(&'static str, &'static str)> { | ||
| crate::variant_params![] | ||
| } | ||
|
|
||
| fn dims(&self) -> Vec<usize> { | ||
| vec![2; self.num_items()] | ||
| } | ||
|
|
||
| fn evaluate(&self, config: &[usize]) -> SolutionSize<i64> { | ||
| if config.len() != self.num_items() { | ||
| return SolutionSize::Invalid; | ||
| } | ||
| if config.iter().any(|&v| v >= 2) { | ||
| return SolutionSize::Invalid; | ||
| } | ||
| let total_weight: i64 = config | ||
| .iter() | ||
| .enumerate() | ||
| .filter(|(_, &x)| x == 1) | ||
| .map(|(i, _)| self.weights[i]) | ||
| .sum(); | ||
| if total_weight > self.capacity { | ||
| return SolutionSize::Invalid; | ||
| } | ||
| let total_value: i64 = config | ||
| .iter() | ||
| .enumerate() | ||
| .filter(|(_, &x)| x == 1) | ||
| .map(|(i, _)| self.values[i]) | ||
| .sum(); | ||
| SolutionSize::Valid(total_value) | ||
| } | ||
| } | ||
|
|
||
| impl OptimizationProblem for Knapsack { | ||
| type Value = i64; | ||
|
|
||
| fn direction(&self) -> Direction { | ||
| Direction::Maximize | ||
| } | ||
| } | ||
|
|
||
| crate::declare_variants! { | ||
| Knapsack => "2^(num_items / 2)", | ||
| } | ||
|
Comment on lines
+137
to
+139
|
||
|
|
||
| #[cfg(test)] | ||
| #[path = "../../unit_tests/models/misc/knapsack.rs"] | ||
| mod tests; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
BPalias forBinPackingis removed from both theALIASESconst and theresolve_alias()function. This is an unrelated breaking change: any existing CLI user who usedpred create BP ...orpred evaluate BP ...will now get an "Unknown problem" error. If this removal was intentional, it should be mentioned in the PR description; otherwise, it should be reverted.