Program Listing for File solver.cpp¶
↰ Return to documentation for file (src/solver.cpp
)
#include <deque>
#include <string>
#include <utility>
#include <vector>
#include <sstream>
#include <unordered_map>
#include "primitive_types.h"
#include "solver.h"
//#include "top_tree_sampler.h"
#include "solver_config.h"
#include "sampler_tools.h"
StopWatch::StopWatch() {
interval_length_.tv_sec = 60;
gettimeofday(&last_interval_start_, nullptr);
start_time_ = stop_time_ = last_interval_start_;
}
timeval StopWatch::getElapsedTime() {
timeval other_time = stop_time_;
if (stop_time_.tv_sec == start_time_.tv_sec
&& stop_time_.tv_usec == start_time_.tv_usec)
gettimeofday(&other_time, nullptr);
long int ad = 0;
unsigned int bd = 0;
if (other_time.tv_usec < start_time_.tv_usec) {
ad = 1;
bd = 1000000;
}
timeval r = (struct timeval) {0};
r.tv_sec = other_time.tv_sec - ad - start_time_.tv_sec;
r.tv_usec = other_time.tv_usec + bd - start_time_.tv_usec;
return r;
}
bool Solver::simplePreProcess() {
if (!config_.perform_pre_processing)
return true;
assert(literal_stack_.empty());
// BEGIN process unit clauses
for (auto lit : unit_clauses_)
setLiteralIfFree(lit);
// END process unit clauses
VariableIndex start_ofs = 0;
bool succeeded = BCP(start_ofs);
if (succeeded)
succeeded &= prepFailedLiteralTest();
// ToDo major slowdown possible without HardWireAndCompact. Need to make a different version.
// if (succeeded)
// HardWireAndCompact();
return succeeded;
}
bool Solver::prepFailedLiteralTest() {
unsigned long last_size;
do {
last_size = literal_stack_.size();
for (unsigned v = 1; v < variables_.size(); v++) {
if (isActive(v)) {
unsigned long sz = literal_stack_.size();
setLiteralIfFree(LiteralID(v, true));
bool res = BCP(sz);
while (literal_stack_.size() > sz) {
unSet(literal_stack_.back());
literal_stack_.pop_back();
}
if (!res) {
sz = literal_stack_.size();
setLiteralIfFree(LiteralID(v, false));
if (!BCP(sz))
return false;
} else {
sz = literal_stack_.size();
setLiteralIfFree(LiteralID(v, false));
bool resb = BCP(sz);
while (literal_stack_.size() > sz) {
unSet(literal_stack_.back());
literal_stack_.pop_back();
}
if (!resb) {
sz = literal_stack_.size();
setLiteralIfFree(LiteralID(v, true));
if (!BCP(sz))
return false;
}
}
}
}
} while (literal_stack_.size() > last_size);
return true;
}
//void Solver::HardWireAndCompact() {
// compactClauses();
// compactVariables();
// literal_stack_.clear();
//
// for (auto l = LiteralID(1, false); l != literals_.end_lit(); l.inc()) {
// literal(l).activity_score_ = literal(l).binary_links_.size() - 1;
// literal(l).activity_score_ += occurrence_lists_[l].size();
// }
//
// statistics_.num_unit_clauses_ = unit_clauses_.size();
//
// statistics_.num_original_binary_clauses_ = statistics_.num_binary_clauses_;
// statistics_.num_original_unit_clauses_ = statistics_.num_unit_clauses_ = unit_clauses_.size();
// initStack(num_variables());
// original_lit_pool_size_ = literal_pool_.size();
//}
void Solver::sample_models() {
if (!createfromFile(statistics_.input_file_)) {
PrintFinalSamplerResults();
return;
}
SampleAssignment::set_num_var(statistics_.num_variables_);
Solver temp_solver(config_, statistics_);
temp_solver.createfromFile(statistics_.input_file_);
LinkConfigAndStatistics();
reservoir_sample_models(PartialAssignment(), temp_solver);
ReportSharpSatResults();
PrintFinalSamplerResults();
}
void Solver::reservoir_sample_models(const PartialAssignment &partial_assn, Solver &temp_solver) {
if (!InitializeSolverAndPreprocess(partial_assn))
return;
PerformInitialSampling();
statistics_.sampler_pass_1_time_ = sampler_stopwatch_.getElapsedSeconds();
// The UNSAT case can be reached in two different ways either by the
// preprocessor or by running sharpSAT's main flow.
if (statistics_.final_solution_count_ > 0 && config_.perform_two_pass_sampling_)
FillPartialAssignments(temp_solver);
// Print the final results after all sampling then quit.
sampler_stopwatch_.stop();
statistics_.sampler_time_elapsed_ = sampler_stopwatch_.getElapsedSeconds();
statistics_.sampler_pass_2_time_ = statistics_.sampler_time_elapsed_
- statistics_.sampler_pass_1_time_;
}
void Solver::PrintFinalSamplerResults() {
if (!config_.quiet)
std::cout << "\nTotal Sampler Execution Time: " << statistics_.sampler_time_elapsed_ << "s\n\n";
// Print any helper messages
if (!config_.quiet && statistics_.final_solution_count_ < config_.num_samples_) {
std::stringstream ss;
ss << "Only " << statistics_.final_solution_count_ << " models exist but "
<< config_.num_samples_ << " samples were requested." << std::endl;
PrintWarning(ss.str());
}
// Export to a file and the console.
if (!config_.quiet)
final_samples_.exportFinal(std::cout, statistics_, config_);
if (!config_.samples_output_file.empty()) {
std::ofstream samples_file(config_.samples_output_file);
final_samples_.exportFinal(samples_file, statistics_, config_);
samples_file.close();
}
// // ToDo Remove Debug Verification
// bool valid_samples = final_samples_.VerifySolutions(statistics_.input_file_,
// config_.skip_partial_assignment_fill);
// if (!valid_samples)
// ExitWithError("INVALID SAMPLES", EX_SOFTWARE);
// if (!final_samples_.IsComplete())
// ExitWithError("INCOMPLETE SAMPLES", EX_SOFTWARE);
}
void Solver::PerformInitialSampling() {
if (!config_.perform_two_pass_sampling_)
config_.EnableSampleCaching();
if (!config_.quiet) {
if (config_.perform_two_pass_sampling_)
std::cout << "STAGE #1: ";
std::cout << "Build the initial partial assignments" << std::endl;
}
// If the solver has not been shown to be UNSAT by the preprocess,
// run the basic sampler and model count
statistics_.exit_state_ = countSAT();
unused_vars_ = stack_.top().emancipated_vars();
statistics_.set_final_solution_count(stack_.top().getTotalModelCount());
statistics_.num_long_conflict_clauses_ = num_conflict_clauses();
// Clean up the component so it is not an issue during pass two
if (stack_.back().isComponentSplit())
stack_.back().unsetAsComponentSplit();
// If UNSAT, report it and exit.
if (statistics_.final_solution_count_ == 0)
return;
if (unused_vars_.empty()) {
if (config_.verbose)
std::cout << "No unused variables. Continuing..." << std::endl;
} else {
if (config_.verbose)
std::cout << "Number of unused variables: " << unused_vars_.size() << std::endl;
samples_stack_.back().AddEmancipatedVars(unused_vars_);
}
final_samples_ = samples_stack_.back();
assert(IsEndSamplesStackSizeValid());
assert(final_samples_.model_count() == statistics_.final_solution_count_);
if (!config_.quiet) {
if (config_.perform_two_pass_sampling_)
std::cout << "STAGE #1: ";
std::cout << "COMPLETED building initial partial assignments" << std::endl;
}
}
inline void Solver::FillPartialAssignments(Solver &temp_solver) {
if (config_.skip_partial_assignment_fill)
return;
if (!config_.quiet)
std::cout << "STAGE #2 - Filling in partial assignments..." << std::endl;
SamplesManager updated_samples = SamplesManager(config_.num_samples_, config_);
// Free the memory to be used by the descendant solver's cache
// unit_clauses_.clear();
comp_manager_.initialize(literals_, literal_pool_, config_.quiet);
deleteConflictClauses(true);
// Group the samples so that same cache ids are tested together
std::vector<std::pair<std::vector<CacheEntryID>, long>> grouped_samples;
std::vector<ListOfSamples> list_elements;
for (auto &sample : final_samples_.samples()) {
auto new_key = sample.cache_comp_ids();
SampleSize i = 0;
for (i = 0; i < grouped_samples.size(); i++)
if (grouped_samples[i].first == new_key)
break;
if (i < grouped_samples.size()) {
grouped_samples[i].second += sample.sample_count();
list_elements[i].emplace_back(sample);
} else {
grouped_samples.emplace_back(std::pair<std::vector<CacheEntryID>, long>(new_key,
sample.sample_count()));
list_elements.emplace_back();
list_elements.back().emplace_back(sample);
}
}
SampleSize group_cnt = 0, tot_count = 0;
ListOfSamples * samples_list;
auto sample_itr = final_samples_.samples().begin();
while (sample_itr != final_samples_.samples().end()) {
auto key = sample_itr->cache_comp_ids();
long new_sample_count = 0;
SampleSize group_idx;
for (group_idx = 0; group_idx < grouped_samples.size(); group_idx++) {
if (grouped_samples[group_idx].first == key) {
new_sample_count = grouped_samples[group_idx].second;
samples_list = &list_elements[group_idx];
break;
}
}
if (new_sample_count < 0) {
if (sample_itr->cache_comp_ids().empty())
++sample_itr;
else
sample_itr = final_samples_.samples().erase(sample_itr);
continue;
}
grouped_samples[group_idx].second = -new_sample_count;
tot_count += new_sample_count;
++group_cnt;
if (sample_itr->IsComplete()) {
++sample_itr;
statistics_.numb_second_pass_vars_.push_back(0);
if (!config_.quiet)
std::cout << "Sample #" << group_cnt << " of " << grouped_samples.size()
<< " is already a complete assignment. Continuing..." << std::endl;
continue;
}
// If the assignment is complete, then go to next sample
VariableIndex num_unset_vars = sample_itr->num_unset_vars();
statistics_.numb_second_pass_vars_.push_back(num_unset_vars);
assert(num_unset_vars < statistics_.num_variables_);
if (!config_.quiet)
std::cout << "Completing sample #" << group_cnt << " of " << grouped_samples.size()
<< " which has " << num_unset_vars << " variable"
<< ((num_unset_vars == 1) ? "" : "s") << " unset and " << new_sample_count
<< " sample" << ((new_sample_count == 1) ? "" : "s") << "." << std::endl;
// Build an intermediary solver
Solver new_solver = temp_solver;
new_solver.config_.quiet = new_solver.config_.quiet || !config_.verbose;
PartialAssignment partial_assn;
sample_itr->GetPartialAssignment(partial_assn);
new_solver.config_.num_samples_ = static_cast<SampleSize>(new_sample_count);
if (new_solver.config_.num_samples_ == 1)
new_solver.config_.EnableSampleCaching();
new_solver.reservoir_sample_models(partial_assn, temp_solver);
assert(new_solver.IsEndSamplesStackSizeValid());
assert(new_solver.stack_.top_const().getTotalModelCount()
== new_solver.final_samples_.model_count());
assert(new_solver.stack_.top_const().getTotalModelCount() > 0);
assert(new_solver.final_samples_.IsComplete());
// Take the updated sample and store it.
if (samples_list->size() > 1)
new_solver.final_samples_.TransferVariableAssignments(*samples_list);
// Insert the new elements into the samples and delete the old one.
final_samples_.samples().splice(sample_itr, new_solver.final_samples_.samples());
sample_itr = final_samples_.samples().erase(sample_itr);
}
if (tot_count != config_.num_samples_)
ExitWithError("Not all samples tested", EX_SOFTWARE);
if (!config_.quiet)
std::cout << "STAGE #2 - COMPLETE" << std::endl;
// Relink the configuration and statistics objects which may have been unlinked during the
// dependent tasks.
LinkConfigAndStatistics();
}
bool Solver::InitializeSolverAndPreprocess(const PartialAssignment &partial_assn) {
statistics_.set_final_solution_count(0); // Zero out initial model count
applyPartialAssignment(partial_assn);
initStack(num_variables());
if (!config_.quiet) {
if (!config_.perform_random_sampling_)
std::cout << "Performing Exact Model Counting..." << std::endl;
else
std::cout << "Performing Uniform Model Sampling..." << std::endl;
std::cout << "Input File: " << statistics_.input_file_ << std::endl;
if (config_.perform_random_sampling_)
std::cout << "Output File: " << config_.samples_output_file << std::endl;
}
if (!config_.quiet)
std::cout << "\nPreprocessing ..." << std::flush;
bool notfoundUNSAT = simplePreProcess();
if (!config_.quiet)
std::cout << " DONE" << std::endl;
if (!notfoundUNSAT) {
statistics_.exit_state_ = SolverExitState::SUCCESS;
statistics_.final_solution_count_ = 0;
if (!config_.quiet)
std::cout << "\nFOUND UNSAT DURING PREPROCESSING " << std::endl;
return notfoundUNSAT;
}
// If preprocessor did not find the formula to be unsatisfiable, then
// get ready for model counting
if (!config_.quiet)
statistics_.printShortFormulaInfo();
last_ccl_deletion_time_ = last_ccl_cleanup_time_ = statistics_.getTime();
violated_clause.reserve(num_variables());
comp_manager_.initialize(literals_, literal_pool_, config_.quiet);
return notfoundUNSAT;
}
void Solver::applyPartialAssignment(const PartialAssignment &partial_assn) {
// Optionally constrain the solution with unit clauses that match the partial assignment
if (partial_assn.empty())
return;
assert(partial_assn.size() == num_variables() + FIRST_VAR);
std::vector<LiteralID> literals;
literals.emplace_back();
for (VariableIndex var_num = FIRST_VAR; var_num <= num_variables(); var_num++) {
AssignmentEncoding var_assn = partial_assn[var_num];
if (var_assn != ASSN_U) {
literals[0] = LiteralID((var_assn == ASSN_F) ? (-1 * static_cast<int>(var_num))
: static_cast<int>(var_num));
statistics_.incorporateClauseData(literals);
addClause(literals);
}
}
statistics_.num_original_unit_clauses_ = statistics_.num_unit_clauses_ = unit_clauses_.size();
}
void Solver::solve(const PartialAssignment &partial_assn) {
if (createfromFile(this->statistics_.input_file_)) {
bool notfoundUNSAT = InitializeSolverAndPreprocess(partial_assn);
if (notfoundUNSAT) {
statistics_.exit_state_ = countSAT();
statistics_.set_final_solution_count(stack_.top().getTotalModelCount());
statistics_.num_long_conflict_clauses_ = num_conflict_clauses();
}
}
ReportSharpSatResults();
}
void Solver::ReportSharpSatResults() {
statistics_.sampler_time_elapsed_ = sampler_stopwatch_.getElapsedSeconds();
comp_manager_.gatherStatistics();
// if (!results_output_file.empty())
// statistics_.writeToFile(results_output_file);
if (!config_.quiet)
statistics_.printShort();
}
SolverExitState Solver::countSAT() {
SolverNextAction state = SolverNextAction::RESOLVED;
// Put the initial item on the top of the samples stack
PushNewSamplesManagerOnStack();
while (true) {
while (comp_manager_.findNextRemainingComponentOf(stack_.top(), literal_stack_,
samples_stack_.back())) {
decideLiteral();
if (sampler_stopwatch_.timeBoundBroken()) {
if (!config_.quiet)
PrintError("TIMEOUT");
exit(EXIT_TIMEOUT);
}
if (sampler_stopwatch_.interval_tick())
printOnlineStats();
while (!bcp()) {
state = resolveConflict();
if (state == SolverNextAction::BACKTRACK)
break;
}
if (state == SolverNextAction::BACKTRACK)
break;
}
state = backtrack();
if (state == SolverNextAction::EXIT)
return SolverExitState::SUCCESS;
while (state != SolverNextAction::PROCESS_COMPONENT && !bcp()) {
state = resolveConflict();
if (state == SolverNextAction::BACKTRACK) {
state = backtrack();
if (state == SolverNextAction::EXIT)
return SolverExitState::SUCCESS;
}
}
}
}
void Solver::decideLiteral() {
// Store the decision level info for determining what to do with the samples
StackLevel *prev_top = &stack_.top();
// establish another decision stack level
StackLevel newLevel = StackLevel(stack_.top().currentRemainingComponent(),
literal_stack_.size(),
comp_manager_.component_stack_size());
if (config_.perform_random_sampling_ && !stack_.top().isComponentSplit())
newLevel.configureNewLevel(stack_.top(), stack_.get_decision_level());
stack_.push_back(newLevel);
// Manage the passing of the samples
if (config_.perform_random_sampling_) {
// Each component splits necessitate formula merging so create blanks
if (prev_top->isComponentSplit()) {
if (prev_top->isFirstComponent()) {
PushNewSamplesManagerOnStack();
prev_top->markFirstComponentComplete();
}
PushNewSamplesManagerOnStack();
}
}
float max_score = -1;
VariableIndex max_score_var = 0;
// Select the variable with the highest score as the branch variable
for (auto it = comp_manager_.superComponentOf(stack_.top()).varsBegin();
*it != varsSENTINEL; it++) {
float score = scoreOf(*it);
if (score > max_score) {
max_score = score;
max_score_var = *it;
}
}
// this assert should always hold,
// if not then there is a bug in the logic of countSAT();
assert(max_score_var != 0);
// Create the literal to assign
// Select either the negated or unnegated literal depending on
// which form of the literal is most active.
LiteralID theLit(max_score_var,
literal(LiteralID(max_score_var, true)).activity_score_
> literal(LiteralID(max_score_var, false)).activity_score_);
setLiteralIfFree(theLit);
statistics_.num_decisions_++;
set_variable_depth_++;
if (set_variable_depth_ > statistics_.max_branch_var_depth_)
statistics_.max_branch_var_depth_ = set_variable_depth_;
if (statistics_.num_decisions_ % 128 == 0)
// if (statistics_.num_conflicts_ % 128 == 0)
decayActivities();
// decayActivitiesOf(comp_manager_.superComponentOf(stack_.top()));
if (config_.verbose)
std::cout << "Literal Stack Location #" << literal_stack_.size() - 1 << ": Variable #"
<< literal_stack_.back().var() << " assigned to "
<< ((literal_stack_.back().sign()) ? "TRUE" : "FALSE") << std::endl;
assert(stack_.top_const().remaining_components_ofs() <= comp_manager_.component_stack_size());
}
SolverNextAction Solver::backtrack() {
assert(stack_.top_const().remaining_components_ofs() <= comp_manager_.component_stack_size());
do {
if (stack_.top().branch_found_unsat())
comp_manager_.removeAllCachePollutionsOf(stack_.top());
else if (stack_.top().anotherCompProcessible())
return SolverNextAction::PROCESS_COMPONENT;
if (!stack_.top().isSecondBranch()) {
LiteralID aLit = TOS_decLit();
assert(stack_.get_decision_level() > 0);
if (stack_.top().isComponentSplit())
ProcessSampleComponentSplitBacktrack();
// Must close branch after processing backtracking as it will affect the flow otherwise
stack_.top().changeBranch();
reactivateTOS();
setLiteralIfFree(aLit.neg(), Antecedent(NOT_A_CLAUSE));
if (config_.verbose)
std::cout << "Literal Stack Location #" << literal_stack_.size() - 1 << ": Variable #"
<< literal_stack_.back().var() << " switched to "
<< (literal_stack_.back().sign() ? "TRUE" : "FALSE") << std::endl;
return SolverNextAction::RESOLVED;
}
if (stack_.get_decision_level() <= 0)
break; // Bottomed out the decision stack so program execution is complete.
reactivateTOS();
assert(stack_.size() >= 2);
// Merge the solution count up the stack.
(stack_.end() - 2)->includeSolution(stack_.top().getTotalModelCount());
if (config_.perform_random_sampling_)
ProcessSampleComponentSplitBacktrack();
// OTHERWISE: backtrack further
// Store the model count AND POTENTIALLY the assignment in cache.
ProcessCacheStore();
// Process top-tree sampling for max depth.
// Must be after processing component splits to ensure that an immediately dependent
// component split is cleared.
if (config_.perform_top_tree_sampling && set_variable_depth_ == config_.max_top_tree_depth_) {
// if (!stack_.top().HasNoDescendentModels() && HasNoUpperComponentSplit()) {
if (!stack_.top().HasNoDescendentModels()) {
mpz_class multiplier;
if (stack_.on_deck().isComponentSplit())
multiplier = 1;
else
multiplier = stack_.on_deck().getSamplerSolutionMultiplier();
}
}
set_variable_depth_--;
assert(set_variable_depth_ >= 0 && set_variable_depth_ <= literal_stack_.size());
stack_.pop_back();
// step to the next component not yet processed
stack_.top().nextUnprocessedComponent();
assert(stack_.top_const().remaining_components_ofs() < comp_manager_.component_stack_size()+1);
} while (stack_.get_decision_level() >= 0);
return SolverNextAction::EXIT;
}
void Solver::ProcessCacheStore() {
if (stack_.size() == 1 || stack_.top().getTotalModelCount() == 0
|| !config_.perform_sample_caching()) {
// Just store the model count as normal
comp_manager_.cacheModelCountOf(stack_.top().super_component(),
stack_.top().getTotalModelCount());
} else {
// Bundle the model count with a partial assignment
StackLevel top = stack_.top();
Component top_comp = comp_manager_.component(top.super_component());
SampleAssignment cache_sample = top.random_cache_sample();
comp_manager_.cacheModelCountAndAssignment(top.super_component(), top.getTotalModelCount(),
cache_sample, top_comp);
stack_.on_deck().set_cache_sample(cache_sample);
}
}
void Solver::ProcessSampleComponentSplitBacktrack() {
// Combine the results of a component split
if (stack_.top().isComponentSplit()) {
// Merge the component split with its parent
if (config_.verbose) {
std::stringstream ss;
ss << "Component " << stack_.top().super_component() << " merging component split "
<< "at depth " << StackLevel::componentSplitDepth() << ".";
PrintInColor(ss, COLOR_MAGENTA);
}
if (config_.store_sampled_models()) {
SampleAssignment cached_sample;
VariableIndex on_deck = samples_stack_.size() - 2;
samples_stack_[on_deck].merge(samples_stack_.back(),
stack_.top().getSamplerSolutionMultiplier(),
stack_.top().emancipated_vars(),
stack_.top().cached_comp_ids(),
stack_.top().cached_assn(), cached_sample);
samples_stack_.pop_back();
if (config_.perform_sample_caching()) {
// ToDo modify to support multiple sample caching
stack_.top().ClearCachedAssn();
stack_.top().set_cache_sample(cached_sample);
}
// assert(samples_stack_.back().VerifySolutions(statistics_.input_file_, true)); // DebugZH
}
stack_.top().unsetAsComponentSplit();
}
// Only close a component branch after testing both true and false paths.
// If there is a back to back component split, may need to close both the component
// split and the branch in a single round.
if (stack_.size() <= 1)
return;
if (stack_.top().isSecondBranch() && stack_.on_deck().isComponentSplit()) {
if (config_.verbose) {
std::stringstream ss;
ss << "Component branch #" << stack_.top().super_component() << " split end reached at depth "
<< StackLevel::componentSplitDepth() << ". Stitching the sample.";
PrintInColor(ss, COLOR_BLUE);
}
if (config_.store_sampled_models()) {
samples_stack_[samples_stack_.size() - 2].stitch(samples_stack_.back());
samples_stack_.pop_back();
// assert(samples_stack_.back().VerifySolutions(statistics_.input_file_, true)); // DebugZH
}
}
}
SolverNextAction Solver::resolveConflict() {
recordLastUIPCauses();
if (statistics_.num_clauses_learned_ - last_ccl_deletion_time_
> statistics_.clause_deletion_interval()) {
deleteConflictClauses();
last_ccl_deletion_time_ = statistics_.num_clauses_learned_;
}
if (statistics_.num_clauses_learned_ - last_ccl_cleanup_time_ > 100000) {
compactConflictLiteralPool();
last_ccl_cleanup_time_ = statistics_.num_clauses_learned_;
}
statistics_.num_conflicts_++;
assert(stack_.top_const().remaining_components_ofs() <= comp_manager_.component_stack_size());
assert(uip_clauses_.size() == 1);
// DEBUG
if (uip_clauses_.back().empty() && !config_.quiet)
std::cerr << "EMPTY CLAUSE FOUND" << std::endl;
// END DEBUG
stack_.top().mark_branch_unsat();
// BEGIN Backtracking
// maybe the other branch had some solutions
if (stack_.top().isSecondBranch()) {
return SolverNextAction::BACKTRACK;
}
Antecedent ant(NOT_A_CLAUSE);
// this has to be checked since using implicit BCP
// and checking literals there not exhaustively
// we cannot guarantee that uip_clauses_.back().front() == TOS_decLit().neg()
// this is because we might have checked a literal
// during implict BCP which has been a failed literal
// due only to assignments made at lower decision levels
if (uip_clauses_.back().front() == TOS_decLit().neg()) {
assert(TOS_decLit().neg() == uip_clauses_.back()[0]);
var(TOS_decLit().neg()).ante = addUIPConflictClause(
uip_clauses_.back());
ant = var(TOS_decLit()).ante;
}
// // RRR
// else if (var(uip_clauses_.back().front()).decision_level
// < stack_.get_decision_level()
// && assertion_level_ < stack_.get_decision_level()) {
// stack_.top().set_both_branches_unsat();
// return BACKTRACK;
// }
//
//
// // RRR
assert(stack_.get_decision_level() > 0);
assert(stack_.top_const().branch_found_unsat());
// we do not have to remove pollutions here,
// since conflicts only arise directly before
// remaining components are stored
// hence
assert(
stack_.top_const().remaining_components_ofs() == comp_manager_.component_stack_size());
stack_.top().changeBranch();
LiteralID lit = TOS_decLit();
reactivateTOS();
setLiteralIfFree(lit.neg(), ant);
// END Backtracking
return SolverNextAction::RESOLVED;
}
bool Solver::bcp() {
// the asserted literal has been set, so we start
// bcp on that literal
VariableIndex start_ofs = literal_stack_.size() - 1;
// BEGIN process unit clauses
for (auto lit : unit_clauses_)
setLiteralIfFree(lit);
// END process unit clauses
bool bSucceeded = BCP(start_ofs);
if (config_.perform_failed_lit_test && bSucceeded) {
bSucceeded = implicitBCP();
}
return bSucceeded;
}
bool Solver::BCP(VariableIndex start_at_stack_ofs) {
for (VariableIndex i = start_at_stack_ofs; i < literal_stack_.size(); i++) {
LiteralID unLit = literal_stack_[i].neg();
// BEGIN Propagate Bin Clauses
for (auto bt = literal(unLit).binary_links_.begin();
*bt != SENTINEL_LIT; bt++) {
if (isResolved(*bt)) {
setConflictState(unLit, *bt);
return false;
}
setLiteralIfFree(*bt, Antecedent(unLit));
}
// END Propagate Bin Clauses
for (auto itcl = literal(unLit).watch_list_.rbegin();
*itcl != SENTINEL_CL; itcl++) {
bool isLitA = (*beginOf(*itcl) == unLit);
auto p_watchLit = beginOf(*itcl) + 1 - isLitA;
auto p_otherLit = beginOf(*itcl) + isLitA;
if (isSatisfied(*p_otherLit))
continue;
auto itL = beginOf(*itcl) + 2;
while (isResolved(*itL))
itL++;
// either we found a free or satisfied lit
if (*itL != SENTINEL_LIT) {
literal(*itL).addWatchLinkTo(*itcl);
std::swap(*itL, *p_watchLit);
*itcl = literal(unLit).watch_list_.back();
literal(unLit).watch_list_.pop_back();
} else {
// or p_unLit stays resolved
// and we have hence no free literal left
// for p_otherLit remain poss: Active or Resolved
if (setLiteralIfFree(*p_otherLit, Antecedent(*itcl))) { // implication
if (isLitA)
std::swap(*p_otherLit, *p_watchLit);
} else {
setConflictState(*itcl);
return false;
}
}
}
}
return true;
}
//bool Solver::implicitBCP() {
// static std::vector<LiteralID> test_lits(num_variables());
// static LiteralIndexedVector<unsigned char> viewed_lits(num_variables() + 1,
// 0);
//
// unsigned stack_ofs = stack_.top().literal_stack_ofs();
// while (stack_ofs < literal_stack_.size()) {
// test_lits.clear();
// for (auto it = literal_stack_.begin() + stack_ofs;
// it != literal_stack_.end(); it++) {
// for (auto cl_ofs : occurrence_lists_[it->neg()])
// if (!isSatisfied(cl_ofs)) {
// for (auto lt = beginOf(cl_ofs); *lt != SENTINEL_LIT; lt++)
// if (isActive(*lt) && !viewed_lits[lt->neg()]) {
// test_lits.push_back(lt->neg());
// viewed_lits[lt->neg()] = true;
//
// }
// }
// }
//
// stack_ofs = literal_stack_.size();
// for (auto jt = test_lits.begin(); jt != test_lits.end(); jt++)
// viewed_lits[*jt] = false;
//
// statistics_.num_failed_literal_tests_ += test_lits.size();
//
// for (auto lit : test_lits)
// if (isActive(lit)) {
// unsigned sz = literal_stack_.size();
// // we increase the decLev artificially
// // s.t. after the tentative BCP call, we can learn a conflict clause
// // relative to the assn_ of *jt
// stack_.startFailedLitTest();
// setLiteralIfFree(lit);
//
// assert(!hasAntecedent(lit));
//
// bool bSucceeded = BCP(sz);
// if (!bSucceeded)
// recordAllUIPCauses();
//
// stack_.stopFailedLitTest();
//
// while (literal_stack_.size() > sz) {
// unSet(literal_stack_.back());
// literal_stack_.pop_back();
// }
//
// if (!bSucceeded) {
// statistics_.num_failed_literals_detected_++;
// sz = literal_stack_.size();
// for (auto it = uip_clauses_.rbegin(); it != uip_clauses_.rend();
// it++) {
// setLiteralIfFree(it->front(), addUIPConflictClause(*it));
// }
// if (!BCP(sz))
// return false;
// }
// }
// }
// return true;
//}
// this is IBCP 30.08
bool Solver::implicitBCP() {
static std::vector<LiteralID> test_lits(num_variables());
static LiteralIndexedVector<unsigned char> viewed_lits(num_variables() + 1, 0);
VariableIndex stack_ofs = stack_.top().literal_stack_ofs();
while (stack_ofs < literal_stack_.size()) {
test_lits.clear();
for (auto it = literal_stack_.begin() + stack_ofs;
it != literal_stack_.end(); it++) {
for (auto cl_ofs : occurrence_lists_[it->neg()])
if (!isSatisfied(cl_ofs)) {
for (auto lt = beginOf(cl_ofs); *lt != SENTINEL_LIT; lt++)
if (isActive(*lt) && !viewed_lits[lt->neg()]) {
test_lits.push_back(lt->neg());
viewed_lits[lt->neg()] = true;
}
}
}
VariableIndex num_curr_lits = literal_stack_.size() - stack_ofs;
stack_ofs = literal_stack_.size();
for (auto jt = test_lits.begin(); jt != test_lits.end(); jt++)
viewed_lits[*jt] = false;
std::vector<float> scores;
scores.clear();
for (auto &test_lit : test_lits) {
scores.push_back(literal(test_lit).activity_score_);
}
sort(scores.begin(), scores.end());
num_curr_lits = 10 + num_curr_lits / 20;
float threshold = 0.0;
if (scores.size() > num_curr_lits) {
threshold = scores[scores.size() - num_curr_lits];
}
statistics_.num_failed_literal_tests_ += test_lits.size();
for (auto lit : test_lits)
if (isActive(lit) && threshold <= literal(lit).activity_score_) {
VariableIndex sz = literal_stack_.size();
// we increase the decLev artificially
// s.t. after the tentative BCP call, we can learn a conflict clause
// relative to the assn_ of *jt
stack_.startFailedLitTest();
setLiteralIfFree(lit);
assert(!hasAntecedent(lit));
bool bSucceeded = BCP(sz);
if (!bSucceeded)
recordAllUIPCauses();
stack_.stopFailedLitTest();
while (literal_stack_.size() > sz) {
unSet(literal_stack_.back());
literal_stack_.pop_back();
}
if (!bSucceeded) {
statistics_.num_failed_literals_detected_++;
sz = literal_stack_.size();
for (auto it = uip_clauses_.rbegin();
it != uip_clauses_.rend(); it++) {
// DEBUG
if (it->empty() && !config_.quiet)
PrintError("EMPTY CLAUSE FOUND");
// END DEBUG
setLiteralIfFree(it->front(), addUIPConflictClause(*it));
}
if (!BCP(sz))
return false;
}
}
}
// BEGIN TEST
// float max_score = -1;
// float score;
// unsigned max_score_var = 0;
// for (auto it =
// component_analyzer_.superComponentOf(stack_.top()).varsBegin();
// *it != varsSENTINEL; it++)
// if (isActive(*it)) {
// score = scoreOf(*it);
// if (score > max_score) {
// max_score = score;
// max_score_var = *it;
// }
// }
// LiteralID theLit(max_score_var,
// literal(LiteralID(max_score_var, true)).activity_score_
// > literal(LiteralID(max_score_var, false)).activity_score_);
// if (!fail_test(theLit.neg())) {
// std::cout << ".";
//
// statistics_.num_failed_literals_detected_++;
// unsigned sz = literal_stack_.size();
// for (auto it = uip_clauses_.rbegin(); it != uip_clauses_.rend(); it++) {
// setLiteralIfFree(it->front(), addUIPConflictClause(*it));
// }
// if (!BCP(sz))
// return false;
//
// }
// END
return true;
}
// BEGIN module conflictAnalyzer
void Solver::minimizeAndStoreUIPClause(LiteralID uipLit,
std::vector<LiteralID> &tmp_clause,
const bool seen[]) {
static std::deque<LiteralID> clause;
clause.clear();
assertion_level_ = 0;
for (auto lit : tmp_clause) {
if (existsUnitClauseOf(lit.var()))
continue;
bool resolve_out = false;
if (hasAntecedent(lit)) {
resolve_out = true;
if (getAntecedent(lit).isAClause()) {
for (auto it = beginOf(getAntecedent(lit).asCl()) + 1;
*it != SENTINEL_CL; it++)
if (!seen[it->var()]) {
resolve_out = false;
break;
}
} else if (!seen[getAntecedent(lit).asLit().var()]) {
resolve_out = false;
}
}
if (!resolve_out) {
// uipLit should be the sole literal of this Decision Level
if (var(lit).decision_level >= assertion_level_) {
assertion_level_ = var(lit).decision_level;
clause.push_front(lit);
} else {
clause.push_back(lit);
}
}
}
if (uipLit.var())
assert(var_const(uipLit).decision_level == stack_.get_decision_level());
// assert(uipLit.var() != 0);
if (uipLit.var() != 0)
clause.push_front(uipLit);
uip_clauses_.emplace_back(std::vector<LiteralID>(clause.begin(), clause.end()));
}
void Solver::recordLastUIPCauses() {
// note:
// variables of lower dl: if seen we dont work with them anymore
// variables of this dl: if seen we incorporate their
// antecedent and set to unseen
bool seen[num_variables() + 1];
memset(seen, false, sizeof(bool) * (num_variables() + 1));
static std::vector<LiteralID> tmp_clause;
tmp_clause.clear();
assertion_level_ = 0;
uip_clauses_.clear();
unsigned long lit_stack_ofs = literal_stack_.size();
long DL = stack_.get_decision_level();
unsigned lits_at_current_dl = 0;
for (auto l : violated_clause) {
if (var(l).decision_level == 0 || existsUnitClauseOf(l.var()))
continue;
if (var(l).decision_level < DL)
tmp_clause.push_back(l);
else
lits_at_current_dl++;
literal(l).increaseActivity();
seen[l.var()] = true;
}
LiteralID curr_lit;
while (lits_at_current_dl) {
assert(lit_stack_ofs != 0);
curr_lit = literal_stack_[--lit_stack_ofs];
if (!seen[curr_lit.var()])
continue;
seen[curr_lit.var()] = false;
if (lits_at_current_dl-- == 1) {
// perform UIP stuff
if (!hasAntecedent(curr_lit)) {
// this should be the decision literal when in first branch
// or it is a literal decided to explore in failed literal testing
// assert(stack_.TOS_decLit() == curr_lit);
// std::cout << "R" << curr_lit.toInt() << "S"
// << var(curr_lit).ante.isAnt() << " " << std::endl;
break;
}
}
assert(hasAntecedent(curr_lit));
// std::cout << "{" << curr_lit.toInt() << "}";
if (getAntecedent(curr_lit).isAClause()) {
updateActivities(getAntecedent(curr_lit).asCl());
assert(curr_lit == *beginOf(antecedent(curr_lit).asCl()));
for (auto it = beginOf(getAntecedent(curr_lit).asCl()) + 1;
*it != SENTINEL_CL; it++) {
if (seen[it->var()] || (var(*it).decision_level == 0)
|| existsUnitClauseOf(it->var()))
continue;
if (var(*it).decision_level < DL)
tmp_clause.push_back(*it);
else
lits_at_current_dl++;
seen[it->var()] = true;
}
} else {
LiteralID alit = getAntecedent(curr_lit).asLit();
literal(alit).increaseActivity();
literal(curr_lit).increaseActivity();
if (!seen[alit.var()] && var(alit).decision_level != 0
&& !existsUnitClauseOf(alit.var())) {
if (var(alit).decision_level < DL)
tmp_clause.push_back(alit);
else
lits_at_current_dl++;
seen[alit.var()] = true;
}
}
curr_lit = NOT_A_LIT;
}
// std::cout << "T" << curr_lit.toInt() << "U "
// << var(curr_lit).decision_level << ", " << stack_.get_decision_level()
// << "\n"
// << "V" << var(curr_lit).ante.isAnt() << " " << std::endl;
minimizeAndStoreUIPClause(curr_lit.neg(), tmp_clause, seen);
// if (var(curr_lit).decision_level > assertion_level_)
// assertion_level_ = var(curr_lit).decision_level;
}
void Solver::recordAllUIPCauses() {
// note:
// variables of lower dl: if seen we dont work with them anymore
// variables of this dl: if seen we incorporate their
// antecedent and set to unseen
bool seen[num_variables() + 1];
memset(seen, false, sizeof(bool) * (num_variables() + 1));
static std::vector<LiteralID> tmp_clause;
tmp_clause.clear();
assertion_level_ = 0;
uip_clauses_.clear();
unsigned long lit_stack_ofs = literal_stack_.size();
long DL = stack_.get_decision_level();
unsigned lits_at_current_dl = 0;
for (auto l : violated_clause) {
if (var(l).decision_level == 0 || existsUnitClauseOf(l.var()))
continue;
if (var(l).decision_level < DL)
tmp_clause.push_back(l);
else
lits_at_current_dl++;
literal(l).increaseActivity();
seen[l.var()] = true;
}
unsigned n = 0;
LiteralID curr_lit;
while (lits_at_current_dl) {
assert(lit_stack_ofs != 0);
curr_lit = literal_stack_[--lit_stack_ofs];
if (!seen[curr_lit.var()])
continue;
seen[curr_lit.var()] = false;
if (lits_at_current_dl-- == 1) {
n++;
if (!hasAntecedent(curr_lit)) {
// this should be the decision literal when in first branch
// or it is a literal decided to explore in failed literal testing
//assert(stack_.TOS_decLit() == curr_lit);
break;
}
// perform UIP stuff
minimizeAndStoreUIPClause(curr_lit.neg(), tmp_clause, seen);
}
assert(hasAntecedent(curr_lit));
if (getAntecedent(curr_lit).isAClause()) {
updateActivities(getAntecedent(curr_lit).asCl());
assert(curr_lit == *beginOf(getAntecedent(curr_lit).asCl()));
for (auto it = beginOf(getAntecedent(curr_lit).asCl()) + 1;
*it != SENTINEL_CL; it++) {
if (seen[it->var()] || (var(*it).decision_level == 0)
|| existsUnitClauseOf(it->var()))
continue;
if (var(*it).decision_level < DL)
tmp_clause.push_back(*it);
else
lits_at_current_dl++;
seen[it->var()] = true;
}
} else {
LiteralID alit = getAntecedent(curr_lit).asLit();
literal(alit).increaseActivity();
literal(curr_lit).increaseActivity();
if (!seen[alit.var()] && var(alit).decision_level != 0
&& !existsUnitClauseOf(alit.var())) {
if (var(alit).decision_level < DL)
tmp_clause.push_back(alit);
else
lits_at_current_dl++;
seen[alit.var()] = true;
}
}
}
if (!hasAntecedent(curr_lit)) {
minimizeAndStoreUIPClause(curr_lit.neg(), tmp_clause, seen);
}
// if (var(curr_lit).decision_level > assertion_level_)
// assertion_level_ = var(curr_lit).decision_level;
}
void Solver::printOnlineStats() {
if (config_.quiet)
return;
std::cout << "\ntime elapsed: " << sampler_stopwatch_.getElapsedSeconds() << "s" << std::endl;
if (config_.verbose) {
std::cout << "conflict clauses (all / bin / unit) \t"
<< num_conflict_clauses()
<< "/" << statistics_.num_binary_conflict_clauses_ << "/"
<< unit_clauses_.size() << "\n"
<< "failed literals found by implicit BCP \t "
<< statistics_.num_failed_literals_detected_ << "\n";
std::cout << "implicit BCP miss rate \t "
<< statistics_.implicitBCP_miss_rate() * 100 << "%\n";
comp_manager_.gatherStatistics();
std::cout << "cache size " << statistics_.cache_MB_memory_usage() << "MB" << "\n"
<< "components (stored / hits) \t\t"
<< statistics_.cached_component_count() << "/"
<< statistics_.cache_hits() << "\n"
<< "avg. variable count (stored / hits) \t"
<< statistics_.getAvgComponentSize() << "/"
<< statistics_.getAvgCacheHitSize() << "\n"
<< "cache miss rate " << statistics_.cache_miss_rate() * 100 << "%"
<< std::endl;
}
}
//bool Solver::IsVarInLiteralStack(const VariableIndex var) {
// for (VariableIndex i = 0; i < literal_stack_.size(); i++) {
// if (literal_stack_[i].var() == var) {
// std::stringstream ss;
// ss << "Var #" << var << " is in the literal stack at level " << i
// << " with val " << literal_stack_[i].sign();
// PrintError(ss);
// return true;
// }
// }
// return false;
//}
//
//
//void Solver::PrintLiteralStackLocation(VariableIndex var) {
// for (VariableIndex i = 0; i < literal_stack_.size(); i++) {
// if (literal_stack_[i].var() == var) {
// std::stringstream ss;
// ss << "Var #" << var << " is located in slot " << i << " and has sign "
// << (literal_stack_[i].sign() ? "POS" : "NEG");
// PrintError(ss);
// }
// }
//}
Solver::Solver(int argc, char *argv[]) : final_samples_(0, config_) {
// Store the configuration for visibility by the stack.
LinkConfigAndStatistics();
time(&statistics_.start_time_);
config_.num_samples_ = 0; // By default initialize the model count to zero
for (int i = 1; i < argc; i++) {
// if (strcmp(argv[i], "-noCC") == 0)
// config_.perform_component_caching = false;
// else if (strcmp(argv[i], "-noIBCP") == 0)
// config_.perform_failed_lit_test = false;
// else if (strcmp(argv[i], "-noPP") == 0)
// config_.perform_pre_processing = false;
// else if (strcmp(argv[i], "-q") == 0)
if (strcmp(argv[i], "-q") == 0) {
config_.quiet = true;
} else if (strcmp(argv[i], "-v") == 0) {
config_.verbose = true;
} else if (strcmp(argv[i], "-d") == 0) {
config_.debug_mode = true;
} else if (strcmp(argv[i], "-tp") == 0) {
config_.perform_two_pass_sampling_ = true;
} else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "-out") == 0) {
if (argc <= i + 1 || !config_.perform_random_sampling_)
ExitInvalidParam("Invalid parameters for sampling");
if (strcmp(argv[i], "-s") == 0) {
long num_samples = strtoul(argv[++i], nullptr, STR_DECIMAL_BASE);
config_.num_samples_ = static_cast<SampleSize>(num_samples);
if (config_.num_samples_ < 1)
ExitInvalidParam("Must sample at least one model");
} else {
config_.samples_output_file = argv[++i];
}
} else if (strcmp(argv[i], "-no-sample-write") == 0) {
config_.disable_samples_write_ = true;
} else if (strcmp(argv[i], "-count-only") == 0) {
config_.perform_random_sampling_ = false;
if (config_.num_samples_ > 0 || !config_.samples_output_file.empty())
ExitInvalidParam("Invalid parameters for counting and sampling");
} else if (strcmp(argv[i], "-t") == 0) {
if (argc <= i + 1)
ExitInvalidParam("Time bound missing");
config_.time_bound_seconds = strtoul(argv[++i], nullptr, STR_DECIMAL_BASE);
} else if (strcmp(argv[i], "-cs") == 0) {
if (argc <= i + 1)
ExitInvalidParam("No cache size specified");
statistics_.maximum_cache_size_bytes_ = strtoul(argv[++i], nullptr, STR_DECIMAL_BASE)
* (uint64_t) 1000000;
} else if (strcmp(argv[i], "-cnf") == 0) {
if (argc <= i + 1)
ExitInvalidParam("No CNF file specified");
statistics_.input_file_ = argv[++i];
} else if (strcmp(argv[i], "-no-partial-fill") == 0) {
// This is a debug only feature. Functionality is not guaranteed.
config_.skip_partial_assignment_fill = true;
} else if (strcmp(argv[i], "-top-tree") == 0) {
config_.perform_top_tree_sampling = true;
} else if (strcmp(argv[i], "-top-tree-depth") == 0) {
long top_tree_depth = strtoul(argv[++i], nullptr, STR_DECIMAL_BASE);
if (top_tree_depth <= 1)
ExitInvalidParam("The top tree depth must be greater than 1.");
config_.max_top_tree_depth_ = static_cast<TreeNodeIndex>(top_tree_depth);
} else if (strcmp(argv[i], "-max-leaf-size") == 0) {
long max_leaf_size = strtoul(argv[++i], nullptr, STR_DECIMAL_BASE);
if (max_leaf_size <= 1)
ExitInvalidParam("The top tree leaf size must be greater than 1.");
config_.max_top_tree_leaf_sample_count = static_cast<SampleSize>(max_leaf_size);
} else {
ExitInvalidParam(static_cast<std::string>("Unknown parameter found \"") + argv[i] + "\"");
}
}
// Perform additional cross-checking of input parameters
if (config_.quiet && config_.verbose)
ExitInvalidParam("Invalid combination of verbose and quiet");
if (config_.num_samples_ < 1 && config_.perform_random_sampling_)
ExitInvalidParam("If sampling is enabled, a sample count greater than or equal \n"
"to 1 must be specified.");
if (config_.perform_two_pass_sampling_ && !config_.perform_random_sampling_)
ExitInvalidParam("The two pass sampling flag is only applicable when sampling is enabled.\n");
if (config_.perform_top_tree_sampling && !config_.perform_random_sampling_)
ExitInvalidParam("Top tree sampling cannot be enabled if random sampling is disabled.");
if (config_.disable_samples_write_ && !config_.perform_random_sampling_)
ExitInvalidParam("Disabling sample write cannot be selected if sampling is disabled.");
if (config_.samples_output_file.empty() && config_.perform_random_sampling_) {
std::string input_file = statistics_.input_file_;
uint64_t os_sep_loc = input_file.rfind('\\');
os_sep_loc = (os_sep_loc != std::string::npos) ? os_sep_loc : input_file.rfind('/');
std::string out_file;
if (os_sep_loc != std::string::npos)
out_file = statistics_.input_file_.substr(0, os_sep_loc + 1);
// Prepend "results_" to the samples filename
out_file += "samples_";
// Append the filename and change extension to ".txt"
uint64_t filename_start_loc = os_sep_loc + 1;
uint64_t file_ext_start = input_file.rfind('.');
if (file_ext_start <= filename_start_loc || file_ext_start == std::string::npos)
file_ext_start = statistics_.input_file_.size();
uint64_t filename_len = file_ext_start - filename_start_loc;
out_file += statistics_.input_file_.substr(filename_start_loc, filename_len);
out_file += ".txt";
config_.samples_output_file = out_file;
if (!config_.quiet)
PrintWarning(static_cast<std::string>("No sample results file specified.\n")
+ "Using default filename: \"" + out_file + "\"");
}
if (config_.perform_two_pass_sampling_ && config_.num_samples_ > 1 && !config_.quiet)
PrintWarning("The two pass sampling flag only has an effect\n"
"when the number of samples equals 1. Ignoring...");
// Initialize the time bound.
sampler_stopwatch_.setTimeBound(config_.time_bound_seconds);
// Any time the same count is larger than one, always do two pass sampling
if (config_.num_samples_ > 1)
config_.perform_two_pass_sampling_ = true;
// Must parse at the end in case debug mode is selected.
Random::init(&config_);
}