summaryrefslogtreecommitdiffstats
path: root/vendor/varisat/src/decision/vsids.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/varisat/src/decision/vsids.rs')
-rw-r--r--vendor/varisat/src/decision/vsids.rs346
1 files changed, 346 insertions, 0 deletions
diff --git a/vendor/varisat/src/decision/vsids.rs b/vendor/varisat/src/decision/vsids.rs
new file mode 100644
index 000000000..e59656d1b
--- /dev/null
+++ b/vendor/varisat/src/decision/vsids.rs
@@ -0,0 +1,346 @@
+//! The VSIDS branching heuristic.
+//!
+//! The VSIDS (Variable State Independent Decaying Sum) branching heuristic keeps an activity value
+//! for each variable. For each conflict some variables are bumped, which means that their activity
+//! is increased by a constant. After bumping some variables, the activity of all variables is
+//! decayed by multiplying it with a constant below 1.
+//!
+//! When a decision is made, it branches on the vairable with the highest activity among the
+//! unassigned variables.
+//!
+//! There are a few variants that differ in which variables are bumped. Varisat follows Minisat (and
+//! others) by bumping all variables in the conflict clause and all variables resolved on during
+//! conflict analysis.
+
+use ordered_float::OrderedFloat;
+
+use varisat_formula::Var;
+
+use crate::config::SolverConfig;
+
+/// The VSIDS branching heuristic.
+///
+/// As an optimization instead of decaying all activities each conflict, the bump value is divided
+/// by the decay factor each conflict. When this would cause a value to overflow all activities and
+/// the bump value are scaled down. Apart from a scaling factor that is the same for all involved
+/// values, this is equivalent to the naive implementation. As we only care about the order of
+/// activities we can ignore the scaling factor.
+pub struct Vsids {
+ /// The activity of each variable.
+ activity: Vec<OrderedFloat<f32>>,
+ /// A binary heap of the variables.
+ heap: Vec<Var>,
+ /// The position in the binary heap for each variable.
+ position: Vec<Option<usize>>,
+ /// The value to add on bumping.
+ bump: f32,
+ /// The inverse of the decay factor.
+ inv_decay: f32,
+}
+
+impl Default for Vsids {
+ fn default() -> Vsids {
+ Vsids {
+ activity: vec![],
+ heap: vec![],
+ position: vec![],
+ bump: 1.0,
+ inv_decay: 1.0 / SolverConfig::default().vsids_decay,
+ }
+ }
+}
+
+impl Vsids {
+ /// Update structures for a new variable count.
+ pub fn set_var_count(&mut self, count: usize) {
+ self.activity.resize(count, OrderedFloat(0.0));
+ self.position.resize(count, None);
+ }
+
+ /// Rescale activities if any value exceeds this value.
+ fn rescale_limit() -> f32 {
+ std::f32::MAX / 16.0
+ }
+
+ /// Change the decay factor.
+ pub fn set_decay(&mut self, decay: f32) {
+ assert!(decay < 1.0);
+ assert!(decay > 1.0 / 16.0);
+ self.inv_decay = 1.0 / decay;
+ }
+
+ /// Bump a variable by increasing its activity.
+ pub fn bump(&mut self, var: Var) {
+ let rescale = {
+ let value = &mut self.activity[var.index()];
+ value.0 += self.bump;
+ value.0 >= Self::rescale_limit()
+ };
+ if rescale {
+ self.rescale();
+ }
+ if let Some(pos) = self.position[var.index()] {
+ self.sift_up(pos);
+ }
+ }
+
+ /// Decay all variable activities.
+ pub fn decay(&mut self) {
+ self.bump *= self.inv_decay;
+ if self.bump >= Self::rescale_limit() {
+ self.rescale();
+ }
+ }
+
+ /// Rescale all values to avoid an overflow.
+ fn rescale(&mut self) {
+ let rescale_factor = 1.0 / Self::rescale_limit();
+ for activity in &mut self.activity {
+ activity.0 *= rescale_factor;
+ }
+ self.bump *= rescale_factor;
+ }
+
+ /// Reset the activity of an unavailable variable to zero.
+ ///
+ /// Panics if the variable is still available.
+ pub fn reset(&mut self, var: Var) {
+ assert!(self.position[var.index()].is_none());
+ self.activity[var.index()] = OrderedFloat(0.0);
+ }
+
+ /// Remove a variable from the heap if present.
+ pub fn make_unavailable(&mut self, var: Var) {
+ if let Some(position) = self.position[var.index()] {
+ self.heap.swap_remove(position);
+ if self.heap.len() > position {
+ let moved_var = self.heap[position];
+ self.position[moved_var.index()] = Some(position);
+ self.sift_down(position);
+ }
+ self.position[var.index()] = None;
+ }
+ }
+
+ /// Insert a variable into the heap if not already present.
+ pub fn make_available(&mut self, var: Var) {
+ if self.position[var.index()].is_none() {
+ let position = self.heap.len();
+ self.position[var.index()] = Some(position);
+ self.heap.push(var);
+ self.sift_up(position);
+ }
+ }
+
+ /// Move a variable closer to the root until the heap property is satisfied.
+ fn sift_up(&mut self, mut pos: usize) {
+ let var = self.heap[pos];
+ loop {
+ if pos == 0 {
+ return;
+ }
+ let parent_pos = (pos - 1) / 2;
+ let parent_var = self.heap[parent_pos];
+ if self.activity[parent_var.index()] >= self.activity[var.index()] {
+ return;
+ }
+ self.position[var.index()] = Some(parent_pos);
+ self.heap[parent_pos] = var;
+ self.position[parent_var.index()] = Some(pos);
+ self.heap[pos] = parent_var;
+ pos = parent_pos;
+ }
+ }
+
+ /// Move a variable away from the root until the heap property is satisfied.
+ fn sift_down(&mut self, mut pos: usize) {
+ let var = self.heap[pos];
+ loop {
+ let mut largest_pos = pos;
+ let mut largest_var = var;
+
+ let left_pos = pos * 2 + 1;
+ if left_pos < self.heap.len() {
+ let left_var = self.heap[left_pos];
+
+ if self.activity[largest_var.index()] < self.activity[left_var.index()] {
+ largest_pos = left_pos;
+ largest_var = left_var;
+ }
+ }
+
+ let right_pos = pos * 2 + 2;
+ if right_pos < self.heap.len() {
+ let right_var = self.heap[right_pos];
+
+ if self.activity[largest_var.index()] < self.activity[right_var.index()] {
+ largest_pos = right_pos;
+ largest_var = right_var;
+ }
+ }
+
+ if largest_pos == pos {
+ return;
+ }
+
+ self.position[var.index()] = Some(largest_pos);
+ self.heap[largest_pos] = var;
+ self.position[largest_var.index()] = Some(pos);
+ self.heap[pos] = largest_var;
+ pos = largest_pos;
+ }
+ }
+}
+
+impl Iterator for Vsids {
+ type Item = Var;
+
+ fn next(&mut self) -> Option<Var> {
+ if self.heap.is_empty() {
+ None
+ } else {
+ let var = self.heap.swap_remove(0);
+ if !self.heap.is_empty() {
+ let top_var = self.heap[0];
+ self.position[top_var.index()] = Some(0);
+ self.sift_down(0);
+ }
+ self.position[var.index()] = None;
+ Some(var)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use varisat_formula::var;
+
+ #[test]
+ fn rescale_bump() {
+ let mut vsids = Vsids::default();
+ vsids.set_var_count(4);
+ vsids.set_decay(1.0 / 8.0);
+
+ for _ in 0..4 {
+ vsids.next();
+ }
+
+ for i in 0..4 {
+ for _ in 0..i {
+ vsids.bump(Var::from_index(i));
+ }
+ }
+
+ for _ in 0..41 {
+ vsids.decay();
+ }
+
+ for _ in 0..30 {
+ vsids.bump(var!(4));
+ }
+
+ // Decay is a power of two so these values are exact
+ #[allow(clippy::float_cmp)]
+ {
+ assert_eq!(vsids.activity[0].0, 0.0);
+ assert_eq!(vsids.activity[2].0, vsids.activity[1].0 * 2.0);
+ assert!(vsids.activity[3] > vsids.activity[2]);
+ }
+ }
+
+ #[test]
+ fn rescale_decay() {
+ let mut vsids = Vsids::default();
+ vsids.set_var_count(4);
+ vsids.set_decay(1.0 / 8.0);
+
+ for _ in 0..4 {
+ vsids.next();
+ }
+
+ for i in 0..4 {
+ for _ in 0..i {
+ vsids.bump(Var::from_index(i));
+ }
+ }
+
+ for _ in 0..60 {
+ vsids.decay();
+ }
+
+ // Decay is a power of two so these values are exact
+ #[allow(clippy::float_cmp)]
+ {
+ assert_eq!(vsids.activity[0].0, 0.0);
+ assert_eq!(vsids.activity[2].0, vsids.activity[1].0 * 2.0);
+ assert_eq!(vsids.activity[3].0, vsids.activity[1].0 * 3.0);
+ }
+ }
+
+ #[test]
+ fn heap_sorts() {
+ let mut vsids = Vsids::default();
+ vsids.set_var_count(8);
+
+ for _ in 0..8 {
+ vsids.next();
+ }
+
+ for i in 0..8 {
+ for _ in 0..i {
+ vsids.bump(Var::from_index(i));
+ }
+ }
+
+ for i in 0..8 {
+ vsids.make_available(Var::from_index((i * 5) % 8));
+ }
+
+ for i in (0..8).rev() {
+ assert_eq!(vsids.next(), Some(Var::from_index(i)));
+ }
+ assert_eq!(vsids.next(), None);
+ }
+
+ #[test]
+ fn heap_bump() {
+ let mut vsids = Vsids::default();
+ vsids.set_var_count(8);
+ vsids.set_decay(1.0 / 8.0);
+
+ for _ in 0..8 {
+ vsids.next();
+ }
+
+ for i in 0..8 {
+ for _ in 0..i {
+ vsids.bump(Var::from_index(i));
+ }
+ }
+
+ for i in 0..8 {
+ vsids.make_available(Var::from_index((i * 5) % 8));
+ }
+
+ for i in (0..4).rev() {
+ assert_eq!(vsids.next(), Some(Var::from_index(i + 4)));
+ }
+
+ vsids.decay();
+ vsids.decay();
+
+ for i in 0..8 {
+ for _ in 0..(8 - i) {
+ vsids.bump(Var::from_index(i));
+ }
+ }
+
+ for i in 0..4 {
+ assert_eq!(vsids.next(), Some(Var::from_index(i)));
+ }
+
+ assert_eq!(vsids.next(), None);
+ }
+}