check_solution/
check-solution.rs1use std::{io, path::PathBuf};
6
7use clap::{Parser, ValueEnum};
8use rustsat::{
9 instances::{
10 fio::{self, opb::Options as OpbOptions},
11 MultiOptInstance, OptInstance, SatInstance,
12 },
13 types::Assignment,
14};
15
16#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
17pub enum FileFormat {
18 Infer,
27 Cnf,
29 Wcnf,
31 Mcnf,
33 Opb,
35}
36
37#[derive(Parser)]
38#[command(author, version, about, long_about = None)]
39struct Args {
40 instance: PathBuf,
42 solution: Option<PathBuf>,
45 #[arg(long, value_enum, default_value_t = FileFormat::Infer)]
48 file_format: FileFormat,
49 #[arg(long, default_value_t = 1)]
51 opb_first_var_idx: u32,
52}
53
54fn main() -> anyhow::Result<()> {
55 let args = Args::parse();
56 let opb_opts = OpbOptions {
57 first_var_idx: args.opb_first_var_idx,
58 ..OpbOptions::default()
59 };
60 let (constrs, objs) = parse_instance(args.instance, args.file_format, opb_opts)?.decompose();
61
62 let mut reader = if let Some(solution) = args.solution {
63 fio::open_compressed_uncompressed_read(solution)?
64 } else {
65 Box::new(io::BufReader::new(io::stdin()))
66 };
67
68 let mut sol = Assignment::default();
69 loop {
70 let mut buf = String::new();
71 let read = reader.read_line(&mut buf)?;
72 if read == 0 {
73 break;
74 }
75 if buf.starts_with('v') {
76 sol.extend_from_vline(&buf)?;
77 }
78 }
79
80 if let Some(constr) = constrs.unsat_constraint(&sol) {
81 println!("unsatisfied constraint: {constr}");
82 std::process::exit(1);
83 }
84 print!("objective values: ");
85 for i in 0..objs.len() {
86 if i < objs.len() - 1 {
87 print!("{}, ", objs[i].evaluate(&sol))
88 } else {
89 print!("{}", objs[i].evaluate(&sol));
90 }
91 }
92 println!();
93 Ok(())
94}
95
96macro_rules! is_one_of {
97 ($a:expr, $($b:expr),*) => {
98 $( $a == $b || )* false
99 }
100}
101
102fn parse_instance(
103 inst_path: PathBuf,
104 file_format: FileFormat,
105 opb_opts: fio::opb::Options,
106) -> anyhow::Result<MultiOptInstance> {
107 match file_format {
108 FileFormat::Infer => {
109 if let Some(ext) = inst_path.extension() {
110 let path_without_compr = inst_path.with_extension("");
111 let ext = if is_one_of!(ext, "gz", "bz2", "xz") {
112 match path_without_compr.extension() {
114 Some(ext) => ext,
115 None => anyhow::bail!("no file extension after compression extension"),
116 }
117 } else {
118 ext
119 };
120 let inst = if is_one_of!(ext, "cnf", "dimacs") {
121 let inst = SatInstance::from_dimacs_path(inst_path)?;
122 MultiOptInstance::compose(inst, vec![])
123 } else if is_one_of!(ext, "wcnf") {
124 let (inst, obj) = OptInstance::from_dimacs_path(inst_path)?.decompose();
125 MultiOptInstance::compose(inst, vec![obj])
126 } else if is_one_of!(ext, "mcnf") {
127 MultiOptInstance::from_dimacs_path(inst_path)?
128 } else if is_one_of!(ext, "opb", "mopb", "pbmo") {
129 MultiOptInstance::from_opb_path(inst_path, opb_opts)?
130 } else {
131 anyhow::bail!("unknown file extension")
132 };
133 Ok(inst)
134 } else {
135 anyhow::bail!("no file extension")
136 }
137 }
138 FileFormat::Cnf => {
139 let inst = SatInstance::from_dimacs_path(inst_path)?;
140 Ok(MultiOptInstance::compose(inst, vec![]))
141 }
142 FileFormat::Wcnf => {
143 let (inst, obj) = OptInstance::from_dimacs_path(inst_path)?.decompose();
144 Ok(MultiOptInstance::compose(inst, vec![obj]))
145 }
146 FileFormat::Mcnf => MultiOptInstance::from_dimacs_path(inst_path),
147 FileFormat::Opb => MultiOptInstance::from_opb_path(inst_path, opb_opts),
148 }
149}