2W5ET5PZOX3Y3LSBQOXO3DCRDON64VCDXCQUDJFTXEKBJQ5B6ASQC use std::collections::VecDeque;use std::usize;// solve() uses a SolveResult generator as iterator.use genawaiter::sync::Gen;use log::{debug, trace};#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]pub struct Literal {id: usize,sign: bool,}impl Literal {pub(crate) fn new(id: usize, sign: bool) -> Literal {Literal { id, sign }}pub fn id(&self) -> usize {self.id}pub fn sign(&self) -> bool {self.sign}pub(crate) fn negate(&self) -> Literal {Literal {id: self.id,sign: !self.sign,}}}#[test]fn test_id() {let lit = Literal { id: 2, sign: false };assert_eq!(lit.id(), 2);}// TODO: Nogoods from a program#[derive(Copy, Clone, Debug)]pub(crate) struct WatchList {pub(crate) first_watch: usize,pub(crate) second_watch: usize,}#[derive(Copy, Clone, Debug, PartialEq, Eq)]enum Watch {First,Second,}/// Solver internal representation of nogoods and assignmentstype Nogood = Vec<Option<bool>>;#[derive(Clone, Debug, PartialEq, Eq)]pub enum SolveResult {UnSat,Sat(Vec<Option<bool>>),}#[derive(Clone, Debug, PartialEq)]enum PropagationResult {Ok,Conflict(Nogood),}/// Map implementation used by the library.pub(crate) type Map<K, V> = rustc_hash::FxHashMap<K, V>;#[derive(Clone, Debug)]pub struct Solver {pub(crate) tight: bool,pub(crate) number_of_variables: usize,pub(crate) assignments: Vec<Option<bool>>,pub(crate) decisions: Vec<Literal>,pub(crate) watch_lists: Vec<WatchList>,pub(crate) nogoods: Vec<Nogood>,pub(crate) var_to_nogoods: Vec<Map<usize, bool>>,pub(crate) nogoods_to_var: Vec<Map<usize, bool>>,pub(crate) decision_level: usize,pub(crate) decision_number: usize,// Stores for each atom that has been decided the (sign, reason_index, decision_level, and decision number)pub(crate) assignments_log: Vec<(Option<bool>, Option<usize>, usize, usize)>,pub(crate) chronological_backtracking_level: usize,}impl Solver {pub fn solve(&mut self) -> impl Iterator<Item = SolveResult> + '_ {Gen::new(|co| async move {let mut sat = true;while sat {if let PropagationResult::Conflict(nogood) = self.propagate() {trace!("conflict with nogood: {:?}",nogood);if self.is_top_level_conflict(&nogood) {sat = false;co.yield_(SolveResult::UnSat).await;} else if self.chronological_backtracking_level < self.decision_level {let (uip, sigma, k) = self.conflict_resolution(&nogood);debug!("uip: {:?}", &uip);debug!("sigma: {:?}", sigma);debug!("k: {:?}", k);if self.chronological_backtracking_level > k {self.decision_level = self.chronological_backtracking_level;} else {self.decision_level = k;}debug!("new_decision_level: {}", self.decision_level);self.backjump();// add complement of sigmaself.decision_number += 1;self.assignments[sigma.id] = Some(!sigma.sign);self.assignments_log[sigma.id] =(Some(!sigma.sign), None, k, self.decision_number);self.record_nogood(uip);} else {//get decision literal from this decision_levellet decision_literal = self.decisions[self.decision_level - 1];self.decision_level -= 1;self.chronological_backtracking_level = self.decision_level;self.backjump();// add complement of the decision literalself.decision_number += 1;self.assignments[decision_literal.id] = Some(!decision_literal.sign);self.assignments_log[decision_literal.id] = (Some(!decision_literal.sign),None,self.decision_level,self.decision_number,);}} else if self.assignment_complete() {co.yield_(SolveResult::Sat(self.assignments.clone())).await;if self.decision_level == 0 {return;} else {// get decision literal from this decision_levellet decision_literal = self.decisions[self.decision_level - 1];// debug!("flip decision literal {:?}", decision_literal);self.decision_level -= 1;debug!("decision_level: {}", self.decision_level);self.chronological_backtracking_level = self.decision_level;debug!("chronological_backtracking_level: {}",self.chronological_backtracking_level);self.backjump();// add complement of the decision literalself.decision_number += 1;self.assignments[decision_literal.id] = Some(!decision_literal.sign);self.assignments_log[decision_literal.id] = (Some(!decision_literal.sign),None,self.decision_level,self.decision_number,);}} else {self.decide()}}}).into_iter()}fn record_nogood(&mut self, nogood: Nogood) {self.watch_lists.push(nogood_to_watch_list(&nogood));self.nogoods.push(nogood);let nogood_id = self.nogoods.len() - 1;for variable_id in 0..self.number_of_variables {if let Some(sign) = self.nogoods[nogood_id][variable_id] {self.var_to_nogoods[variable_id].insert(nogood_id, sign);self.nogoods_to_var.push(Map::default());self.nogoods_to_var[nogood_id].insert(variable_id, sign);}}}/// Analyze conflict and learn UIP nogoodfn conflict_resolution(&self, nogood: &[Option<bool>]) -> (Nogood, Literal, usize) {let mut nogood = nogood.to_owned();let sigma = loop {debug!("delta: {:?}", nogood);let iter = nogood.iter().enumerate();// Determine sigma the last literal in delta that has been added to the assignmentlet mut sigma = Literal { id: 0, sign: true };let mut nogood_index = None;let mut decision_level_sigma = 0;let mut biggest_decision_number = 0;for (id, assignment) in iter {if let Some(sign) = *assignment {let (_, ng_index, decision_level, decision_number) = self.assignments_log[id];if decision_number >= biggest_decision_number {biggest_decision_number = decision_number;sigma = Literal { id, sign };nogood_index = ng_index;decision_level_sigma = decision_level;}}}debug!("sigma: {:?}", sigma);// A nogood is a unique implication point if there is no other literal// from the same decision level as sigmalet mut iter = nogood.iter().enumerate();let unique = loop {match iter.next() {Some((id, &Some(sign))) => {let literal = Literal { id, sign };if sigma != literal {// debug!("literal: {:?}", literal);let (_, _, decision_level, _) = self.assignments_log[literal.id];if decision_level == decision_level_sigma {break false;}}}Some((_, &None)) => {}None => break true,}};if unique {break sigma;}// debug!("not unique");if let Some(nogood_index) = nogood_index {let reason = &self.nogoods[nogood_index];debug!("reason: {:?}", reason);let res = resolve(&nogood, &sigma, reason);nogood = res;} else {// sigma is a decision_literals// the reason is emptylet reason: Vec<Option<bool>> = vec![None; self.number_of_variables];// debug!("reason: {:?}", reason);let res = resolve(&nogood, &sigma, &reason);nogood = res;}};let mut k = 0;for (id, assignment) in nogood.iter().enumerate() {if let Some(sign) = *assignment {let literal = Literal { id, sign };if literal != sigma {let (_, _, decision_level, _) = self.assignments_log[literal.id];if decision_level > k {k = decision_level}}}}// debug!("k: {}", k);// debug!("sigma: {:?}", sigma);(nogood, sigma, k)}/// Increase decision level assign truth value to a previously unassigned variablefn decide(&mut self) {self.decision_level += 1;self.decision_number += 1;debug!("decision_level: {:?}", self.decision_level);let decision_literal = self.choose();debug!("decision_literal: {:?}", decision_literal);self.assignments[decision_literal.id()] = Some(decision_literal.sign);self.decisions.push(decision_literal);self.assignments_log[decision_literal.id] = (Some(decision_literal.sign),None,self.decision_level,self.decision_number,);}fn choose(&self) -> Literal {for id in 0..self.number_of_variables {if self.assignments[id] == Some(true) {continue;}if self.assignments[id] == Some(false) {continue;}return Literal { id, sign: true };}panic!("Logic error: calling choose on complete assignment-");}/// Return true if all variables have a truth value assignmentfn assignment_complete(&self) -> bool {for id in 0..self.number_of_variables {if self.assignments[id] == Some(true) {continue;}if self.assignments[id] == Some(false) {continue;}return false;}true}/// Return true if there is a conflict on decision level 0fn is_top_level_conflict(&self, nogood: &[Option<bool>]) -> bool {for (id, assignment) in nogood.iter().enumerate() {if assignment.is_some() {let (_, _, decision_level, _) = self.assignments_log[id];if decision_level > 0 {return false;}}}true}/// Backjump to decision level x and rewind assignmentfn backjump(&mut self) {// debug!("backjump to decision_level {}", self.decision_level);for (id, assignment) in self.assignments.iter_mut().enumerate() {if let Some(sign) = *assignment {let lit = Literal { id, sign };let (_, _, decision_level, decision_number) = self.assignments_log[lit.id];if decision_level > self.decision_level {self.assignments_log[lit.id] = (None, None, 0, 0);*assignment = None;}if decision_number > self.decision_number {self.decision_number = decision_number - 1;}}}// backtrack decisionswhile self.decisions.len() > self.decision_level {self.decisions.pop();}}/// Run unit propagation and unfounded set checkfn propagate(&mut self) -> PropagationResult {trace!("propagate");trace!("assignment: {:?}", self.assignments);if let PropagationResult::Conflict(nogood) = self.unit_propagation() {return PropagationResult::Conflict(nogood);}if !self.tight {self.unfounded_loop_learning();}PropagationResult::Ok}/// Learn a nogood for an unfounded loopfn unfounded_loop_learning(&mut self) {todo!()}fn unit_propagation(&mut self) -> PropagationResult {let mut propagation_queue: VecDeque<Literal> = VecDeque::new();for (id, assignment) in self.assignments.iter().enumerate() {if let Some(sign) = *assignment {propagation_queue.push_back(Literal { id, sign });}}loop {if let Some(p) = propagation_queue.pop_front() {debug!("prp: {:?}", p);for (index, sign) in &self.var_to_nogoods[p.id] {let watch_list = &mut self.watch_lists[*index];// debug!(// "wl.0: {} wl.1: {} nogood: {:?}",// watch_list.first_watch, watch_list.second_watch, self.nogoods[*index]// );if watch_list.first_watch == p.id || watch_list.second_watch == p.id {let dirty_watch = if watch_list.first_watch == p.id {Watch::First} else {Watch::Second};if *sign == p.sign {// propagated literal and nogood literal have the same sign// try to update watchif let Some(new_watch) = update_watches(&self.nogoods_to_var[*index],&self.assignments,p,watch_list.first_watch,watch_list.second_watch,) {if dirty_watch == Watch::First {watch_list.first_watch = new_watch;} else {watch_list.second_watch = new_watch;}continue;} else {// debug!("one watch could not be updated. it's a conflict or an implication.");let other_watch = if dirty_watch == Watch::First {&mut watch_list.second_watch} else {&mut watch_list.first_watch};// debug!("check the other watch");let sign = self.nogoods_to_var[*index][other_watch];match self.assignments[*other_watch] {Some(x) => {if sign == x {// also the other watched literal is in the assignment. it's a conflictreturn PropagationResult::Conflict(self.nogoods[*index].clone(),);}}None => {// the other watch is unassigned. we can imply the complementlet complement = !sign;self.assignments[*other_watch] = Some(complement);let lit = Literal {id: *other_watch,sign: complement,};debug!("infer: {:?}", lit);self.decision_number += 1;propagation_queue.push_back(lit);self.assignments_log[*other_watch] = (Some(complement),Some(*index),self.decision_level,self.decision_number,);debug!("infer2: {:?} {:?}",*other_watch,(Some(complement), Some(*index), self.decision_level));}}}}}}}if propagation_queue.is_empty() {return PropagationResult::Ok;}}}}fn update_watches(nogood_vars: &Map<usize, bool>,assignments: &[Option<bool>],p: Literal,first_watch: usize,second_watch: usize,) -> Option<usize> {for (id, sign) in nogood_vars {if *id != p.id&& *id != first_watch&& *id != second_watch&& assignments[*id] != Some(*sign){return Some(*id);}}None}fn resolve(nogood: &[Option<bool>], sigma: &Literal, reason: &[Option<bool>]) -> Nogood {// assert sigma in nogood and reasonlet mut res = vec![];for (id, assignment) in nogood.iter().enumerate() {match *assignment {Some(sign) => {let literal = Literal { id, sign };if literal != *sigma {res.push(Some(sign))} else {res.push(None)}}None => res.push(None),}}let neg_sigma = sigma.negate();for (id, assignment) in reason.iter().enumerate() {if let Some(sign) = *assignment {let literal = Literal { id, sign };if literal != neg_sigma {res[id] = Some(sign);}}}res}fn nogood_to_watch_list(nogood: &[Option<bool>]) -> WatchList {// TODO: special handling for nogoods of size 1let mut first_watch = 0;while nogood[first_watch].is_none() {first_watch += 1;}let mut second_watch = nogood.len() - 1;while nogood[second_watch].is_none() {second_watch -= 1;}WatchList {first_watch,second_watch,}}// only for testsfn create_watch_lists(nogoods: &[Vec<Option<bool>>]) -> Vec<WatchList> {let mut watch_lists = vec![];for nogood in nogoods {// TODO: special handling for nogoods of size 1watch_lists.push(nogood_to_watch_list(nogood))}watch_lists}/// only for testingfn mock_decide(solver: &mut Solver) {solver.decision_level += 1;solver.decision_number += 1;solver.assignments[0] = Some(true);solver.assignments_log[0] = (Some(true),None,solver.decision_level,solver.decision_number,);}/// only for testingfn mock_decide2(solver: &mut Solver) {solver.decision_level += 1;solver.decision_number += 1;solver.assignments[2] = Some(true);solver.assignments_log[2] = (Some(true),None,solver.decision_level,solver.decision_number,);}#[test]fn test_unit_propagation() {let solver_nogoods = vec![vec![None, Some(true), Some(false)]];let number_of_variables = 3;let mut var_to_nogoods: Vec<Map<usize, bool>> = vec![Map::default(); number_of_variables];let mut nogoods_to_var: Vec<Map<usize, bool>> = vec![Map::default(); solver_nogoods.len()];for nogood_id in 0..solver_nogoods.len() {for variable_id in 0..number_of_variables {if let Some(sign) = solver_nogoods[nogood_id][variable_id] {var_to_nogoods[variable_id].insert(nogood_id, sign);nogoods_to_var[nogood_id].insert(variable_id, sign);}}}let mut solver = Solver {tight: true,number_of_variables,assignments: vec![None, Some(true), None],decisions: vec![Literal { id: 1, sign: true }],watch_lists: vec![],nogoods: solver_nogoods,var_to_nogoods,nogoods_to_var,decision_level: 0,decision_number: 0,assignments_log: vec![(None, None, 0, 0); number_of_variables],chronological_backtracking_level: 0,};solver.watch_lists = create_watch_lists(&solver.nogoods);solver.unit_propagation();let res = &solver.assignments;assert_eq!(*res, vec![None, Some(true), Some(true)]);}#[test]fn test_unit_propagation_conflict() {use crate::convert::Builder;let builder = Builder {nogoods: vec![vec![Literal { id: 0, sign: true },Literal { id: 1, sign: false },],vec![Literal { id: 1, sign: true },Literal { id: 2, sign: false },],vec![Literal { id: 1, sign: true }, Literal { id: 2, sign: true }],],};let mut solver = builder.build();mock_decide(&mut solver); // assign Literal(0)let prop_result = solver.unit_propagation();if let PropagationResult::Conflict(nogood) = prop_result {assert_eq!(nogood, vec![None, Some(true), Some(true)]);let top_level_conflict = solver.is_top_level_conflict(&nogood);assert_eq!(top_level_conflict, false);let (uip, sigma, k) = solver.conflict_resolution(&nogood);assert_eq!(uip, vec![None, Some(true), None]);assert_eq!(sigma, Literal { id: 1, sign: true });if solver.chronological_backtracking_level > k {solver.decision_level = solver.chronological_backtracking_level;} else {solver.decision_level = k;}println!("new_decision_level: {}", solver.decision_level);solver.backjump();// add complement of sigmasolver.decision_number += 1;solver.assignments[sigma.id] = Some(!sigma.sign);solver.assignments_log[sigma.id] = (Some(!sigma.sign), None, k, solver.decision_number);solver.record_nogood(uip);let res = solver.unit_propagation();assert_eq!(res, PropagationResult::Ok);assert_eq!(solver.assignments, vec![Some(false), Some(false), None]);mock_decide2(&mut solver); // assign Literal(2)let res = solver.unit_propagation();assert_eq!(res, PropagationResult::Ok);assert_eq!(solver.assignments,vec![Some(false), Some(false), Some(true)]);assert_eq!(solver.assignment_complete(), true);}}#[test]pub fn test_solve_1() {use crate::convert::Builder;let builder = Builder {nogoods: vec![vec![Literal { id: 0, sign: true },Literal { id: 1, sign: false },],vec![Literal { id: 1, sign: true },Literal { id: 2, sign: false },],vec![Literal { id: 1, sign: true }, Literal { id: 2, sign: true }],],};let mut solver = builder.build();let mut solutions = solver.solve();let res = solutions.next();assert_eq!(res,Some(SolveResult::Sat(vec![Some(false), Some(false), Some(true)])));let res = solutions.next();assert_eq!(res,Some(SolveResult::Sat(vec![Some(false),Some(false),Some(false)])));let res = solutions.next();assert_eq!(res, None);}#[test]pub fn test_solve_unsat_1() {use crate::convert::Builder;let builder = Builder {nogoods: vec![vec![Literal { id: 0, sign: true }],vec![Literal { id: 0, sign: false }],],};let mut solver = builder.build();let mut solutions = solver.solve();let res = solutions.next();assert_eq!(res, Some(SolveResult::UnSat));let res = solutions.next();assert_eq!(res, None);}#[test]pub fn test_solve_unsat_2() {use crate::convert::Builder;let builder = Builder {nogoods: vec![vec![Literal { id: 1, sign: true }],vec![Literal { id: 1, sign: false }],],};let mut solver = builder.build();let mut solutions = solver.solve();let res = solutions.next();assert_eq!(res, Some(SolveResult::UnSat));let res = solutions.next();assert_eq!(res, None);}pub fn create_test_solver() -> Solver {use crate::convert::Builder;let builder = Builder {nogoods: vec![vec![Literal { id: 3, sign: false },Literal { id: 2, sign: true },],vec![Literal { id: 0, sign: false },Literal { id: 5, sign: true },],vec![Literal { id: 7, sign: false },Literal { id: 4, sign: false },Literal { id: 6, sign: true },],vec![Literal { id: 3, sign: false },Literal { id: 9, sign: true },],vec![Literal { id: 0, sign: false },Literal { id: 7, sign: false },Literal { id: 10, sign: true },],vec![Literal { id: 1, sign: false },Literal { id: 12, sign: true },],vec![Literal { id: 0, sign: true },Literal { id: 5, sign: false },Literal {id: 11,sign: false,},],vec![Literal { id: 7, sign: true },Literal { id: 8, sign: false },Literal { id: 8, sign: false },],vec![Literal { id: 4, sign: true },Literal { id: 5, sign: false },],vec![Literal { id: 1, sign: true },Literal {id: 12,sign: false,},],vec![Literal { id: 3, sign: true },Literal { id: 2, sign: false },Literal { id: 9, sign: false },],vec![Literal { id: 8, sign: true }, Literal { id: 7, sign: true }],vec![Literal { id: 8, sign: false },Literal { id: 7, sign: false },],vec![Literal { id: 9, sign: true },Literal { id: 4, sign: false },Literal { id: 7, sign: true },],vec![Literal { id: 9, sign: true },Literal { id: 4, sign: true },Literal { id: 7, sign: false },],vec![Literal { id: 9, sign: false },Literal { id: 4, sign: true },Literal { id: 7, sign: true },],vec![Literal { id: 11, sign: true },Literal { id: 0, sign: true },],vec![Literal {id: 11,sign: false,},Literal { id: 0, sign: false },],vec![Literal { id: 5, sign: true },Literal { id: 3, sign: false },Literal { id: 4, sign: false },],vec![Literal { id: 5, sign: true },Literal { id: 3, sign: true },Literal { id: 4, sign: true },],vec![Literal { id: 5, sign: false },Literal { id: 3, sign: true },Literal { id: 4, sign: false },],vec![Literal { id: 10, sign: true },Literal { id: 1, sign: true },],vec![Literal {id: 10,sign: false,},Literal { id: 1, sign: false },],vec![Literal { id: 12, sign: true },Literal { id: 0, sign: true },Literal { id: 7, sign: false },],vec![Literal { id: 12, sign: true },Literal { id: 0, sign: false },Literal { id: 7, sign: true },],vec![Literal {id: 12,sign: false,},Literal { id: 0, sign: false },Literal { id: 7, sign: false },],vec![Literal { id: 6, sign: true },Literal { id: 3, sign: false },],vec![Literal { id: 6, sign: false },Literal { id: 3, sign: true },],vec![Literal { id: 2, sign: true },Literal { id: 0, sign: false },Literal { id: 1, sign: false },],vec![Literal { id: 2, sign: true },Literal { id: 0, sign: true },Literal { id: 1, sign: true },],vec![Literal { id: 2, sign: false },Literal { id: 0, sign: true },Literal { id: 1, sign: false },],],};builder.build()}
pub mod convert;pub mod solver;use thiserror::Error;#[derive(Error, Debug)]pub enum AspifError {#[error("More than one solve item")]MultipleSolveItems,#[error("No solve item")]NoSolveItem,#[error("ParseError: {msg}")]ParseError { msg: String },}
use crate::solver::{Literal, Map, Solver, WatchList};use aspif::AspifProgram;use log::{debug, info};use string_interner::symbol::SymbolU32;use string_interner::StringInterner;#[derive(Default)]pub struct LiteralMapper {aspif_literals: Map<u64, usize>,pub bodies: Map<Vec<Literal>, usize>,literal_count: usize,supports: Map<usize, Vec<(Vec<Literal>, Vec<Literal>)>>,}impl LiteralMapper {fn u64_to_solver_literal(&mut self, a: &u64) -> Literal {if let Some((_key, value)) = self.aspif_literals.get_key_value(a) {Literal::new(*value, true)} else {self.aspif_literals.insert(*a, self.literal_count);let literal = Literal::new(self.literal_count, true);self.literal_count += 1;literal}}fn i64_to_solver_literal(&mut self, a: &i64) -> Literal {if *a >= 0 {let b = *a as u64;if let Some((_key, value)) = self.aspif_literals.get_key_value(&b) {Literal::new(*value, true)} else {self.aspif_literals.insert(b, self.literal_count);let literal = Literal::new(self.literal_count, true);self.literal_count += 1;literal}} else {let b = -a as u64;if let Some((_key, value)) = self.aspif_literals.get_key_value(&b) {Literal::new(*value, false)} else {self.aspif_literals.insert(b, self.literal_count);let literal = Literal::new(self.literal_count, false);self.literal_count += 1;literal}}}/// Returns corresponding solver literal if the body already exist in the LiteralMap/// else creates a new entry and returns the literalfn body2solver_literal(&mut self, body: &[Literal]) -> Literal {if let Some((_key, value)) = self.bodies.get_key_value(body) {Literal::new(*value, true)} else {self.bodies.insert(body.to_owned(), self.literal_count);let literal = Literal::new(self.literal_count, true);self.literal_count += 1;literal}}/// Returns corresponding solver literal if the body already exist in the LiteralMapfn get_body_literal(&self, body: &[Literal]) -> Option<Literal> {self.bodies.get(body).map(|value| Literal::new(*value, true))}/// This function creates rule nogoods as in Definition 1 in *Advanced Conflict-Driven Disjunctive Answer Set Solving*/// It also collects support body clauses for atom wise shifted rules as in *Advanced Conflict-Driven Disjunctive Answer Set Solving*fn write_rule_nogood(&mut self, rule: &aspif::Rule, nogoods: &mut Vec<Vec<Literal>>) {let body_clause = match &rule.body {aspif::Body::NormalBody { elements } => {// Create clause that makes the body truelet mut body_clause = vec![];for e in elements {body_clause.push(self.i64_to_solver_literal(e));}// TODO: is sort and dedup here necessary ?body_clause.sort();body_clause.dedup();body_clause}aspif::Body::WeightBody {lowerbound: _,elements: _,} => {panic!("Unsupported Body")}};match &rule.head {aspif::Head::Disjunction { elements } => {let ori_body_lit = self.body2solver_literal(&body_clause);debug!("Body_lit:{:?} -> {:?}", ori_body_lit, body_clause);// Create rule nogoodlet mut rule_nogood = vec![];for e in elements {let head_lit = self.u64_to_solver_literal(e);let neg_head_lit = head_lit.negate();rule_nogood.push(neg_head_lit)}rule_nogood.push(ori_body_lit);debug!("Rule nogood: {rule_nogood:?}");nogoods.push(rule_nogood);// Shift head atoms to the new bodyfor (idx, e) in elements.iter().enumerate() {let new_head_lit = self.u64_to_solver_literal(e);let mut body_extension = vec![];for (idx2, e) in elements.iter().enumerate() {if idx2 != idx {let lit2 = self.u64_to_solver_literal(e);let neg_lit = lit2.negate();body_extension.push(neg_lit);}}// debug!("Support for {new_head_lit:?}: {:?}{:?}",body_clause,body_extension);self.supports.entry(new_head_lit.id()).and_modify(|e| e.push((body_clause.clone(), body_extension.clone()))).or_insert(vec![(body_clause.clone(), body_extension)]);}}aspif::Head::Choice { elements } => {panic!("Unsupported Head : Choice")}};}/// This function creates support nogoods as in Definition 2 in *Advanced Conflict-Driven Disjunctive Answer Set Solving*fn write_support_nogoods(&mut self, nogoods: &mut Vec<Vec<Literal>>) {for (k, support) in &self.supports {let mut support_nogood = vec![];// Create support nogoodssupport_nogood.push(Literal::new(*k, true));for (old_body, extension) in support {let mut new_body = old_body.clone();new_body.extend_from_slice(extension);if let Some(lit) = self.get_body_literal(&new_body) {// new_body is knownlet neg_lit = lit.negate();support_nogood.push(neg_lit);} else if let Some(lit) = self.get_body_literal(old_body) {let neg_lit = lit.negate();support_nogood.push(neg_lit);for lit in extension {let neg_lit = lit.negate();support_nogood.push(neg_lit);}} else {unreachable!()}}debug!("Support nogood: {:?}", support_nogood);nogoods.push(support_nogood);}}/// This function creates conjunction nogoods as in Definition 3 in *Advanced Conflict-Driven Disjunctive Answer Set Solving*fn write_conjuction_nogoods(&self, nogoods: &mut Vec<Vec<Literal>>) {for (body, lit_id) in &self.bodies {debug!("CNG lit:{} body{:?}", lit_id, body);let mut conjunction_nogood1 = vec![];let lit = Literal::new(*lit_id, true);let neg_lit = Literal::new(*lit_id, false);conjunction_nogood1.push(neg_lit);for l in body.iter() {conjunction_nogood1.push(*l);let mut conjunction_nogoodn = vec![lit];conjunction_nogoodn.push(l.negate());debug!("Conjunction nogood: {:?}", conjunction_nogoodn);nogoods.push(conjunction_nogoodn);}debug!("Conjunction nogood: {:?}", conjunction_nogood1);nogoods.push(conjunction_nogood1);}}}#[test]fn test_aspif_i64_to_solver_literal() {let mut lm = LiteralMapper::default();let l0 = Literal::new(0, true);let l1 = Literal::new(1, false);let l2 = Literal::new(0, false);let l3 = Literal::new(1, true);let sl0 = lm.i64_to_solver_literal(&12);let sl1 = lm.i64_to_solver_literal(&-2);let sl2 = lm.i64_to_solver_literal(&-12);let sl3 = lm.i64_to_solver_literal(&-2);let sl4 = lm.i64_to_solver_literal(&2);assert_eq!(l0, sl0);assert_eq!(l1, sl1);assert_eq!(l2, sl2);assert_eq!(l1, sl3);assert_eq!(l3, sl4);}#[test]fn test_aspif_u64_to_solver_literal() {let mut lm = LiteralMapper::default();let l0 = Literal::new(0, true);let l1 = Literal::new(1, false);let l2 = Literal::new(0, false);let l3 = Literal::new(1, true);let sl0 = lm.u64_to_solver_literal(&12);let sl1 = lm.i64_to_solver_literal(&-2);let sl2 = lm.i64_to_solver_literal(&-12);let sl3 = lm.u64_to_solver_literal(&2);assert_eq!(l0, sl0);assert_eq!(l1, sl1);assert_eq!(l2, sl2);assert_eq!(l3, sl3);}#[test]fn test_body_to_solver_literal() {// TODO// let mut lm = LiteralMapper::default();// let la = Literal::new(0, true);// let lb = Literal::new(1, false);// let lc = Literal::new(0, false);// let ld = Literal::new(1, true);// let sl0 = lm.body2solver_literal(&[la,lb]);// let sl1 = lm.body2solver_literal(&[lb,lc]);// let sl2 = lm.body2solver_literal(&[lc,ld]);// let sl3 = lm.body2solver_literal(&[lb,ld]);// let l0 = Literal::new(2, true);// let l1 = Literal::new(3, true);// let l2 = Literal::new(4, true);// let l3 = Literal::new(5, true);// assert_eq!(l0, sl0);// assert_eq!(l1, sl1);// assert_eq!(l2, sl2);// assert_eq!(l3, sl3);}pub type SymbolMapper = Map<Vec<Literal>, SymbolU32>;fn insert_into_symbol_mapper(symbol_mapper: &mut SymbolMapper,literal_mapper: &mut LiteralMapper,symbol: SymbolU32,condition: &[i64],) {let mut new_condition = vec![];for e in condition {new_condition.push(literal_mapper.i64_to_solver_literal(e));}// TODO: This is buggy if there could be multiple symbols with the same condition// q :- not p.// p :- not q.// #show t:p.symbol_mapper.insert(new_condition, symbol);}use petgraph::algo::tarjan_scc;use petgraph::graph::{DiGraph, NodeIndex};/// Create a (directed) positive atom dependency graph/// The graph will be used to compute scc's which correspond to loops of the programpub fn graph_from_aspif(aspif_program: &AspifProgram) {let mut literal_mapper = LiteralMapper::default();let mut positive_atom_dependency_graph: DiGraph<u32, ()> = DiGraph::default();for statement in &aspif_program.statements {debug!("stmt:{:?}", statement);match statement {aspif::Statement::Rule(rule) => {let body_clause = match &rule.body {aspif::Body::NormalBody { elements } => {let mut body_clause = vec![];for e in elements {body_clause.push(literal_mapper.i64_to_solver_literal(e));}body_clause.sort();body_clause}aspif::Body::WeightBody {lowerbound: _,elements: _,} => {panic!("Unsupported Body")}};match &rule.head {aspif::Head::Disjunction { elements } => {for body_lit in &body_clause {if body_lit.sign() {for head_atom_id in elements {while positive_atom_dependency_graph.node_count()<= *head_atom_id as usize{let _a = positive_atom_dependency_graph.add_node(0);}let a = NodeIndex::from(*head_atom_id as u32);while positive_atom_dependency_graph.node_count()<= body_lit.id(){let _a = positive_atom_dependency_graph.add_node(0);}let b = NodeIndex::from(body_lit.id() as u32);positive_atom_dependency_graph.add_edge(a, b, ());}}}}aspif::Head::Choice { elements } => {panic!("Unsupported Head : Choice")}}}// aspif::Statement::Minimize(_) => todo!(),// aspif::Statement::Projection(_) => todo!(),aspif::Statement::Output(_) => {}// aspif::Statement::External { atom, init } => todo!(),// aspif::Statement::Assumption(_) => todo!(),// aspif::Statement::Heuristic {// modifier,// atom,// k,// priority,// condition,// } => todo!(),// aspif::Statement::Edge { u, v, condition } => todo!(),// aspif::Statement::NumericTheoryTerm { id, w } => todo!(),// aspif::Statement::SymbolicTheoryTerm { id, string } => todo!(),// aspif::Statement::CompoundTheoryTerm { id, t, terms } => todo!(),// aspif::Statement::TheoryAtomElements {// id,// theory_terms,// literals,// } => todo!(),// aspif::Statement::TheoryAtom {// atom,// symbolic_term,// theory_atom_elements,// theory_operation,// } => todo!(),// aspif::Statement::TheoryDirective {// symbolic_term,// theory_atom_elements,// theory_operation,// } => todo!(),// aspif::Statement::Comment => todo!(),_ => {panic!("Unsupported Statement");}}}debug!("positive_atom_dependency_graph: {positive_atom_dependency_graph:?}");info!("Strongly connected components ...");let components = tarjan_scc(&positive_atom_dependency_graph);for scc in components {debug!("{scc:?}");}}pub struct Builder {pub(crate) nogoods: Vec<Vec<Literal>>,}impl Builder {pub fn from_aspif(aspif_program: &AspifProgram) -> (Self, SymbolMapper, StringInterner) {let mut interner = StringInterner::default();let mut literal_mapper = LiteralMapper::default();let mut symbol_mapper = SymbolMapper::default();let mut nogoods = vec![];for statement in &aspif_program.statements {match statement {aspif::Statement::Rule(rule) => {literal_mapper.write_rule_nogood(rule, &mut nogoods);}// aspif::Statement::Minimize(_) => todo!(),// aspif::Statement::Projection(_) => todo!(),aspif::Statement::Output(output) => {let sym = interner.get_or_intern(&output.string);insert_into_symbol_mapper(&mut symbol_mapper,&mut literal_mapper,sym,&output.condition,)}// aspif::Statement::External { atom, init } => todo!(),// aspif::Statement::Assumption(_) => todo!(),// aspif::Statement::Heuristic {// modifier,// atom,// k,// priority,// condition,// } => todo!(),// aspif::Statement::Edge { u, v, condition } => todo!(),// aspif::Statement::NumericTheoryTerm { id, w } => todo!(),// aspif::Statement::SymbolicTheoryTerm { id, string } => todo!(),// aspif::Statement::CompoundTheoryTerm { id, t, terms } => todo!(),// aspif::Statement::TheoryAtomElements {// id,// theory_terms,// literals,// } => todo!(),// aspif::Statement::TheoryAtom {// atom,// symbolic_term,// theory_atom_elements,// theory_operation,// } => todo!(),// aspif::Statement::TheoryDirective {// symbolic_term,// theory_atom_elements,// theory_operation,// } => todo!(),// aspif::Statement::Comment => todo!(),_ => {panic!("Unsupported Statement");}}}literal_mapper.write_support_nogoods(&mut nogoods);literal_mapper.write_conjuction_nogoods(&mut nogoods);(Builder { nogoods }, symbol_mapper, interner)}pub fn build(self) -> Solver {let mut number_of_variables = 0;for nogood in &self.nogoods {for lit in nogood {if lit.id() + 1 > number_of_variables {number_of_variables = lit.id() + 1;}}}let mut solver_nogoods = vec![];for nogood in self.nogoods {let mut solver_nogood = vec![None; number_of_variables];for (id, item) in solver_nogood.iter_mut().enumerate() {if nogood.contains(&Literal::new(id, true)) {*item = Some(true);} else if nogood.contains(&Literal::new(id, false)) {*item = Some(false);}}debug!("Solver nogood: {:?}", solver_nogood);solver_nogoods.push(solver_nogood);}let mut watch_lists = vec![];for nogood in &solver_nogoods {// TODO: special handling for nogoods of size 1let mut left_watch = 0;while nogood[left_watch].is_none() {left_watch += 1;}let mut right_watch = nogood.len() - 1;while nogood[right_watch].is_none() {right_watch -= 1;}watch_lists.push(WatchList {first_watch: left_watch,second_watch: right_watch,})}let mut var_to_nogoods: Vec<Map<usize, bool>> = vec![Map::default(); number_of_variables];let mut nogoods_to_var: Vec<Map<usize, bool>> = vec![Map::default(); solver_nogoods.len()];for nogood_id in 0..solver_nogoods.len() {for (variable_id, map) in var_to_nogoods.iter_mut().enumerate().take(number_of_variables){if let Some(sign) = solver_nogoods[nogood_id][variable_id] {map.insert(nogood_id, sign);nogoods_to_var[nogood_id].insert(variable_id, sign);}}}Solver {tight: true,number_of_variables,assignments: vec![None; number_of_variables],decisions: vec![],watch_lists,nogoods: solver_nogoods,var_to_nogoods,nogoods_to_var,decision_level: 0,decision_number: 0,assignments_log: vec![(None, None, 0, 0); number_of_variables],chronological_backtracking_level: 0,}}}#[test]fn test_write_rule_nogood() {let mut lm = LiteralMapper::default();let l0 = Literal::new(0, true);let l1 = Literal::new(1, false);let l2 = Literal::new(2, true);let l3 = Literal::new(3, false);let mut nogoods = vec![];// Rule with empty body and empty head `:-.` the corresponding rule nogood should contain only the literal corresponding to the empty bodyif let aspif::Statement::Rule(rule) = aspif::read_statement_line("1 0 0 0 0").unwrap() {lm.write_rule_nogood(&rule, &mut nogoods);assert_eq!(nogoods[0], vec![l0])}// Rule with one head and empty body `a.`if let aspif::Statement::Rule(rule) = aspif::read_statement_line("1 0 1 1 0 0").unwrap() {lm.write_rule_nogood(&rule, &mut nogoods);assert_eq!(nogoods[1], vec![l1, l0])}// :- not a.if let aspif::Statement::Rule(rule) = aspif::read_statement_line("1 0 0 0 1 -1").unwrap() {lm.write_rule_nogood(&rule, &mut nogoods);assert_eq!(nogoods[2], vec![l2])}// a :- not a.if let aspif::Statement::Rule(rule) = aspif::read_statement_line("1 0 1 1 0 1 -1").unwrap() {lm.write_rule_nogood(&rule, &mut nogoods);assert_eq!(nogoods[3], vec![l1, l2])}// b :- not a.if let aspif::Statement::Rule(rule) = aspif::read_statement_line("1 0 1 2 0 1 -1").unwrap() {lm.write_rule_nogood(&rule, &mut nogoods);assert_eq!(nogoods[4], vec![l3, l2])}// a;b :- not a.if let aspif::Statement::Rule(rule) = aspif::read_statement_line("1 0 2 1 2 0 1 -1").unwrap() {lm.write_rule_nogood(&rule, &mut nogoods);assert_eq!(nogoods[5], vec![l1, l3, l2])}}#[test]fn test_write_nogoods() {let mut lm = LiteralMapper::default();let na = Literal::new(0, false);let ne = Literal::new(1, false);let b1 = Literal::new(2, true);let nb = Literal::new(3, false);let mut nogoods = vec![];// b :- a, not e.if let aspif::Statement::Rule(rule) = aspif::read_statement_line("1 0 1 4 0 2 1 -3").unwrap() {lm.write_rule_nogood(&rule, &mut nogoods);assert_eq!(nogoods[0], vec![nb, b1])}let nd = Literal::new(4, false);let b2 = Literal::new(5, true);// a :- b, not d.if let aspif::Statement::Rule(rule) = aspif::read_statement_line("1 0 1 1 0 2 4 -5").unwrap() {lm.write_rule_nogood(&rule, &mut nogoods);assert_eq!(nogoods[1], vec![na, b2])}let b3 = Literal::new(6, true);let nc = Literal::new(7, false);// c;d :- b.if let aspif::Statement::Rule(rule) = aspif::read_statement_line("1 0 2 2 5 0 1 4").unwrap() {lm.write_rule_nogood(&rule, &mut nogoods);assert_eq!(nogoods[2], vec![nc, nd, b3])}let b4 = Literal::new(8, true);// b :- d, c.if let aspif::Statement::Rule(rule) = aspif::read_statement_line("1 0 1 4 0 2 5 2").unwrap() {lm.write_rule_nogood(&rule, &mut nogoods);assert_eq!(nogoods[3], vec![nb, b4])}let b5 = Literal::new(9, true);// a;c :- not e.if let aspif::Statement::Rule(rule) = aspif::read_statement_line("1 0 2 1 2 0 1 -3").unwrap() {lm.write_rule_nogood(&rule, &mut nogoods);assert_eq!(nogoods[4], vec![na, nc, b5])}let b6 = Literal::new(10, true);// e :- not a, not c.if let aspif::Statement::Rule(rule) = aspif::read_statement_line("1 0 1 3 0 2 -1 -2").unwrap() {lm.write_rule_nogood(&rule, &mut nogoods);assert_eq!(nogoods[5], vec![ne, b6])}}#[test]fn test_collect_atom_support() {//TODO}// pub struct GroundDisjunctiveRule {// head: Vec<u32>,// body: Body,// }// impl GroundDisjunctiveRule {// fn new(head: &[u32], pbody: &[u32], nbody: &[u32]) -> Self {// let mut head_atoms = Vec::from(head);// head_atoms.dedup();// let body = Body::new(pbody, nbody);// GroundDisjunctiveRule {// head: head_atoms,// body,// }// }// }// #[derive(PartialEq, Clone, Debug)]// pub enum Body {// // Body contains a Contradiction and can never be satisfied// Contradiction,// Conditions {// positive_body: Vec<u32>,// negative_body: Vec<u32>,// },// }// impl Body {// fn new(pbody: &[u32], nbody: &[u32]) -> Self {// let mut positive_body = Vec::from(pbody);// positive_body.dedup();// let mut negative_body = Vec::from(nbody);// negative_body.dedup();// for a1 in &negative_body {// for a2 in &positive_body {// if a1 == a2 {// return Body::Contradiction;// }// }// }// Body::Conditions {// positive_body,// negative_body,// }// }// }// type SolversAtoms = u32;// fn atoms(program: &[GroundDisjunctiveRule]) -> Vec<SolversAtoms> {// Vec::default()// }// fn bodies(program: &[GroundDisjunctiveRule]) -> Vec<SolversAtoms> {// Vec::default()// }
use anyhow::Result;use clap::Parser;use log::{debug, error, info, warn};use solver::convert::Builder;use solver::solver::create_test_solver;use std::fs;use std::io;use std::path::PathBuf;/// Solver#[derive(Parser, Debug)]#[clap(name = "solver")]#[clap(version, author)]struct Opt {/// Input file in aspif format#[clap(name = "FILE", parse(from_os_str))]file: Option<PathBuf>,/// Verbose mode (-v, -vv, -vvv, etc)#[clap(short = 'v', long = "verbose", parse(from_occurrences))]verbose: usize,}pub enum Reader<'a> {File(io::BufReader<fs::File>),Stdin(io::StdinLock<'a>),}impl<'a> io::Read for Reader<'a> {fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {match self {Self::File(reader) => reader.read(buf),Self::Stdin(guard) => guard.read(buf),}}}impl<'a> io::BufRead for Reader<'a> {fn fill_buf(&mut self) -> io::Result<&[u8]> {match self {Self::File(reader) => reader.fill_buf(),Self::Stdin(guard) => guard.fill_buf(),}}fn consume(&mut self, amt: usize) {match self {Self::File(reader) => reader.consume(amt),Self::Stdin(guard) => guard.consume(amt),}}}fn main() {let opt = Opt::parse();stderrlog::new().module(module_path!()).verbosity(opt.verbose * 4).module("solver").module("solver::convert").init().unwrap();if let Err(err) = run(opt) {error!("{:?}", err);std::process::exit(1);}}fn run(opt: Opt) -> Result<()> {let stdin = io::stdin();let input = match opt.file {Some(path) => {let file = fs::File::open(path)?;Reader::File(io::BufReader::new(file))}None => {let guard = stdin.lock();Reader::Stdin(guard)}};// let mut out = std::io::stdout();info!("Reading input ...");let parse_result = match input {Reader::File(buf_reader) => aspif::read_aspif(buf_reader)?,Reader::Stdin(buf_reader) => aspif::read_aspif(buf_reader)?,};info!("Translate to nogoods (wip) ...");let (builder, symbol_mapper, interner) = match parse_result {aspif::ParseResult::Complete(aspif_program) => {info!("Create a (directed) positive atom dependecy graph (wip)...");let res = solver::convert::graph_from_aspif(&aspif_program);info!("Create a builder (wip) ...");Builder::from_aspif(&aspif_program)}aspif::ParseResult::Incomplete(_) => {warn!("Could not read complete aspif program.");panic!("Could not read complete aspif program.");}};info!("Build solver ...");let mut solver = builder.build();for (condition, symbol) in &symbol_mapper {println!("condition:{:?} symbol:{} ",condition, interner.resolve(*symbol).unwrap());}// let mut solver = create_test_solver();info!("Solve ...");use solver::solver::SolveResult;let mut solutions = solver.solve().enumerate();loop {if let Some((c, res)) = solutions.next() {match res {SolveResult::Sat(assignments) => {print!("Solution {}: ", c + 1);for (condition, symbol) in &symbol_mapper {let mut satisfied = true;for literal in condition {match assignments[literal.id()] {Some(sign) => satisfied = sign == literal.sign(),None => panic!("Partial assignment!"),}if !satisfied {break;};}if satisfied {print!("{} ", interner.resolve(*symbol).unwrap());}// debug!("condition: {:?} sym: {}",condition,interner.resolve(*symbol).unwrap());}let mut string = String::new();for (id, e) in assignments.iter().enumerate() {match e {Some(true) => string = format!("{} {}", string, id),Some(false) => string = format!("{} ~{}", string, id),None => {}}}debug!("solution: {string}");println!();}SolveResult::UnSat => {println!("UNSAT");}}} else {println!("EXHAUSTED");break;}}Ok(())}
[package]name = "solver"version = "0.1.0"authors = ["robauke"]description = "A CDNL solver."edition = "2021"# documentation = "https://docs.rs/versuch"[dependencies]rustc-hash = "1.1.0"genawaiter = { version = "0.99" }string-interner = "0.14"fnv = "1.0" # deterministic HashMappetgraph = "0.6"aspif = { git = "https://github.com/sthiele/aspif-parser", branch = "st/wip" }# aspif = {path = "../../aspif-parser"}clap = { version = "3.0", features = ["derive"] }anyhow = "1.0"log = "0.4"thiserror = "1.0"stderrlog = "0.5"
# VersuchProduce aspif output with clingo:```shclingo test.lp --pre=aspif > test.aspif```Run versuch:```shcargo run --example versuch test.aspif -v```
[workspace]members = ["solver",]