use crate::cnf::{Clause, CnfFormula, Literal, Reduced};
use crate::error::VitriError;
use crate::preprocess::VarMap;
use std::os::raw::{c_char, c_int};
use std::time::Instant;
#[allow(non_camel_case_types)]
mod ffi {
use std::os::raw::{c_char, c_int};
#[repr(C)]
pub(super) struct ArjunShim {
_private: [u8; 0],
}
unsafe extern "C" {
pub(super) fn arjun_shim_new(seed: u32) -> *mut ArjunShim;
pub(super) fn arjun_shim_new_weighted(seed: u32) -> *mut ArjunShim;
pub(super) fn arjun_shim_free(s: *mut ArjunShim);
pub(super) fn arjun_shim_set_lit_weight(
s: *mut ArjunShim,
lit: i32,
weight_str: *const c_char,
);
pub(super) fn arjun_shim_clean_sampl(s: *mut ArjunShim);
pub(super) fn arjun_shim_lit_weight(
s: *mut ArjunShim,
lit: i32,
buf: *mut c_char,
cap: usize,
) -> usize;
pub(super) fn arjun_shim_new_vars(s: *mut ArjunShim, n: u32);
pub(super) fn arjun_shim_add_clause(s: *mut ArjunShim, lits: *const i32, n: usize);
pub(super) fn arjun_shim_set_sampl(s: *mut ArjunShim, vars0: *const u32, n: usize);
pub(super) fn arjun_shim_set_backbone_max_confl(s: *mut ArjunShim, max_confl: i64);
pub(super) fn arjun_shim_set_oracle_mult(s: *mut ArjunShim, mult: f64);
pub(super) fn arjun_shim_set_deadline_ms(s: *mut ArjunShim, ms_from_now: i64);
pub(super) fn arjun_shim_stage_minimize_indep(s: *mut ArjunShim, all_indep: c_int)
-> c_int;
pub(super) fn arjun_shim_stage_simplify(
s: *mut ArjunShim,
all_indep: c_int,
oracle_enabled: c_int,
no_sbva: c_int,
no_bve: c_int,
) -> c_int;
pub(super) fn arjun_shim_cur_nvars(s: *mut ArjunShim) -> u32;
pub(super) fn arjun_shim_cur_clauses(s: *mut ArjunShim, buf: *mut i32, cap: usize)
-> usize;
pub(super) fn arjun_shim_cur_sampl(s: *mut ArjunShim, buf: *mut u32, cap: usize) -> usize;
pub(super) fn arjun_shim_backbone(s: *mut ArjunShim, buf: *mut i32, cap: usize) -> usize;
pub(super) fn arjun_shim_eq_lits(s: *mut ArjunShim, buf: *mut i32, cap: usize) -> usize;
pub(super) fn arjun_shim_red_clauses(s: *mut ArjunShim, buf: *mut i32, cap: usize)
-> usize;
pub(super) fn arjun_shim_orig_to_new(s: *mut ArjunShim, buf: *mut i32, cap: usize)
-> usize;
pub(super) fn arjun_shim_cur_multiplier(
s: *mut ArjunShim,
buf: *mut c_char,
cap: usize,
) -> usize;
}
}
pub(in crate::preprocess) struct ArjunLib {
raw: *mut ffi::ArjunShim,
deadline_armed: bool,
}
impl ArjunLib {
fn from_raw(raw: *mut ffi::ArjunShim) -> Option<Self> {
(!raw.is_null()).then_some(ArjunLib {
raw,
deadline_armed: false,
})
}
pub(in crate::preprocess) fn new(seed: u32) -> Option<Self> {
Self::from_raw(unsafe { ffi::arjun_shim_new(seed) })
}
pub(in crate::preprocess) fn new_weighted(seed: u32) -> Option<Self> {
Self::from_raw(unsafe { ffi::arjun_shim_new_weighted(seed) })
}
pub(in crate::preprocess) fn set_lit_weight(
&mut self,
lit: Literal,
weight_str: &str,
) -> Result<(), VitriError> {
let dimacs = lit.to_dimacs();
let c = std::ffi::CString::new(weight_str).map_err(|_| {
VitriError::input(format!(
"the weight for literal {dimacs} cannot be passed to the shim: {weight_str:?}"
))
})?;
unsafe { ffi::arjun_shim_set_lit_weight(self.raw, dimacs, c.as_ptr()) };
Ok(())
}
pub(in crate::preprocess) fn clean_sampl(&mut self) {
unsafe { ffi::arjun_shim_clean_sampl(self.raw) };
}
pub(in crate::preprocess) fn lit_weight_decimal(&self, lit: i32) -> Result<String, VitriError> {
self.read_c_string(|buf, cap| unsafe {
ffi::arjun_shim_lit_weight(self.raw, lit, buf, cap)
})
.map_err(|e| {
VitriError::input(format!(
"the weight reported for literal {lit} is not text: {e}"
))
})
}
pub(in crate::preprocess) fn set_backbone_max_confl(&mut self, max_confl: i64) {
unsafe { ffi::arjun_shim_set_backbone_max_confl(self.raw, max_confl) };
}
pub(in crate::preprocess) fn set_oracle_mult(&mut self, mult: f64) {
unsafe { ffi::arjun_shim_set_oracle_mult(self.raw, mult) };
}
pub(in crate::preprocess) fn set_deadline(&mut self, deadline: Instant) {
let ms = crate::budget::remaining(deadline).as_millis();
unsafe { ffi::arjun_shim_set_deadline_ms(self.raw, ms.min(i64::MAX as u128) as i64) };
self.deadline_armed = true;
}
pub(in crate::preprocess) fn deadline_armed(&self) -> bool {
self.deadline_armed
}
pub(in crate::preprocess) fn new_vars(&mut self, n: u32) {
unsafe { ffi::arjun_shim_new_vars(self.raw, n) };
}
pub(in crate::preprocess) fn add_clause_dimacs(&mut self, lits: &[i32]) {
unsafe { ffi::arjun_shim_add_clause(self.raw, lits.as_ptr(), lits.len()) };
}
pub(in crate::preprocess) fn set_sampl(&mut self, vars0: &[u32]) {
unsafe { ffi::arjun_shim_set_sampl(self.raw, vars0.as_ptr(), vars0.len()) };
}
pub(in crate::preprocess) fn stage_minimize_indep(&mut self, all_indep: bool) -> bool {
unsafe { ffi::arjun_shim_stage_minimize_indep(self.raw, all_indep as c_int) == 0 }
}
pub(in crate::preprocess) fn stage_simplify(
&mut self,
all_indep: bool,
oracle: bool,
no_sbva: bool,
no_bve: bool,
) -> bool {
unsafe {
ffi::arjun_shim_stage_simplify(
self.raw,
all_indep as c_int,
oracle as c_int,
no_sbva as c_int,
no_bve as c_int,
) == 0
}
}
fn read_list<T: Copy + Default>(
&self,
get: unsafe extern "C" fn(*mut ffi::ArjunShim, *mut T, usize) -> usize,
) -> Vec<T> {
unsafe {
let need = get(self.raw, std::ptr::null_mut(), 0);
let mut buf = vec![T::default(); need];
get(self.raw, buf.as_mut_ptr(), buf.len());
buf
}
}
fn read_c_string(
&self,
get: impl Fn(*mut c_char, usize) -> usize,
) -> Result<String, std::string::FromUtf8Error> {
let need = get(std::ptr::null_mut(), 0);
let mut buf = vec![0u8; need + 1];
get(buf.as_mut_ptr().cast::<c_char>(), buf.len());
if let Some(nul) = buf.iter().position(|&b| b == 0) {
buf.truncate(nul);
}
String::from_utf8(buf)
}
pub(in crate::preprocess) fn cur_nvars(&self) -> u32 {
unsafe { ffi::arjun_shim_cur_nvars(self.raw) }
}
pub(in crate::preprocess) fn cur_clauses_dimacs(&self) -> Vec<i32> {
self.read_list(ffi::arjun_shim_cur_clauses)
}
pub(in crate::preprocess) fn red_clauses(&self) -> Vec<Vec<i32>> {
let buf = self.read_list(ffi::arjun_shim_red_clauses);
if buf.is_empty() {
return Vec::new();
}
let mut out = Vec::new();
let mut cur = Vec::new();
for &val in &buf {
if val == 0 {
if !cur.is_empty() {
out.push(std::mem::take(&mut cur));
}
} else {
cur.push(val);
}
}
out
}
pub(in crate::preprocess) fn cur_sampl(&self) -> Vec<u32> {
self.read_list(ffi::arjun_shim_cur_sampl)
}
pub(in crate::preprocess) fn backbone(&self) -> Vec<Literal> {
self.read_list(ffi::arjun_shim_backbone)
.into_iter()
.map(Literal::from)
.collect()
}
pub(in crate::preprocess) fn eq_lits(&self) -> Vec<(Literal, Literal)> {
let lits = self.read_list(ffi::arjun_shim_eq_lits);
lits.as_chunks::<2>()
.0
.iter()
.map(|&[a, b]| (Literal::from(a), Literal::from(b)))
.collect()
}
pub(in crate::preprocess) fn orig_to_new_lits(
&self,
input_num_vars: u32,
) -> VarMap<Reduced, Reduced> {
let buf = self.read_list(ffi::arjun_shim_orig_to_new);
let mut map = vec![None; input_num_vars as usize];
if buf.is_empty() {
return VarMap::from_entries(map);
}
for &[orig, new_lit] in buf.as_chunks::<2>().0 {
if orig < 1 || new_lit == 0 {
continue;
}
let idx = (orig as u32 - 1) as usize;
if idx < map.len() {
map[idx] = Some(new_lit);
}
}
VarMap::from_entries(map)
}
pub(in crate::preprocess) fn cur_multiplier_decimal(&self) -> Result<String, VitriError> {
self.read_c_string(|buf, cap| unsafe { ffi::arjun_shim_cur_multiplier(self.raw, buf, cap) })
.map_err(|e| VitriError::input(format!("the count multiplier is not text: {e}")))
}
pub(in crate::preprocess) fn cur_formula(&self) -> CnfFormula {
let flat = self.cur_clauses_dimacs();
let mut clauses = Vec::new();
let mut declared = self.cur_nvars();
let mut lits = Vec::new();
for &val in &flat {
if val == 0 {
let max_var = lits
.iter()
.map(|l: &Literal| l.var.to_dimacs() as u32)
.max()
.unwrap_or(0);
if max_var > declared {
declared = max_var;
}
clauses.push(Clause::new(std::mem::take(&mut lits)));
} else {
lits.push(Literal::from(val));
}
}
CnfFormula {
num_vars: declared,
clauses,
}
}
}
impl Drop for ArjunLib {
fn drop(&mut self) {
unsafe { ffi::arjun_shim_free(self.raw) };
}
}
const PRESENCE_ONLY_FORM: &str = "set to any value to turn the pass off, or left \
unset to leave it on — an off-looking value still \
turns it off, so it is refused rather than obeyed";
const OFF_LOOKING_FORMS: &[&str] = &["", "0", "off", "false"];
pub(in crate::preprocess) fn reads_as_off(value: &str) -> bool {
OFF_LOOKING_FORMS
.iter()
.any(|form| crate::env::is_form(value, form))
}
const BVE_GROW_FORM: &str = "a clause-growth budget, a whole number from 0 to 2147483647";
pub(in crate::preprocess) fn bve_grow_value(value: Option<&str>) -> Result<i32, VitriError> {
let grow = crate::env::parse_value("VITRI_ARJUN_BVE_GROW", value, 0, BVE_GROW_FORM)?;
if grow < 0 {
return Err(VitriError::env(
"VITRI_ARJUN_BVE_GROW",
format!("must be {BVE_GROW_FORM}; got {grow}"),
));
}
Ok(grow)
}
pub(in crate::preprocess) fn validate_shim_env() -> Result<(), VitriError> {
bve_grow_value(crate::env::env_raw("VITRI_ARJUN_BVE_GROW", BVE_GROW_FORM)?.as_deref())?;
for var in ["VITRI_ARJUN_NO_BVE", "VITRI_ARJUN_NO_ORACLE"] {
if let Some(value) = crate::env::env_raw(var, PRESENCE_ONLY_FORM)?
&& reads_as_off(&value)
{
return Err(VitriError::env(
var,
format!("must be {PRESENCE_ONLY_FORM}; got {value:?}"),
));
}
}
Ok(())
}