Skip to content
Merged
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
18 changes: 16 additions & 2 deletions docs/paper/reductions.typ
Original file line number Diff line number Diff line change
Expand Up @@ -11098,10 +11098,17 @@ Each reduction is presented as a *Rule* (with linked problem names and overhead
]


#let mis_clique = load-example("MaximumIndependentSet", "MaximumClique")
#let mis_clique = load-example(
"MaximumIndependentSet",
"MaximumClique",
source-variant: (graph: "SimpleGraph", weight: "i32"),
target-variant: (graph: "SimpleGraph", weight: "i32"),
)
#let mis_clique_sol = mis_clique.solutions.at(0)
#reduction-rule("MaximumIndependentSet", "MaximumClique",
example: true,
example-source-variant: (graph: "SimpleGraph", weight: "i32"),
example-target-variant: (graph: "SimpleGraph", weight: "i32"),
example-caption: [Path graph $P_5$: IS $arrow.r$ Clique via complement],
extra: [
#pred-commands(
Expand Down Expand Up @@ -12689,10 +12696,17 @@ The following reductions to Integer Linear Programming are straightforward formu
_Solution extraction._ Identity: return the ILP variable vector $bold(c)$ as the Integer Knapsack multiplicities.
]

#let clique_mis = load-example("MaximumClique", "MaximumIndependentSet")
#let clique_mis = load-example(
"MaximumClique",
"MaximumIndependentSet",
source-variant: (graph: "SimpleGraph", weight: "i32"),
target-variant: (graph: "SimpleGraph", weight: "i32"),
)
#let clique_mis_sol = clique_mis.solutions.at(0)
#reduction-rule("MaximumClique", "MaximumIndependentSet",
example: true,
example-source-variant: (graph: "SimpleGraph", weight: "i32"),
example-target-variant: (graph: "SimpleGraph", weight: "i32"),
example-caption: [Path graph $P_4$: clique in $G$ maps to independent set in complement $overline(G)$.],
extra: [
#pred-commands(
Expand Down
7 changes: 4 additions & 3 deletions src/models/graph/maximum_clique.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
use crate::topology::{Graph, SimpleGraph};
use crate::traits::Problem;
use crate::types::{Max, WeightElement};
use crate::types::{Max, One, WeightElement};
use num_traits::Zero;
use serde::{Deserialize, Serialize};

Expand All @@ -17,7 +17,7 @@ inventory::submit! {
aliases: &[],
dimensions: &[
VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
VariantDimension::new("weight", "i32", &["i32"]),
VariantDimension::new("weight", "One", &["One", "i32"]),
],
module_path: module_path!(),
description: "Find maximum weight clique in a graph",
Expand Down Expand Up @@ -165,7 +165,8 @@ fn is_clique_config<G: Graph>(graph: &G, config: &[usize]) -> bool {
}

crate::declare_variants! {
default MaximumClique<SimpleGraph, i32> => "1.1996^num_vertices",
MaximumClique<SimpleGraph, i32> => "1.1996^num_vertices",
default MaximumClique<SimpleGraph, One> => "1.1996^num_vertices",
}

#[cfg(feature = "example-db")]
Expand Down
16 changes: 15 additions & 1 deletion src/rules/graph_helpers.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Shared helpers for graph-based reductions.

use crate::topology::Graph;
use crate::topology::{Graph, SimpleGraph};

/// Extract a Hamiltonian cycle vertex ordering from edge-selection configs on complete graphs.
///
Expand Down Expand Up @@ -57,3 +57,17 @@ pub(crate) fn edges_to_cycle_order<G: Graph>(graph: &G, target_solution: &[usize

order
}

/// Build the complement graph edges: edges between all non-adjacent vertex pairs.
pub(crate) fn complement_edges(graph: &SimpleGraph) -> Vec<(usize, usize)> {
let n = graph.num_vertices();
let mut edges = Vec::new();
for u in 0..n {
for v in (u + 1)..n {
if !graph.has_edge(u, v) {
edges.push((u, v));
}
}
}
edges
}
14 changes: 1 addition & 13 deletions src/rules/kcoloring_partitionintocliques.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use crate::models::graph::{KColoring, PartitionIntoCliques};
use crate::reduction;
use crate::rules::graph_helpers::complement_edges;
use crate::rules::traits::{ReduceTo, ReductionResult};
use crate::topology::{Graph, SimpleGraph};
use crate::variant::KN;
Expand All @@ -29,19 +30,6 @@ impl ReductionResult for ReductionKColoringToPartitionIntoCliques {
}
}

fn complement_edges(graph: &SimpleGraph) -> Vec<(usize, usize)> {
let n = graph.num_vertices();
let mut edges = Vec::new();
for u in 0..n {
for v in (u + 1)..n {
if !graph.has_edge(u, v) {
edges.push((u, v));
}
}
}
edges
}

#[reduction(
overhead = {
num_vertices = "num_vertices",
Expand Down
101 changes: 64 additions & 37 deletions src/rules/maximumclique_maximumindependentset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::models::graph::{MaximumClique, MaximumIndependentSet};
use crate::reduction;
use crate::rules::traits::{ReduceTo, ReductionResult};
use crate::topology::{Graph, SimpleGraph};
use crate::types::WeightElement;
use crate::types::{One, WeightElement};

/// Result of reducing MaximumClique to MaximumIndependentSet.
#[derive(Debug, Clone)]
Expand All @@ -33,18 +33,15 @@ where
}
}

/// Build the complement graph: edges between all non-adjacent vertex pairs.
fn complement_edges(graph: &SimpleGraph) -> Vec<(usize, usize)> {
let n = graph.num_vertices();
let mut edges = Vec::new();
for u in 0..n {
for v in (u + 1)..n {
if !graph.has_edge(u, v) {
edges.push((u, v));
}
}
}
edges
fn reduce_clique_to_is<W: WeightElement>(
src: &MaximumClique<SimpleGraph, W>,
) -> ReductionCliqueToIS<W> {
let comp_edges = super::graph_helpers::complement_edges(src.graph());
let target = MaximumIndependentSet::new(
SimpleGraph::new(src.graph().num_vertices(), comp_edges),
src.weights().to_vec(),
);
ReductionCliqueToIS { target }
}

#[reduction(
Expand All @@ -57,38 +54,68 @@ impl ReduceTo<MaximumIndependentSet<SimpleGraph, i32>> for MaximumClique<SimpleG
type Result = ReductionCliqueToIS<i32>;

fn reduce_to(&self) -> Self::Result {
let comp_edges = complement_edges(self.graph());
let target = MaximumIndependentSet::new(
SimpleGraph::new(self.graph().num_vertices(), comp_edges),
self.weights().to_vec(),
);
ReductionCliqueToIS { target }
reduce_clique_to_is(self)
}
}

#[reduction(
overhead = {
num_vertices = "num_vertices",
num_edges = "num_vertices * (num_vertices - 1) / 2 - num_edges",
}
)]
impl ReduceTo<MaximumIndependentSet<SimpleGraph, One>> for MaximumClique<SimpleGraph, One> {
type Result = ReductionCliqueToIS<One>;

fn reduce_to(&self) -> Self::Result {
reduce_clique_to_is(self)
}
}

