Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

SgBookBuilder.cpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file SgBookBuilder.cpp  */
00003 //----------------------------------------------------------------------------
00004 
00005 #include "SgSystem.h"
00006 #include "SgBookBuilder.h"
00007 
00008 #include <sstream>
00009 #include <boost/numeric/conversion/bounds.hpp>
00010 #include "SgDebug.h"
00011 #include "SgPoint.h"
00012 #include "SgTimer.h"
00013 
00014 //----------------------------------------------------------------------------
00015 
00016 const float SgBookNode::LEAF_PRIORITY = 0.0;
00017 
00018 bool SgBookNode::IsTerminal() const
00019 {
00020     if (m_value < -100 || m_value > 100)
00021         return true;
00022     return false;
00023 }
00024 
00025 bool SgBookNode::IsLeaf() const
00026 {
00027     return m_count == 0;
00028 }
00029 
00030 std::string SgBookNode::ToString() const
00031 {
00032     std::ostringstream os;
00033     os << std::showpos << std::fixed << std::setprecision(6);
00034     os << "Val " << m_value;
00035     os << std::noshowpos << " ExpP " << m_priority;
00036     os << std::showpos << " Heur " << m_heurValue << " Cnt " << m_count;
00037     return os.str();
00038 }
00039 
00040 void SgBookNode::FromString(const std::string& str)
00041 {
00042     std::istringstream is(str);
00043     std::string dummy;
00044     is >> dummy;
00045     is >> m_value;
00046     is >> dummy;
00047     is >> m_priority;
00048     is >> dummy;
00049     is >> m_heurValue;
00050     is >> dummy;
00051     is >> m_count;
00052 }
00053 
00054 //----------------------------------------------------------------------------
00055 
00056 SgBookBuilder::SgBookBuilder()
00057     : m_alpha(50),
00058       m_useWidening(true),
00059       m_expandWidth(16),
00060       m_expandThreshold(1000),
00061       m_flushIterations(100)
00062 {
00063 }
00064 
00065 SgBookBuilder::~SgBookBuilder()
00066 {
00067 }
00068 
00069 //----------------------------------------------------------------------------
00070 
00071 void SgBookBuilder::StartIteration()
00072 {
00073     // DEFAULT IMPLEMENTATION DOES NOTHING
00074 }
00075 
00076 void SgBookBuilder::EndIteration()
00077 {
00078     // DEFAULT IMPLEMENTATION DOES NOTHING
00079 }
00080 
00081 void SgBookBuilder::BeforeEvaluateChildren()
00082 {
00083     // DEFAULT IMPLEMENTATION DOES NOTHING
00084 }
00085 
00086 void SgBookBuilder::AfterEvaluateChildren()
00087 {
00088     // DEFAULT IMPLEMENTATION DOES NOTHING
00089 }
00090 
00091 void SgBookBuilder::Init()
00092 {
00093     // DEFAULT IMPLEMENTATION DOES NOTHING
00094 }
00095 
00096 void SgBookBuilder::Fini()
00097 {
00098     // DEFAULT IMPLEMENTATION DOES NOTHING
00099 }
00100 
00101 void SgBookBuilder::Expand(int numExpansions)
00102 {
00103     m_numEvals = 0;
00104     m_numWidenings = 0;
00105 
00106     SgTimer timer;
00107     Init();
00108     EnsureRootExists();
00109     int num = 0;
00110     for (; num < numExpansions; ++num) 
00111     {
00112         {
00113             std::ostringstream os;
00114             os << "\n--Iteration " << num << "--\n";
00115             PrintMessage(os.str());
00116         }
00117         {
00118             SgBookNode root;
00119             GetNode(root);
00120             if (root.IsTerminal()) 
00121             {
00122                 PrintMessage("Root is terminal!\n");
00123                 break;
00124             }
00125         }
00126         StartIteration();
00127         std::vector<SgMove> pv;
00128         DoExpansion(pv);
00129         EndIteration();
00130 
00131         if (num && (num % m_flushIterations) == 0) 
00132             FlushBook();
00133     }
00134     FlushBook();
00135     Fini();
00136     timer.Stop();
00137     double elapsed = timer.GetTime();
00138     std::ostringstream os;
00139     os << '\n'
00140        << "Statistics\n"
00141        << "Total Time     " << elapsed << '\n'
00142        << "Expansions     " << num 
00143        << std::fixed << std::setprecision(2) 
00144        << " (" << (num / elapsed) << "/s)\n"
00145        << "Evaluations    " << m_numEvals 
00146        << std::fixed << std::setprecision(2)
00147        << " (" << (double(m_numEvals) / elapsed) << "/s)\n"
00148        << "Widenings      " << m_numWidenings << '\n';
00149     PrintMessage(os.str());
00150 }
00151 
00152 void SgBookBuilder::Cover(int requiredExpansions, bool additive, 
00153                           const std::vector< std::vector<SgMove> >& lines)
00154 {
00155     m_numEvals = 0;
00156     m_numWidenings = 0;
00157     std::size_t newLines = 0;
00158     SgTimer timer;
00159     Init();
00160     int num = 0;
00161     for (std::size_t i = 0; i < lines.size(); ++i)
00162     {
00163         const std::size_t size = lines[i].size();
00164         std::vector<SgMove> played;
00165         for (std::size_t j = 0; j <= size; ++j)
00166         {
00167             int expansionsToDo = requiredExpansions;
00168             SgBookNode node;
00169             if (GetNode(node))
00170             {
00171                 if (!additive)
00172                     expansionsToDo = requiredExpansions - node.m_count;
00173             }
00174             else
00175             {
00176                 EnsureRootExists();
00177                 GetNode(node);
00178                 newLines++;
00179             }
00180             if (node.IsTerminal())
00181                 break;
00182             for (int k = 0; k < expansionsToDo; ++k)
00183             {
00184                 {
00185                     std::ostringstream os;
00186                     os << "\n--Iteration " << num << ": " 
00187                        << (i + 1) << '/' << lines.size() << ' '
00188                        << (j + 1) << '/' << (size + 1) << ' '
00189                        << (k + 1) << '/' << expansionsToDo << "--\n";
00190                     PrintMessage(os.str());
00191                 }
00192 
00193                 StartIteration();
00194                 std::vector<SgMove> pv;
00195                 DoExpansion(pv);
00196                 EndIteration();
00197 
00198                 num++;
00199                 if (num % m_flushIterations == 0)
00200                     FlushBook();
00201             }
00202             if (j < lines[i].size())
00203             {
00204                 PlayMove(lines[i][j]);
00205                 played.push_back(lines[i][j]);
00206             }
00207         }
00208         for (std::size_t j = 0; j < played.size(); ++j)
00209         {
00210             UndoMove(played[played.size() - 1 - j]);
00211             SgBookNode node;
00212             GetNode(node);
00213             UpdateValue(node);
00214             UpdatePriority(node);
00215         }
00216     }
00217     FlushBook();
00218     Fini();
00219     timer.Stop();
00220     double elapsed = timer.GetTime();
00221     std::ostringstream os;
00222     os << '\n'
00223        << "Statistics\n"
00224        << "Total Time     " << elapsed << '\n'
00225        << "Expansions     " << num 
00226        << std::fixed << std::setprecision(2) 
00227        << " (" << (num / elapsed) << "/s)\n"
00228        << "Evaluations    " << m_numEvals 
00229        << std::fixed << std::setprecision(2)
00230        << " (" << (double(m_numEvals) / elapsed) << "/s)\n"
00231        << "Widenings      " << m_numWidenings << '\n'
00232        << "New Lines      " << newLines << '\n';
00233     PrintMessage(os.str());
00234 }
00235 
00236 void SgBookBuilder::Refresh()
00237 {
00238     m_numEvals = 0;
00239     m_numWidenings = 0;
00240     m_valueUpdates = 0;
00241     m_priorityUpdates = 0;
00242     m_internalNodes = 0;
00243     m_leafNodes = 0;
00244     m_terminalNodes = 0;
00245 
00246     SgTimer timer;
00247     Init();
00248     ClearAllVisited();
00249     Refresh(true);
00250     FlushBook();
00251     Fini();
00252     timer.Stop();
00253 
00254     double elapsed = timer.GetTime();
00255     std::ostringstream os;
00256     os << '\n'
00257        << "Statistics\n"
00258        << "Total Time       " << elapsed << '\n'
00259        << "Value Updates    " << m_valueUpdates << '\n'
00260        << "Priority Updates " << m_priorityUpdates << '\n'
00261        << "Internal Nodes   " << m_internalNodes << '\n'
00262        << "Terminal Nodes   " << m_terminalNodes << '\n'
00263        << "Leaf Nodes       " << m_leafNodes << '\n'
00264        << "Evaluations      " << m_numEvals 
00265        << std::fixed << std::setprecision(2)
00266        << " (" << (double(m_numEvals) / elapsed) << "/s)\n"
00267        << "Widenings        " << m_numWidenings << '\n';
00268     PrintMessage(os.str());
00269 }
00270 
00271 void SgBookBuilder::IncreaseWidth()
00272 {
00273     if (!m_useWidening)
00274     {
00275         PrintMessage("Widening not enabled!\n");
00276         return;
00277     }
00278 
00279     m_numEvals = 0;
00280     m_numWidenings = 0;
00281 
00282     SgTimer timer;
00283     Init();
00284     ClearAllVisited();
00285     IncreaseWidth(true);
00286     FlushBook();
00287     Fini();
00288     timer.Stop();
00289     double elapsed = timer.GetTime();
00290 
00291     std::ostringstream os;
00292     os << '\n'
00293        << "Statistics\n"
00294        << "Total Time     " << elapsed << '\n'
00295        << "Widenings      " << m_numWidenings << '\n'
00296        << "Evaluations    " << m_numEvals 
00297        << std::fixed << std::setprecision(2)
00298        << " (" << (double(m_numEvals) / elapsed) << "/s)\n";
00299     PrintMessage(os.str());
00300 }
00301 
00302 //----------------------------------------------------------------------------
00303 
00304 /** Creates a node for each of the leaf's first count children that
00305     have not been created yet. Returns true if at least one new node
00306     was created, false otherwise. */
00307 bool SgBookBuilder::ExpandChildren(std::size_t count)
00308 {
00309     // It is possible the state is determined, even though it was
00310     // already evaluated. This can happen in Hex: it is not very likey
00311     // if the evaluation function is reasonably heavyweight, but if
00312     // just using fillin and vcs, it is possible that the fillin
00313     // prevents a winning vc from being created.
00314     float value = 0;
00315     std::vector<SgMove> children;
00316     if (GenerateMoves(children, value))
00317     {
00318         PrintMessage("ExpandChildren: State is determined!\n");
00319         WriteNode(SgBookNode(value));
00320         return false;
00321     }
00322     std::size_t limit = std::min(count, children.size());
00323     std::vector<SgMove> childrenToDo;
00324     for (std::size_t i = 0; i < limit; ++i)
00325     {
00326         PlayMove(children[i]);
00327         SgBookNode child;
00328         if (!GetNode(child))
00329             childrenToDo.push_back(children[i]);
00330         UndoMove(children[i]);
00331     }
00332     if (!childrenToDo.empty())
00333     {
00334         BeforeEvaluateChildren();
00335         std::vector<std::pair<SgMove, float> > scores;
00336         EvaluateChildren(childrenToDo, scores);
00337         AfterEvaluateChildren();
00338         for (std::size_t i = 0; i < scores.size(); ++i)
00339         {
00340             PlayMove(scores[i].first);
00341             WriteNode(scores[i].second);
00342             UndoMove(scores[i].first);
00343         }
00344         m_numEvals += childrenToDo.size();
00345         return true;
00346     }
00347     else
00348         PrintMessage("Children already evaluated.\n");
00349     return false;
00350 }
00351 
00352 std::size_t SgBookBuilder::NumChildren(const std::vector<SgMove>& legal)
00353 {
00354     std::size_t num = 0;
00355     for (size_t i = 0; i < legal.size(); ++i) 
00356     {
00357     PlayMove(legal[i]);
00358     SgBookNode child;
00359         if (GetNode(child))
00360             ++num;
00361         UndoMove(legal[i]);
00362     }
00363     return num;
00364 }
00365 
00366 void SgBookBuilder::UpdateValue(SgBookNode& node, 
00367                                 const std::vector<SgMove>& legal)
00368 {
00369     bool hasChild = false;
00370     float bestValue = boost::numeric::bounds<float>::lowest();
00371     for (std::size_t i = 0; i < legal.size(); ++i)
00372     {
00373     PlayMove(legal[i]);
00374     SgBookNode child;
00375         if (GetNode(child))
00376         {
00377             hasChild = true;
00378             float value = InverseEval(Value(child));
00379             if (value > bestValue)
00380         bestValue = value;
00381         }
00382         UndoMove(legal[i]);
00383     }
00384     if (hasChild)
00385         node.m_value = bestValue;
00386 }
00387 
00388 /** Updates the node's value, taking special care if the value is a
00389     loss. In this case, widenings are performed until a non-loss child
00390     is added or no new children are added. The node is then set with
00391     the proper value. */
00392 void SgBookBuilder::UpdateValue(SgBookNode& node)
00393 {
00394     while (true)
00395     {
00396         std::vector<SgMove> legal;
00397         GetAllLegalMoves(legal);
00398         UpdateValue(node, legal);
00399         if (!IsLoss(Value(node)))
00400             break;
00401         // Round up to next nearest multiple of m_expandWidth that is
00402         // larger than the current number of children.
00403         std::size_t numChildren = NumChildren(legal);
00404         std::size_t width = (numChildren / m_expandWidth + 1) 
00405             * m_expandWidth;
00406         {
00407             std::ostringstream os;
00408             os << "Forced Widening[" << numChildren << "->" << width << "]\n";
00409             PrintMessage(os.str());
00410         }
00411         if (!ExpandChildren(width))
00412             break;
00413         ++m_numWidenings;
00414     }
00415 }
00416 
00417 float SgBookBuilder::ComputePriority(const SgBookNode& parent,
00418                                      const float childValue,
00419                                      const float childPriority) const
00420 {
00421     float delta = parent.m_value - InverseEval(childValue);
00422     SG_ASSERT(delta >= 0.0);
00423     return m_alpha * delta + childPriority + 1;
00424 }
00425 
00426 /** Re-computes node's priority and returns the best child to
00427     expand. Requires that UpdateValue() has been called on this
00428     node. Returns SG_NULLMOVE if node has no children. */
00429 SgMove SgBookBuilder::UpdatePriority(SgBookNode& node)
00430 {
00431     bool hasChild = false;
00432     float bestPriority = boost::numeric::bounds<float>::highest();
00433     SgMove bestChild = SG_NULLMOVE;
00434     std::vector<SgMove> legal;
00435     GetAllLegalMoves(legal);
00436     for (std::size_t i = 0; i < legal.size(); ++i)
00437     {
00438     PlayMove(legal[i]);
00439     SgBookNode child;
00440         if (GetNode(child))
00441         {
00442             hasChild = true;
00443             // Must adjust child value for swap, but not the parent
00444             // because we are comparing with the best child's value,
00445             // ie, the minmax value.
00446             float childValue = Value(child);
00447             float childPriority = child.m_priority;
00448             float priority = ComputePriority(node, childValue, childPriority);
00449             if (priority < bestPriority)
00450             {
00451                 bestPriority = priority;
00452                 bestChild = legal[i];
00453             }
00454         }
00455         UndoMove(legal[i]);
00456     }
00457     if (hasChild)
00458         node.m_priority = bestPriority;
00459     return bestChild;
00460 }
00461 
00462 void SgBookBuilder::DoExpansion(std::vector<SgMove>& pv)
00463 {
00464     SgBookNode node;
00465     if (!GetNode(node))
00466         SG_ASSERT(false);
00467     if (node.IsTerminal())
00468         return;
00469     if (node.IsLeaf())
00470     {
00471         ExpandChildren(m_expandWidth);
00472     }
00473     else
00474     {
00475         // Widen this non-terminal non-leaf node if necessary
00476         if (m_useWidening && (node.m_count % m_expandThreshold == 0))
00477         {
00478             std::size_t width = (node.m_count / m_expandThreshold + 1)
00479                               * m_expandWidth;
00480             ++m_numWidenings;
00481             ExpandChildren(width);
00482         }
00483         // Compute value and priority. It's possible this node is newly
00484         // terminal if one of its new children is a winning move.
00485         GetNode(node);
00486         UpdateValue(node);
00487         SgMove mostUrgent = UpdatePriority(node);
00488         WriteNode(node);
00489 
00490         // Recurse on most urgent child only if non-terminal.
00491         if (!node.IsTerminal())
00492         {
00493             PlayMove(mostUrgent);
00494             pv.push_back(mostUrgent);
00495             DoExpansion(pv);
00496             pv.pop_back();
00497             UndoMove(mostUrgent);
00498         }
00499     }
00500     GetNode(node);
00501     UpdateValue(node);
00502     UpdatePriority(node);
00503     node.IncrementCount();
00504     WriteNode(node);
00505 }
00506 
00507 //----------------------------------------------------------------------------
00508 
00509 /** Refresh's each child of the given state. UpdateValue() and
00510     UpdatePriority() are called on internal nodes. Returns true if
00511     state exists in book.  
00512     @ref bookrefresh */
00513 bool SgBookBuilder::Refresh(bool root)
00514 {
00515     if (HasBeenVisited())
00516         return true;
00517     MarkAsVisited();
00518     SgBookNode node;
00519     if (!GetNode(node))
00520         return false;
00521     if (node.IsLeaf())
00522     {
00523         m_leafNodes++;
00524         if (node.IsTerminal())
00525             m_terminalNodes++;
00526         return true;
00527     }
00528     double oldValue = Value(node);
00529     double oldPriority = node.m_priority;
00530     std::vector<SgMove> legal;
00531     GetAllLegalMoves(legal);
00532     for (std::size_t i = 0; i < legal.size(); ++i)
00533     {
00534         PlayMove(legal[i]);
00535         Refresh(false);
00536         if (root)
00537         {
00538             std::ostringstream os;
00539             os << "Finished " << MoveString(legal[i]) << '\n';
00540             PrintMessage(os.str());
00541         }
00542         UndoMove(legal[i]);
00543     }
00544     UpdateValue(node);
00545     UpdatePriority(node);
00546     if (fabs(oldValue - Value(node)) > 0.0001)
00547         m_valueUpdates++;
00548     if (fabs(oldPriority - node.m_priority) > 0.0001)
00549         m_priorityUpdates++;
00550     WriteNode(node);
00551     if (node.IsTerminal())
00552         m_terminalNodes++;
00553     else
00554         m_internalNodes++;
00555     return true;
00556 }
00557 
00558 //----------------------------------------------------------------------------
00559 
00560 void SgBookBuilder::IncreaseWidth(bool root)
00561 {
00562     if (HasBeenVisited())
00563         return;
00564     MarkAsVisited();
00565     SgBookNode node;
00566     if (!GetNode(node))
00567         return;
00568     if (node.IsTerminal() || node.IsLeaf())
00569         return;
00570     std::vector<SgMove> legal;
00571     GetAllLegalMoves(legal);
00572     for (std::size_t i = 0; i < legal.size(); ++i)
00573     {
00574         PlayMove(legal[i]);
00575         IncreaseWidth(false);
00576         if (root)
00577         {
00578             std::ostringstream os;
00579             os << "Finished " << MoveString(legal[i]) << '\n';
00580             PrintMessage(os.str());
00581         }
00582         UndoMove(legal[i]);
00583     }
00584     std::size_t width = (node.m_count / m_expandThreshold + 1)
00585         * m_expandWidth;
00586     if (ExpandChildren(width))
00587         ++m_numWidenings;
00588 }
00589 
00590 //----------------------------------------------------------------------------


Sun Mar 13 2011 Doxygen 1.7.1