pumpkin_solver/engine/proof/
mod.rsmod dimacs;
mod proof_literals;
use std::fs::File;
use std::num::NonZero;
use std::num::NonZeroU64;
use std::path::Path;
use std::path::PathBuf;
use drcp_format::writer::ProofWriter;
pub use drcp_format::Format;
use self::dimacs::DimacsProof;
use self::proof_literals::ProofLiterals;
use super::variables::Literal;
use super::VariableLiteralMappings;
use crate::variable_names::VariableNames;
#[cfg(doc)]
use crate::Solver;
#[derive(Debug, Default)]
pub struct ProofLog {
internal_proof: Option<ProofImpl>,
}
const DUMMY_STEP_ID: NonZeroU64 = unsafe { NonZeroU64::new_unchecked(1) };
impl ProofLog {
pub fn cp(
file_path: &Path,
format: Format,
log_inferences: bool,
log_hints: bool,
) -> std::io::Result<ProofLog> {
let definitions_path = file_path.with_extension("lits");
let file = File::create(file_path)?;
let writer = ProofWriter::new(format, file, ProofLiterals::default());
Ok(ProofLog {
internal_proof: Some(ProofImpl::CpProof {
writer,
log_inferences,
definitions_path,
propagation_order_hint: if log_hints { Some(vec![]) } else { None },
}),
})
}
pub fn dimacs(file_path: &Path) -> std::io::Result<ProofLog> {
let file = File::create(file_path)?;
Ok(ProofLog {
internal_proof: Some(ProofImpl::DimacsProof(DimacsProof::new(file))),
})
}
pub(crate) fn log_inference(
&mut self,
constraint_tag: Option<NonZero<u32>>,
premises: impl IntoIterator<Item = Literal>,
propagated: Literal,
) -> std::io::Result<NonZeroU64> {
let Some(ProofImpl::CpProof {
writer,
log_inferences: true,
propagation_order_hint,
..
}) = self.internal_proof.as_mut()
else {
return Ok(DUMMY_STEP_ID);
};
let id = writer.log_inference(constraint_tag, None, premises, propagated)?;
if let Some(hints) = propagation_order_hint {
hints.push(id);
}
Ok(id)
}
pub(crate) fn add_propagation(&mut self, step_id: NonZeroU64) {
let Some(ProofImpl::CpProof {
propagation_order_hint: Some(ref mut hints),
..
}) = self.internal_proof.as_mut()
else {
return;
};
hints.push(step_id);
}
pub(crate) fn log_learned_clause(
&mut self,
literals: impl IntoIterator<Item = Literal>,
) -> std::io::Result<NonZeroU64> {
match &mut self.internal_proof {
Some(ProofImpl::CpProof {
writer,
propagation_order_hint,
..
}) => {
let propagation_hints = propagation_order_hint
.as_ref()
.map(|vec| vec.iter().rev().copied());
let id = writer.log_nogood_clause(literals, propagation_hints)?;
if let Some(hints) = propagation_order_hint.as_mut() {
hints.clear();
}
Ok(id)
}
Some(ProofImpl::DimacsProof(writer)) => writer.learned_clause(literals),
None => Ok(DUMMY_STEP_ID),
}
}
pub(crate) fn unsat(
self,
variable_names: &VariableNames,
variable_literal_mapping: &VariableLiteralMappings,
) -> std::io::Result<()> {
match self.internal_proof {
Some(ProofImpl::CpProof {
writer,
definitions_path,
..
}) => {
let literals = writer.unsat()?;
let file = File::create(definitions_path)?;
literals.write(file, variable_names, variable_literal_mapping)
}
Some(ProofImpl::DimacsProof(mut writer)) => {
writer.learned_clause(std::iter::empty()).map(|_| ())
}
None => Ok(()),
}
}
pub(crate) fn optimal(
self,
objective_bound: Literal,
variable_names: &VariableNames,
variable_literal_mapping: &VariableLiteralMappings,
) -> std::io::Result<()> {
match self.internal_proof {
Some(ProofImpl::CpProof {
writer,
definitions_path,
..
}) => {
let literals = writer.optimal(objective_bound)?;
let file = File::create(definitions_path)?;
literals.write(file, variable_names, variable_literal_mapping)
}
Some(ProofImpl::DimacsProof(_)) => {
panic!("Cannot conclude optimality in DIMACS proof")
}
None => Ok(()),
}
}
pub(crate) fn is_logging_inferences(&self) -> bool {
matches!(
self.internal_proof,
Some(ProofImpl::CpProof {
log_inferences: true,
..
})
)
}
}
#[derive(Debug)]
enum ProofImpl {
CpProof {
writer: ProofWriter<File, ProofLiterals>,
log_inferences: bool,
definitions_path: PathBuf,
propagation_order_hint: Option<Vec<NonZeroU64>>,
},
DimacsProof(DimacsProof<File>),
}