mirror of
https://github.com/NationalSecurityAgency/dnf-model-counting.git
synced 2026-11-08 08:11:56 -10:00
322 lines
9.1 KiB
C++
322 lines
9.1 KiB
C++
#include <DMCrand.h>
|
|
#include <random>
|
|
#include <fstream>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <bit_array.h>
|
|
#include <DMChelp.h>
|
|
#include <map>
|
|
#include <sys/time.h>
|
|
#include <gmp.h>
|
|
#include <chrono>
|
|
#include <thread>
|
|
#include <atomic>
|
|
#include <future>
|
|
#include <algorithm>
|
|
#include <ctime>
|
|
#include <cmath>
|
|
|
|
using namespace std;
|
|
|
|
void shuffle(int**& DNF, long*& dnf_ind, int*& dnf, int*& dnfMax, int*& set_vars, int& current_var ,int& level, int& m,
|
|
unsigned long& form_size, unsigned long& current_loc,
|
|
unsigned long& w_tot, int& w_min, double& beta, double& rand_clause, int& min_loc) {
|
|
int swap;
|
|
rand_clause = drand48();
|
|
int* k = 0;
|
|
int clause_size;
|
|
unsigned long clause_loc;
|
|
|
|
int var = 0;
|
|
int sign = 1;
|
|
|
|
// decide whether to swap with a random clause or with the least remaining ordered clause
|
|
if (rand_clause < 1.0-(1.0-beta)*min(1.0,w_tot/((m-level)*static_cast<double>(w_min)))) {
|
|
while (true) {
|
|
swap = rand() % (m - min_loc) + min_loc; // random remaining clause
|
|
k = DNF[swap];
|
|
if (k!=0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
clause_loc = form_size - current_loc;
|
|
dnf_ind[level] = clause_loc;
|
|
DNF[swap] = 0;
|
|
|
|
clause_size = k[0];
|
|
dnf[clause_loc] = clause_size;
|
|
w_tot -= clause_size;
|
|
|
|
// remap variables for tracking max var up to this clause
|
|
for (int i=1; i<clause_size+1; ++i) {
|
|
var = abs(k[i]);
|
|
if (k[i] > 0) {
|
|
sign = 1;
|
|
} else {
|
|
sign = -1;
|
|
}
|
|
if (set_vars[var] == 0) {
|
|
set_vars[var] = current_var;
|
|
dnf[clause_loc + i] = sign * current_var;
|
|
++current_var;
|
|
} else {
|
|
dnf[clause_loc + i] = sign * set_vars[var];
|
|
}
|
|
}
|
|
|
|
// sort clause vars and store max var
|
|
sort(dnf + clause_loc + 1, dnf + clause_loc + clause_size + 1, [](int a, int b) {return abs(a) < abs(b);});
|
|
dnfMax[level] = current_var-1;
|
|
current_loc -= k[0]+1;
|
|
|
|
// calculate least remaining clause
|
|
if (swap == min_loc) {
|
|
while (true) {
|
|
min_loc += 1;
|
|
k = DNF[min_loc];
|
|
if (k!=0) {
|
|
break;
|
|
}
|
|
}
|
|
w_min = DNF[min_loc][0];
|
|
}
|
|
} else {
|
|
k = DNF[min_loc]; // least remaining clause
|
|
|
|
clause_loc = form_size - current_loc;
|
|
dnf_ind[level] = clause_loc;
|
|
DNF[min_loc] = 0;
|
|
|
|
clause_size = k[0];
|
|
dnf[clause_loc] = clause_size;
|
|
w_tot -= k[0];
|
|
|
|
// remap variables for tracking max var up to this clause
|
|
for (int i=1; i<clause_size+1; ++i) {
|
|
var = abs(k[i]);
|
|
if (k[i] > 0) {
|
|
sign = 1;
|
|
} else {
|
|
sign = -1;
|
|
}
|
|
if (set_vars[var] == 0) {
|
|
set_vars[var] = current_var;
|
|
dnf[clause_loc + i] = sign * current_var;
|
|
++current_var;
|
|
} else {
|
|
dnf[clause_loc + i] = sign * set_vars[var];
|
|
}
|
|
}
|
|
// sort clause vars and store max var
|
|
sort(dnf + clause_loc + 1, dnf + clause_loc + clause_size + 1, [](int a, int b) {return abs(a) < abs(b);});
|
|
current_loc -= clause_size+1;
|
|
dnfMax[level] = current_var-1;
|
|
|
|
// calculate least remaining clause
|
|
while(true) {
|
|
min_loc += 1;
|
|
k = DNF[min_loc];
|
|
if (k!=0) {
|
|
break;
|
|
}
|
|
}
|
|
w_min = DNF[min_loc][0];
|
|
}
|
|
}
|
|
|
|
double ouralg(int*& dnf, long*& dnf_ind, int*& dnfMax, int& maxvar, int*& A, mpf_t*& Pr, int& nz,
|
|
mpf_t& z, int& nvars, int& m, double& eps, double& delta, bit_array_read<gmt::byte, int>& bitRead,
|
|
bit_array_write<gmt::byte, int>& bitWrite, mt19937& gen, mt19937_64& gen64, uniform_real_distribution<double>& dis, uniform_int_distribution<int>& idis, int Thresh) {
|
|
|
|
int Y = 0;
|
|
long count = 0;
|
|
|
|
int bytesize = int(ceil(maxvar/8.0));
|
|
// set_vars stores the affirmation or negation of the variable as a 1 or 0 bit
|
|
gmt::byte* set_vars = new gmt::byte[bytesize];
|
|
// clause_vars stores which variables have been assigned
|
|
gmt::byte* clause_vars = new gmt::byte[bytesize]();
|
|
|
|
uniform_int_distribution<unsigned long> dist(numeric_limits<unsigned long>::min(), numeric_limits<unsigned long>::max());
|
|
int index;
|
|
long test_ind;
|
|
long C_ind;
|
|
bool clause_true;
|
|
unsigned long buff = 0;
|
|
int buff_length = 0;
|
|
|
|
// Trial loop
|
|
while (Y < Thresh) {
|
|
// Select a clause with probability proportional to its weight
|
|
index = select_clause(index, A, Pr, z, nz, m, gen, dis, idis);
|
|
C_ind = dnf_ind[index];
|
|
|
|
int max_var = -1;
|
|
|
|
// assign only variables from the selected clause
|
|
lazyassign(dnf, C_ind, nvars, bytesize, clause_vars, set_vars, bitWrite, bitRead);
|
|
|
|
double t1 = 1;
|
|
bool abort = false;
|
|
double u1 = generate_random_real(gen,dis);
|
|
|
|
// step loop
|
|
for (int level=0; level<m; ++level) {
|
|
|
|
if (level == index) {
|
|
continue;
|
|
}
|
|
|
|
test_ind = dnf_ind[level];
|
|
clause_true = lazySAT(dnf, test_ind, set_vars, clause_vars, bitRead, bitWrite, gen64, dist, buff, buff_length);
|
|
|
|
if (clause_true == true) {
|
|
t1 += 1;
|
|
|
|
if (t1 >= 1/u1) {
|
|
abort = true;
|
|
max_var = dnfMax[level];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (abort == false) {
|
|
Y += 1;
|
|
max_var = maxvar;
|
|
}
|
|
for (int i=dnf[C_ind]; i>0; --i) {
|
|
if (abs(dnf[C_ind+i])-1 <= max_var) {
|
|
break;
|
|
}
|
|
bitWrite.clear_byte(clause_vars,abs(dnf[C_ind+i])-1);
|
|
}
|
|
memset(clause_vars, 0, ceil((max_var)/8.0));
|
|
count += 1;
|
|
}
|
|
|
|
delete[] set_vars;
|
|
delete[] clause_vars;
|
|
|
|
return 1.0*Y/count;
|
|
}
|
|
|
|
int main() {
|
|
int exp = 0;
|
|
int nvars = 4;
|
|
double eps = .05;
|
|
double delta = .05;
|
|
|
|
mpf_set_default_prec(128);
|
|
|
|
bit_array_read<gmt::byte,int> bitRead;
|
|
bit_array_write<gmt::byte,int> bitWrite;
|
|
|
|
srand(time(0));
|
|
random_device rd;
|
|
mt19937 gen(rd());
|
|
mt19937_64 gen64(rd());
|
|
|
|
tuple<int**, int, int, unsigned long> DNFINFO = readDNF("test.dnf");
|
|
|
|
int** DNF = get<0>(DNFINFO);
|
|
int n = get<1>(DNFINFO);
|
|
int m = get<2>(DNFINFO);
|
|
unsigned long w_tot = get<3>(DNFINFO);
|
|
|
|
uniform_real_distribution<double> dis(0.0,1.0);
|
|
|
|
double beta = 0.01;
|
|
unsigned long form_size = w_tot + m;
|
|
|
|
timeval start;
|
|
timeval end;
|
|
gettimeofday(&start,0);
|
|
|
|
double rand_clause;
|
|
|
|
// create the ordered partition
|
|
sort(DNF, DNF+m, [](int* a, int* b) {return a[0] < b[0];});
|
|
|
|
int w_min = DNF[0][0];
|
|
|
|
long* dnf_ind = new long[m];
|
|
int* dnfMax = new int[m];
|
|
int* dnf = new int[form_size];
|
|
|
|
int min_loc = 0;
|
|
|
|
unsigned long current_loc = form_size;
|
|
// during the shuffling, set_vars will remap unique vars as encountered to keep track of the maximum variable encountered up to a given clause.
|
|
int* set_vars = new int[n+1]();
|
|
int current_var = 1;
|
|
|
|
unsigned long randN = 0;
|
|
|
|
for (int j=0; j<m-1; ++j) {
|
|
shuffle(DNF, dnf_ind, dnf, dnfMax, set_vars, current_var, j, m, form_size, current_loc, w_tot, w_min, beta, rand_clause, min_loc);
|
|
}
|
|
|
|
unsigned long clause_loc = form_size - current_loc;
|
|
dnf_ind[m-1] = clause_loc;
|
|
int* k = DNF[min_loc];
|
|
int clause_size = k[0];
|
|
dnf[clause_loc] = clause_size;
|
|
int var = 0;
|
|
int sign = 1;
|
|
|
|
for (int i=1; i<clause_size+1; ++i) {
|
|
var = abs(k[i]);
|
|
if (k[i] > 0) {
|
|
sign = 1;
|
|
} else {
|
|
sign = -1;
|
|
}
|
|
if (set_vars[var] == 0) {
|
|
set_vars[var] = current_var;
|
|
dnf[clause_loc + i] = sign * current_var;
|
|
++current_var;
|
|
} else {
|
|
dnf[clause_loc + i] = sign * set_vars[var];
|
|
}
|
|
}
|
|
dnfMax[m-1] = --current_var;
|
|
|
|
// Calculate the threshold T for the number of successful trials needed
|
|
int T = ceil( log(2/delta) / (log(1+eps) - eps/(1+eps)) );
|
|
|
|
double upper = std::exp(eps/(1+eps)) / (1+eps);
|
|
double lower = std::exp(-eps/(1-eps)) / (1-eps);
|
|
while (pow(upper,T) + pow(lower,T) <= delta) {
|
|
T--;
|
|
}
|
|
T++;
|
|
|
|
// Compute the clause selection distribution
|
|
mpf_pair prob_table1 = prob_CG(dnf, dnf_ind, m, n);
|
|
mpf_t* Pr1 = prob_table1.Pr;
|
|
mpf_t z_mpf1;
|
|
mpf_init(z_mpf1);
|
|
mpf_set(z_mpf1, prob_table1.z);
|
|
double z1 = mpf_get_d(z_mpf1);
|
|
|
|
// Form the data structure for efficient sampling
|
|
pair<int*, int> SAMPLING1 = sample_info(Pr1, m, z_mpf1);
|
|
int* A1 = SAMPLING1.first;
|
|
int nz1 = SAMPLING1.second;
|
|
uniform_int_distribution<int> idis(0,nz1-1);
|
|
|
|
|
|
double overlap = ouralg(dnf, dnf_ind, dnfMax, current_var, A1, Pr1, nz1, z_mpf1, n, m, eps, delta, bitRead, bitWrite, gen, gen64, dis, idis, T);
|
|
|
|
double approx = overlap * z1;
|
|
|
|
cout << approx << endl;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// g++ -lgmp -I include/ -O3 -o count count.cpp rand.cpp counthelp.cpp
|
|
|