Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 77 additions & 15 deletions docs/paper/reductions.typ

Large diffs are not rendered by default.

18 changes: 6 additions & 12 deletions problemreductions-cli/tests/cli_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5601,14 +5601,7 @@ fn test_path_preserves_exact_variables_and_bounded_quadratic_terms() {
#[test]
fn test_path_overall_preserves_unavailable_fields_alongside_exact_fields() {
let output = pred()
.args([
"path",
"DecisionLongestCircuit",
"ILP/bool",
"--limit",
"1",
"--json",
])
.args(["path", "BMF", "BicliqueCover", "--limit", "1", "--json"])
.output()
.unwrap();
assert!(output.status.success());
Expand All @@ -5625,14 +5618,15 @@ fn test_path_overall_preserves_unavailable_fields_alongside_exact_fields() {
)
})
.collect::<std::collections::BTreeMap<_, _>>();
assert_eq!(relations["num_constraints"], "exact");
assert_eq!(relations["num_vars"], "exact");
assert_eq!(relations["max_constraint_magnitude_bits"], "unavailable");
for field in ["num_vertices", "left_size", "right_size", "rank"] {
assert_eq!(relations[field], "exact");
}
assert_eq!(relations["num_edges"], "unavailable");
let unavailable = fields
.iter()
.find(|field| field["relation"] == "unavailable")
.unwrap();
assert!(unavailable["reason"].as_str().unwrap().contains("length"));
assert!(!unavailable["reason"].as_str().unwrap().is_empty());
}

#[test]
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,7 @@ mod test_reduction_graph;
#[cfg(test)]
#[path = "unit_tests/unitdiskmapping_algorithms/mod.rs"]
mod test_unitdiskmapping_algorithms;

#[cfg(test)]
#[path = "unit_tests/ilp_overhead.rs"]
mod ilp_overhead;
17 changes: 16 additions & 1 deletion src/models/graph/acyclic_partition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,17 @@ impl<W: WeightElement> AcyclicPartition<W> {
!W::IS_UNIT
}

/// Smallest h >= 1 bounding vertex weights, arc costs, and both budgets in magnitude by 2^h.
pub fn max_numeric_magnitude_bits(&self) -> u64 {
crate::types::max_numeric_magnitude_bits(
self.vertex_weights
.iter()
.chain(&self.arc_costs)
.map(|value| value.to_sum())
.chain([self.weight_bound.clone(), self.cost_bound.clone()]),
)
}

