problemreductions/models/graph/
minimum_feedback_vertex_set.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
7use crate::topology::DirectedGraph;
8use crate::traits::Problem;
9use crate::types::{Min, WeightElement};
10use num_traits::Zero;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "MinimumFeedbackVertexSet",
16 display_name: "Minimum Feedback Vertex Set",
17 aliases: &["FVS"],
18 dimensions: &[
19 VariantDimension::new("weight", "i32", &["i32"]),
20 ],
21 module_path: module_path!(),
22 description: "Find minimum weight feedback vertex set in a directed graph",
23 fields: &[
24 FieldInfo { name: "graph", type_name: "DirectedGraph", description: "The directed graph G=(V,A)" },
25 FieldInfo { name: "weights", type_name: "Vec<W>", description: "Vertex weights w: V -> R" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct MinimumFeedbackVertexSet<W> {
56 graph: DirectedGraph,
58 weights: Vec<W>,
60}
61
62impl<W: Clone + Default> MinimumFeedbackVertexSet<W> {
63 pub fn new(graph: DirectedGraph, weights: Vec<W>) -> Self {
65 assert_eq!(
66 weights.len(),
67 graph.num_vertices(),
68 "weights length must match graph num_vertices"
69 );
70 Self { graph, weights }
71 }
72
73 pub fn graph(&self) -> &DirectedGraph {
75 &self.graph
76 }
77
78 pub fn weights(&self) -> &[W] {
80 &self.weights
81 }
82
83 pub fn set_weights(&mut self, weights: Vec<W>) {
85 assert_eq!(
86 weights.len(),
87 self.graph.num_vertices(),
88 "weights length must match graph num_vertices"
89 );
90 self.weights = weights;
91 }
92
93 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
95 if config.len() != self.graph.num_vertices() {
96 return false;
97 }
98 let keep: Vec<bool> = config.iter().map(|&c| c == 0).collect();
99 self.graph.induced_subgraph(&keep).is_dag()
100 }
101}
102
103impl<W: WeightElement> MinimumFeedbackVertexSet<W> {
104 pub fn is_weighted(&self) -> bool {
106 !W::IS_UNIT
107 }
108
109 pub fn num_vertices(&self) -> usize {
111 self.graph.num_vertices()
112 }
113
114 pub fn num_arcs(&self) -> usize {
116 self.graph.num_arcs()
117 }
118}
119
120impl<W> Problem for MinimumFeedbackVertexSet<W>
121where
122 W: WeightElement + crate::variant::VariantParam,
123{
124 const NAME: &'static str = "MinimumFeedbackVertexSet";
125 type Value = Min<W::Sum>;
126
127 fn variant() -> Vec<(&'static str, &'static str)> {
128 crate::variant_params![W]
129 }
130
131 fn dims(&self) -> Vec<usize> {
132 vec![2; self.graph.num_vertices()]
133 }
134
135 fn evaluate(&self, config: &[usize]) -> Min<W::Sum> {
136 if config.len() != self.graph.num_vertices() {
137 return Min(None);
138 }
139 let keep: Vec<bool> = config.iter().map(|&c| c == 0).collect();
141 let subgraph = self.graph.induced_subgraph(&keep);
142 if !subgraph.is_dag() {
143 return Min(None);
144 }
145 let mut total = W::Sum::zero();
146 for (i, &selected) in config.iter().enumerate() {
147 if selected == 1 {
148 total += self.weights[i].to_sum();
149 }
150 }
151 Min(Some(total))
152 }
153}
154
155crate::declare_variants! {
156 default MinimumFeedbackVertexSet<i32> => "1.9977^num_vertices",
157}
158
159#[cfg(feature = "example-db")]
160pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
161 use crate::topology::DirectedGraph;
162 vec![crate::example_db::specs::ModelExampleSpec {
163 id: "minimum_feedback_vertex_set_i32",
164 instance: Box::new(MinimumFeedbackVertexSet::new(
165 DirectedGraph::new(
166 5,
167 vec![(0, 1), (1, 2), (2, 0), (0, 3), (3, 4), (4, 1), (4, 2)],
168 ),
169 vec![1i32; 5],
170 )),
171 optimal_config: vec![1, 0, 0, 0, 0],
172 optimal_value: serde_json::json!(1),
173 }]
174}
175
176#[cfg(test)]
181pub(crate) fn is_feedback_vertex_set(graph: &DirectedGraph, selected: &[bool]) -> bool {
182 assert_eq!(
183 selected.len(),
184 graph.num_vertices(),
185 "selected length must match num_vertices"
186 );
187 let keep: Vec<bool> = selected.iter().map(|&s| !s).collect();
189 graph.induced_subgraph(&keep).is_dag()
190}
191
192#[cfg(test)]
193#[path = "../../unit_tests/models/graph/minimum_feedback_vertex_set.rs"]
194mod tests;