featureSet.cc
Go to the documentation of this file.
00001 /* featureSet.cc
00002  */
00003 #include "osl/move_probability/featureSet.h"
00004 #include "osl/move_probability/feature.h"
00005 #include "osl/move_generator/legalMoves.h"
00006 #include "osl/container/moveVector.h"
00007 #include "osl/record/csa.h"
00008 #include "osl/misc/binaryIO.h"
00009 #include "osl/oslConfig.h"
00010 #include <boost/accumulators/accumulators.hpp>
00011 #include <boost/accumulators/statistics/stats.hpp>
00012 #include <boost/accumulators/statistics/mean.hpp>
00013 #include <boost/accumulators/statistics/min.hpp>
00014 #include <boost/accumulators/statistics/max.hpp>
00015 #include <boost/foreach.hpp>
00016 #include <boost/format.hpp>
00017 #include <boost/thread/mutex.hpp>
00018 #include <fstream>
00019 #include <iomanip>
00020 #include <iostream>
00021 #include <cstdio>
00022 
00023 osl::move_probability::
00024 FeatureSet::FeatureSet()
00025 {
00026 }
00027 
00028 osl::move_probability::
00029 FeatureSet::~FeatureSet()
00030 {
00031 }
00032 
00033 void osl::move_probability::
00034 FeatureSet::pushBack(Feature *f, bool light)
00035 {
00036   features.push_back(f);
00037   if (light)
00038     light_features.push_back(features.size()-1);
00039 }
00040 
00041 void osl::move_probability::
00042 FeatureSet::addFinished()
00043 {
00044   offsets.resize(features.size()+1);
00045   offsets[0] = 0;
00046   for (size_t i=0; i<features.size(); ++i)
00047     offsets[i+1] = offsets[i] + features[i].dimension();
00048 }
00049 
00050 double osl::move_probability::FeatureSet::
00051 matchExp(const StateInfo& state, Move move, const double * weights) const
00052 {
00053   MoveInfo info(state, move);
00054   assert(offsets.size() == features.size()+1);
00055   double sum = 0.0;
00056   for (size_t i=0; i<features.size(); ++i) {
00057     sum += features[i].match(state, info, offsets[i], weights);
00058   }
00059   return exp(sum);
00060 }
00061 
00062 double osl::move_probability::FeatureSet::
00063 matchLight(const StateInfo& state, Move move, const double * weights) const
00064 {
00065   MoveInfo info(state, move);
00066   assert(offsets.size() == features.size()+1);
00067   double sum = 0.0;
00068   BOOST_FOREACH(size_t i, light_features) {
00069     sum += features[i].match(state, info, offsets[i], weights);
00070   }
00071   return sum;
00072 }
00073 
00074 
00075 void osl::move_probability::FeatureSet::
00076 analyze(const StateInfo& state, Move move, const double * weights) const
00077 {
00078   MoveInfo info(state, move);
00079   std::cerr << record::csa::show(move) << "\n";
00080   vector<std::pair<double, std::string> > out;
00081   for (size_t i=0; i<features.size(); ++i) {
00082     double s = features[i].match(state, info, offsets[i], weights);
00083     if (s)
00084       out.push_back(make_pair(s, features[i].name()));
00085   }
00086   std::sort(out.begin(), out.end());
00087   std::reverse(out.begin(), out.end());
00088   for (size_t i=0; i<out.size(); ++i) {
00089     std::cerr << boost::format("%16s %6.2f ") % out[i].second % out[i].first;
00090     if (i % 3 == 2)
00091       std::cerr << "\n";
00092   }
00093   if (out.size() % 3 != 0)
00094     std::cerr << "\n";
00095 }
00096 
00097 double osl::move_probability::FeatureSet::
00098 generateRating(const StateInfo& state, WeightedMoveVector& out,
00099                const double * weights) const
00100 {
00101   assert(! state.dirty);
00102   MoveVector moves;
00103   LegalMoves::generate(*state.state, moves);
00104   double sum = 0.0;
00105   FixedCapacityVector<Move,128> unpromote_moves;
00106   BOOST_FOREACH(Move move, moves) {
00107     double score = matchExp(state, move, weights);
00108     out.push_back(WeightedMove(score, move));
00109     sum += score;
00110   }
00111   return sum;
00112 }
00113 
00114 void osl::move_probability::FeatureSet::
00115 ratingToLogProb(const WeightedMoveVector& rating,
00116                 double sum, MoveLogProbVector& out)
00117 {
00118   static const double scale = 100.0 / log(0.5);
00119   BOOST_FOREACH(WeightedMove move, rating) {
00120     double p = move.first/sum;
00121     if (std::isnan(p) || p <= 1.0/(1<<12)) 
00122       p = 1.0/(1<<12);
00123     const int logp = std::max(50, static_cast<int>(log(p)*scale));
00124     out.push_back(MoveLogProb(move.second, logp));
00125   }
00126   out.sortByProbability();
00127 }
00128 
00129 void osl::move_probability::FeatureSet::
00130 generateLogProb(const StateInfo& state, MoveLogProbVector& out,
00131                 const double * weights) const
00132 {
00133   WeightedMoveVector moves;
00134   double sum = generateRating(state, moves, weights);
00135   ratingToLogProb(moves, sum, out);
00136 }
00137 
00138 bool osl::move_probability::FeatureSet::
00139 load(const char *base_filename, double * weights) const
00140 {
00141   std::string filename = std::string(base_filename) + ".txt";
00142   std::fill(weights, weights+dimension(), 0.0);
00143   std::ifstream is(filename.c_str());
00144   for (int i=0; i<dimension(); ++i) {
00145     is >> weights[i];
00146     if (! is) {
00147       std::cerr << "load failed at " << i << " in " << dimension()
00148                 << " file " << filename << "\n";
00149       break;
00150     }
00151   }
00152   return is;
00153 }
00154 
00155 bool osl::move_probability::FeatureSet::
00156 load_binary(const char *base_filename, double * weights) const
00157 {
00158   std::string filename = std::string(base_filename) + ".bin";
00159   std::fill(weights, weights+dimension(), 0.0);
00160   std::ifstream is(filename.c_str(), std::ios_base::binary);
00161   misc::BinaryElementReader<double> reader(is);
00162   for (int i=0; i<dimension(); ++i) {
00163     if (! reader.hasNext()) {
00164       std::cerr << "load failed at " << i << " in " << dimension()
00165                 << " file " << filename << "\n";
00166       return false;
00167     }
00168     double value = reader.read();
00169     weights[i] = value;
00170   }
00171   return true;
00172 }
00173 
00174 void osl::move_probability::FeatureSet::
00175 showSummary(const double * weights) const
00176 {
00177   for (size_t i=0; i<features.size(); ++i) {
00178     const Feature& f = features[i];
00179 #if (__GNUC_MINOR__ < 5)
00180     using namespace boost::accumulators;
00181     accumulator_set<double, stats<tag::mean, tag::min, tag::max> > acc;
00182 #endif
00183     int zero = 0;
00184     for (int j=offsets[i]; j<offsets[i+1]; ++j)
00185       if (weights[j]) {
00186 #if (__GNUC_MINOR__ < 5)
00187         acc(weights[j]);
00188 #endif
00189       }
00190       else 
00191         ++zero;
00192     std::cerr << std::setw(16) << f.name() 
00193               << " dim " << std::setw(5) << f.dimension() - zero
00194               << "/" << std::setw(5) << f.dimension()
00195 #if (__GNUC_MINOR__ < 5)
00196               << " min " << std::setw(6) << min(acc)
00197               << " max " << std::setw(6) << max(acc)
00198               << " mean " << std::setw(6) << mean(acc)
00199 #endif
00200               << "\n";
00201   }
00202 }
00203 
00204 
00205 
00206 boost::scoped_array<double> osl::move_probability::
00207 StandardFeatureSet::weights;
00208 boost::scoped_array<double> osl::move_probability::
00209 StandardFeatureSet::tactical_weights;
00210 
00211 osl::move_probability::StandardFeatureSet::
00212 StandardFeatureSet() : initialized(false)
00213 {
00214   pushBack(new TakeBackFeature, 1);
00215   pushBack(new CheckFeature, 1);
00216   pushBack(new SeeFeature, 1);
00217   pushBack(new ContinueCapture, 1);
00218   pushBack(new DropCaptured);
00219   pushBack(new SquareY, 1);
00220   pushBack(new SquareX, 1);
00221   pushBack(new KingRelativeY, 1);
00222   pushBack(new KingRelativeX, 1);
00223   pushBack(new FromEffect, 1);
00224   pushBack(new ToEffect, 1);
00225   pushBack(new FromEffectLong, 1);
00226   pushBack(new ToEffectLong, 1);
00227   pushBack(new Pattern(0,-1));  // U
00228   pushBack(new Pattern(1,-1));  // UL
00229   pushBack(new Pattern(1,0));   // L
00230   pushBack(new Pattern(0,1));   // D
00231   pushBack(new Pattern(1,1));   // DL
00232   pushBack(new Pattern(1,-2));  // UUL
00233   pushBack(new Pattern(0,-2));  // UU
00234   pushBack(new Pattern(0,2));   // DD
00235   pushBack(new Pattern(2,0));   // LL
00236   pushBack(new Pattern(1,2));   // DDL
00237   pushBack(new MoveFromOpposingSliders);
00238   pushBack(new AttackToOpposingSliders);
00239   pushBack(new PawnAttack);
00240   pushBack(new CapturePtype, 1);
00241   pushBack(new BlockLong);
00242   pushBack(new BlockLongFrom);
00243   pushBack(new LanceAttack);
00244   pushBack(new BishopAttack);
00245   pushBack(new RookAttack);
00246   pushBack(new BreakThreatmate);
00247   pushBack(new SendOff);
00248   pushBack(new CheckmateIfCapture);
00249   pushBack(new OpposingPawn);
00250   pushBack(new DropAfterOpposingPawn);
00251   pushBack(new LongRecapture);
00252   pushBack(new SacrificeAttack);
00253   pushBack(new AddEffectLong);
00254   pushBack(new King5x5Ptype);
00255   pushBack(new KingBlockade);
00256   pushBack(new CoverFork);
00257   pushBack(new ThreatmateByCapture);
00258   pushBack(new LureDefender);
00259   pushBack(new CoverPawn);
00260   pushBack(new PromotionBySacrifice);
00261   pushBack(new EscapeThreatened);
00262   pushBack(new BookMove);
00263   addFinished();
00264 }
00265 
00266 osl::move_probability::StandardFeatureSet::
00267 ~StandardFeatureSet()
00268 {
00269 }
00270 
00271 const osl::move_probability::StandardFeatureSet& 
00272 osl::move_probability::StandardFeatureSet::
00273 instance(bool verbose)
00274 {
00275   static StandardFeatureSet the_instance;
00276   the_instance.setUp(verbose);
00277   return the_instance;
00278 }
00279 
00280 bool osl::move_probability::StandardFeatureSet::
00281 healthCheck()
00282 {
00283   return instance(true).ok();
00284 }
00285 
00286 namespace osl
00287 {
00288   namespace move_probability 
00289   {
00290     boost::mutex standardfeatureset_lock;
00291   }
00292 }
00293 bool osl::move_probability::StandardFeatureSet::
00294 setUp(bool verbose)
00295 {
00296   boost::mutex::scoped_lock lk(standardfeatureset_lock);
00297   static bool initialized = false;
00298   if (initialized)
00299     return true;
00300   initialized = true;
00301   weights.reset(new double[dimension()]);
00302   std::string filename = OslConfig::home();
00303   filename += "/data/move-order";
00304   if (verbose)
00305     std::cerr << "loading " << filename << ".bin ";
00306   const bool success = load_binary(filename.c_str(), &weights[0]);
00307   if (verbose)
00308     std::cerr << (success ? "success" : "failed\a") << "\n";
00309 
00310   filename = OslConfig::home();
00311   filename += "/data/move-tactical.txt";
00312   const int tactical_dimension = 8*4;
00313   tactical_weights.reset(new double[tactical_dimension]);
00314   if (verbose)
00315     std::cerr << "loading " << filename << " ";
00316   std::ifstream is(filename.c_str());
00317   for (int i=0; i<tactical_dimension; ++i)
00318     is >> tactical_weights[i];
00319   if (verbose)
00320     std::cerr << (is ? "success" : "failed\a") << "\n";
00321   this->initialized = success && is;
00322   return this->initialized;
00323 }
00324 
00325 void osl::move_probability::StandardFeatureSet::
00326 generateLogProb(const StateInfo& state, MoveLogProbVector& out) const
00327 {
00328   FeatureSet::generateLogProb(state, out, &weights[0]);
00329 }
00330 
00331 void osl::move_probability::StandardFeatureSet::
00332 generateLogProb2(const StateInfo& state, MoveLogProbVector& out) const
00333 {
00334   WeightedMoveVector moves;
00335   double sum = FeatureSet::generateRating(state, moves, &weights[0]);
00336   double elapsed = 0.0, welapsed = 0.0, last_p = 1.0;
00337   std::sort(moves.begin(), moves.end());
00338   for (int i=moves.size()-1; i>=0; --i) {
00339     WeightedMove move = moves[i];
00340     static const double scale = 100.0 / log(0.5);
00341     if (i+1<(int)moves.size())
00342       welapsed = std::max(welapsed, std::min(moves[i+1].first,move.first*4));
00343     double p = move.first/(sum-elapsed+welapsed);
00344     if (std::isnan(p) || p <= 1.0/(1<<12)) 
00345       p = 1.0/(1<<12);
00346     else
00347       p = std::min(last_p, p);
00348     int logp = std::max(50, static_cast<int>(log(p)*scale));
00349     if (moves.size() - i <= 8)
00350       logp = std::min(logp, 300);
00351     else if (moves.size() - i <= 16)
00352       logp = std::min(logp, 500);
00353     out.push_back(MoveLogProb(move.second, logp));
00354     elapsed += move.first;
00355     welapsed = (welapsed+move.first)*(moves.size()-i)/moves.size();
00356   }
00357 }
00358 
00359 void osl::move_probability::StandardFeatureSet::
00360 generateLogProb(const StateInfo& state, int /*limit*/, MoveLogProbVector& out, bool /*in_pv*/) const
00361 {
00362   generateLogProb2(state, out);
00363 }
00364 
00365 double osl::move_probability::StandardFeatureSet::
00366 matchLight(const StateInfo& state, Move move) const
00367 {
00368   return FeatureSet::matchLight(state, move, &weights[0]);
00369 }
00370 
00371 int osl::move_probability::StandardFeatureSet::
00372 logProbTakeBack(const StateInfo& state, Move target) const
00373 {
00374   const int progress8 = state.progress8();
00375   const double sum = matchLight(state, target);
00376   return tacticalLogProb(progress8*4 + 0, sum);
00377 }
00378 
00379 int osl::move_probability::StandardFeatureSet::
00380 logProbSeePlus(const StateInfo& state, Move target) const
00381 {
00382   const int progress8 = state.progress8();
00383   const double sum = matchLight(state, target);
00384   return tacticalLogProb(progress8*4 + 2, sum);
00385 }
00386 
00387 int osl::move_probability::StandardFeatureSet::
00388 tacticalLogProb(int offset, double sum) const
00389 {
00390   static const double scale = 100.0 / log(0.5);
00391   double x = tactical_weights[offset] * sum + tactical_weights[offset+1];
00392   double p = 1/(1.0+exp(-x));
00393   return std::max(50, static_cast<int>(log(p)*scale));
00394 }
00395 
00396 
00397 
00398 
00399 // ;;; Local Variables:
00400 // ;;; mode:c++
00401 // ;;; c-basic-offset:2
00402 // ;;; End:
00403 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines