| //! Variable metadata. |
| use partial_ref::{partial, PartialRef}; |
| use rustc_hash::FxHashSet as HashSet; |
| |
| use varisat_formula::Var; |
| |
| use crate::{ |
| context::{parts::*, Context}, |
| processing::{process_step, CheckedProofStep, CheckedSamplingMode, CheckedUserVar}, |
| CheckerError, |
| }; |
| |
| /// Data for each literal. |
| #[derive(Clone, Default)] |
| pub struct LitData { |
| pub clause_count: usize, |
| } |
| |
| #[derive(Copy, Clone, PartialEq, Eq)] |
| pub enum SamplingMode { |
| Sample, |
| Witness, |
| Hide, |
| } |
| |
| /// Data for each variable. |
| #[derive(Clone)] |
| pub struct VarData { |
| pub user_var: Option<Var>, |
| pub sampling_mode: SamplingMode, |
| } |
| |
| impl Default for VarData { |
| fn default() -> VarData { |
| VarData { |
| user_var: None, |
| sampling_mode: SamplingMode::Sample, |
| } |
| } |
| } |
| |
| #[derive(Default)] |
| pub struct Variables { |
| /// Information about literals in the current formula. |
| pub lit_data: Vec<LitData>, |
| /// Information about variables in the current formula. |
| pub var_data: Vec<VarData>, |
| /// User var names in use. |
| /// |
| /// This is used to check for colliding mappings which are not allowed. |
| used_user_vars: HashSet<Var>, |
| } |
| |
| /// Check that var is a sampling user var and create new variables as necessary. |
| pub fn ensure_sampling_var( |
| mut ctx: partial!(Context, mut ClausesP, mut VariablesP, CheckerStateP), |
| var: Var, |
| ) -> Result<(), CheckerError> { |
| ensure_var(ctx.borrow(), var); |
| |
| let variables = ctx.part_mut(VariablesP); |
| |
| if variables.var_data[var.index()].sampling_mode != SamplingMode::Sample { |
| return Err(CheckerError::check_failed( |
| ctx.part(CheckerStateP).step, |
| format!("variable {:?} is not a sampling variable", var), |
| )); |
| } |
| Ok(()) |
| } |
| |
| /// Ensure that a variable is present. |
| pub fn ensure_var(mut ctx: partial!(Context, mut ClausesP, mut VariablesP), var: Var) { |
| let (variables, mut ctx) = ctx.split_part_mut(VariablesP); |
| |
| if variables.var_data.len() <= var.index() { |
| variables |
| .var_data |
| .resize(var.index() + 1, VarData::default()); |
| variables |
| .lit_data |
| .resize((var.index() + 1) * 2, LitData::default()); |
| ctx.part_mut(ClausesP) |
| .unit_clauses |
| .resize(var.index() + 1, None); |
| } |
| } |
| |
| /// Add a user/global var mapping. |
| pub fn add_user_mapping<'a>( |
| mut ctx: partial!(Context<'a>, mut ClausesP, mut ProcessingP<'a>, mut VariablesP, CheckerStateP), |
| global_var: Var, |
| user_var: Var, |
| ) -> Result<(), CheckerError> { |
| ensure_var(ctx.borrow(), global_var); |
| |
| let mut ctx_in = ctx; |
| let (variables, ctx) = ctx_in.split_part_mut(VariablesP); |
| |
| // TODO will the first check cause problems when observing solver added variables? |
| // That check is required for the workaround in CheckerData's user from proof method |
| if user_var.index() >= variables.var_data.len() || variables.used_user_vars.contains(&user_var) |
| { |
| return Err(CheckerError::check_failed( |
| ctx.part(CheckerStateP).step, |
| format!("user name {:?} used for two different variables", user_var), |
| )); |
| } |
| |
| let var_data = &mut variables.var_data[global_var.index()]; |
| |
| // sampling_mode will be Witness for a newly observed internal variable and Sample for a a |
| // fresh variable |
| if var_data.sampling_mode == SamplingMode::Hide { |
| return Err(CheckerError::check_failed( |
| ctx.part(CheckerStateP).step, |
| format!( |
| "user name added to variable {:?} which is still hidden", |
| global_var |
| ), |
| )); |
| } |
| |
| if var_data.user_var.is_some() { |
| return Err(CheckerError::check_failed( |
| ctx.part(CheckerStateP).step, |
| format!("change of user name for in use varible {:?}", global_var), |
| )); |
| } |
| |
| var_data.user_var = Some(user_var); |
| |
| variables.used_user_vars.insert(user_var); |
| |
| let sampling_mode = if var_data.sampling_mode == SamplingMode::Witness { |
| CheckedSamplingMode::Witness |
| } else { |
| CheckedSamplingMode::Sample |
| }; |
| |
| process_step( |
| ctx_in.borrow(), |
| &CheckedProofStep::UserVar { |
| var: global_var, |
| user_var: Some(CheckedUserVar { |
| user_var, |
| sampling_mode, |
| new_var: true, |
| }), |
| }, |
| )?; |
| |
| Ok(()) |
| } |
| |
| /// Remove a user/global var mapping. |
| pub fn remove_user_mapping<'a>( |
| mut ctx: partial!(Context<'a>, mut ClausesP, mut ProcessingP<'a>, mut VariablesP, CheckerStateP), |
| global_var: Var, |
| ) -> Result<(), CheckerError> { |
| ensure_var(ctx.borrow(), global_var); |
| |
| let variables = ctx.part_mut(VariablesP); |
| |
| let var_data = &variables.var_data[global_var.index()]; |
| |
| // We process this step before removing the mapping, so the processor can still look up the |
| // mapping. |
| |
| if var_data.user_var.is_some() { |
| process_step( |
| ctx.borrow(), |
| &CheckedProofStep::UserVar { |
| var: global_var, |
| user_var: None, |
| }, |
| )?; |
| } else { |
| return Err(CheckerError::check_failed( |
| ctx.part(CheckerStateP).step, |
| format!("no user name to remove for variable {:?}", global_var), |
| )); |
| } |
| |
| let variables = ctx.part_mut(VariablesP); |
| |
| let var_data = &mut variables.var_data[global_var.index()]; |
| if let Some(user_var) = var_data.user_var { |
| variables.used_user_vars.remove(&user_var); |
| var_data.user_var = None; |
| var_data.sampling_mode = SamplingMode::Hide; |
| } |
| |
| Ok(()) |
| } |