use std::collections::{HashMap, HashSet, VecDeque};
use oxilean_kernel::{Declaration, Environment, Expr, Level, Name};
use super::types::{
find_root, BasisMatroid, Circuit, Element, GraphicMatroid, GreedyResult,
MatroidIntersectionInput, MatroidIntersectionResult, MatroidUnion, PartitionMatroid,
RankFunction, TransversalMatroid, TruncatedMatroid, UniformMatroid, WeightedElement,
};
fn cst(s: &str) -> Expr {
Expr::Const(Name::str(s), vec![])
}
fn type0() -> Expr {
Expr::Sort(Level::succ(Level::zero()))
}
fn prop() -> Expr {
Expr::Sort(Level::zero())
}
fn arrow(a: Expr, b: Expr) -> Expr {
Expr::Pi(
oxilean_kernel::BinderInfo::Default,
Name::str("_"),
Box::new(a),
Box::new(b),
)
}
fn app(f: Expr, a: Expr) -> Expr {
Expr::App(Box::new(f), Box::new(a))
}
fn nat_ty() -> Expr {
cst("Nat")
}
fn bool_ty() -> Expr {
cst("Bool")
}
fn set_ty(elem: Expr) -> Expr {
app(cst("Finset"), elem)
}
fn list_ty(elem: Expr) -> Expr {
app(cst("List"), elem)
}
pub fn ground_set_ty() -> Expr {
type0()
}
pub fn indep_family_ty() -> Expr {
arrow(set_ty(cst("E")), prop())
}
pub fn matroid_ty() -> Expr {
type0()
}
pub fn uniform_matroid_ty() -> Expr {
arrow(nat_ty(), arrow(nat_ty(), matroid_ty()))
}
pub fn graphic_matroid_ty() -> Expr {
arrow(cst("Graph"), matroid_ty())
}
pub fn matroid_rank_ty() -> Expr {
arrow(matroid_ty(), arrow(set_ty(cst("E")), nat_ty()))
}
pub fn matroid_dual_ty() -> Expr {
arrow(matroid_ty(), matroid_ty())
}
pub fn matroid_minor_ty() -> Expr {
arrow(
matroid_ty(),
arrow(set_ty(cst("E")), arrow(set_ty(cst("E")), matroid_ty())),
)
}
pub fn circuit_ty() -> Expr {
arrow(set_ty(cst("E")), prop())
}
pub fn matroid_greedy_ty() -> Expr {
arrow(
matroid_ty(),
arrow(list_ty(cst("WeightedElem")), set_ty(cst("E"))),
)
}
pub fn matroid_intersection_ty() -> Expr {
arrow(matroid_ty(), arrow(matroid_ty(), set_ty(cst("E"))))
}
pub fn indep_empty_ty() -> Expr {
app(cst("IndepFamily"), cst("Finset.empty"))
}
pub fn indep_hereditary_ty() -> Expr {
arrow(
set_ty(cst("E")),
arrow(set_ty(cst("E")), arrow(prop(), arrow(prop(), prop()))),
)
}
pub fn indep_augmentation_ty() -> Expr {
arrow(
set_ty(cst("E")),
arrow(set_ty(cst("E")), arrow(prop(), prop())),
)
}
pub fn rank_submodular_ty() -> Expr {
arrow(set_ty(cst("E")), arrow(set_ty(cst("E")), prop()))
}
pub fn greedy_correctness_ty() -> Expr {
arrow(matroid_ty(), arrow(list_ty(cst("Weight")), prop()))
}
pub fn matroid_union_rank_ty() -> Expr {
arrow(
matroid_ty(),
arrow(matroid_ty(), arrow(set_ty(cst("E")), nat_ty())),
)
}
pub fn whitney_theorem_ty() -> Expr {
arrow(cst("Graph"), arrow(cst("Graph"), prop()))
}
pub fn build_matroid_theory_env(env: &mut Environment) {
let axioms: &[(&str, Expr)] = &[
("GroundSet", ground_set_ty()),
("IndepFamily", indep_family_ty()),
("Matroid", matroid_ty()),
("UniformMatroid", uniform_matroid_ty()),
("GraphicMatroid", graphic_matroid_ty()),
("MatroidRank", matroid_rank_ty()),
("MatroidDual", matroid_dual_ty()),
("MatroidMinor", matroid_minor_ty()),
("Circuit", circuit_ty()),
("MatroidGreedy", matroid_greedy_ty()),
("MatroidIntersection", matroid_intersection_ty()),
("IndepEmpty", indep_empty_ty()),
("IndepHereditary", indep_hereditary_ty()),
("IndepAugmentation", indep_augmentation_ty()),
("RankSubmodular", rank_submodular_ty()),
("GreedyCorrectness", greedy_correctness_ty()),
("MatroidUnionRank", matroid_union_rank_ty()),
("WhitneyTheorem", whitney_theorem_ty()),
];
for (name, ty) in axioms {
env.add(Declaration::Axiom {
name: Name::str(*name),
univ_params: vec![],
ty: ty.clone(),
})
.ok();
}
}
pub fn greedy_max_weight_basis(
matroid: &BasisMatroid,
weights: &[WeightedElement],
) -> GreedyResult {
let mut sorted = weights.to_vec();
sorted.sort_by(|a, b| {
b.weight
.partial_cmp(&a.weight)
.unwrap_or(std::cmp::Ordering::Equal)
});
let mut basis: Vec<usize> = Vec::new();
let mut total_weight = 0.0f64;
for welem in &sorted {
let mut candidate = basis.clone();
candidate.push(welem.index);
if matroid.is_independent(&candidate) {
basis.push(welem.index);
total_weight += welem.weight;
}
}
GreedyResult {
basis,
total_weight,
}
}
pub fn greedy_uniform(matroid: &UniformMatroid, weights: &[WeightedElement]) -> GreedyResult {
let mut sorted = weights.to_vec();
sorted.sort_by(|a, b| {
b.weight
.partial_cmp(&a.weight)
.unwrap_or(std::cmp::Ordering::Equal)
});
let mut basis: Vec<usize> = Vec::new();
let mut total_weight = 0.0f64;
for welem in &sorted {
if basis.len() < matroid.k {
basis.push(welem.index);
total_weight += welem.weight;
}
}
GreedyResult {
basis,
total_weight,
}
}
pub fn greedy_graphic(matroid: &GraphicMatroid, edge_weights: &[f64]) -> GreedyResult {
assert_eq!(edge_weights.len(), matroid.edges.len());
let n = matroid.edges.len();
let mut indices: Vec<usize> = (0..n).collect();
indices.sort_by(|&a, &b| {
edge_weights[b]
.partial_cmp(&edge_weights[a])
.unwrap_or(std::cmp::Ordering::Equal)
});
let mut parent: Vec<usize> = (0..matroid.num_vertices).collect();
let mut basis = Vec::new();
let mut total_weight = 0.0f64;
for idx in indices {
let e = matroid.edges[idx];
let pu = find_root(&mut parent, e.u);
let pv = find_root(&mut parent, e.v);
if pu != pv {
parent[pu] = pv;
basis.push(idx);
total_weight += edge_weights[idx];
}
}
GreedyResult {
basis,
total_weight,
}
}
pub fn enumerate_circuits(matroid: &BasisMatroid) -> Vec<Circuit> {
let n = matroid.ground_set.len();
let mut circuits = Vec::new();
for mask in 1u32..(1u32 << n) {
let set: Vec<usize> = (0..n).filter(|&i| mask & (1 << i) != 0).collect();
if matroid.is_independent(&set) {
continue; }
let mut is_minimal = true;
for omit in 0..set.len() {
let sub: Vec<usize> = set
.iter()
.enumerate()
.filter(|&(i, _)| i != omit)
.map(|(_, &e)| e)
.collect();
if !matroid.is_independent(&sub) {
is_minimal = false;
break;
}
}
if is_minimal {
circuits.push(Circuit::new(set));
}
}
circuits
}
pub fn fundamental_circuit(matroid: &BasisMatroid, basis: &[usize], e: usize) -> Option<Circuit> {
let mut extended = basis.to_vec();
extended.push(e);
if matroid.is_independent(&extended) {
return None; }
let mut circuit_elements = extended.clone();
circuit_elements.retain(|&x| x == e || basis.contains(&x));
let mut result = circuit_elements.clone();
let mut changed = true;
while changed {
changed = false;
for i in 0..result.len() {
if result[i] == e {
continue;
}
let candidate: Vec<usize> = result
.iter()
.enumerate()
.filter(|&(j, _)| j != i)
.map(|(_, &x)| x)
.collect();
if !matroid.is_independent(&candidate) {
result = candidate;
changed = true;
break;
}
}
}
Some(Circuit::new(result))
}
pub fn matroid_intersection(input: &MatroidIntersectionInput) -> MatroidIntersectionResult {
let n = input.n;
let m1 = BasisMatroid {
ground_set: (0..n).map(Element).collect(),
bases: input.m1_bases.clone(),
rank: input.m1_bases.first().map_or(0, |b| b.len()),
};
let m2 = BasisMatroid {
ground_set: (0..n).map(Element).collect(),
bases: input.m2_bases.clone(),
rank: input.m2_bases.first().map_or(0, |b| b.len()),
};
let mut indep: HashSet<usize> = HashSet::new();
loop {
let x1: Vec<usize> = (0..n)
.filter(|e| !indep.contains(e))
.filter(|e| {
let mut s: Vec<usize> = indep.iter().copied().collect();
s.push(*e);
m1.is_independent(&s)
})
.collect();
let x2: HashSet<usize> = (0..n)
.filter(|e| !indep.contains(e))
.filter(|e| {
let mut s: Vec<usize> = indep.iter().copied().collect();
s.push(*e);
m2.is_independent(&s)
})
.collect();
let not_indep: Vec<usize> = (0..n).filter(|e| !indep.contains(e)).collect();
let indep_vec: Vec<usize> = indep.iter().copied().collect();
let mut adj: HashMap<usize, Vec<usize>> = HashMap::new();
for &y in ¬_indep {
let mut edges = Vec::new();
for &x in &indep_vec {
let s1: Vec<usize> = indep_vec
.iter()
.filter(|&&e| e != x)
.copied()
.chain(std::iter::once(y))
.collect();
if !m1.is_independent(&s1) {
continue;
}
for &z in ¬_indep {
if z == y {
continue;
}
let s2: Vec<usize> = indep_vec
.iter()
.filter(|&&e| e != x)
.copied()
.chain(std::iter::once(z))
.collect();
if m2.is_independent(&s2) {
edges.push(z);
}
}
}
adj.insert(y, edges);
}
let mut prev: HashMap<usize, Option<usize>> = HashMap::new();
let mut queue: VecDeque<usize> = VecDeque::new();
for &y in &x1 {
prev.insert(y, None);
queue.push_back(y);
}
let mut found_end: Option<usize> = None;
'bfs: while let Some(cur) = queue.pop_front() {
if x2.contains(&cur) {
found_end = Some(cur);
break 'bfs;
}
if let Some(neighbors) = adj.get(&cur) {
for &nxt in neighbors {
let entry = prev.entry(nxt);
if let std::collections::hash_map::Entry::Vacant(e) = entry {
e.insert(Some(cur));
queue.push_back(nxt);
}
}
}
}
match found_end {
None => break, Some(end) => {
let mut path = vec![end];
let mut cur = end;
while let Some(&Some(p)) = prev.get(&cur) {
path.push(p);
cur = p;
}
path.reverse();
for &e in &path {
indep.insert(e);
}
}
}
}
let common_independent: Vec<usize> = indep.into_iter().collect();
let size = common_independent.len();
MatroidIntersectionResult {
common_independent,
size,
}
}
pub fn graphic_rank_function(matroid: &GraphicMatroid) -> RankFunction {
let n = matroid.edges.len();
assert!(
n <= 20,
"graphic_rank_function: too many edges for bitmask encoding"
);
let mut values = HashMap::new();
for mask in 0u32..(1u32 << n) {
let set: Vec<usize> = (0..n).filter(|&i| mask & (1 << i) != 0).collect();
values.insert(mask, matroid.rank_of(&set));
}
RankFunction { n, values }
}
pub fn verify_matroid_axioms(matroid: &BasisMatroid) -> Result<(), String> {
let n = matroid.ground_set.len();
if !matroid.is_independent(&[]) {
return Err("I1 violated: empty set is not independent".to_string());
}
for mask in 1u32..(1u32 << n) {
let set: Vec<usize> = (0..n).filter(|&i| mask & (1 << i) != 0).collect();
if matroid.is_independent(&set) {
for omit in 0..set.len() {
let sub: Vec<usize> = set
.iter()
.enumerate()
.filter(|&(i, _)| i != omit)
.map(|(_, &e)| e)
.collect();
if !matroid.is_independent(&sub) {
return Err(format!(
"I2 violated: {:?} is independent but subset {:?} is not",
set, sub
));
}
}
}
}
for mask_a in 0u32..(1u32 << n) {
let a: Vec<usize> = (0..n).filter(|&i| mask_a & (1 << i) != 0).collect();
if !matroid.is_independent(&a) {
continue;
}
for mask_b in 0u32..(1u32 << n) {
let b: Vec<usize> = (0..n).filter(|&i| mask_b & (1 << i) != 0).collect();
if !matroid.is_independent(&b) {
continue;
}
if a.len() >= b.len() {
continue;
}
let a_set: HashSet<usize> = a.iter().copied().collect();
let b_minus_a: Vec<usize> = b
.iter()
.filter(|&&e| !a_set.contains(&e))
.copied()
.collect();
let augmented = b_minus_a.iter().any(|&x| {
let mut cand = a.clone();
cand.push(x);
matroid.is_independent(&cand)
});
if !augmented {
return Err(format!(
"I3 violated: A={:?} and B={:?} with |A|<|B| but no augmenting element found",
a, b
));
}
}
}
Ok(())
}
pub fn partition_matroid_greedy(matroid: &PartitionMatroid, weights: &[f64]) -> Vec<usize> {
assert_eq!(weights.len(), matroid.ground_set.len());
let mut selected: Vec<usize> = Vec::new();
let mut group_counts: Vec<usize> = vec![0; matroid.groups.len()];
let mut order: Vec<usize> = (0..matroid.ground_set.len()).collect();
order.sort_by(|&a, &b| {
weights[b]
.partial_cmp(&weights[a])
.unwrap_or(std::cmp::Ordering::Equal)
});
for idx in order {
let group_idx = matroid.groups.iter().position(|g| g.contains(&idx));
if let Some(gi) = group_idx {
if group_counts[gi] < matroid.caps[gi] {
selected.push(idx);
group_counts[gi] += 1;
}
}
}
selected
}
pub fn cocircuits(matroid: &BasisMatroid) -> Vec<Circuit> {
let dual = matroid.dual();
enumerate_circuits(&dual)
}
pub fn is_matroid_connected(matroid: &BasisMatroid) -> bool {
let n = matroid.ground_set.len();
if n <= 1 {
return true;
}
let circuits = enumerate_circuits(matroid);
for i in 0..n {
for j in (i + 1)..n {
let has_circuit = circuits
.iter()
.any(|c| c.elements.contains(&i) && c.elements.contains(&j));
if !has_circuit {
return false;
}
}
}
true
}
pub fn matroid_girth(matroid: &BasisMatroid) -> Option<usize> {
let circuits = enumerate_circuits(matroid);
circuits.iter().map(|c| c.elements.len()).min()
}
pub fn corank(matroid: &BasisMatroid, set: &[usize]) -> usize {
let n = matroid.ground_set.len();
let set_h: HashSet<usize> = set.iter().copied().collect();
let complement: Vec<usize> = (0..n).filter(|e| !set_h.contains(e)).collect();
set.len() + matroid.rank_of(&complement)
- matroid
.rank .min(set.len() + matroid.rank_of(&complement))
}
pub fn closure(matroid: &BasisMatroid, set: &[usize]) -> Vec<usize> {
let n = matroid.ground_set.len();
let r_a = matroid.rank_of(set);
(0..n)
.filter(|&x| {
let mut extended = set.to_vec();
if !extended.contains(&x) {
extended.push(x);
}
matroid.rank_of(&extended) == r_a
})
.collect()
}
pub fn is_flat(matroid: &BasisMatroid, set: &[usize]) -> bool {
let cl = closure(matroid, set);
let set_h: HashSet<usize> = set.iter().copied().collect();
cl.len() == set_h.len() && cl.iter().all(|e| set_h.contains(e))
}
pub fn count_bases(matroid: &BasisMatroid) -> usize {
matroid.bases.len()
}
pub fn beta_invariant(matroid: &BasisMatroid) -> i64 {
let n = matroid.ground_set.len();
let r = matroid.rank;
let mut beta: i64 = 0;
for mask in 0u32..(1u32 << n) {
let set: Vec<usize> = (0..n).filter(|&i| mask & (1 << i) != 0).collect();
let rank_a = matroid.rank_of(&set);
if rank_a == r {
let size = set.len();
let sign: i64 = if (size - r) % 2 == 0 { 1 } else { -1 };
beta += sign;
}
}
beta
}
#[cfg(test)]
mod tests {
use super::*;
use crate::matroid_theory::types::*;
fn make_basis_matroid_u2_4() -> BasisMatroid {
let ground_set = vec![Element(0), Element(1), Element(2), Element(3)];
let bases = vec![
vec![0, 1],
vec![0, 2],
vec![0, 3],
vec![1, 2],
vec![1, 3],
vec![2, 3],
];
BasisMatroid::new(ground_set, bases).expect("valid matroid")
}
fn make_graphic_triangle() -> GraphicMatroid {
let edges = vec![Edge::new(0, 1), Edge::new(1, 2), Edge::new(0, 2)];
GraphicMatroid::new(3, edges)
}
#[test]
fn test_uniform_matroid_independence() {
let m = UniformMatroid::new(2, 4);
assert!(m.is_independent(&[Element(0)]));
assert!(m.is_independent(&[Element(0), Element(1)]));
assert!(!m.is_independent(&[Element(0), Element(1), Element(2)]));
}
#[test]
fn test_uniform_matroid_rank() {
let m = UniformMatroid::new(2, 4);
assert_eq!(m.rank(), 2);
assert_eq!(m.rank_of(&[Element(0)]), 1);
assert_eq!(m.rank_of(&[Element(0), Element(1), Element(2)]), 2);
}
#[test]
fn test_uniform_matroid_dual() {
let m = UniformMatroid::new(2, 4);
let d = m.dual();
assert_eq!(d.k, 2);
assert_eq!(d.n, 4);
}
#[test]
fn test_graphic_matroid_forests() {
let gm = make_graphic_triangle();
assert!(gm.is_independent(&[0]));
assert!(gm.is_independent(&[1]));
assert!(gm.is_independent(&[0, 1]));
assert!(!gm.is_independent(&[0, 1, 2]));
}
#[test]
fn test_graphic_matroid_rank() {
let gm = make_graphic_triangle();
assert_eq!(gm.rank(), 2);
}
#[test]
fn test_partition_matroid() {
let ground_set = vec![Element(0), Element(1), Element(2), Element(3)];
let groups = vec![vec![0, 1], vec![2, 3]];
let caps = vec![1, 1];
let pm = PartitionMatroid::new(ground_set, groups, caps);
assert!(pm.is_independent(&[0, 2]));
assert!(pm.is_independent(&[1, 3]));
assert!(!pm.is_independent(&[0, 1])); assert_eq!(pm.rank(), 2);
}
#[test]
fn test_basis_matroid_independence() {
let m = make_basis_matroid_u2_4();
assert!(m.is_independent(&[0, 1]));
assert!(m.is_independent(&[0]));
assert!(m.is_independent(&[]));
assert!(!m.is_independent(&[0, 1, 2]));
}
#[test]
fn test_basis_matroid_dual() {
let m = make_basis_matroid_u2_4();
let d = m.dual();
assert_eq!(d.rank, 2); assert!(d.bases.contains(&vec![2, 3]));
assert!(d.bases.contains(&vec![1, 3]));
}
#[test]
fn test_verify_matroid_axioms() {
let m = make_basis_matroid_u2_4();
assert!(verify_matroid_axioms(&m).is_ok());
}
#[test]
fn test_greedy_uniform() {
let m = UniformMatroid::new(2, 4);
let weights = vec![
WeightedElement::new(0, 3.0),
WeightedElement::new(1, 1.0),
WeightedElement::new(2, 4.0),
WeightedElement::new(3, 2.0),
];
let result = greedy_uniform(&m, &weights);
assert_eq!(result.basis.len(), 2);
assert!((result.total_weight - 7.0).abs() < 1e-10); }
#[test]
fn test_greedy_graphic_kruskal() {
let gm = make_graphic_triangle();
let weights = vec![3.0, 1.0, 4.0];
let result = greedy_graphic(&gm, &weights);
assert_eq!(result.basis.len(), 2); assert!((result.total_weight - 7.0).abs() < 1e-10);
}
#[test]
fn test_enumerate_circuits() {
let m = make_basis_matroid_u2_4();
let circuits = enumerate_circuits(&m);
assert_eq!(circuits.len(), 4);
for c in &circuits {
assert_eq!(c.elements.len(), 3);
}
}
#[test]
fn test_closure() {
let m = make_basis_matroid_u2_4();
let cl = closure(&m, &[0, 1]);
assert_eq!(cl.len(), 4);
}
#[test]
fn test_rank_function_submodular() {
let rf = RankFunction::from_uniform(2, 4);
assert!(rf.verify_submodular());
}
#[test]
fn test_matroid_girth() {
let m = make_basis_matroid_u2_4();
assert_eq!(matroid_girth(&m), Some(3));
}
#[test]
fn test_transversal_matroid() {
let tm = TransversalMatroid::new(4, vec![vec![0, 1], vec![1, 2], vec![2, 3]]);
assert!(tm.is_independent(&[0, 2])); assert_eq!(tm.rank(), 3); }
#[test]
fn test_truncated_matroid() {
let m = make_basis_matroid_u2_4();
let t = TruncatedMatroid::new(m, 1);
assert!(t.is_independent(&[0]));
assert!(!t.is_independent(&[0, 1]));
assert_eq!(t.rank_of(&[0, 1, 2]), 1);
}
#[test]
fn test_count_bases() {
let m = make_basis_matroid_u2_4();
assert_eq!(count_bases(&m), 6); }
#[test]
fn test_is_flat() {
let m = make_basis_matroid_u2_4();
assert!(is_flat(&m, &[0, 1, 2, 3]));
assert!(is_flat(&m, &[]));
}
#[test]
fn test_matroid_connected() {
let m = make_basis_matroid_u2_4();
assert!(is_matroid_connected(&m));
}
#[test]
fn test_matroid_union_rank() {
let m1 = make_basis_matroid_u2_4();
let m2 = make_basis_matroid_u2_4();
let union = MatroidUnion::new(m1, m2);
let all = vec![0, 1, 2, 3];
let r = union.rank_of(&all);
assert!(r <= 4);
assert!(r >= 2);
}
#[test]
fn test_build_env() {
let mut env = Environment::new();
build_matroid_theory_env(&mut env);
}
}