#[cfg(feature = "example-db")]
pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
use crate::export::SolutionPair;

vec![crate::example_db::specs::RuleExampleSpec {
id: "maximumclique_to_maximumindependentset",
build: || {
let source = MaximumClique::new(
SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]),
vec![1i32; 4],
);
crate::example_db::specs::rule_example_with_witness::<
_,
MaximumIndependentSet<SimpleGraph, i32>,
>(
source,
SolutionPair {
source_config: vec![0, 1, 1, 0],
target_config: vec![0, 1, 1, 0],
},
)
vec![
crate::example_db::specs::RuleExampleSpec {
id: "maximumclique_to_maximumindependentset",
build: || {
let source = MaximumClique::new(
SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]),
vec![1i32; 4],
);
crate::example_db::specs::rule_example_with_witness::<
_,
MaximumIndependentSet<SimpleGraph, i32>,
>(
source,
SolutionPair {
source_config: vec![0, 1, 1, 0],
target_config: vec![0, 1, 1, 0],
},
)
},
},
crate::example_db::specs::RuleExampleSpec {
id: "maximumclique_to_maximumindependentset_one",
build: || {
let source = MaximumClique::new(
SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]),
vec![One; 4],
);
crate::example_db::specs::rule_example_with_witness::<
_,
MaximumIndependentSet<SimpleGraph, One>,
>(
source,
SolutionPair {
source_config: vec![0, 1, 1, 0],
target_config: vec![0, 1, 1, 0],
},
)
},
},
}]
]
}

