mirror of
https://github.com/ellmau/adf-obdd.git
synced 2025-12-19 09:29:36 +01:00
Tidied up the application
added documentation added clap for program argument handling fully clippy approved (except where it shall not be changed) updated workflow for win and linux builds Signed-off-by: Stefan Ellmauthaler <stefan.ellmauthaler@tu-dresden.de>
This commit is contained in:
parent
3138cf7de5
commit
275adbe7f1
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
target: [x86_64-pc-windows-gnu, x86_64-unknown-linux-musl]
|
target: [x86_64-pc-windows-gnu, x86_64-unknown-linux-gnu]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
- name: Compile and release
|
- name: Compile and release
|
||||||
@ -25,7 +25,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
target: [x86_64-unknown-linux-musl]
|
target: [x86_64-unknown-linux-gnu]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
- name: Compile and release
|
- name: Compile and release
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "adf_bdd"
|
name = "adf_bdd"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
authors = ["Stefan Ellmauthaler <stefan.ellmauthaler@tu-dresden.de>"]
|
authors = ["Stefan Ellmauthaler <stefan.ellmauthaler@tu-dresden.de>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
description = "Solver for ADFs grounded semantics by utilising OBDDs - ordered binary decision diagrams"
|
||||||
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = "2.33.*"
|
||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
## Abstract Dialectical Frameworks
|
## Abstract Dialectical Frameworks
|
||||||
An abstract dialectical framework (ADF) consists of abstract statements. Each statement has an unique label and might be related to other statements (s) in the ADF. This relation is defined by a so-called acceptance condition (ac), which intuitively is a propositional formula, where the variable symbols are the labels of the statements. An interpretation is a three valued function which maps to each statement a truth value (true, false, undecided). We call such an interpretation a model, if each acceptance condition agrees to the interpration.
|
An abstract dialectical framework (ADF) consists of abstract statements. Each statement has an unique label and might be related to other statements (s) in the ADF. This relation is defined by a so-called acceptance condition (ac), which intuitively is a propositional formula, where the variable symbols are the labels of the statements. An interpretation is a three valued function which maps to each statement a truth value (true, false, undecided). We call such an interpretation a model, if each acceptance condition agrees to the interpration.
|
||||||
## ordered Binary Decision Diagram
|
## Ordered Binary Decision Diagram
|
||||||
An ordered binary decision diagram is a normalised representation of binary functions, where satisfiability- and validity checks can be done relatively cheap.
|
An ordered binary decision diagram is a normalised representation of binary functions, where satisfiability- and validity checks can be done relatively cheap.
|
||||||
|
|
||||||
## Input-file format:
|
## Input-file format:
|
||||||
|
|||||||
181
src/adf.rs
181
src/adf.rs
@ -1,4 +1,18 @@
|
|||||||
use std::{collections::HashMap, str::{self, FromStr}, usize};
|
//! This module describes the abstract dialectical framework
|
||||||
|
//!
|
||||||
|
//! It handles
|
||||||
|
//! - parsing of statements and acceptance functions
|
||||||
|
//! - computing interpretations
|
||||||
|
//! - computing fixpoints
|
||||||
|
//! - computing the least fixpoint by using a shortcut
|
||||||
|
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
str::{self, FromStr},
|
||||||
|
usize,
|
||||||
|
};
|
||||||
|
|
||||||
use super::obdd::Bdd;
|
use super::obdd::Bdd;
|
||||||
|
|
||||||
@ -17,12 +31,12 @@ impl Statement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn _add_ac(&mut self, ac: usize) {
|
pub fn _add_ac(&mut self, ac: usize) {
|
||||||
self.ac = Some(ac);
|
self.ac = Some(ac);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ADF structure which offers some statice and instance methods for handling ADFs.
|
||||||
pub struct Adf {
|
pub struct Adf {
|
||||||
bdd: Bdd,
|
bdd: Bdd,
|
||||||
stmts: Vec<Statement>,
|
stmts: Vec<Statement>,
|
||||||
@ -36,6 +50,10 @@ impl Default for Adf{
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Adf {
|
impl Adf {
|
||||||
|
/// Creates a new and empty ADF.
|
||||||
|
///
|
||||||
|
/// To add statements call [`init_statements`](Adf::init_statements()) and then add to each statement it's corresponding acceptance contidion via
|
||||||
|
/// [`add_ac`](Adf::add_ac()).
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Adf {
|
Adf {
|
||||||
bdd: Bdd::new(),
|
bdd: Bdd::new(),
|
||||||
@ -59,7 +77,8 @@ impl Adf {
|
|||||||
/// Initialise statements
|
/// Initialise statements
|
||||||
///
|
///
|
||||||
/// The order of statements given as a parameter will determine die ordering for the OBDD.
|
/// The order of statements given as a parameter will determine die ordering for the OBDD.
|
||||||
/// Note that only initialised statements will regocnised as variables later on
|
/// Note that only initialised statements will be regocnised as variables later on.
|
||||||
|
/// This method can be called multiple times to add more statements.
|
||||||
pub fn init_statements(&mut self, stmts: Vec<&str>) -> usize {
|
pub fn init_statements(&mut self, stmts: Vec<&str>) -> usize {
|
||||||
for i in stmts.iter() {
|
for i in stmts.iter() {
|
||||||
self.add_statement(*i);
|
self.add_statement(*i);
|
||||||
@ -70,6 +89,7 @@ impl Adf {
|
|||||||
/// Adds to a given statement its acceptance condition.
|
/// Adds to a given statement its acceptance condition.
|
||||||
///
|
///
|
||||||
/// This ac needs to be in the prefix notation for ADFs as defined by the DIAMOND implementation.
|
/// This ac needs to be in the prefix notation for ADFs as defined by the DIAMOND implementation.
|
||||||
|
/// Every statement needs to be initialised before by [`init_statements'](Adf::init_statements()).
|
||||||
pub fn add_ac(&mut self, statement: &str, ac: &str) {
|
pub fn add_ac(&mut self, statement: &str, ac: &str) {
|
||||||
if let Some(stmt) = self.dict.get(statement) {
|
if let Some(stmt) = self.dict.get(statement) {
|
||||||
let st = *stmt;
|
let st = *stmt;
|
||||||
@ -78,7 +98,7 @@ impl Adf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_ac_by_number(&mut self, st: usize, ac: &str) {
|
fn add_ac_by_number(&mut self, st: usize, ac: &str) {
|
||||||
let ac_num = self.parseformula(ac);
|
let ac_num = self.parse_formula(ac);
|
||||||
self.set_ac(st, ac_num);
|
self.set_ac(st, ac_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +106,9 @@ impl Adf {
|
|||||||
self.stmts[st].ac = Some(ac);
|
self.stmts[st].ac = Some(ac);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes the grounded model of the adf.
|
||||||
|
///
|
||||||
|
/// Note that this computation will shortcut interpretation updates and needs less steps than computing the least fixpoint by an usual fixpoint-construction
|
||||||
pub fn grounded(&mut self) -> Vec<usize> {
|
pub fn grounded(&mut self) -> Vec<usize> {
|
||||||
let mut interpretation: Vec<usize> = Vec::new();
|
let mut interpretation: Vec<usize> = Vec::new();
|
||||||
let mut change: bool;
|
let mut change: bool;
|
||||||
@ -99,26 +122,37 @@ impl Adf {
|
|||||||
let curint = interpretation.clone();
|
let curint = interpretation.clone();
|
||||||
match curint[pos] {
|
match curint[pos] {
|
||||||
super::obdd::BDD_BOT => {
|
super::obdd::BDD_BOT => {
|
||||||
if let Some(n) = self.setvarvalue(curint,self.bdd.nodes[self.stmts[pos].var].var(),false){
|
if let Some(n) = self.setvarvalue(
|
||||||
|
curint,
|
||||||
|
self.bdd.nodes[self.stmts[pos].var].var(),
|
||||||
|
false,
|
||||||
|
) {
|
||||||
interpretation.clone_from(&n);
|
interpretation.clone_from(&n);
|
||||||
change = true;
|
change = true;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
super::obdd::BDD_TOP => {
|
super::obdd::BDD_TOP => {
|
||||||
if let Some(n) = self.setvarvalue(curint,self.bdd.nodes[self.stmts[pos].var].var(),true){
|
if let Some(n) = self.setvarvalue(
|
||||||
|
curint,
|
||||||
|
self.bdd.nodes[self.stmts[pos].var].var(),
|
||||||
|
true,
|
||||||
|
) {
|
||||||
interpretation.clone_from(&n);
|
interpretation.clone_from(&n);
|
||||||
change = true;
|
change = true;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !change {break;}
|
if !change {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
interpretation
|
interpretation
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn complete(&mut self) -> Vec<Vec<usize>> {
|
/// Function not working - do not use
|
||||||
|
pub(crate) fn _complete(&mut self) -> Vec<Vec<usize>> {
|
||||||
let base_int = self.cur_interpretation();
|
let base_int = self.cur_interpretation();
|
||||||
let mut complete: Vec<Vec<usize>> = Vec::new();
|
let mut complete: Vec<Vec<usize>> = Vec::new();
|
||||||
let mut change: bool;
|
let mut change: bool;
|
||||||
@ -126,7 +160,7 @@ impl Adf {
|
|||||||
let mut cache: HashMap<Vec<usize>, usize> = HashMap::new();
|
let mut cache: HashMap<Vec<usize>, usize> = HashMap::new();
|
||||||
|
|
||||||
// compute grounded interpretation
|
// compute grounded interpretation
|
||||||
complete.push(self.to_fixpoint(base_int.as_ref()).unwrap());
|
complete.push(self.compute_fixpoint(base_int.as_ref()).unwrap());
|
||||||
loop {
|
loop {
|
||||||
change = false;
|
change = false;
|
||||||
let interpr = complete.get(pos).unwrap().clone();
|
let interpr = complete.get(pos).unwrap().clone();
|
||||||
@ -134,34 +168,28 @@ impl Adf {
|
|||||||
if *it > 1 {
|
if *it > 1 {
|
||||||
let mut int1 = interpr.clone();
|
let mut int1 = interpr.clone();
|
||||||
int1[pos] = 0;
|
int1[pos] = 0;
|
||||||
match self.to_fixpoint(int1.as_ref()){
|
if let Some(n) = self.compute_fixpoint(int1.as_ref()) {
|
||||||
Some(n) => {
|
|
||||||
if !cache.contains_key(&n) {
|
if !cache.contains_key(&n) {
|
||||||
cache.insert(n.clone(), complete.len());
|
cache.insert(n.clone(), complete.len());
|
||||||
complete.push(n);
|
complete.push(n);
|
||||||
change = true;
|
change = true;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
None => (),
|
|
||||||
}
|
}
|
||||||
int1[pos] = 1;
|
int1[pos] = 1;
|
||||||
match self.to_fixpoint(int1.as_ref()){
|
if let Some(n) = self.compute_fixpoint(int1.as_ref()) {
|
||||||
Some(n) => {
|
|
||||||
if !cache.contains_key(&n) {
|
if !cache.contains_key(&n) {
|
||||||
cache.insert(n.clone(), complete.len());
|
cache.insert(n.clone(), complete.len());
|
||||||
complete.push(n);
|
complete.push(n);
|
||||||
change = true;
|
change = true;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !change {break};
|
}
|
||||||
|
if !change {
|
||||||
|
break;
|
||||||
|
};
|
||||||
pos += 1;
|
pos += 1;
|
||||||
// println!("{}",complete.len());
|
// println!("{}",complete.len());
|
||||||
|
|
||||||
}
|
}
|
||||||
complete
|
complete
|
||||||
}
|
}
|
||||||
@ -175,42 +203,53 @@ impl Adf {
|
|||||||
interpretation
|
interpretation
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_fixpoint(&mut self, interpretation:&Vec<usize>) -> Option<Vec<usize>>{
|
/// Given an Interpretation, follow the `Γ`-Operator to a fixpoint. Returns `None` if no valid fixpoint can be reached from the given interpretation and
|
||||||
|
/// the interpretation which represents the fixpoint otherwise.
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
|
pub fn compute_fixpoint(&mut self, interpretation: &Vec<usize>) -> Option<Vec<usize>> {
|
||||||
let new_interpretation = self.apply_interpretation(interpretation.as_ref());
|
let new_interpretation = self.apply_interpretation(interpretation.as_ref());
|
||||||
match Adf::information_enh(interpretation.as_ref(), new_interpretation.as_ref()){
|
match Adf::information_enh(interpretation, new_interpretation.as_ref()) {
|
||||||
Some(n) => {
|
Some(n) => {
|
||||||
if n {
|
if n {
|
||||||
return self.to_fixpoint(new_interpretation.as_ref());
|
self.compute_fixpoint(new_interpretation.as_ref())
|
||||||
} else {
|
} else {
|
||||||
return Some(new_interpretation)
|
Some(new_interpretation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
fn apply_interpretation(&mut self, interpretation: &Vec<usize>) -> Vec<usize> {
|
fn apply_interpretation(&mut self, interpretation: &Vec<usize>) -> Vec<usize> {
|
||||||
let mut new_interpretation = interpretation.clone();
|
let mut new_interpretation = interpretation.clone();
|
||||||
for (pos, it) in interpretation.iter().enumerate() {
|
for (pos, it) in interpretation.iter().enumerate() {
|
||||||
match *it {
|
match *it {
|
||||||
super::obdd::BDD_BOT => {
|
super::obdd::BDD_BOT => {
|
||||||
if let Some(n) = self.setvarvalue(new_interpretation.clone(),self.bdd.nodes[self.stmts[pos].var].var(),false){
|
if let Some(n) = self.setvarvalue(
|
||||||
|
new_interpretation.clone(),
|
||||||
|
self.bdd.nodes[self.stmts[pos].var].var(),
|
||||||
|
false,
|
||||||
|
) {
|
||||||
new_interpretation.clone_from(&n);
|
new_interpretation.clone_from(&n);
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
super::obdd::BDD_TOP => {
|
super::obdd::BDD_TOP => {
|
||||||
if let Some(n) = self.setvarvalue(new_interpretation.clone(),self.bdd.nodes[self.stmts[pos].var].var(),true){
|
if let Some(n) = self.setvarvalue(
|
||||||
|
new_interpretation.clone(),
|
||||||
|
self.bdd.nodes[self.stmts[pos].var].var(),
|
||||||
|
true,
|
||||||
|
) {
|
||||||
new_interpretation.clone_from(&n);
|
new_interpretation.clone_from(&n);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
new_interpretation
|
new_interpretation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
fn information_enh(i1: &Vec<usize>, i2: &Vec<usize>) -> Option<bool> {
|
fn information_enh(i1: &Vec<usize>, i2: &Vec<usize>) -> Option<bool> {
|
||||||
let mut enhanced = false;
|
let mut enhanced = false;
|
||||||
for i in 0..i1.len() {
|
for i in 0..i1.len() {
|
||||||
@ -225,7 +264,12 @@ impl Adf {
|
|||||||
Some(enhanced)
|
Some(enhanced)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setvarvalue(&mut self,interpretation:Vec<usize>, var:usize, val:bool) -> Option<Vec<usize>>{
|
fn setvarvalue(
|
||||||
|
&mut self,
|
||||||
|
interpretation: Vec<usize>,
|
||||||
|
var: usize,
|
||||||
|
val: bool,
|
||||||
|
) -> Option<Vec<usize>> {
|
||||||
let mut interpretation2: Vec<usize> = vec![0; interpretation.len()];
|
let mut interpretation2: Vec<usize> = vec![0; interpretation.len()];
|
||||||
let mut change: bool = false;
|
let mut change: bool = false;
|
||||||
for (pos, _it) in interpretation.iter().enumerate() {
|
for (pos, _it) in interpretation.iter().enumerate() {
|
||||||
@ -241,64 +285,82 @@ impl Adf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseformula(&mut self, ac: &str) -> usize {
|
fn parse_formula(&mut self, ac: &str) -> usize {
|
||||||
if ac.len() > 3 {
|
if ac.len() > 3 {
|
||||||
match &ac[..4] {
|
match &ac[..4] {
|
||||||
"and(" => {
|
"and(" => {
|
||||||
let (left, right) = Adf::findpairs(&ac[4..]);
|
let (left, right) = Adf::findpairs(&ac[4..]);
|
||||||
let lterm = self.parseformula(left);
|
let lterm = self.parse_formula(left);
|
||||||
let rterm = self.parseformula(right);
|
let rterm = self.parse_formula(right);
|
||||||
return self.bdd.and(lterm, rterm);
|
return self.bdd.and(lterm, rterm);
|
||||||
},
|
}
|
||||||
"iff(" => {
|
"iff(" => {
|
||||||
let (left, right) = Adf::findpairs(&ac[4..]);
|
let (left, right) = Adf::findpairs(&ac[4..]);
|
||||||
let lterm = self.parseformula(left);
|
let lterm = self.parse_formula(left);
|
||||||
let rterm = self.parseformula(right);
|
let rterm = self.parse_formula(right);
|
||||||
let notlt = self.bdd.not(lterm);
|
let notlt = self.bdd.not(lterm);
|
||||||
let notrt = self.bdd.not(rterm);
|
let notrt = self.bdd.not(rterm);
|
||||||
let con1 = self.bdd.and(lterm, rterm);
|
let con1 = self.bdd.and(lterm, rterm);
|
||||||
let con2 = self.bdd.and(notlt, notrt);
|
let con2 = self.bdd.and(notlt, notrt);
|
||||||
return self.bdd.or(con1, con2);
|
return self.bdd.or(con1, con2);
|
||||||
},
|
}
|
||||||
"xor(" => {
|
"xor(" => {
|
||||||
let (left, right) = Adf::findpairs(&ac[4..]);
|
let (left, right) = Adf::findpairs(&ac[4..]);
|
||||||
let lterm = self.parseformula(left);
|
let lterm = self.parse_formula(left);
|
||||||
let rterm = self.parseformula(right);
|
let rterm = self.parse_formula(right);
|
||||||
let notlt = self.bdd.not(lterm);
|
let notlt = self.bdd.not(lterm);
|
||||||
let notrt = self.bdd.not(rterm);
|
let notrt = self.bdd.not(rterm);
|
||||||
let con1 = self.bdd.and(notlt, rterm);
|
let con1 = self.bdd.and(notlt, rterm);
|
||||||
let con2 = self.bdd.and(lterm, notrt);
|
let con2 = self.bdd.and(lterm, notrt);
|
||||||
return self.bdd.or(con1, con2);
|
return self.bdd.or(con1, con2);
|
||||||
},
|
}
|
||||||
"neg(" => {
|
"neg(" => {
|
||||||
let pos = Adf::findterm(&ac[4..]).unwrap() + 4;
|
let pos = Adf::findterm(&ac[4..]).unwrap() + 4;
|
||||||
let term = self.parseformula(&ac[4..pos]);
|
let term = self.parse_formula(&ac[4..pos]);
|
||||||
return self.bdd.not(term);
|
return self.bdd.not(term);
|
||||||
},
|
}
|
||||||
"c(f)" => return self.bdd.constant(false),
|
"c(f)" => return self.bdd.constant(false),
|
||||||
"c(v)" => return self.bdd.constant(true),
|
"c(v)" => return self.bdd.constant(true),
|
||||||
_ if &ac[..3] == "or(" => {
|
_ if &ac[..3] == "or(" => {
|
||||||
let (left, right) = Adf::findpairs(&ac[3..]);
|
let (left, right) = Adf::findpairs(&ac[3..]);
|
||||||
let lterm = self.parseformula(left);
|
let lterm = self.parse_formula(left);
|
||||||
let rterm = self.parseformula(right);
|
let rterm = self.parse_formula(right);
|
||||||
return self.bdd.or(lterm, rterm);
|
return self.bdd.or(lterm, rterm);
|
||||||
},
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
match self.dict.get(ac) {
|
match self.dict.get(ac) {
|
||||||
Some(it) => self.bdd.variable(*it),
|
Some(it) => self.bdd.variable(*it),
|
||||||
_ => {println!("{}",ac); unreachable!()}
|
_ => {
|
||||||
|
println!("{}", ac);
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given a pair of literals, returns both literals as strings
|
||||||
|
/// # Example
|
||||||
|
/// ```rust
|
||||||
|
/// use adf_bdd::adf::Adf;
|
||||||
|
/// assert_eq!(Adf::findpairs("a,or(b,c))"), ("a", "or(b,c)"));
|
||||||
|
/// assert_eq!(Adf::findpairs("a,or(b,c)"), ("a", "or(b,c)"));
|
||||||
|
/// ```
|
||||||
pub fn findpairs(formula: &str) -> (&str, &str) {
|
pub fn findpairs(formula: &str) -> (&str, &str) {
|
||||||
let lpos = Adf::findterm(formula).unwrap();
|
let lpos = Adf::findterm(formula).unwrap();
|
||||||
let rpos = Adf::findterm(&formula[lpos + 1..]).unwrap() + lpos;
|
let rpos = Adf::findterm(&formula[lpos + 1..]).unwrap() + lpos;
|
||||||
(&formula[..lpos], &formula[lpos + 1..rpos + 1])
|
(&formula[..lpos], &formula[lpos + 1..rpos + 1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the first term in the given slice as a string slice
|
||||||
|
/// # Example
|
||||||
|
/// ```rust
|
||||||
|
/// use adf_bdd::adf::Adf;
|
||||||
|
/// assert_eq!(Adf::findterm_str("formula"), "formula");
|
||||||
|
/// assert_eq!(Adf::findterm_str("and(a,b),or(a,b)"), "and(a,b)");
|
||||||
|
/// assert_eq!(Adf::findterm_str("neg(atom(a.d.ee))"), "neg(atom(a.d.ee))");
|
||||||
|
/// assert_eq!(Adf::findterm_str("formula)"), "formula");
|
||||||
|
/// ```
|
||||||
pub fn findterm_str(formula: &str) -> &str {
|
pub fn findterm_str(formula: &str) -> &str {
|
||||||
&formula[..Adf::findterm(formula).unwrap()]
|
&formula[..Adf::findterm(formula).unwrap()]
|
||||||
}
|
}
|
||||||
@ -331,7 +393,6 @@ impl Adf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(formula.len())
|
Some(formula.len())
|
||||||
@ -357,30 +418,30 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parseformula() {
|
fn parse_formula() {
|
||||||
let mut adf = Adf::new();
|
let mut adf = Adf::new();
|
||||||
|
|
||||||
adf.add_statement("a");
|
adf.add_statement("a");
|
||||||
adf.add_statement("b");
|
adf.add_statement("b");
|
||||||
adf.add_statement("c");
|
adf.add_statement("c");
|
||||||
|
|
||||||
assert_eq!(adf.parseformula("and(a,or(b,c))"), 6);
|
assert_eq!(adf.parse_formula("and(a,or(b,c))"), 6);
|
||||||
assert_eq!(adf.parseformula("xor(a,b)"), 11);
|
assert_eq!(adf.parse_formula("xor(a,b)"), 11);
|
||||||
assert_eq!(adf.parseformula("or(c(f),b)"), 3); // is b
|
assert_eq!(adf.parse_formula("or(c(f),b)"), 3); // is b
|
||||||
|
|
||||||
adf.parseformula("and(or(c(f),a),and(b,c))");
|
adf.parse_formula("and(or(c(f),a),and(b,c))");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn parseformula_panic() {
|
fn parse_formula_panic() {
|
||||||
let mut adf = Adf::new();
|
let mut adf = Adf::new();
|
||||||
|
|
||||||
adf.add_statement("a");
|
adf.add_statement("a");
|
||||||
adf.add_statement("b");
|
adf.add_statement("b");
|
||||||
adf.add_statement("c");
|
adf.add_statement("c");
|
||||||
|
|
||||||
adf.parseformula("and(a,or(b,d))");
|
adf.parse_formula("and(a,or(b,d))");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
33
src/lib.rs
33
src/lib.rs
@ -1,2 +1,35 @@
|
|||||||
|
//! This library contains an efficient representation of `Abstract Dialectical Frameworks (ADf)` by utilising an implementation of `Ordered Binary Decision Diagrams (OBDD)`
|
||||||
|
//!
|
||||||
|
//! # Abstract Dialectical Frameworks
|
||||||
|
//! An `abstract dialectical framework` consists of abstract statements. Each statement has an unique label and might be related to other statements (s) in the ADF. This relation is defined by a so-called acceptance condition (ac), which intuitively is a propositional formula, where the variable symbols are the labels of the statements. An interpretation is a three valued function which maps to each statement a truth value (true, false, undecided). We call such an interpretation a model, if each acceptance condition agrees to the interpration.
|
||||||
|
//! # Ordered Binary Decision Diagram
|
||||||
|
//! An `ordered binary decision diagram` is a normalised representation of binary functions, where satisfiability- and validity checks can be done relatively cheap.
|
||||||
|
//!
|
||||||
|
//! Note that one advantage of this implementation is that only one oBDD is used for all acceptance conditions. This can be done because all of them have the identical signature (i.e. the set of all statements + top and bottom concepts).
|
||||||
|
//! Due to this uniform representation reductions on subformulae which are shared by two or more statements only need to be computed once and is already cached in the data structure for further applications.
|
||||||
|
//!
|
||||||
|
//! # Input-file format:
|
||||||
|
//! Each statement is defined by an ASP-style unary predicate s, where the enclosed term represents the label of the statement.
|
||||||
|
//! The binary predicate ac relates each statement to one propositional formula in prefix notation, with the logical operations and constants as follows:
|
||||||
|
//! - and(x,y): conjunction
|
||||||
|
//! - or(x,y): disjunctin
|
||||||
|
//! - iff(x,Y): if and only if
|
||||||
|
//! - xor(x,y): exclusive or
|
||||||
|
//! - neg(x): classical negation
|
||||||
|
//! - c(v): constant symbol "verum" - tautology/top
|
||||||
|
//! - c(f): constant symbol "falsum" - inconsistency/bot
|
||||||
|
|
||||||
|
//! ## Example input file:
|
||||||
|
//! ```prolog
|
||||||
|
//! s(a).
|
||||||
|
//! s(b).
|
||||||
|
//! s(c).
|
||||||
|
//! s(d).
|
||||||
|
//!
|
||||||
|
//! ac(a,c(v)).
|
||||||
|
//! ac(b,or(a,b)).
|
||||||
|
//! ac(c,neg(b)).
|
||||||
|
//! ac(d,d).
|
||||||
|
//! ```
|
||||||
pub mod obdd;
|
pub mod obdd;
|
||||||
pub mod adf;
|
pub mod adf;
|
||||||
81
src/main.rs
81
src/main.rs
@ -3,23 +3,36 @@ use std::io::{self, BufRead};
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::usize;
|
use std::usize;
|
||||||
use std::{env, process::exit};
|
|
||||||
|
|
||||||
pub mod adf;
|
pub mod adf;
|
||||||
pub mod obdd;
|
pub mod obdd;
|
||||||
|
|
||||||
use adf::Adf;
|
use adf::Adf;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let matches = clap_app!(myapp =>
|
||||||
|
(version: crate_version!())
|
||||||
|
(author: crate_authors!())
|
||||||
|
(name: crate_name!())
|
||||||
|
(about: crate_description!())
|
||||||
|
(@arg fast: -f --fast "fast algorithm instead of the direct fixpoint-computation")
|
||||||
|
(@arg verbose: -v +multiple "Sets verbosity")
|
||||||
|
(@arg INPUT: +required "Input file")
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
let verbose = matches.occurrences_of("verbose") > 0;
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
let args: Vec<String> = env::args().collect();
|
//let args: Vec<String> = env::args().collect();
|
||||||
if args.len() != 2 {
|
//if args.len() != 2 {
|
||||||
eprintln!("No Filename given");
|
// eprintln!("No Filename given");
|
||||||
exit(1);
|
// exit(1);
|
||||||
}
|
//}
|
||||||
let mut statements: Vec<String> = Vec::new();
|
let mut statements: Vec<String> = Vec::new();
|
||||||
let mut ac: Vec<(String, String)> = Vec::new();
|
let mut ac: Vec<(String, String)> = Vec::new();
|
||||||
let path = Path::new(args[1].as_str());
|
let path = Path::new(matches.value_of("INPUT").unwrap());
|
||||||
if let Ok(lines) = read_lines(path) {
|
if let Ok(lines) = read_lines(path) {
|
||||||
for line in lines.flatten() {
|
for line in lines.flatten() {
|
||||||
//if let Ok(line) = resline {
|
//if let Ok(line) = resline {
|
||||||
@ -40,8 +53,16 @@ fn main() {
|
|||||||
let file_read = start_time.elapsed();
|
let file_read = start_time.elapsed();
|
||||||
let start_shortcut = Instant::now();
|
let start_shortcut = Instant::now();
|
||||||
|
|
||||||
println!("parsed {} statements after {}ms", statements.len(), file_read.as_millis());
|
if verbose {
|
||||||
|
println!(
|
||||||
|
"parsed {} statements after {}ms",
|
||||||
|
statements.len(),
|
||||||
|
file_read.as_millis()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if !statements.is_empty() && !ac.is_empty() {
|
if !statements.is_empty() && !ac.is_empty() {
|
||||||
|
if matches.is_present("fast") {
|
||||||
let mut my_adf = Adf::new();
|
let mut my_adf = Adf::new();
|
||||||
my_adf.init_statements(statements.iter().map(AsRef::as_ref).collect());
|
my_adf.init_statements(statements.iter().map(AsRef::as_ref).collect());
|
||||||
for (s, c) in ac.clone() {
|
for (s, c) in ac.clone() {
|
||||||
@ -49,39 +70,26 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let result = my_adf.grounded();
|
let result = my_adf.grounded();
|
||||||
//println!("{:?}",result);
|
|
||||||
// for (p, s) in statements.iter().enumerate() {
|
|
||||||
// match result[p] {
|
|
||||||
// 0 => print!("f("),
|
|
||||||
// 1 => print!("t("),
|
|
||||||
// _ => print!("u("),
|
|
||||||
// }
|
|
||||||
// println!("{}) ", *s);
|
|
||||||
// }
|
|
||||||
print_interpretation(result);
|
print_interpretation(result);
|
||||||
|
if verbose {
|
||||||
println!("finished after {}ms", start_shortcut.elapsed().as_millis());
|
println!("finished after {}ms", start_shortcut.elapsed().as_millis());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
let start_fp = Instant::now();
|
let start_fp = Instant::now();
|
||||||
|
|
||||||
let mut my_adf2 = Adf::default();
|
let mut my_adf = Adf::default();
|
||||||
my_adf2.init_statements(statements.iter().map(AsRef::as_ref).collect());
|
my_adf.init_statements(statements.iter().map(AsRef::as_ref).collect());
|
||||||
for (s, c) in ac.clone() {
|
for (s, c) in ac.clone() {
|
||||||
my_adf2.add_ac(s.as_str(), c.as_str());
|
my_adf.add_ac(s.as_str(), c.as_str());
|
||||||
}
|
}
|
||||||
let empty_int = my_adf2.cur_interpretation().to_owned();
|
let empty_int = my_adf.cur_interpretation();
|
||||||
let result2 = my_adf2.to_fixpoint(empty_int.as_ref()).unwrap();
|
let result = my_adf.compute_fixpoint(empty_int.as_ref()).unwrap();
|
||||||
// for (p, s) in statements.iter().enumerate() {
|
|
||||||
// match result2[p] {
|
|
||||||
// 0 => print!("f("),
|
|
||||||
// 1 => print!("t("),
|
|
||||||
// _ => print!("u("),
|
|
||||||
// }
|
|
||||||
// println!("{}) ", *s);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
print_interpretation(result);
|
||||||
print_interpretation(result2);
|
if verbose {
|
||||||
println!("finished after {}ms", start_fp.elapsed().as_millis());
|
println!("finished after {}ms", start_fp.elapsed().as_millis());
|
||||||
|
}
|
||||||
|
}
|
||||||
// optional test of complete extensions
|
// optional test of complete extensions
|
||||||
// let mut my_adf3 = Adf::default();
|
// let mut my_adf3 = Adf::default();
|
||||||
// my_adf3.init_statements(statements.iter().map(AsRef::as_ref).collect());
|
// my_adf3.init_statements(statements.iter().map(AsRef::as_ref).collect());
|
||||||
@ -111,12 +119,15 @@ fn print_interpretation(interpretation: Vec<usize>) {
|
|||||||
match *it {
|
match *it {
|
||||||
0 => print!("f"),
|
0 => print!("f"),
|
||||||
1 => print!("t"),
|
1 => print!("t"),
|
||||||
_ => {print!("u");stable=false},
|
_ => {
|
||||||
|
print!("u");
|
||||||
|
stable = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if stable {
|
if stable {
|
||||||
println!(" stm");
|
println!(" stm");
|
||||||
} else {
|
} else {
|
||||||
println!("");
|
println!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,18 @@
|
|||||||
use std::usize;
|
use std::usize;
|
||||||
|
|
||||||
|
/// constant which represents the bottom concept (i.e. inconsistency)
|
||||||
pub const BDD_BOT: usize = 0;
|
pub const BDD_BOT: usize = 0;
|
||||||
|
/// constant which represents the top concept (i.e. universal truth)
|
||||||
pub const BDD_TOP: usize = 1;
|
pub const BDD_TOP: usize = 1;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// Convenience type substition
|
||||||
type Term = usize;
|
type Term = usize;
|
||||||
|
|
||||||
|
/// Represents a node in the BDD
|
||||||
|
///
|
||||||
|
///
|
||||||
#[derive(Eq, PartialEq, Hash, Clone, Copy)]
|
#[derive(Eq, PartialEq, Hash, Clone, Copy)]
|
||||||
pub (crate) struct BddNode {
|
pub (crate) struct BddNode {
|
||||||
var: usize,
|
var: usize,
|
||||||
@ -73,6 +79,7 @@ impl Bdd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn variable(&mut self, var: usize) -> Term {
|
pub fn variable(&mut self, var: usize) -> Term {
|
||||||
self.create_node(var, BDD_BOT, BDD_TOP)
|
self.create_node(var, BDD_BOT, BDD_TOP)
|
||||||
}
|
}
|
||||||
@ -87,7 +94,7 @@ impl Bdd {
|
|||||||
|
|
||||||
pub fn restrict(&mut self, subtree: Term, var: usize, val: bool) -> Term {
|
pub fn restrict(&mut self, subtree: Term, var: usize, val: bool) -> Term {
|
||||||
let treenode = self.nodes[subtree];
|
let treenode = self.nodes[subtree];
|
||||||
#[allow(clippy::clippy::collapsible_else_if)] // Better readabilty of the if/then/else structure of the algorithm
|
#[allow(clippy::collapsible_else_if)] // Better readabilty of the if/then/else structure of the algorithm
|
||||||
if treenode.var > var || treenode.var == usize::MAX {
|
if treenode.var > var || treenode.var == usize::MAX {
|
||||||
subtree
|
subtree
|
||||||
} else if treenode.var < var {
|
} else if treenode.var < var {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user