boolean_circuit/file_formats/
dimacs.rs1use std::{
2 collections::HashMap,
3 io::{BufRead, Read, Write},
4};
5
6use itertools::Itertools;
7
8use crate::Literal;
9
10pub fn to_dimacs(mut w: impl Write, cnf: &[Vec<Literal>]) -> HashMap<u32, String> {
13 let var_map = var_mapping(cnf);
14 writeln!(w, "p cnf {} {}", var_map.len(), cnf.len()).unwrap();
15
16 for clause in cnf {
17 for literal in clause {
18 if literal.is_negated() {
19 write!(w, "-").unwrap();
20 }
21 write!(w, "{} ", var_map[literal.var()]).unwrap();
22 }
23 writeln!(w, "0").unwrap();
24 }
25 var_map
27 .into_iter()
28 .map(|(name, id)| (id, name.to_string()))
29 .collect()
30}
31
32fn var_mapping(cnf: &[Vec<Literal>]) -> HashMap<String, u32> {
33 cnf.iter()
34 .flatten()
35 .map(|literal| literal.var())
36 .unique()
37 .enumerate()
38 .map(|(i, var)| (var.to_string(), (i + 1) as u32))
39 .collect()
40}
41
42pub fn from_dimacs(i: impl Read) -> Result<Vec<Vec<Literal>>, String> {
45 let mut reader = std::io::BufReader::new(i);
46 let mut header = Default::default();
47 reader.read_line(&mut header).map_err(|e| e.to_string())?;
48 if !header.starts_with("p cnf") {
49 return Err("Invalid DIMACS header, expected to start with 'p cnf'.".to_string());
50 }
51 reader
53 .lines()
54 .map(|line| {
55 let line = line.map_err(|e| e.to_string())?;
56 let mut items = line.split_whitespace();
57 if items.next_back() != Some("0") {
58 return Err("Invalid DIMACS clause, expected to end with '0'.".to_string());
59 }
60 items
61 .map(|item| {
62 if item == "0" {
63 Err(format!(
64 "Invalid DIMACS clause, inner literal {item} not allowed."
65 ))
66 } else if let Some(name) = item.strip_prefix('-') {
67 Ok(!Literal::from(name))
68 } else {
69 Ok(Literal::from(item))
70 }
71 })
72 .collect::<Result<Vec<_>, String>>()
73 })
74 .collect::<Result<Vec<_>, String>>()
75}
76
77#[cfg(test)]
78mod test {
79 use super::*;
80
81 #[test]
82 fn test_var_mapping() {
83 let cnf = vec![vec!["a".into(), "b".into(), !Literal::from("a")]];
84 let var_map = var_mapping(&cnf);
85 assert_eq!(var_map.len(), 2);
86 assert_eq!(var_map["a"], 1);
87 assert_eq!(var_map["b"], 2);
88 }
89
90 #[test]
91 fn test_to_dimacs() {
92 let x: Literal = "x".into();
93 let y: Literal = "y".into();
94 let z: Literal = "z".into();
95
96 let cnf = vec![
97 vec![x.clone(), y.clone()],
98 vec![!x.clone(), z.clone()],
99 vec![y.clone(), z.clone()],
100 vec![],
101 ];
102 let mut buf = Vec::new();
103 let var_map = to_dimacs(&mut buf, &cnf);
104 let expected = "p cnf 3 4\n1 2 0\n-1 3 0\n2 3 0\n0\n";
105 assert_eq!(String::from_utf8(buf).unwrap(), expected);
106 assert_eq!(var_map.len(), 3);
107 assert_eq!(var_map[&1], "x");
108 assert_eq!(var_map[&2], "y");
109 assert_eq!(var_map[&3], "z");
110 }
111
112 #[test]
113 fn test_from_dimacs() {
114 let input = "p cnf 2 4\n1 2 0\n-2 0\n1 0\n0\n";
115 let cnf = from_dimacs(input.as_bytes()).unwrap();
116 assert_eq!(
117 cnf,
118 vec![
119 vec![Literal::from("1"), Literal::from("2")],
120 vec![!Literal::from("2")],
121 vec![Literal::from("1")],
122 vec![],
123 ]
124 );
125 let mut buf = Vec::new();
126 to_dimacs(&mut buf, &cnf);
127 assert_eq!(String::from_utf8(buf).unwrap(), input);
128 }
129}