maxwalksat.h

00001 /*
00002  * All of the documentation and software included in the
00003  * Alchemy Software is copyrighted by Stanley Kok, Parag
00004  * Singla, Matthew Richardson, Pedro Domingos, Marc
00005  * Sumner, Hoifung Poon, and Daniel Lowd.
00006  * 
00007  * Copyright [2004-07] Stanley Kok, Parag Singla, Matthew
00008  * Richardson, Pedro Domingos, Marc Sumner, Hoifung
00009  * Poon, and Daniel Lowd. All rights reserved.
00010  * 
00011  * Contact: Pedro Domingos, University of Washington
00012  * (pedrod@cs.washington.edu).
00013  * 
00014  * Redistribution and use in source and binary forms, with
00015  * or without modification, are permitted provided that
00016  * the following conditions are met:
00017  * 
00018  * 1. Redistributions of source code must retain the above
00019  * copyright notice, this list of conditions and the
00020  * following disclaimer.
00021  * 
00022  * 2. Redistributions in binary form must reproduce the
00023  * above copyright notice, this list of conditions and the
00024  * following disclaimer in the documentation and/or other
00025  * materials provided with the distribution.
00026  * 
00027  * 3. All advertising materials mentioning features or use
00028  * of this software must display the following
00029  * acknowledgment: "This product includes software
00030  * developed by Stanley Kok, Parag Singla, Matthew
00031  * Richardson, Pedro Domingos, Marc Sumner, Hoifung
00032  * Poon, and Daniel Lowd in the Department of Computer Science and
00033  * Engineering at the University of Washington".
00034  * 
00035  * 4. Your publications acknowledge the use or
00036  * contribution made by the Software to your research
00037  * using the following citation(s): 
00038  * Stanley Kok, Parag Singla, Matthew Richardson and
00039  * Pedro Domingos (2005). "The Alchemy System for
00040  * Statistical Relational AI", Technical Report,
00041  * Department of Computer Science and Engineering,
00042  * University of Washington, Seattle, WA.
00043  * http://www.cs.washington.edu/ai/alchemy.
00044  * 
00045  * 5. Neither the name of the University of Washington nor
00046  * the names of its contributors may be used to endorse or
00047  * promote products derived from this software without
00048  * specific prior written permission.
00049  * 
00050  * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY OF WASHINGTON
00051  * AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
00052  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00053  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00054  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY
00055  * OF WASHINGTON OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00056  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00057  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00058  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00059  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
00060  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00061  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00062  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
00063  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00064  * 
00065  */
00066 #ifndef MAXWALKSAT_H
00067 #define MAXWALKSAT_H
00068 
00069 #include <cfloat>
00070 #include <unistd.h>
00071 #include <sys/time.h>
00072 #include <sys/resource.h>
00073 #include <cstring>
00074 #include <sstream>
00075 #include <fstream>
00076 #include "array.h"
00077 #include "timer.h"
00078 #include "util.h"
00079 #include "sat.h"
00080 #include "maxwalksatparams.h"
00081 
00082 const bool mwsdebug = false;
00083 
00094 class MaxWalkSat : public SAT
00095 {
00096  public:
00097 
00101   MaxWalkSat(VariableState* state, int seed, const bool& trackClauseTrueCnts,
00102              MaxWalksatParams* params)
00103     : SAT(state, seed, trackClauseTrueCnts), printInfo_(true)
00104   {
00105     int numAtoms = state_->getNumAtoms();
00106 
00107       // User-set parameters
00108     maxSteps_ = params->maxSteps;
00109     maxTries_ = params->maxTries;
00110     targetCost_ = params->targetCost;
00111     hard_ = params->hard;
00112       // Set this in the state
00113     state_->setBreakHardClauses(hard_);
00114     numSolutions_ = params->numSolutions;
00115     heuristic_ = params->heuristic;
00116     tabuLength_ = params->tabuLength;
00117     lazyLowState_ = params->lazyLowState;
00118       // For SampleSat
00119     saRatio_ = params->ssParams->saRatio;
00120     saTemp_ = params->ssParams->saTemp;
00121     lateSa_ = params->ssParams->lateSa;
00122     
00123       // Info from SAT class
00124     if (heuristic_ == TABU || heuristic_ == SS)
00125       changed_.growToSize(numAtoms + 1);
00126     numFlips_ = 0;
00127 
00128       // Info from MaxWalkSat
00129     numerator_ = 50; // User-set?
00130     denominator_ = 100; // User-set?
00131       // We assume we are not in a block
00132     inBlock_ = -1;
00133     flipInBlock_ = NOVALUE;
00134 
00135       // Initialize array of function pointers
00136     int idx = 0;
00137     pickcode[idx++] = &MaxWalkSat::pickRandom;
00138     pickcode[idx++] = &MaxWalkSat::pickBest;
00139     pickcode[idx++] = &MaxWalkSat::pickTabu;
00140     pickcode[idx++] = &MaxWalkSat::pickSS;
00141   }
00142   
00146   ~MaxWalkSat()
00147   {
00148 /*
00149     if (state_)
00150     {
00151       delete state_;
00152       state_ = NULL;
00153     }
00154 */
00155   }
00156 
00161   void init()
00162   {
00163       // Init changed array if using tabu
00164     if (heuristic_ == TABU || heuristic_ == SS)
00165     {
00166       int numAtoms = state_->getNumAtoms();
00167       if (changed_.size() != numAtoms + 1)
00168       {
00169         if (changed_.size() < numAtoms + 1)
00170           changed_.growToSize(numAtoms + 1);
00171         else
00172           changed_.shrinkToSize(numAtoms + 1);
00173       }
00174       for (int i = 1; i <= numAtoms; i++)
00175         changed_[i] = -(tabuLength_ + 1);
00176     }
00177     state_->initRandom();
00178   }
00179   
00183   void infer()
00184   {
00185     int numtry = 0;
00186     int numsuccesstry = 0;
00187     long double lowCost = LDBL_MAX;
00188 
00189       /* If keeping track of true clause groundings, then init to zero
00190     if (trackClauseTrueCnts_)
00191       resetCnts();
00192       */
00193 
00194       // Perform up to maxTries tries or numSolutions successful tries
00195     //while (numsuccesstry < numSolutions_ && numtry < maxTries_*numSolutions_)
00196     while (numsuccesstry < numSolutions_ && numtry < maxTries_)
00197     {
00198       numtry++;
00199       numFlips_ = 0;
00200       long double tryLowCost = LDBL_MAX;
00201       int tryLowBad = INT_MAX;
00202       bool lowStateInThisTry = false;
00203       if (lazyLowState_)
00204         varsFlippedSinceLowState_.clear();
00205 
00206         // Make random assigment in subsequent tries
00207         // (for first try, init() should have been called)
00208       if (numtry > 1 && numsuccesstry == 0) init();
00209 
00210       if (printInfo_ || mwsdebug)
00211       {
00212         cout << "\nIn the beginning of try " << numtry << ": " << endl;
00213         cout << "Number of clauses: " << state_->getNumClauses() << endl;
00214         cout << "Number of false clauses: " << state_->getNumFalseClauses()
00215              << endl;
00216         cout << "Cost of false clauses: " << state_->getCostOfFalseClauses()
00217              << endl;
00218         cout << "Target cost: " << targetCost_ << endl;
00219       }
00220       
00221       while (numFlips_ < maxSteps_ && numsuccesstry < numSolutions_)
00222       {
00223         numFlips_++;
00224         flipAtom((this->*(pickcode[heuristic_]))());
00225           // If in a block, then another atom was also chosen to flip
00226         if (inBlock_ > -1)
00227         {
00228           if (mwsdebug)
00229           {
00230             cout << "In block " << inBlock_ << ": flip atom " << flipInBlock_
00231                  << endl;
00232           }
00233           flipAtom(flipInBlock_);
00234         }
00235 
00236           // set in block back to false
00237         inBlock_ = -1;
00238         flipInBlock_ = NOVALUE;
00239         
00240         long double costOfFalseClauses = state_->getCostOfFalseClauses();
00241           // Keep track of lowest num. and cost in this try
00242         if (costOfFalseClauses < tryLowCost)
00243         {
00244           tryLowCost = costOfFalseClauses;
00245           tryLowBad = state_->getNumFalseClauses();
00246         }
00247         
00248           // If the cost of false clauses is less than the low cost
00249           // of all tries, then save this as the low state. Or if we have
00250           // reached a solution the cost could be equal
00251         if ((costOfFalseClauses <= targetCost_ + 0.0001 &&
00252              costOfFalseClauses <= lowCost) || (costOfFalseClauses < lowCost))
00253         {
00254           if (mwsdebug)
00255           {
00256             cout << "Cost of false clauses: " << costOfFalseClauses
00257                  << " less than lowest cost: " << lowCost << endl;
00258           }
00259           lowCost = costOfFalseClauses;
00260           if (!lazyLowState_)
00261             state_->saveLowState();
00262           else
00263           {
00264             varsFlippedSinceLowState_.clear();
00265             lowStateInThisTry = true;
00266           }
00267         }
00268         
00269           // If succesful try
00270           // Add 0.0001 to targetCost_ because of double precision error
00271           // This needs to be fixed
00272         if (costOfFalseClauses <= targetCost_ + 0.0001)
00273           numsuccesstry++;
00274       }
00275       
00276       if (lowStateInThisTry)
00277         reconstructLowState();
00278       state_->saveLowStateToDB();
00279       
00280       if (printInfo_ || mwsdebug)
00281       {
00282         cout<< "In the end of try " << numtry << ": " << endl;
00283         cout<< "Lowest num. of false clauses: " << tryLowBad << endl;
00284         cout<< "Lowest cost of false clauses: " << tryLowCost << endl;
00285         cout<< "Number of flips: " << numFlips_ << endl;
00286         //cout<< "Active atoms " << state_->getNumActiveAtoms() << endl; 
00287       }
00288     }
00289 
00290       // If keeping track of true clause groundings
00291     if (trackClauseTrueCnts_)
00292       tallyCntsFromState();
00293   }
00294   
00295   const int getHeuristic()
00296   {
00297     return heuristic_;
00298   }
00299   
00300   void setHeuristic(const int& heuristic)
00301   {
00302     heuristic_ = heuristic;
00303   }
00304 
00305   int getMaxSteps()
00306   {
00307     return maxSteps_;
00308   }
00309   
00310   void setMaxSteps(const int& maxSteps)
00311   {
00312     maxSteps_ = maxSteps;
00313   }
00314 
00315   void setPrintInfo(const bool& printInfo)
00316   {
00317     printInfo_ = printInfo;
00318   }
00319 
00320   const bool getPrintInfo()
00321   {
00322     return printInfo_;
00323   }
00324   
00325  protected:
00326   
00333   void flipAtom(int toFlip)
00334   {
00335     //if (mwsdebug) cout << "Entering MaxWalkSat::flipAtom" << endl;
00336     if (toFlip == NOVALUE)
00337       return;
00338 
00339       // Flip the atom in the state
00340     state_->flipAtom(toFlip, inBlock_);
00341       // Mark this flip as last time atom was changed if tabu is used
00342     if (heuristic_ == TABU || heuristic_ == SS)
00343     {
00344       changed_.growToSize(state_->getNumAtoms() + 1, -(tabuLength_ + 1));
00345       changed_[toFlip] = numFlips_;
00346     }
00347     
00348     if (lazyLowState_)
00349     {
00350         // Mark this variable as flipped since last low state. If already
00351         // present, then it has been flipped back to its value in the low
00352         // state and we can remove it.
00353       if (varsFlippedSinceLowState_.find(toFlip) !=
00354           varsFlippedSinceLowState_.end())
00355       {
00356         if (mwsdebug)
00357         {
00358           //cout << "Atom " << toFlip << " has been flipped since low state, "
00359           //     << "so removing it" << endl;
00360         }
00361         varsFlippedSinceLowState_.erase(toFlip);
00362       }
00363       else
00364       {
00365         if (mwsdebug)
00366         {
00367           //cout << "Atom " << toFlip << " not flipped since low state, "
00368           //     << "so adding it" << endl;
00369         }
00370         varsFlippedSinceLowState_.insert(toFlip);
00371       }
00372     }
00373     //if (mwsdebug) cout << "Leaving MaxWalkSat::flipAtom" << endl;
00374   }
00375 
00382   int pickRandom()
00383   {
00384     //if (mwsdebug) cout << "Entering MaxWalkSat::pickRandom" << endl;
00385     assert(inBlock_ == -1);
00386     int atomIdx = state_->getIndexOfAtomInRandomFalseClause();
00387     if (atomIdx == NOVALUE) return NOVALUE;
00388     if (state_->getImprovementByFlipping(atomIdx) == -LDBL_MAX) return NOVALUE;
00389 
00390       // if false => fix atoms
00391     bool ignoreActivePreds = false;
00392     bool groundOnly = false;
00393     if (!state_->activateAtom(atomIdx, ignoreActivePreds, groundOnly))
00394       return NOVALUE;
00395 
00396       // If atom is in a block, then another one has to be picked
00397     int blockIdx = state_->getBlockIndex(atomIdx - 1);
00398     if (blockIdx >= 0)
00399     {
00400         // Atom is in a block
00401         // If evidence atom exists or in block of size 1, then can not flip
00402       if (state_->getDomain()->getBlockEvidence(blockIdx) ||
00403           state_->getDomain()->getBlockSize(blockIdx) == 1)
00404         return NOVALUE;
00405       else
00406       {
00407           //Dealing with atom in a block
00408         int blockSize = state_->getDomain()->getBlockSize(blockIdx);
00409           // 0->1: choose atom in block which is already 1
00410         if (state_->getValueOfAtom(atomIdx) == 0)
00411         {
00412           for (int i = 0; i < blockSize; i++)
00413           {
00414             const Predicate* pred =
00415               state_->getDomain()->getPredInBlock(i, blockIdx);
00416             GroundPredicate* gndPred = new GroundPredicate((Predicate*)pred);
00417 
00418             int idx = state_->getIndexOfGroundPredicate(gndPred);
00419 
00420             delete gndPred;
00421             delete pred;
00422 
00423               // Pred is in the state
00424             if (idx >= 0)
00425             {
00426               if (state_->getValueOfAtom(idx + 1) == 1)
00427               {
00428                 inBlock_ = blockIdx;
00429                 flipInBlock_ = idx + 1;
00430                 return atomIdx;
00431               }
00432             }
00433           }
00434         }
00435           // 1->0: choose to flip the other randomly
00436         else
00437         {
00438           bool ok = false;
00439           while (!ok)
00440           {
00441             const Predicate* pred =
00442               state_->getDomain()->getRandomPredInBlock(blockIdx);
00443             GroundPredicate* gndPred = new GroundPredicate((Predicate*)pred);
00444 
00445             int idx = state_->getIndexOfGroundPredicate(gndPred);
00446             
00447             delete gndPred;
00448             delete pred;
00449 
00450             if (idx + 1 != atomIdx)
00451             {
00452               inBlock_ = blockIdx;
00453               flipInBlock_ = idx + 1;
00454               return atomIdx;
00455             }
00456           }
00457         }
00458       }
00459     }
00460     //if (mwsdebug) cout << "Leaving MaxWalkSat::pickRandom" << endl;
00461     return atomIdx;
00462   }
00463 
00470   int pickBest()
00471   {
00472     //if (mwsdebug) cout << "Entering MaxWalkSat::pickBest" << endl;
00473       // With prob. do a noisy pick
00474     bool noisyPick = (numerator_ > 0 && random()%denominator_ < numerator_); 
00475     if (noisyPick)
00476       return pickRandom();
00477 
00478     assert(inBlock_ == -1);
00479       // Clause to fix picked at random    
00480     int toFix = state_->getRandomFalseClauseIndex();
00481     if (toFix == NOVALUE) return NOVALUE;
00482 
00483     if (mwsdebug) cout << "Looking to fix clause " << toFix << endl;
00484 
00485     int clauseSize = state_->getClauseSize(toFix);
00486     long double cost = state_->getClauseCost(toFix);
00487 
00488     long double improvement;
00489       // Arrays to hold information on candidate flips
00490     Array<Array<int> > canNotFlip;
00491       // If var in candidateBlockedVars is chosen, then
00492       // corresponding var in othersToFlip is flipped as well
00493     Array<Array<int> > candidateBlockedVars;
00494     Array<Array<int> > othersToFlip;
00495     Array<int> blockIdx;
00496 
00497     canNotFlip.growToSize(clauseSize);
00498     candidateBlockedVars.growToSize(clauseSize);
00499     othersToFlip.growToSize(clauseSize);
00500     blockIdx.growToSize(clauseSize);
00501 
00502       // Holds the best atoms so far
00503     Array<int> best;
00504       // How many are tied for best
00505     register int numbest = 0;
00506       // Best value so far
00507     long double bestvalue = -LDBL_MAX;
00508 
00509       // Look at each atom and pick best ones
00510     for (int i = 0; i < clauseSize; i++)
00511     {
00512       register int lit = state_->getAtomInClause(i, toFix);
00513         // Neg. clause: Only look at true literals
00514       if (cost < 0 && !state_->isTrueLiteral(lit)) continue;
00515         // var is the index of the atom
00516       register int var = abs(lit);
00517 
00518       if (state_->isFixedAtom(var)) continue;
00519       bool ignoreActivePreds = false;
00520       bool groundOnly = false;
00521           if (!state_->activateAtom(var, ignoreActivePreds, groundOnly))
00522         return NOVALUE;
00523 
00524       improvement = calculateImprovement(var, canNotFlip[i],
00525                                          candidateBlockedVars[i],
00526                                          othersToFlip[i], blockIdx[i]);
00527       if (mwsdebug)
00528         cout << "Improvement of var " << var << " is: " << improvement << endl;
00529 
00530       if (improvement > -LDBL_MAX && improvement >= bestvalue)
00531       { // Tied or better than previous best
00532         if (improvement > bestvalue)
00533         {
00534           numbest = 0;
00535           best.clear();
00536         }
00537         bestvalue = improvement;
00538         best.append(i);
00539         numbest++;
00540       }
00541     }
00542 
00543       // If only false literals in a neg. clause, then numbest is 0
00544     if (numbest == 0) return NOVALUE;
00545       // Choose one of the best at random
00546       // (default if none of the following 2 cases occur)
00547     int toFlip = best[random()%numbest];
00548 
00549       // Exactly one best atom
00550     if (numbest == 1)
00551       toFlip = best[0];
00552     int toFlipAtom = abs(state_->getAtomInClause(toFlip, toFix));
00553 
00554     if (blockIdx[toFlip] > -1)
00555     {
00556         // If atom can not be flipped, then null flip
00557       if (canNotFlip[toFlip].contains(toFlipAtom))
00558         toFlipAtom = NOVALUE;
00559       else
00560       { // If toFlip is in block, then set other to flip
00561         int idx = candidateBlockedVars[toFlip].find(toFlipAtom);
00562         if (idx >= 0)
00563         {
00564           inBlock_ = blockIdx[toFlip];
00565           flipInBlock_ = othersToFlip[toFlip][idx];
00566         }
00567       }
00568     }
00569     
00570     //if (mwsdebug) cout << "Leaving MaxWalkSat::pickBest" << endl;
00571     return toFlipAtom;
00572   }
00573 
00579   int pickTabu()
00580   {
00581     //if (mwsdebug) cout << "Entering MaxWalkSat::pickTabu" << endl;
00582     assert(inBlock_ == -1);
00583       // Clause to fix picked at random    
00584     int toFix = state_->getRandomFalseClauseIndex();
00585     if (toFix == NOVALUE)
00586     {
00587       //if (mwsdebug) cout << "Leaving MaxWalkSat::pickTabu (NOVALUE)" << endl;      
00588       return NOVALUE;
00589     }
00590     if (mwsdebug) cout << "Looking to fix clause " << toFix << endl;
00591 
00592     int clauseSize = state_->getClauseSize(toFix);
00593     long double cost = state_->getClauseCost(toFix);
00594 
00595     long double improvement;
00596       // Arrays to hold information on candidate flips
00597     Array<Array<int> > canNotFlip;
00598       // If var in candidateBlockedVars is chosen, then
00599       // corresponding var in othersToFlip is flipped as well
00600     Array<Array<int> > candidateBlockedVars;
00601     Array<Array<int> > othersToFlip;
00602     Array<int> blockIdx;
00603 
00604     canNotFlip.growToSize(clauseSize);
00605     candidateBlockedVars.growToSize(clauseSize);
00606     othersToFlip.growToSize(clauseSize);
00607     blockIdx.growToSize(clauseSize);
00608 
00609       // Holds the best atoms so far
00610     Array<int> best;
00611       // How many are tied for best
00612     register int numbest = 0;
00613       // Best value so far
00614     long double bestvalue = -LDBL_MAX;
00615 
00616       // With prob. do a noisy pick
00617     bool noisyPick = (numerator_ > 0 && random()%denominator_ < numerator_); 
00618 
00619       // if random move, exclude illegitimate ones and place others in a lottery
00620     if (noisyPick)
00621     {
00622       for (int i = 0; i < clauseSize; i++)
00623       {       
00624         register int lit = state_->getAtomInClause(i, toFix);
00625           // Neg. clause: Only look at true literals
00626         if (cost < 0 && !state_->isTrueLiteral(lit)) continue;
00627           // var is the index of the atom
00628         register int var = abs(lit);
00629 
00630         if (state_->isFixedAtom(var)) continue;
00631         bool ignoreActivePreds = false;
00632         bool groundOnly = false;
00633         if (!state_->activateAtom(var, ignoreActivePreds, groundOnly))
00634           return NOVALUE;
00635 
00636           // We don't need improvement, but this function fills the block arrays
00637         calculateImprovement(var, canNotFlip[i],
00638                              candidateBlockedVars[i],
00639                              othersToFlip[i], blockIdx[i]);
00640 
00641         best.append(i);
00642         numbest++;
00643       }
00644     }
00645       // greedy: pick the best value
00646     else for (int i = 0; i < clauseSize; i++)
00647     {
00648       register int lit = state_->getAtomInClause(i, toFix);
00649         // Neg. clause: Only look at true literals
00650       if (cost < 0 && !state_->isTrueLiteral(lit)) continue;
00651         // var is the index of the atom
00652       register int var = abs(lit);
00653       
00654       if (state_->isFixedAtom(var)) continue;
00655       bool ignoreActivePreds = false;
00656       bool groundOnly = false;
00657       if (!state_->activateAtom(var, ignoreActivePreds, groundOnly))
00658         return NOVALUE;
00659 
00660       improvement = calculateImprovement(var, canNotFlip[i],
00661                                          candidateBlockedVars[i],
00662                                          othersToFlip[i], blockIdx[i]);
00663       if (mwsdebug)
00664         cout << "Improvement of var " << var << " is: " << improvement << endl;
00665 
00666         // TABU: If pos. improvement, then always add it to best
00667       if (improvement > 0 && improvement >= bestvalue)
00668       { // Tied or better than previous best
00669         if (improvement > bestvalue)
00670         {
00671           numbest = 0;
00672           best.clear();
00673         }
00674         bestvalue = improvement;
00675         best.append(i);
00676         numbest++;
00677       }
00678       else if (improvement > -LDBL_MAX && 
00679                tabuLength_ < numFlips_ - changed_[var])
00680       {
00681         if (improvement >= bestvalue)
00682         { // Tied or better than previous best
00683           if (improvement > bestvalue)
00684           {
00685             numbest = 0;
00686             best.clear();
00687           }
00688           bestvalue = improvement;
00689           best.append(i);
00690           numbest++;
00691         }
00692       }
00693     }
00694 
00695     if (numbest == 0) 
00696     {
00697       //if (mwsdebug) cout << "Leaving MaxWalkSat::pickTabu (NOVALUE)" << endl;
00698       return NOVALUE;
00699     }
00700 
00701     int toFlip = NOVALUE;
00702       // Exactly one best atom
00703     if (numbest == 1)
00704       toFlip = best[0];
00705     else
00706       // Choose one of the best at random
00707       toFlip = best[random()%numbest];
00708     int toFlipAtom = abs(state_->getAtomInClause(toFlip, toFix));
00709 
00710     if (blockIdx[toFlip] > -1)
00711     {
00712         // If atom can not be flipped, then null flip
00713       if (canNotFlip[toFlip].contains(toFlipAtom))
00714         toFlipAtom = NOVALUE;
00715       else
00716       { // If toFlip is in block, then set other to flip
00717         int idx = candidateBlockedVars[toFlip].find(toFlipAtom);
00718         if (idx >= 0)
00719         {
00720           inBlock_ = blockIdx[toFlip];
00721           flipInBlock_ = othersToFlip[toFlip][idx];
00722         }
00723       }
00724     }
00725 
00726     //if (mwsdebug) cout << "Leaving MaxWalkSat::pickTabu" << endl;
00727     return toFlipAtom;
00728   }
00729 
00736   int pickSS()
00737   {
00738     //if (mwsdebug) cout << "Entering MaxWalkSat::pickSS" << endl;
00739     assert(inBlock_ == -1);
00740     Array<int> canNotFlip;
00741       // If var in candidateBlockedVars is chosen, then
00742       // corresponding var in othersToFlip is flipped as well
00743     Array<int> candidateBlockedVars;
00744     Array<int> othersToFlip;
00745     int blockIdx;
00746     
00747     long double costOfFalseClauses = state_->getCostOfFalseClauses();
00748 
00749       // If we have already reached a solution or if in an SA step,
00750       // then perform SA
00751       // Add 0.0001 to targetCost_ because of double precision error
00752       // This needs to be fixed
00753     if (costOfFalseClauses <= targetCost_ + 0.0001 ||
00754         (!lateSa_ && random() % 100 < saRatio_ ))
00755     {
00756         // Choose a random atom to flip
00757       int toFlip = state_->getIndexOfRandomAtom();
00758       if (toFlip == NOVALUE) return NOVALUE;
00759 
00760       if (state_->isFixedAtom(toFlip)) return NOVALUE;
00761       bool ignoreActivePreds = false;
00762         // SA step: we don't want to activate
00763       bool groundOnly = true;
00764           if (!state_->activateAtom(toFlip, ignoreActivePreds, groundOnly))
00765         return NOVALUE;
00766       long double improvement = calculateImprovement(toFlip, canNotFlip,
00767                                                      candidateBlockedVars,
00768                                                      othersToFlip, blockIdx);
00769       if (mwsdebug) cout << "Total improvement: " << improvement << endl;
00770 
00771         // If pos. or no improvement, then the atom will be flipped
00772         // Or neg. improvement: According to temp. flip atom anyway
00773       if (improvement > -LDBL_MAX && 
00774           ((improvement >= 0) ||
00775           (random() <= exp(improvement/(saTemp_/100.0)) * RAND_MAX)))
00776       {
00777           // If atom can not be flipped, then null flip
00778         if (canNotFlip.contains(toFlip)) toFlip = NOVALUE;
00779         else
00780         { // If toflip is in block, then set other to flip
00781           int idx = candidateBlockedVars.find(toFlip);
00782           if (idx >= 0)
00783           {
00784             inBlock_ = blockIdx;
00785             flipInBlock_ = othersToFlip[idx];
00786           }
00787         }
00788         
00789           // SA
00790         if (toFlip != NOVALUE)
00791         {
00792           state_->setActive(toFlip);
00793         }
00794         return toFlip;
00795       }
00796       else
00797       {
00798         return NOVALUE;
00799       }
00800     }
00801       // Not in a solution or SA step: perform normal MWS step
00802     else
00803     {
00804       return pickTabu();
00805     }
00806   }
00807 
00808 
00826   long double calculateImprovement(const int& atomIdx, Array<int>& canNotFlip,
00827                                    Array<int>& candidateBlockedVars,
00828                                    Array<int>& othersToFlip, int& blockIdx)
00829   {
00830     blockIdx = state_->getBlockIndex(atomIdx - 1);
00831     if (blockIdx == -1)
00832     {
00833         // Atom not in a block
00834       return state_->getImprovementByFlipping(atomIdx);
00835     }
00836     else
00837     {
00838         // Atom is in a block
00839         // If evidence atom exists or in block of size 1, then can not flip
00840       if (state_->getDomain()->getBlockEvidence(blockIdx) ||
00841           state_->getDomain()->getBlockSize(blockIdx) == 1)
00842       {
00843         canNotFlip.append(atomIdx);
00844         return -LDBL_MAX;
00845       }
00846       else
00847       {
00848           // Dealing with atom in a block
00849         int blockSize = state_->getDomain()->getBlockSize(blockIdx);
00850         int chosen = -1;
00851         int otherToFlip = -1;
00852 
00853           // 0->1: choose atom in block which is already 1
00854         if (state_->getValueOfAtom(atomIdx) == 0)
00855         {
00856           for (int i = 0; i < blockSize; i++)
00857           {
00858             const Predicate* pred =
00859               state_->getDomain()->getPredInBlock(i, blockIdx);
00860             GroundPredicate* gndPred = new GroundPredicate((Predicate*)pred);
00861 
00862             int idx = state_->getIndexOfGroundPredicate(gndPred);
00863 
00864             delete gndPred;
00865             delete pred;
00866 
00867               // Pred is in the state
00868             if (idx >= 0)
00869             {
00870               if (state_->getValueOfAtom(idx + 1) == 1)
00871               {
00872                 chosen = idx + 1;
00873                 break;
00874               }
00875             }
00876           }
00877         }
00878           // 1->0: choose to flip the other randomly
00879           // TODO: choose to flip the other with best improvement
00880         else
00881         {
00882           bool ok = false;
00883           while (!ok)
00884           {
00885             const Predicate* pred =
00886               state_->getDomain()->getRandomPredInBlock(blockIdx);
00887             GroundPredicate* gndPred = new GroundPredicate((Predicate*)pred);
00888 
00889             int idx = state_->getIndexOfGroundPredicate(gndPred);
00890             if (idx == -1)
00891             {
00892               delete gndPred;
00893               delete pred;
00894               continue;
00895               // TODO: Other atom not yet in state (lazy) - must activate it              
00896             }
00897             chosen = idx + 1;
00898             
00899             delete gndPred;
00900             delete pred;
00901 
00902             if (chosen != atomIdx)
00903               ok = true;
00904           }
00905         }
00906 
00907         assert(chosen >= 0);
00908         candidateBlockedVars.append(atomIdx);
00909         otherToFlip = chosen;
00910         othersToFlip.append(otherToFlip);
00911         return (state_->getImprovementByFlipping(atomIdx) +
00912                 state_->getImprovementByFlipping(otherToFlip));
00913       }
00914     }
00915   }
00916 
00921   void reconstructLowState()
00922   {
00923     assert(lazyLowState_);
00924     if (mwsdebug) cout << "Reconstructing low state ..." << endl;
00925     hash_set<int>::const_iterator it = varsFlippedSinceLowState_.begin();
00926     for (; it != varsFlippedSinceLowState_.end(); it++)
00927     {
00928       if (mwsdebug)
00929       {
00930         cout << "Flipping atom " << (*it) << endl;
00931       }
00932       state_->flipAtomValue(*it, -1);
00933     }
00934     state_->saveLowState();
00935     if (mwsdebug) cout << "Done reconstructing low state ..." << endl;
00936   }
00937 
00938  private:
00939  
00941     // Heuristic to be used to pick an atom
00942   int heuristic_;
00943     // At least this many flips must occur before flipping the same atom
00944   int tabuLength_;
00945   
00946     // BEGIN: SampleSat parameters
00947     // Percent of sim. annealing steps
00948   int saRatio_;
00949     // Sim. annealing temperature
00950   int saTemp_;
00951     // Run sim. annealing only at a plateur
00952   bool lateSa_;
00953     // END: SampleSat parameters
00955 
00956     // Function pointer holds which function is to be used to pick an atom
00957     // = {pickRandom, pickBest, pickTabu, pickSS};
00958   int (MaxWalkSat::*pickcode[15])(void);
00959     // If true, then a hard clause can be broken
00960   bool hard_;
00961 
00962     // Make random flip with numerator/denominator probability
00963   int numerator_;
00964   int denominator_;
00965       
00966     // Which other atom to flip in block
00967   int inBlock_;
00968   int flipInBlock_;
00969   
00970     // If false, the naive way of saving low states (each time a low state is
00971     // found, the whole state is saved) is used; otherwise, a list of variables
00972     // flipped since the last low state is kept and the low state is
00973     // reconstructed. This can be much faster for very large data sets.
00974   bool lazyLowState_;
00975     // List of variables flipped since last low state was found. This is used
00976     // to reconstruct the low state when lazyLowState_ is set.
00977   hash_set<int> varsFlippedSinceLowState_;
00978 
00979     // If true, information for each try is printed out.
00980   bool printInfo_;
00981 };
00982 
00983 #endif

Generated on Sun Jun 7 11:55:12 2009 for Alchemy by  doxygen 1.5.1