#[cfg(test)]
Expand Down
97 changes: 66 additions & 31 deletions src/rules/maximumindependentset_maximumclique.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::models::graph::{MaximumClique, MaximumIndependentSet};
use crate::reduction;
use crate::rules::traits::{ReduceTo, ReductionResult};
use crate::topology::{Graph, SimpleGraph};
use crate::types::WeightElement;
use crate::types::{One, WeightElement};

/// Result of reducing MaximumIndependentSet to MaximumClique.
#[derive(Debug, Clone)]
Expand All @@ -33,6 +33,17 @@ where
}
}

fn reduce_is_to_clique<W: WeightElement>(
src: &MaximumIndependentSet<SimpleGraph, W>,
) -> ReductionISToClique<W> {
let comp_edges = super::graph_helpers::complement_edges(src.graph());
let target = MaximumClique::new(
SimpleGraph::new(src.graph().num_vertices(), comp_edges),
src.weights().to_vec(),
);
ReductionISToClique { target }
}

#[reduction(
overhead = {
num_vertices = "num_vertices",
Expand All @@ -43,44 +54,68 @@ impl ReduceTo<MaximumClique<SimpleGraph, i32>> for MaximumIndependentSet<SimpleG
type Result = ReductionISToClique<i32>;

fn reduce_to(&self) -> Self::Result {
let n = self.graph().num_vertices();
// Build complement graph edges
let mut complement_edges = Vec::new();
for u in 0..n {
for v in (u + 1)..n {
if !self.graph().has_edge(u, v) {
complement_edges.push((u, v));
}
}
}
let target = MaximumClique::new(
SimpleGraph::new(n, complement_edges),
self.weights().to_vec(),
);
ReductionISToClique { target }
reduce_is_to_clique(self)
}
}

#[reduction(
overhead = {
num_vertices = "num_vertices",
num_edges = "num_vertices * (num_vertices - 1) / 2 - num_edges",
}
)]
impl ReduceTo<MaximumClique<SimpleGraph, One>> for MaximumIndependentSet<SimpleGraph, One> {
type Result = ReductionISToClique<One>;

fn reduce_to(&self) -> Self::Result {
reduce_is_to_clique(self)
}
}

#[cfg(feature = "example-db")]
pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
use crate::export::SolutionPair;

vec![crate::example_db::specs::RuleExampleSpec {
id: "maximumindependentset_to_maximumclique",
build: || {
let source = MaximumIndependentSet::new(
SimpleGraph::new(5, vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
vec![1i32; 5],
);
crate::example_db::specs::rule_example_with_witness::<_, MaximumClique<SimpleGraph, i32>>(
source,
SolutionPair {
source_config: vec![1, 0, 1, 0, 1],
target_config: vec![1, 0, 1, 0, 1],
},
)
vec![
crate::example_db::specs::RuleExampleSpec {
id: "maximumindependentset_to_maximumclique",
build: || {
let source = MaximumIndependentSet::new(
SimpleGraph::new(5, vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
vec![1i32; 5],
);
crate::example_db::specs::rule_example_with_witness::<
_,
MaximumClique<SimpleGraph, i32>,
>(
source,
SolutionPair {
source_config: vec![1, 0, 1, 0, 1],
target_config: vec![1, 0, 1, 0, 1],
},
)
},
},
crate::example_db::specs::RuleExampleSpec {
id: "maximumindependentset_to_maximumclique_one",
build: || {
let source = MaximumIndependentSet::new(
SimpleGraph::new(5, vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
vec![One; 5],
);
crate::example_db::specs::rule_example_with_witness::<
_,
MaximumClique<SimpleGraph, One>,
>(
source,
SolutionPair {
source_config: vec![1, 0, 1, 0, 1],
target_config: vec![1, 0, 1, 0, 1],
},
)
},
},
}]
]
}

#[cfg(test)]
Expand Down
Loading
Loading