TreeEP::TreeEP( const FactorGraph &fg, const PropertySet &opts ) : JTree(fg, opts("updates",string("HUGIN")), false), _maxdiff(0.0), _iters(0), props(), _Q() { setProperties( opts ); if( opts.hasKey("tree") ) { construct( fg, opts.getAs<RootedTree>("tree") ); } else { if( props.type == Properties::TypeType::ORG || props.type == Properties::TypeType::ALT ) { // ORG: construct weighted graph with as weights a crude estimate of the // mutual information between the nodes // ALT: construct weighted graph with as weights an upper bound on the // effective interaction strength between pairs of nodes WeightedGraph<Real> wg; // in order to get a connected weighted graph, we start // by connecting every variable to the zero'th variable with weight 0 for( size_t i = 1; i < fg.nrVars(); i++ ) wg[UEdge(i,0)] = 0.0; for( size_t i = 0; i < fg.nrVars(); i++ ) { SmallSet<size_t> delta_i = fg.bipGraph().delta1( i, false ); const Var& v_i = fg.var(i); bforeach( size_t j, delta_i ) if( i < j ) { const Var& v_j = fg.var(j); VarSet v_ij( v_i, v_j ); SmallSet<size_t> nb_ij = fg.bipGraph().nb1Set( i ) | fg.bipGraph().nb1Set( j ); Factor piet; bforeach( size_t I, nb_ij ) { const VarSet& Ivars = fg.factor(I).vars(); if( props.type == Properties::TypeType::ORG ) { if( (Ivars == v_i) || (Ivars == v_j) ) piet *= fg.factor(I); else if( Ivars >> v_ij ) piet *= fg.factor(I).marginal( v_ij ); } else { if( Ivars >> v_ij ) piet *= fg.factor(I); } } if( props.type == Properties::TypeType::ORG ) { if( piet.vars() >> v_ij ) { piet = piet.marginal( v_ij ); Factor pietf = piet.marginal(v_i) * piet.marginal(v_j); wg[UEdge(i,j)] = dist( piet, pietf, DISTKL ); } else { // this should never happen... DAI_ASSERT( 0 == 1 ); wg[UEdge(i,j)] = 0; } } else wg[UEdge(i,j)] = piet.strength(v_i, v_j); }
void RegionGraph::constructCVM( const FactorGraph &fg, const std::vector<VarSet> &cl, size_t verbose ) { if( verbose ) cerr << "constructCVM called (" << fg.nrVars() << " vars, " << fg.nrFactors() << " facs, " << cl.size() << " clusters)" << endl; // Retain only maximal clusters if( verbose ) cerr << " Constructing ClusterGraph" << endl; ClusterGraph cg( cl ); if( verbose ) cerr << " Erasing non-maximal clusters" << endl; cg.eraseNonMaximal(); // Create inner regions - first pass if( verbose ) cerr << " Creating inner regions (first pass)" << endl; set<VarSet> betas; for( size_t alpha = 0; alpha < cg.nrClusters(); alpha++ ) for( size_t alpha2 = alpha; (++alpha2) != cg.nrClusters(); ) { VarSet intersection = cg.cluster(alpha) & cg.cluster(alpha2); if( intersection.size() > 0 ) betas.insert( intersection ); } // Create inner regions - subsequent passes if( verbose ) cerr << " Creating inner regions (next passes)" << endl; set<VarSet> new_betas; do { new_betas.clear(); for( set<VarSet>::const_iterator gamma = betas.begin(); gamma != betas.end(); gamma++ ) for( set<VarSet>::const_iterator gamma2 = gamma; (++gamma2) != betas.end(); ) { VarSet intersection = (*gamma) & (*gamma2); if( (intersection.size() > 0) && (betas.count(intersection) == 0) ) new_betas.insert( intersection ); } betas.insert(new_betas.begin(), new_betas.end()); } while( new_betas.size() ); // Create inner regions - final phase if( verbose ) cerr << " Creating inner regions (final phase)" << endl; vector<Region> irs; irs.reserve( betas.size() ); for( set<VarSet>::const_iterator beta = betas.begin(); beta != betas.end(); beta++ ) irs.push_back( Region(*beta,0.0) ); // Create edges if( verbose ) cerr << " Creating edges" << endl; vector<pair<size_t,size_t> > edges; for( size_t beta = 0; beta < irs.size(); beta++ ) for( size_t alpha = 0; alpha < cg.nrClusters(); alpha++ ) if( cg.cluster(alpha) >> irs[beta] ) edges.push_back( pair<size_t,size_t>(alpha,beta) ); // Construct region graph if( verbose ) cerr << " Constructing region graph" << endl; construct( fg, cg.clusters(), irs, edges ); // Calculate counting numbers if( verbose ) cerr << " Calculating counting numbers" << endl; calcCVMCountingNumbers(); if( verbose ) cerr << "Done." << endl; }
void TestHSMM::test_marginal_cut(const char* filename, size_t ID){ HSMMparam param(filename); FactorGraph *graph; JTree *jt; // Set some constants size_t maxiter = 10000; Real tol = 1e-9; size_t verb = 0; //window size size_t W = 20; size_t start = 0; // Store the constants in a PropertySet object PropertySet opts; opts.set("maxiter",maxiter); // Maximum number of iterations opts.set("tol",tol); // Tolerance for convergence opts.set("verbose",verb); // Verbosity (amount of output generated) Factor O_last; vector< vector<Real> > all_marginal; vector<Real> sequence_marginal; all_marginal.reserve(test_data.size()); cout << "Now we do testing...\n"; for(size_t i=0; i<test_data.size(); i++) { //allocate memory sequence_marginal.reserve(test_data[i].size()); //initialize HSMM of ever increasing size up to test_data[i].size() graph = new FactorGraph(); graph->createHSMMFactorGraph(param.init, param.dist, test_data[i].size()); jt = new JTree(*graph, opts("updates",string("HUGIN"))("heuristic",string("MINWEIGHT")) ); jt->init(); jt->run(); O_last = jt->calcMarginal(graph->var(4)); sequence_marginal.push_back( log(O_last.p().get(test_data[i][0].second)) ); delete jt; for(size_t k=1; k < test_data[i].size(); k++){ jt = new JTree(*graph, opts("updates",string("HUGIN"))("heuristic",string("MINWEIGHT")) ); //clamp a window of observable variables to their values, except last variable which is not clamped start = k-W; if(start < 0) start = 0; for(size_t j = start; j <= k-1; j++){ jt->clamp(test_data[i][j].first, test_data[i][j].second); } jt->init(); jt->run(); //compute p(o_last=c | o_1...o_{last-1}) //this will give us a distribution: {o_last=1, o_last=2, ... o_last=M} O_last = jt->calcMarginal(graph->var(3*k+4)); //since we have a specific observation at last time step: o_last=c, get its probability: sequence_marginal.push_back( log(O_last.p().get(test_data[i][k].second)) ); delete jt; } cout << "Tested point " << i << " out of " << test_data.size() <<"\n"; all_marginal.push_back(sequence_marginal); sequence_marginal.clear(); delete graph; } cout << "Testing done.\n"; ofstream os; stringstream result; result << string("data/HSMMmarginal_test_") << ID << string(".txt"); os.open(result.str().c_str(), ios::trunc); for(size_t i=0; i<all_marginal.size(); i++){ for(size_t j=0; j<all_marginal[i].size(); j++){ os << all_marginal[i][j]<<" "; } os << "\n"; } }
void TestHSMM::test_loglik(const char* filename, size_t ID, string type, int dummy, int ID2){ //"type" specifies the suffix of the output file "test" or "true" //read in HSMM parameters from textfile HSMMparam param(filename, 0); vector<Real> likelihood_test; FactorGraph *graph; JTree *jt; VarSet X; // Set some constants size_t maxiter = 10000; Real tol = 1e-9; size_t verb = 0; // Store the constants in a PropertySet object PropertySet opts; opts.set("maxiter",maxiter); // Maximum number of iterations opts.set("tol",tol); // Tolerance for convergence opts.set("verbose",verb); // Verbosity (amount of output generated) cout << "Now we do testing...\n"; for(size_t i=0; i<test_data.size(); i++) { //initialize HSMM of size equal the number of observations graph = new FactorGraph(); graph->createHSMMFactorGraph(param.init, param.dist, test_data[i].size(), 0); jt = new JTree(*graph, opts("updates",string("HUGIN"))("heuristic",string("MINWEIGHT")) ); //clamp the observation variables to their observed values for(size_t j = 0; j < test_data[i].size(); j++ ){ //cout << "clamping var" << test_data[i][j].first << " to value " << test_data[i][j].second << "\n"; jt->clamp(test_data[i][j].first, test_data[i][j].second); } jt->init(); jt->run(); //compute normalized loglikelihood //likelihood_test.push_back(jt->logZ()/test_data[i].size()); likelihood_test.push_back(jt->logZ()); delete jt; delete graph; cout << "Tested point " << i << " out of " << test_data.size() <<"\n"; //cout << "jt->logZ() = " << likelihood_test.at(0) << "\n"; //exit(1); } cout << "done.\n"; ofstream os; stringstream result; //ID2 is used to wirte test results with fixed number of training iterations if(ID2 >= 0){ result << string("data/HSMMlikelihood_") << type << string("_") << ID << string("-") << ID2 << string(".txt"); } else{ result << string("data/HSMMlikelihood_") << type << string("_") << ID << string(".txt"); } os.open(result.str().c_str(), ios::trunc); os.unsetf ( std::ios::floatfield ); os.precision(18); for(size_t i=0; i<likelihood_test.size(); i++){ os << likelihood_test.at(i)<<"\n"; } }
/// Main function int main( int argc, char *argv[] ) { // Variables to store command line options // Filename of factor graph string filename; // Filename for aliases string aliases; // Approximate Inference methods to use vector<string> methods; // Which marginals to output MarginalsOutputType marginals; // Output number of iterations? bool report_iters = true; // Output calculation time? bool report_time = true; // Define required command line options po::options_description opts_required("Required options"); opts_required.add_options() ("filename", po::value< string >(&filename), "Filename of factor graph") ("methods", po::value< vector<string> >(&methods)->multitoken(), "DAI methods to perform") ; // Define allowed command line options po::options_description opts_optional("Allowed options"); opts_optional.add_options() ("help", "Produce help message") ("aliases", po::value< string >(&aliases), "Filename for aliases") ("marginals", po::value< MarginalsOutputType >(&marginals), "Output marginals? (NONE/VAR/FAC/VARFAC/ALL, default=NONE)") ("report-time", po::value< bool >(&report_time), "Output calculation time (default==1)?") ("report-iters", po::value< bool >(&report_iters), "Output iterations needed (default==1)?") ; // Define all command line options po::options_description cmdline_options; cmdline_options.add(opts_required).add(opts_optional); // Parse command line po::variables_map vm; po::store(po::parse_command_line(argc, argv, cmdline_options), vm); po::notify(vm); // Display help message if necessary if( vm.count("help") || !(vm.count("filename") && vm.count("methods")) ) { cout << "This program is part of libDAI - http://www.libdai.org/" << endl << endl; cout << "Usage: ./testdai --filename <filename.fg> --methods <method1> [<method2> <method3> ...]" << endl << endl; cout << "Reads factor graph <filename.fg> and performs the approximate inference algorithms" << endl; cout << "<method*>, reporting for each method:" << endl; cout << " o the calculation time needed, in seconds (if report-time == 1);" << endl; cout << " o the number of iterations needed (if report-iters == 1);" << endl; cout << " o the maximum (over all variables) total variation error in the variable marginals;" << endl; cout << " o the average (over all variables) total variation error in the variable marginals;" << endl; cout << " o the maximum (over all factors) total variation error in the factor marginals;" << endl; cout << " o the average (over all factors) total variation error in the factor marginals;" << endl; cout << " o the error (difference) of the logarithm of the partition sums;" << endl << endl; cout << "All errors are calculated by comparing the results of the current method with" << endl; cout << "the results of the first method (the base method). If marginals==VAR, additional" << endl; cout << "output consists of the variable marginals, if marginals==FAC, the factor marginals" << endl; cout << "if marginals==VARFAC, both variable and factor marginals, and if marginals==ALL, all" << endl; cout << "marginals calculated by the method are reported." << endl << endl; cout << "<method*> should be a list of one or more methods, seperated by spaces, in the format:" << endl << endl; cout << " name[key1=val1,key2=val2,key3=val3,...,keyn=valn]" << endl << endl; cout << "where name should be the name of an algorithm in libDAI (or an alias, if an alias" << endl; cout << "filename is provided), followed by a list of properties (surrounded by rectangular" << endl; cout << "brackets), where each property consists of a key=value pair and the properties are" << endl; cout << "seperated by commas. If an alias file is specified, alias substitution is performed." << endl; cout << "This is done by looking up the name in the alias file and substituting the alias" << endl; cout << "by its corresponding method as defined in the alias file. Properties are parsed from" << endl; cout << "left to right, so if a property occurs repeatedly, the right-most value is used." << endl << endl; cout << opts_required << opts_optional << endl; #ifdef DAI_DEBUG cout << "Note: this is a debugging build of libDAI." << endl << endl; #endif cout << "Example: ./testdai --filename testfast.fg --aliases aliases.conf --methods JTREE_HUGIN BP_SEQFIX BP_PARALL[maxiter=5]" << endl; return 1; } try { // Read aliases map<string,string> Aliases; if( !aliases.empty() ) Aliases = readAliasesFile( aliases ); // Read factor graph FactorGraph fg; fg.ReadFromFile( filename.c_str() ); // Declare variables used for storing variable factor marginals and log partition sum of base method vector<Factor> varMarginals0; vector<Factor> facMarginals0; Real logZ0 = 0.0; // Output header cout.setf( ios_base::scientific ); cout.precision( 3 ); cout << "# " << filename << endl; cout.width( 39 ); cout << left << "# METHOD" << "\t"; if( report_time ) cout << right << "SECONDS " << "\t"; if( report_iters ) cout << "ITERS" << "\t"; cout << "MAX VAR ERR" << "\t"; cout << "AVG VAR ERR" << "\t"; cout << "MAX FAC ERR" << "\t"; cout << "AVG FAC ERR" << "\t"; cout << "LOGZ ERROR" << "\t"; cout << "MAXDIFF" << "\t"; cout << endl; // For each method... for( size_t m = 0; m < methods.size(); m++ ) { // Parse method pair<string, PropertySet> meth = parseNameProperties( methods[m], Aliases ); // Construct object for running the method TestDAI testdai(fg, meth.first, meth.second ); // Run the method testdai.doDAI(); // For the base method, store its variable marginals and logarithm of the partition sum if( m == 0 ) { varMarginals0 = testdai.varMarginals; facMarginals0 = testdai.facMarginals; logZ0 = testdai.logZ; } // Calculate errors relative to base method testdai.calcErrors( varMarginals0, facMarginals0 ); // Output method name cout.width( 39 ); cout << left << methods[m] << "\t"; // Output calculation time, if requested if( report_time ) cout << right << testdai.time << "\t"; // Output number of iterations, if requested if( report_iters ) { if( testdai.has_iters ) { cout << testdai.iters << "\t"; } else { cout << "N/A \t"; } } // If this is not the base method if( m > 0 ) { cout.setf( ios_base::scientific ); cout.precision( 3 ); // Output maximum error in variable marginals Real mev = clipReal( testdai.maxVarErr(), 1e-9 ); cout << mev << "\t"; // Output average error in variable marginals Real aev = clipReal( testdai.avgVarErr(), 1e-9 ); cout << aev << "\t"; // Output maximum error in factor marginals Real mef = clipReal( testdai.maxFacErr(), 1e-9 ); if( mef == INFINITY ) cout << "N/A \t"; else cout << mef << "\t"; // Output average error in factor marginals Real aef = clipReal( testdai.avgFacErr(), 1e-9 ); if( aef == INFINITY ) cout << "N/A \t"; else cout << aef << "\t"; // Output error in log partition sum if( testdai.has_logZ ) { cout.setf( ios::showpos ); Real le = clipReal( testdai.logZ - logZ0, 1e-9 ); cout << le << "\t"; cout.unsetf( ios::showpos ); } else cout << "N/A \t"; // Output maximum difference in last iteration if( testdai.has_maxdiff ) { Real md = clipReal( testdai.maxdiff, 1e-9 ); if( dai::isnan( mev ) ) md = mev; if( dai::isnan( aev ) ) md = aev; if( md == INFINITY ) md = 1.0; cout << md << "\t"; } else cout << "N/A \t"; } cout << endl; // Output marginals, if requested if( marginals == MarginalsOutputType::VAR || marginals == MarginalsOutputType::VARFAC ) for( size_t i = 0; i < testdai.varMarginals.size(); i++ ) cout << "# " << testdai.varMarginals[i] << endl; if( marginals == MarginalsOutputType::FAC || marginals == MarginalsOutputType::VARFAC ) for( size_t I = 0; I < testdai.facMarginals.size(); I++ ) cout << "# " << testdai.facMarginals[I] << endl; if( marginals == MarginalsOutputType::ALL ) for( size_t I = 0; I < testdai.allMarginals.size(); I++ ) cout << "# " << testdai.allMarginals[I] << endl; } return 0; } catch( string &s ) { // Abort with error message cerr << "Exception: " << s << endl; return 2; } }
void MaximumCompositeLikelihood::SetupTrainingData( const std::vector<labeled_instance_type>& training_data, const std::vector<InferenceMethod*> inference_methods) { assert(comp_training_data.size() == 0); assert(comp_inference_methods.size() == 0); assert(inference_methods.size() == training_data.size()); // Number of times each component will be covered unsigned int cover_count = 1; assert(decomp >= -1); if (decomp == DecomposePseudolikelihood) { cover_count = 1; } else if (decomp > 0) { cover_count = decomp; } // Produce composite factor graphs boost::timer decomp_timer; int training_data_size = static_cast<int>(training_data.size()); fg_cc_var_label.resize(cover_count * training_data_size); fg_cc_count.resize(cover_count * training_data_size); fg_orig_index.resize(cover_count * training_data_size); std::fill(fg_cc_count.begin(), fg_cc_count.end(), 0); unsigned int cn = 0; for (int n = 0; n < training_data_size; ++n) { FactorGraph* fg = training_data[n].first; size_t var_count = fg->Cardinalities().size(); // Get observation const FactorGraphObservation* obs = training_data[n].second; // Obtain one or more decomposition(s) for (unsigned int cover_iter = 0; cover_iter < cover_count; ++cover_iter) { VAcyclicDecomposition vac(fg); std::vector<bool> factor_is_removed; if (decomp == DecomposePseudolikelihood) { factor_is_removed.resize(fg->Factors().size()); std::fill(factor_is_removed.begin(), factor_is_removed.end(), true); } else { std::vector<double> factor_weight(fg->Factors().size(), 0.0); if (decomp == DecomposeUniform) { // Use constant weights std::fill(factor_weight.begin(), factor_weight.end(), 1.0); } else { // Use uniform random weights boost::uniform_real<double> uniform_dist(0.0, 1.0); boost::variate_generator<boost::mt19937&, boost::uniform_real<double> > rgen(RandomSource::GlobalRandomSampler(), uniform_dist); for (unsigned int fi = 0; fi < factor_weight.size(); ++fi) factor_weight[fi] = rgen(); } vac.ComputeDecompositionSP(factor_weight, factor_is_removed); } // Shatter factor graph into trees fg_cc_count[cn] += FactorGraphStructurizer::ConnectedComponents( fg, factor_is_removed, fg_cc_var_label[cn]); #if 0 std::cout << "MCL, instance " << n << " decomposed into " << cc_count << " components" << std::endl; #endif // Add each component as separate factor graph for (unsigned int ci = 0; ci < fg_cc_count[cn]; ++ci) { std::vector<unsigned int> cond_var_set; cond_var_set.reserve(var_count); // Add all variables not in this component to the conditioning set for (size_t vi = 0; vi < var_count; ++vi) { if (fg_cc_var_label[cn][vi] != ci) cond_var_set.push_back(static_cast<unsigned int>(vi)); } AddTrainingComponentCond(fg, obs, inference_methods[n], cond_var_set); } fg_orig_index[cn] = n; cn += 1; } } std::cout << "MCL, decomposed " << training_data.size() << " instances " << "into " << comp_training_data.size() << " instances " << (decomp == DecomposeUniform ? "(uniform)" : "(randomized)") << " in " << decomp_timer.elapsed() << "s." << std::endl; // Initialize MLE training data from created components SetupMLETrainingData(); }
VarIds readQueryAndEvidence ( FactorGraph& fg, int argc, const char* argv[], int start) { VarIds queryIds; for (int i = start; i < argc; i++) { const string& arg = argv[i]; if (arg.find ('=') == std::string::npos) { if (Util::isInteger (arg) == false) { cerr << "error: `" << arg << "' " ; cerr << "is not a variable id" ; cerr << endl; exit (0); } VarId vid = Util::stringToUnsigned (arg); VarNode* queryVar = fg.getVarNode (vid); if (queryVar == false) { cerr << "error: unknow variable with id " ; cerr << "`" << vid << "'" << endl; exit (0); } queryIds.push_back (vid); } else { size_t pos = arg.find ('='); string leftArg = arg.substr (0, pos); string rightArg = arg.substr (pos + 1); if (leftArg.empty()) { cerr << "error: missing left argument" << endl; cerr << USAGE << endl; exit (0); } if (Util::isInteger (leftArg) == false) { cerr << "error: `" << leftArg << "' " ; cerr << "is not a variable id" << endl ; exit (0); continue; } VarId vid = Util::stringToUnsigned (leftArg); VarNode* observedVar = fg.getVarNode (vid); if (observedVar == false) { cerr << "error: unknow variable with id " ; cerr << "`" << vid << "'" << endl; exit (0); } if (rightArg.empty()) { cerr << "error: missing right argument" << endl; cerr << USAGE << endl; exit (0); } if (Util::isInteger (rightArg) == false) { cerr << "error: `" << rightArg << "' " ; cerr << "is not a state index" << endl ; exit (0); } unsigned stateIdx = Util::stringToUnsigned (rightArg); if (observedVar->isValidState (stateIdx) == false) { cerr << "error: `" << stateIdx << "' " ; cerr << "is not a valid state index for variable with id " ; cerr << "`" << vid << "'" << endl; exit (0); } observedVar->setEvidence (stateIdx); } } return queryIds; }
int main( int argc, char *argv[] ) { if( argc != 3 ) { cout << "Usage: " << argv[0] << " <in.fg> <tw>" << endl << endl; cout << "Reports some characteristics of the .fg network." << endl; cout << "Also calculates treewidth (which may take some time) unless <tw> == 0." << endl; return 1; } else { // Read factorgraph FactorGraph fg; char *infile = argv[1]; int calc_tw = atoi(argv[2]); fg.ReadFromFile( infile ); cout << "Number of variables: " << fg.nrVars() << endl; cout << "Number of factors: " << fg.nrFactors() << endl; cout << "Connected: " << fg.isConnected() << endl; cout << "Tree: " << fg.isTree() << endl; cout << "Has short loops: " << hasShortLoops(fg.factors()) << endl; cout << "Has negatives: " << hasNegatives(fg.factors()) << endl; cout << "Binary variables? " << fg.isBinary() << endl; cout << "Pairwise interactions? " << fg.isPairwise() << endl; if( calc_tw ) { std::pair<size_t,size_t> tw = treewidth(fg); cout << "Treewidth: " << tw.first << endl; cout << "Largest cluster for JTree has " << tw.second << " states " << endl; } double stsp = 1.0; for( size_t i = 0; i < fg.nrVars(); i++ ) stsp *= fg.var(i).states(); cout << "Total state space: " << stsp << endl; double cavsum_lcbp = 0.0; double cavsum_lcbp2 = 0.0; size_t max_Delta_size = 0; map<size_t,size_t> cavsizes; for( size_t i = 0; i < fg.nrVars(); i++ ) { VarSet di = fg.delta(i); if( cavsizes.count(di.size()) ) cavsizes[di.size()]++; else cavsizes[di.size()] = 1; size_t Ds = fg.Delta(i).nrStates(); if( Ds > max_Delta_size ) max_Delta_size = Ds; cavsum_lcbp += di.nrStates(); for( VarSet::const_iterator j = di.begin(); j != di.end(); j++ ) cavsum_lcbp2 += j->states(); } cout << "Maximum pancake has " << max_Delta_size << " states" << endl; cout << "LCBP with full cavities needs " << cavsum_lcbp << " BP runs" << endl; cout << "LCBP with only pairinteractions needs " << cavsum_lcbp2 << " BP runs" << endl; cout << "Cavity sizes: "; for( map<size_t,size_t>::const_iterator it = cavsizes.begin(); it != cavsizes.end(); it++ ) cout << it->first << "(" << it->second << ") "; cout << endl; cout << "Type: " << (fg.isPairwise() ? "pairwise" : "higher order") << " interactions, " << (fg.isBinary() ? "binary" : "nonbinary") << " variables" << endl; if( fg.isPairwise() ) { bool girth_reached = false; size_t loopdepth; for( loopdepth = 2; loopdepth <= fg.nrVars() && !girth_reached; loopdepth++ ) { size_t nr_loops = countLoops( fg, loopdepth ); cout << "Loops up to " << loopdepth << " variables: " << nr_loops << endl; if( nr_loops > 0 ) girth_reached = true; } if( girth_reached ) cout << "Girth: " << loopdepth-1 << endl; else cout << "Girth: infinity" << endl; } return 0; } }
int main( int argc, char *argv[] ) { try { size_t N, K, k, d, j, n1, n2, n3; size_t prime; size_t seed; Real beta, sigma_w, sigma_th, noise, mean_w, mean_th; string type; size_t states = 2; // Declare the supported options. po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") ("type", po::value<string>(&type), "factor graph type:\n\t'full', 'grid', 'grid_torus', 'dreg', 'loop', 'tree', 'hoi', 'ldpc_random', 'ldpc_group', 'ldpc_small', 'potts3d'") ("seed", po::value<size_t>(&seed), "random number seed (tries to read from /dev/urandom if not specified)") ("N", po::value<size_t>(&N), "number of variables (not for type=='ldpc_small')") ("n1", po::value<size_t>(&n1), "width of 3D grid (only for type=='potts3d')") ("n2", po::value<size_t>(&n2), "height of 3D grid (only for type=='potts3d')") ("n3", po::value<size_t>(&n3), "length of 3D grid (only for type=='potts3d')") ("K", po::value<size_t>(&K), "number of factors\n\t(only for type=='hoi' and 'type=='ldpc_{random,group}')") ("k", po::value<size_t>(&k), "number of variables per factor\n\t(only for type=='hoi' and type=='ldpc_{random,group}')") ("d", po::value<size_t>(&d), "variable connectivity\n\t(only for type=='dreg')") ("j", po::value<size_t>(&j), "number of parity checks per bit\n\t(only for type=='ldpc_{random,group}')") ("prime", po::value<size_t>(&prime), "prime number for construction of LDPC code\n\t(only for type=='ldpc_group')") ("beta", po::value<Real>(&beta), "stddev of log-factor entries\n\t(only for type=='hoi', 'potts3d', 'grid' if states>2)") ("mean_w", po::value<Real>(&mean_w), "mean of pairwise interactions w_{ij}\n\t(not for type=='hoi', 'ldpc_*', 'potts3d')") ("mean_th", po::value<Real>(&mean_th), "mean of singleton interactions th_i\n\t(not for type=='hoi', 'ldpc_*', 'potts3d')") ("sigma_w", po::value<Real>(&sigma_w), "stddev of pairwise interactions w_{ij}\n\t(not for type=='hoi', 'ldpc_*', 'potts3d')") ("sigma_th", po::value<Real>(&sigma_th), "stddev of singleton interactions th_i\n\t(not for type=='hoi', 'ldpc_*', 'potts3d'") ("noise", po::value<Real>(&noise), "bitflip probability for binary symmetric channel (only for type=='ldpc')") ("states", po::value<size_t>(&states), "number of states of each variable (should be 2 for all but type=='grid', 'grid_torus', 'loop', 'potts3d')") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); if( vm.count("help") || !vm.count("type") ) { if( vm.count("type") ) { if( type == FULL_TYPE ) { cout << "Creates fully connected pairwise graphical model of <N> binary variables;" << endl; } else if( type == GRID_TYPE ) { cout << "Creates (non-periodic) 2D Ising grid of (approx.) <N> variables (which need not be binary);" << endl; } else if( type == GRID_TORUS_TYPE ) { cout << "Creates periodic 2D Ising grid of (approx.) <N> variables (which need not be binary);" << endl; } else if( type == DREG_TYPE ) { cout << "Creates random d-regular graph of <N> binary variables with uniform degree <d>" << endl; cout << "(where <d><N> should be even);" << endl; } else if( type == LOOP_TYPE ) { cout << "Creates a pairwise graphical model consisting of a single loop of" << endl; cout << "<N> variables (which need not be binary);" << endl; } else if( type == TREE_TYPE ) { cout << "Creates a pairwise, connected graphical model without cycles (i.e., a tree)" << endl; cout << "of <N> binary variables;" << endl; } else if( type == HOI_TYPE ) { cout << "Creates a random factor graph of <N> binary variables and" << endl; cout << "<K> factors, each factor being an interaction of <k> variables." << endl; cout << "The entries of the factors are exponentials of i.i.d. Gaussian" << endl; cout << "variables with mean 0 and standard deviation <beta>." << endl; } else if( type == LDPC_RANDOM_TYPE ) { cout << "Simulates LDPC decoding problem, using a LDPC code of <N> bits and <K> parity" << endl; cout << "checks, with <k> bits per check and <j> checks per bit, transmitted on a binary" << endl; cout << "symmetric channel with probability <noise> of flipping a bit. The transmitted" << endl; cout << "codeword has all bits set to zero. The LDPC code is randomly generated." << endl; } else if( type == LDPC_GROUP_TYPE ) { cout << "Simulates LDPC decoding problem, using a LDPC code of <N> bits and <K> parity" << endl; cout << "checks, with <k> bits per check and <j> checks per bit, transmitted on a binary" << endl; cout << "symmetric channel with probability <noise> of flipping a bit. The transmitted" << endl; cout << "codeword has all bits set to zero. The LDPC code is constructed (using group" << endl; cout << "theory) using a parameter <prime>; <j> and <k> should both be divisors of <prime>-1." << endl; } else if( type == LDPC_SMALL_TYPE ) { cout << "Simulates LDPC decoding problem, using a LDPC code of 4 bits and 4 parity" << endl; cout << "checks, with 3 bits per check and 3 checks per bit, transmitted on a binary" << endl; cout << "symmetric channel with probability <noise> of flipping a bit. The transmitted" << endl; cout << "codeword has all bits set to zero. The LDPC code is fixed." << endl; } else if( type == POTTS3D_TYPE ) { cout << "Builds 3D Potts model of size <n1>x<n2>x<n3> with nearest-neighbour Potts" << endl; cout << "interactions with <states> states and inverse temperature <beta>." << endl; } else cerr << "Unknown type (should be one of 'full', 'grid', 'grid_torus', 'dreg', 'loop', 'tree', 'hoi', 'ldpc_random', 'ldpc_group', 'ldpc_small', 'potts3d')" << endl; if( type == FULL_TYPE || type == GRID_TYPE || type == GRID_TORUS_TYPE || type == DREG_TYPE || type == LOOP_TYPE || type == TREE_TYPE ) { if( type == GRID_TYPE || type == GRID_TORUS_TYPE || type == LOOP_TYPE ) { cout << "if <states> > 2: factor entries are exponents of Gaussians with mean 0 and standard deviation beta; otherwise," << endl; } cout << "singleton interactions are Gaussian with mean <mean_th> and standard" << endl; cout << "deviation <sigma_th>; pairwise interactions are Gaussian with mean" << endl; cout << "<mean_w> and standard deviation <sigma_w>." << endl; } } cout << endl << desc << endl; return 1; } if( !vm.count("states") ) states = 2; if( !vm.count("seed") ) { ifstream infile; bool success; infile.open( "/dev/urandom" ); success = infile.is_open(); if( success ) { infile.read( (char *)&seed, sizeof(size_t) / sizeof(char) ); success = infile.good(); infile.close(); } if( !success ) throw "Please specify random number seed."; } rnd_seed( seed ); FactorGraph fg; cout << "# Factor graph made by " << argv[0] << endl; cout << "# type = " << type << endl; if( type == FULL_TYPE ) { if( !vm.count("N") || !vm.count("mean_w") || !vm.count("mean_th") || !vm.count("sigma_w") || !vm.count("sigma_th") ) throw "Please specify all required arguments"; MakeFullFG( N, mean_w, mean_th, sigma_w, sigma_th, fg ); cout << "# N = " << N << endl; cout << "# mean_w = " << mean_w << endl; cout << "# mean_th = " << mean_th << endl; cout << "# sigma_w = " << sigma_w << endl; cout << "# sigma_th = " << sigma_th << endl; } else if( type == GRID_TYPE || type == GRID_TORUS_TYPE ) { #define NEED_ARG(name, desc) do { if(!vm.count(name)) throw "Please specify " desc " with --" name; } while(0); if( states > 2 ) { NEED_ARG("N", "number of nodes"); NEED_ARG("beta", "stddev of log-factor entries"); } else { NEED_ARG("N", "number of nodes"); NEED_ARG("mean_w", "mean of pairwise interactions"); NEED_ARG("mean_th", "mean of singleton interactions"); NEED_ARG("sigma_w", "stddev of pairwise interactions"); NEED_ARG("sigma_th", "stddev of singleton interactions"); } size_t n = (size_t)sqrt((long double)N); N = n * n; bool periodic = false; if( type == GRID_TYPE ) periodic = false; else periodic = true; if( states > 2 ) MakeGridNonbinaryFG( periodic, n, states, beta, fg ); else MakeGridFG( periodic, n, mean_w, mean_th, sigma_w, sigma_th, fg ); cout << "# n = " << n << endl; cout << "# N = " << N << endl; if( states > 2 ) cout << "# beta = " << beta << endl; else { cout << "# mean_w = " << mean_w << endl; cout << "# mean_th = " << mean_th << endl; cout << "# sigma_w = " << sigma_w << endl; cout << "# sigma_th = " << sigma_th << endl; } } else if( type == DREG_TYPE ) { if( !vm.count("N") || !vm.count("mean_w") || !vm.count("mean_th") || !vm.count("sigma_w") || !vm.count("sigma_th") || !vm.count("d") ) throw "Please specify all required arguments"; MakeDRegFG( N, d, mean_w, mean_th, sigma_w, sigma_th, fg ); cout << "# N = " << N << endl; cout << "# d = " << d << endl; cout << "# mean_w = " << mean_w << endl; cout << "# mean_th = " << mean_th << endl; cout << "# sigma_w = " << sigma_w << endl; cout << "# sigma_th = " << sigma_th << endl; } else if( type == LOOP_TYPE ) { if( states > 2 ) { if( !vm.count("N") || !vm.count("beta") ) throw "Please specify all required arguments"; } else { if( !vm.count("N") || !vm.count("mean_w") || !vm.count("mean_th") || !vm.count("sigma_w") || !vm.count("sigma_th") ) throw "Please specify all required arguments"; } if( states > 2 ) MakeLoopNonbinaryFG( N, states, beta, fg ); else MakeLoopFG( N, mean_w, mean_th, sigma_w, sigma_th, fg ); cout << "# N = " << N << endl; if( states > 2 ) cout << "# beta = " << beta << endl; else { cout << "# mean_w = " << mean_w << endl; cout << "# mean_th = " << mean_th << endl; cout << "# sigma_w = " << sigma_w << endl; cout << "# sigma_th = " << sigma_th << endl; } } else if( type == TREE_TYPE ) { if( !vm.count("N") || !vm.count("mean_w") || !vm.count("mean_th") || !vm.count("sigma_w") || !vm.count("sigma_th") ) throw "Please specify all required arguments"; MakeTreeFG( N, mean_w, mean_th, sigma_w, sigma_th, fg ); cout << "# N = " << N << endl; cout << "# mean_w = " << mean_w << endl; cout << "# mean_th = " << mean_th << endl; cout << "# sigma_w = " << sigma_w << endl; cout << "# sigma_th = " << sigma_th << endl; } else if( type == HOI_TYPE ) { if( !vm.count("N") || !vm.count("K") || !vm.count("k") || !vm.count("beta") ) throw "Please specify all required arguments"; do { MakeHOIFG( N, K, k, beta, fg ); } while( !fg.isConnected() ); cout << "# N = " << N << endl; cout << "# K = " << K << endl; cout << "# k = " << k << endl; cout << "# beta = " << beta << endl; } else if( type == LDPC_RANDOM_TYPE || type == LDPC_GROUP_TYPE || type == LDPC_SMALL_TYPE ) { if( !vm.count("noise") ) throw "Please specify all required arguments"; if( type == LDPC_RANDOM_TYPE ) { if( !vm.count("N") || !vm.count("K") || !vm.count("j") || !vm.count("k") ) throw "Please specify all required arguments"; if( N * j != K * k ) throw "Parameters should satisfy N * j == K * k"; } else if( type == LDPC_GROUP_TYPE ) { if( !vm.count("prime") || !vm.count("j") || !vm.count("k") ) throw "Please specify all required arguments"; if( !isPrime(prime) ) throw "Parameter <prime> should be prime"; if( !((prime-1) % j == 0 ) ) throw "Parameters should satisfy (prime-1) % j == 0"; if( !((prime-1) % k == 0 ) ) throw "Parameters should satisfy (prime-1) % k == 0"; N = prime * k; K = prime * j; } else if( type == LDPC_SMALL_TYPE ) { N = 4; K = 4; j = 3; k = 3; } cout << "# N = " << N << endl; cout << "# K = " << K << endl; cout << "# j = " << j << endl; cout << "# k = " << k << endl; if( type == LDPC_GROUP_TYPE ) cout << "# prime = " << prime << endl; cout << "# noise = " << noise << endl; // p = 31, j = 3, k = 5 // p = 37, j = 3, k = 4 // p = 7 , j = 2, k = 3 // p = 29, j = 2, k = 4 // Construct likelihood and paritycheck factors Real likelihood[4] = {1.0 - noise, noise, noise, 1.0 - noise}; Real *paritycheck = new Real[1 << k]; MakeParityCheck(paritycheck, k, 0.0); // Create LDPC structure BipartiteGraph ldpcG; bool regular; do { if( type == LDPC_GROUP_TYPE ) ldpcG = CreateGroupStructuredLDPCGraph( prime, j, k ); else if( type == LDPC_RANDOM_TYPE ) ldpcG = CreateRandomBipartiteGraph( N, K, j, k ); else if( type == LDPC_SMALL_TYPE ) ldpcG = CreateSmallLDPCGraph(); regular = true; for( size_t i = 0; i < N; i++ ) if( ldpcG.nb1(i).size() != j ) regular = false; for( size_t I = 0; I < K; I++ ) if( ldpcG.nb2(I).size() != k ) regular = false; } while( !regular && !ldpcG.isConnected() ); // Convert to FactorGraph vector<Factor> factors; for( size_t I = 0; I < K; I++ ) { VarSet vs; for( size_t _i = 0; _i < k; _i++ ) { size_t i = ldpcG.nb2(I)[_i]; vs |= Var( i, 2 ); } factors.push_back( Factor( vs, paritycheck ) ); } delete paritycheck; // Generate noise vector vector<char> noisebits(N,0); size_t bitflips = 0; for( size_t i = 0; i < N; i++ ) { if( rnd_uniform() < noise ) { noisebits[i] = 1; bitflips++; } } cout << "# bitflips = " << bitflips << endl; // Simulate transmission of all-zero codeword vector<char> input(N,0); vector<char> output(N,0); for( size_t i = 0; i < N; i++ ) output[i] = (input[i] + noisebits[i]) & 1; // Add likelihoods for( size_t i = 0; i < N; i++ ) factors.push_back( Factor(Var(i,2), likelihood + output[i]*2) ); // Construct Factor Graph fg = FactorGraph( factors ); } else if( type == POTTS3D_TYPE ) { if( !vm.count("n1") || !vm.count("n2") || !vm.count("n3") || !vm.count("beta") || !vm.count("states") ) throw "Please specify all required arguments"; Make3DPotts( n1, n2, n3, states, beta, fg ); cout << "# N = " << n1*n2*n3 << endl; cout << "# n1 = " << n1 << endl; cout << "# n2 = " << n2 << endl; cout << "# n3 = " << n3 << endl; cout << "# beta = " << beta << endl; cout << "# states = " << states << endl; } else { throw "Invalid type"; } cout << "# seed = " << seed << endl; cout << fg; } catch( const char *e ) { cerr << "Error: " << e << endl; return 1; } return 0; }