use bit_set::BitSet;
use clap::ValueEnum;
use crate::{
molecule::{Bond, Element, Molecule},
state::State,
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum Bound {
Log,
Int,
VecSimple,
VecSmallFrags,
MatchableEdges,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct EdgeType {
bond: Bond,
ends: (Element, Element),
}
pub(crate) fn state_bounds(
mol: &Molecule,
state: &State,
best_index: usize,
bounds: &[Bound],
) -> bool {
let fragments = state.fragments();
let state_index = state.index();
let largest_removed = state.largest_removed();
for bound_type in bounds {
let exceeds = match bound_type {
Bound::Log => state_index - log_bound(fragments) >= best_index,
Bound::Int => state_index - int_bound(fragments, largest_removed) >= best_index,
Bound::VecSimple => {
state_index - vec_simple_bound(fragments, largest_removed, mol) >= best_index
}
Bound::VecSmallFrags => {
state_index - vec_small_frags_bound(fragments, largest_removed, mol) >= best_index
}
_ => false,
};
if exceeds {
return true;
}
}
false
}
pub(crate) fn match_bounds(
state_index: usize,
best_index: usize,
matchable_edge_masks: &[Vec<BitSet>],
bounds: &[Bound],
) -> usize {
for removal_size in 2..matchable_edge_masks.len() + 2 {
let mut bounded = false;
for bound_type in bounds {
bounded |= match bound_type {
Bound::MatchableEdges => {
state_index
>= usable_edges_bound(matchable_edge_masks, removal_size) + best_index
}
_ => false,
};
if bounded {
break;
}
}
if !bounded {
return removal_size;
}
}
matchable_edge_masks.len() + 2
}
fn log_bound(fragments: &[BitSet]) -> usize {
let mut size = 0;
for f in fragments {
size += f.len();
}
size - (size as f32).log2().ceil() as usize
}
fn int_bound(fragments: &[BitSet], m: usize) -> usize {
let mut max_s: usize = 0;
let mut frag_sizes: Vec<usize> = Vec::new();
for f in fragments {
frag_sizes.push(f.len());
}
let size_sum: usize = frag_sizes.iter().sum();
for max in 2..m + 1 {
let log = (max as f32).log2().ceil();
let mut aux_sum: usize = 0;
for len in &frag_sizes {
aux_sum += (len / max) + (len % max != 0) as usize
}
max_s = max_s.max(size_sum - log as usize - aux_sum);
}
max_s
}
fn unique_edges(fragment: &BitSet, mol: &Molecule) -> Vec<EdgeType> {
let g = mol.graph();
let mut nodes: Vec<Element> = Vec::new();
for v in g.node_weights() {
nodes.push(v.element());
}
let edges: Vec<petgraph::prelude::EdgeIndex> = g.edge_indices().collect();
let weights: Vec<Bond> = g.edge_weights().copied().collect();
let mut types: Vec<EdgeType> = Vec::new();
for idx in fragment.iter() {
let bond = weights[idx];
let e = edges[idx];
let (e1, e2) = g.edge_endpoints(e).expect("bad");
let e1 = nodes[e1.index()];
let e2 = nodes[e2.index()];
let ends = if e1 < e2 { (e1, e2) } else { (e2, e1) };
let edge_type = EdgeType { bond, ends };
if types.contains(&edge_type) {
continue;
} else {
types.push(edge_type);
}
}
types
}
fn vec_simple_bound(fragments: &[BitSet], m: usize, mol: &Molecule) -> usize {
let mut s = 0;
for f in fragments {
s += f.len();
}
let mut union_set = BitSet::new();
for f in fragments {
union_set.union_with(f);
}
let z = unique_edges(&union_set, mol).len();
(s - z) - ((s - z) as f32 / m as f32).ceil() as usize
}
fn vec_small_frags_bound(fragments: &[BitSet], m: usize, mol: &Molecule) -> usize {
let mut size_two_fragments: Vec<BitSet> = Vec::new();
let mut large_fragments: Vec<BitSet> = fragments.to_owned();
let mut indices_to_remove: Vec<usize> = Vec::new();
for (i, frag) in fragments.iter().enumerate() {
if frag.len() == 2 {
indices_to_remove.push(i);
}
}
for &index in indices_to_remove.iter().rev() {
let removed_bitset = large_fragments.remove(index);
size_two_fragments.push(removed_bitset);
}
let mut fragments_union = BitSet::new();
let mut size_two_fragments_union = BitSet::new();
for f in fragments {
fragments_union.union_with(f);
}
for f in size_two_fragments.iter() {
size_two_fragments_union.union_with(f);
}
let z = unique_edges(&fragments_union, mol).len()
- unique_edges(&size_two_fragments_union, mol).len();
let mut s = 0;
let mut sl = 0;
for f in fragments {
s += f.len();
}
for f in large_fragments {
sl += f.len();
}
let mut size_two_types: Vec<(EdgeType, EdgeType)> = Vec::new();
for f in size_two_fragments.iter() {
let mut types = unique_edges(f, mol);
types.sort();
if types.len() == 1 {
size_two_types.push((types[0], types[0]));
} else {
size_two_types.push((types[0], types[1]));
}
}
size_two_types.sort();
size_two_types.dedup();
s - (z + size_two_types.len() + size_two_fragments.len())
- ((sl - z) as f32 / m as f32).ceil() as usize
}
fn usable_edges_bound(matchable_edge_masks: &[Vec<BitSet>], removal_size: usize) -> usize {
let mut bound = 0;
for (frag_ix, frag) in matchable_edge_masks[removal_size - 2].iter().enumerate() {
let total_removable_edges = matchable_edge_masks[0][frag_ix].len();
let removable_edges = frag.len();
let leftover_edges =
(total_removable_edges - removable_edges) + (removable_edges % removal_size);
bound += total_removable_edges
- (removable_edges / removal_size)
- leftover_edges.div_ceil(removal_size - 1);
}
bound.saturating_sub((removal_size as f32).log2().ceil() as usize)
}