/// Get the number of vertices.
pub fn num_vertices(&self) -> usize {
self.graph.num_vertices()
Expand Down Expand Up @@ -253,7 +264,11 @@ where
type Solution = Vec<usize>;
type Value = crate::types::Or;

crate::problem_parameters![("num_arcs", num_arcs), ("num_vertices", num_vertices),];
crate::problem_parameters![
("max_numeric_magnitude_bits", max_numeric_magnitude_bits),
("num_arcs", num_arcs),
("num_vertices", num_vertices),
];

fn variant() -> Vec<(&'static str, &'static str)> {
crate::variant_params![W]
Expand Down
11 changes: 11 additions & 0 deletions src/models/graph/bounded_component_spanning_forest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,16 @@ impl<G: Graph, W: WeightElement> BoundedComponentSpanningForest<G, W> {
&self.max_weight
}

/// Smallest h >= 1 with every vertex weight and the component weight limit below 2^h.
pub fn max_weight_bits(&self) -> u64 {
crate::types::max_numeric_magnitude_bits(
self.weights
.iter()
.map(|weight| weight.to_sum())
.chain(std::iter::once(self.max_weight.clone())),
)
}

/// Get the number of vertices in the underlying graph.
pub fn num_vertices(&self) -> usize {
self.graph.num_vertices()
Expand Down Expand Up @@ -263,6 +273,7 @@ where
type Value = crate::types::Or;

crate::problem_parameters![
("max_weight_bits", max_weight_bits),
("max_components", max_components),
("num_edges", num_edges),
("num_vertices", num_vertices),
Expand Down
13 changes: 12 additions & 1 deletion src/models/graph/longest_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,13 @@ impl<G: Graph, W: WeightElement> LongestCircuit<G, W> {
self.edge_lengths.clone()
}

/// Smallest h >= 1 with every edge length strictly below 2^h.
pub fn max_length_bits(&self) -> u64 {
crate::types::max_numeric_magnitude_bits(
self.edge_lengths.iter().map(|length| length.to_sum()),
)
}

/// Get the number of vertices in the graph.
pub fn num_vertices(&self) -> usize {
self.graph.num_vertices()
Expand Down Expand Up @@ -211,7 +218,11 @@ where
type Solution = Vec<bool>;
type Value = Max<W::Sum>;

crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
crate::problem_parameters![
("max_length_bits", max_length_bits),
("num_edges", num_edges),
("num_vertices", num_vertices),
];

fn variant() -> Vec<(&'static str, &'static str)> {
crate::variant_params![G, W]
Expand Down
34 changes: 26 additions & 8 deletions src/models/graph/min_max_multicenter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,16 @@ impl<G: Graph, W: WeightElement> MinMaxMulticenter<G, W> {
self.k
}

/// Smallest h >= 1 with every vertex weight and edge length strictly below 2^h.
pub fn max_numeric_magnitude_bits(&self) -> u64 {
crate::types::max_numeric_magnitude_bits(
self.vertex_weights
.iter()
.chain(&self.edge_lengths)
.map(|value| value.to_sum()),
)
}

/// Get the number of vertices in the underlying graph.
pub fn num_vertices(&self) -> usize {
self.graph().num_vertices()
Expand All @@ -258,11 +268,11 @@ impl<G: Graph, W: WeightElement> MinMaxMulticenter<G, W> {
/// Correct because all edge lengths are non-negative.
///
/// Returns `None` if any vertex is unreachable from all centers.
fn shortest_distances(&self, config: &[bool]) -> Option<Vec<W::Sum>> {
fn shortest_distances(
&self,
config: &[bool],
) -> Result<Option<Vec<W::Sum>>, crate::traits::EvaluationError> {
let n = self.graph.num_vertices();
if config.len() != n {
return None;
}
let edges = self.graph.edges();

let mut adj: Vec<Vec<(usize, W::Sum)>> = vec![Vec::new(); n];
Expand Down Expand Up @@ -312,7 +322,11 @@ impl<G: Graph, W: WeightElement> MinMaxMulticenter<G, W> {
if visited[next] {
continue;
}
let new_dist = du.clone() + len.clone();
let new_dist = W::checked_add_to_sum(
du.clone(),
len.clone(),
"adding min-max multicenter path lengths",
)?;
let update = match &dist[next] {
None => true,
Some(d) => new_dist < *d,
Expand All @@ -323,7 +337,7 @@ impl<G: Graph, W: WeightElement> MinMaxMulticenter<G, W> {
}
}

dist.into_iter().collect()
Ok(dist.into_iter().collect())
}
}

Expand All @@ -336,7 +350,11 @@ where
type Solution = Vec<bool>;
type Value = Min<W::Sum>;

crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
crate::problem_parameters![
("max_numeric_magnitude_bits", max_numeric_magnitude_bits),
("num_edges", num_edges),
("num_vertices", num_vertices),
];

fn variant() -> Vec<(&'static str, &'static str)> {
crate::variant_params![G, W]
Expand All @@ -360,7 +378,7 @@ where
}

// Compute shortest distances to nearest center
let distances = match self.shortest_distances(config) {
let distances = match self.shortest_distances(config)? {
Some(d) => d,
None => {
return Ok(Min(None));
Expand Down
16 changes: 15 additions & 1 deletion src/models/graph/minimum_capacitated_spanning_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,16 @@ impl<G: Graph, W: WeightElement> MinimumCapacitatedSpanningTree<G, W> {
&self.capacity
}

/// Smallest h >= 1 bounding vertex requirements and capacity in magnitude strictly by 2^h.
pub fn max_requirement_bits(&self) -> u64 {
crate::types::max_numeric_magnitude_bits(
self.requirements
.iter()
.map(|value| value.to_sum())
.chain(std::iter::once(self.capacity.clone())),
)
}

/// Get the number of vertices in the underlying graph.
pub fn num_vertices(&self) -> usize {
self.graph.num_vertices()
Expand Down Expand Up @@ -381,7 +391,11 @@ where
type Solution = Vec<bool>;
type Value = Min<W::Sum>;

crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
crate::problem_parameters![
("max_requirement_bits", max_requirement_bits),
("num_edges", num_edges),
("num_vertices", num_vertices),
];

fn variant() -> Vec<(&'static str, &'static str)> {
crate::variant_params![G, W]
Expand Down
11 changes: 11 additions & 0 deletions src/models/graph/multiple_choice_branching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,16 @@ impl<W: WeightElement> MultipleChoiceBranching<W> {
&self.threshold
}

/// Smallest h >= 1 bounding arc weights and the threshold in magnitude strictly by 2^h.
pub fn max_weight_bits(&self) -> u64 {
crate::types::max_numeric_magnitude_bits(
self.weights
.iter()
.map(|weight| weight.to_sum())
.chain(std::iter::once(self.threshold.clone())),
)
}

/// Get the number of vertices.
pub fn num_vertices(&self) -> usize {
self.graph.num_vertices()
Expand Down Expand Up @@ -232,6 +242,7 @@ where
type Value = crate::types::Or;

crate::problem_parameters![
("max_weight_bits", max_weight_bits),
("num_vertices", num_vertices),
("num_arcs", num_arcs),
("num_partition_groups", num_partition_groups),
Expand Down
16 changes: 15 additions & 1 deletion src/models/graph/shortest_weight_constrained_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,16 @@ impl<G: Graph, N: WeightElement> ShortestWeightConstrainedPath<G, N> {
!N::IS_UNIT
}

/// Smallest h >= 1 with every edge weight and the weight limit strictly below 2^h.
pub fn max_weight_bits(&self) -> u64 {
crate::types::max_numeric_magnitude_bits(
self.edge_weights
.iter()
.map(|weight| weight.to_sum())
.chain(std::iter::once(self.weight_bound.clone())),
)
}

/// Get the number of vertices in the graph.
pub fn num_vertices(&self) -> usize {
self.graph.num_vertices()
Expand Down Expand Up @@ -341,7 +351,11 @@ where
type Solution = Vec<bool>;
type Value = Min<N::Sum>;

crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
crate::problem_parameters![
("max_weight_bits", max_weight_bits),
("num_edges", num_edges),
("num_vertices", num_vertices),
];

fn variant() -> Vec<(&'static str, &'static str)> {
crate::variant_params![G, N]
Expand Down
17 changes: 16 additions & 1 deletion src/models/misc/capacity_assignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ impl CapacityAssignment {
})
}

/// Smallest h >= 1 bounding every delay and the delay budget in magnitude strictly by 2^h.
pub fn max_delay_bits(&self) -> u64 {
crate::types::max_numeric_magnitude_bits(
self.delay
.iter()
.flatten()
.copied()
.chain(std::iter::once(self.delay_budget)),
)
}

/// Number of communication links.
pub fn num_links(&self) -> usize {
self.cost.len()
Expand Down Expand Up @@ -189,7 +200,11 @@ impl Problem for CapacityAssignment {
type Solution = Vec<usize>;
type Value = crate::types::Min<i64>;

crate::problem_parameters![("num_capacities", num_capacities), ("num_links", num_links),];
crate::problem_parameters![
("max_delay_bits", max_delay_bits),
("num_capacities", num_capacities),
("num_links", num_links),
];

fn evaluate(
&self,
Expand Down
17 changes: 16 additions & 1 deletion src/models/misc/flow_shop_scheduling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,17 @@ impl FlowShopScheduling {
self.deadline
}

/// Smallest h >= 1 with every processing time and the deadline strictly below 2^h.
pub fn max_time_bits(&self) -> u64 {
crate::types::max_numeric_magnitude_bits(
self.task_lengths
.iter()
.flatten()
.copied()
.chain(std::iter::once(self.deadline)),
)
}

/// Get the number of jobs.
pub fn num_jobs(&self) -> usize {
self.task_lengths.len()
Expand Down Expand Up @@ -206,7 +217,11 @@ impl Problem for FlowShopScheduling {
type Solution = Vec<usize>;
type Value = crate::types::Or;

crate::problem_parameters![("num_jobs", num_jobs), ("num_processors", num_processors),];
crate::problem_parameters![
("max_time_bits", max_time_bits),
("num_jobs", num_jobs),
("num_processors", num_processors),
];

fn variant() -> Vec<(&'static str, &'static str)> {
crate::variant_params![]
Expand Down
7 changes: 7 additions & 0 deletions src/models/misc/minimum_tardiness_sequencing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ fn validate_task_data(
}

impl<W: WeightElement> MinimumTardinessSequencing<W> {
/// Smallest h >= 1 with every processing time strictly below 2^h.
pub fn max_processing_time_bits(&self) -> u64 {
crate::types::max_numeric_magnitude_bits(self.lengths.iter().map(|length| length.to_sum()))
}

/// Returns the number of tasks.
pub fn num_tasks(&self) -> usize {
self.deadlines.len()
Expand Down Expand Up @@ -253,6 +258,7 @@ impl Problem for MinimumTardinessSequencing<One> {
type Value = Min<i64>;

crate::problem_parameters![
("max_processing_time_bits", max_processing_time_bits),
("num_precedences", num_precedences),
("num_tasks", num_tasks),
];
Expand Down Expand Up @@ -318,6 +324,7 @@ impl Problem for MinimumTardinessSequencing<i64> {
type Value = Min<i64>;

crate::problem_parameters![
("max_processing_time_bits", max_processing_time_bits),
("num_precedences", num_precedences),
("num_tasks", num_tasks),
];
Expand Down
Loading
Loading