00001 //---------------------------------------------------------------------------- 00002 /** @file Resistance.cpp 00003 00004 Resistance/energy calculation based very closely on Six's circuit 00005 implementation. 00006 00007 We use the same open source code to solve the linear system that 00008 Six uses. We actually tried linking with two different external 00009 linear algebra libraries (GNU Scientific library and 00010 boost::numeric::ublas) but both fail in some instances that our 00011 current code handles without complaint. 00012 00013 These instances seem to include a linear dependence among the rows 00014 of our G matrix. In this simplest case, this can happen if one 00015 group's set of connections is a superset of another group's. We 00016 haven't been able to find a way around this, as any fix seems more 00017 expensive than worthwhile. 00018 00019 We also hoped these external libraries would be faster; again, 00020 this does not appear to be the case. 00021 00022 It is possible that a more powerful library like LAPACK would 00023 address both of these issues, but the hassle of adding this 00024 external dependency to benzene probably outweighs the gain. 00025 */ 00026 //---------------------------------------------------------------------------- 00027 00028 #include <cmath> 00029 00030 #include "Hex.hpp" 00031 #include "BitsetIterator.hpp" 00032 #include "VCSet.hpp" 00033 #include "Resistance.hpp" 00034 #include "Pattern.hpp" 00035 #include "HashedPatternSet.hpp" 00036 00037 #include "lssolve.h" 00038 00039 using namespace benzene; 00040 00041 //---------------------------------------------------------------------------- 00042 00043 Resistance::Resistance() 00044 : m_score(0), 00045 m_simulate_and_over_edge(false) 00046 { 00047 } 00048 00049 Resistance::~Resistance() 00050 { 00051 } 00052 00053 //---------------------------------------------------------------------------- 00054 00055 void Resistance::Evaluate(const HexBoard& brd) 00056 { 00057 AdjacencyGraph graph[BLACK_AND_WHITE]; 00058 ResistanceUtil::AddAdjacencies(brd, graph); 00059 Evaluate(brd, graph); 00060 } 00061 00062 void Resistance::Evaluate(const HexBoard& brd, 00063 AdjacencyGraph graph[BLACK_AND_WHITE]) 00064 { 00065 ConductanceValues values; 00066 00067 // augment connection graph if option is on 00068 if (m_simulate_and_over_edge) 00069 ResistanceUtil::SimulateAndOverEdge(brd, graph); 00070 00071 for (BWIterator c; c; ++c) 00072 ComputeScores(*c, brd.GetGroups(), graph[*c], values, m_scores[*c]); 00073 ComputeScore(); 00074 } 00075 00076 void Resistance::Evaluate(const Groups& groups, 00077 AdjacencyGraph graph[BLACK_AND_WHITE]) 00078 { 00079 ConductanceValues values; 00080 for (BWIterator c; c; ++c) 00081 ComputeScores(*c, groups, graph[*c], values, m_scores[*c]); 00082 ComputeScore(); 00083 } 00084 00085 //---------------------------------------------------------------------------- 00086 00087 namespace 00088 { 00089 /** Sets all cell scores to an explicitly undefined value. */ 00090 void SetAllToInfinity(const StoneBoard& brd, HexEval* out) 00091 { 00092 for (BoardIterator it(brd.Const().Interior()); it; ++it) 00093 out[*it] = EVAL_INFINITY; 00094 } 00095 00096 /** Returns the conductance between two cells by comparing their 00097 colors and whether they are connected or not. */ 00098 double Conductance(const StoneBoard& brd, HexColor color, 00099 HexPoint a, HexPoint b, bool connected, 00100 const ConductanceValues& values) 00101 { 00102 if (!connected) 00103 return values.no_connection; 00104 00105 HexColor ac = brd.GetColor(a); 00106 HexColor bc = brd.GetColor(b); 00107 00108 if (ac == EMPTY && bc == EMPTY) 00109 return values.empty_to_empty; 00110 00111 if (ac == color && bc == color) 00112 return values.color_to_color; 00113 00114 return values.color_to_empty; 00115 } 00116 } 00117 00118 void Resistance::ComputeScores(HexColor color, const Groups& groups, 00119 const AdjacencyGraph& graph, 00120 const ConductanceValues& values, 00121 HexEval* out) 00122 { 00123 const StoneBoard& brd = groups.Board(); 00124 SetAllToInfinity(brd, out); 00125 00126 const HexColorSet not_other = HexColorSetUtil::ColorOrEmpty(color); 00127 const HexPoint source = HexPointUtil::colorEdge1(color); 00128 const HexPoint sink = HexPointUtil::colorEdge2(color); 00129 const int n = groups.NumGroups(not_other) - 1; 00130 00131 // Compute index that does not contain the sink 00132 int pointToIndex[BITSETSIZE]; 00133 HexPoint indexToPoint[BITSETSIZE]; 00134 { 00135 int index = 0; 00136 for (GroupIterator i(groups, not_other); i; ++i) 00137 { 00138 if (i->Captain() == sink) 00139 continue; 00140 pointToIndex[i->Captain()] = index; 00141 indexToPoint[index] = i->Captain(); 00142 index++; 00143 } 00144 } 00145 00146 // Compute conductances between groups 00147 Mat<double> G(n, n); 00148 std::vector<double> sinkG(n, 0.0); 00149 G = 0.0; 00150 for (int i = 0; i < n; ++i) 00151 { 00152 HexPoint ip = indexToPoint[i]; 00153 for (int j = 0; j < i; ++j) 00154 { 00155 HexPoint jp = indexToPoint[j]; 00156 double c = Conductance(brd, color, ip, jp, graph[ip][jp], values); 00157 G(i, i) += c; 00158 G(j, j) += c; 00159 G(i, j) -= c; 00160 G(j, i) -= c; 00161 } 00162 double c = Conductance(brd, color, ip, sink, graph[ip][sink], values); 00163 G(i, i) += c; 00164 sinkG[i] += c; 00165 } 00166 00167 // Put some current on the source 00168 Vec<double> I(n); 00169 I = 0.0; 00170 I[pointToIndex[source]] = 1.0; 00171 00172 // Solve for voltages 00173 const Vec<double>& V = lsSolve(G, I); 00174 m_resistance[color] = fabs(V[pointToIndex[source]]); 00175 00176 // Compute energy 00177 for (int i = 0; i < n; ++i) 00178 { 00179 double sum = fabs(sinkG[i] * V[i]); 00180 for (int j = 0; j < n; ++j) 00181 sum += fabs(G(i,j) * (V[i] - V[j])); 00182 out[indexToPoint[i]] = sum; 00183 } 00184 } 00185 00186 void Resistance::ComputeScore() 00187 { 00188 double r = m_resistance[WHITE] / m_resistance[BLACK]; 00189 m_score = log(r); 00190 } 00191 00192 double Resistance::Resist(HexColor color) const 00193 { 00194 return log(m_resistance[color]); 00195 } 00196 00197 //--------------------------------------------------------------------------- 00198 // AdjacencyGraph computations 00199 00200 namespace 00201 { 00202 00203 /** Computes a AdjacencyGraph for color on the given board. */ 00204 void AddAdjacent(HexColor color, const HexBoard& brd, 00205 AdjacencyGraph& graph) 00206 { 00207 HexColorSet not_other = HexColorSetUtil::ColorOrEmpty(color); 00208 for (BoardIterator x(brd.GetPosition().Stones(not_other)); x; ++x) 00209 { 00210 for (BoardIterator y(brd.GetPosition().Stones(not_other)); *y != *x; ++y) 00211 { 00212 HexPoint cx = brd.GetGroups().CaptainOf(*x); 00213 HexPoint cy = brd.GetGroups().CaptainOf(*y); 00214 if ((cx==cy) || brd.Cons(color).Exists(cx, cy, VC::FULL)) 00215 { 00216 graph[*x][*y] = true; 00217 graph[*y][*x] = true; 00218 } 00219 } 00220 } 00221 } 00222 00223 bool g_ResistanceUtilInitialized = false; 00224 00225 PatternSet s_capmiai[BLACK_AND_WHITE]; 00226 HashedPatternSet* s_hash_capmiai[BLACK_AND_WHITE]; 00227 00228 void InitializeCapMiai() 00229 { 00230 if (g_ResistanceUtilInitialized) return; 00231 00232 LogFine() << "--InitializeCapMiai" << '\n'; 00233 00234 // 00235 // The center 'B' is marked so the group can be determined. 00236 // 00237 // * 00238 // . . 00239 // B B B [capmiai/0] 00240 // m:0,0,0,0,0;0,0,0,0,0;0,0,0,0,0;0,0,0,0,0;7,6,0,4,0;3,2,0,0,0;1 00241 // 00242 std::string capmiai = "m:0,0,0,0,0;0,0,0,0,0;0,0,0,0,0;0,0,0,0,0;7,6,0,4,0;3,2,0,0,0;1"; 00243 00244 Pattern pattern; 00245 if (!pattern.unserialize(capmiai)) { 00246 HexAssert(false); 00247 } 00248 pattern.setName("capmiai"); 00249 00250 s_capmiai[BLACK].push_back(pattern); 00251 s_hash_capmiai[BLACK] = new HashedPatternSet(); 00252 s_hash_capmiai[BLACK]->hash(s_capmiai[BLACK]); 00253 00254 pattern.flipColors(); 00255 s_capmiai[WHITE].push_back(pattern); 00256 s_hash_capmiai[WHITE] = new HashedPatternSet(); 00257 s_hash_capmiai[WHITE]->hash(s_capmiai[WHITE]); 00258 } 00259 00260 /** Simulates and'ing over the edge for the given color and edge. All 00261 empty neighbours of an edge get all the connections the edge 00262 has. All cells that are a captured-miai away from the edge get all 00263 connections the edge has. */ 00264 void SimulateAndOverEdge1(const HexBoard& brd, HexColor color, 00265 HexPoint edge, AdjacencyGraph& graph) 00266 { 00267 bitset_t augment = brd.GetGroups().Nbs(edge, EMPTY); 00268 00269 // add in miai-captured empty cells adjacent to edge 00270 HexAssert(g_ResistanceUtilInitialized); 00271 00272 bitset_t adjToEdge; 00273 for (BitsetIterator p(brd.GetPosition().GetEmpty()); p; ++p) 00274 { 00275 PatternHits hits; 00276 brd.GetPatternState().MatchOnCell(*s_hash_capmiai[color], *p, 00277 PatternState::MATCH_ALL, hits); 00278 for (unsigned j=0; j<hits.size(); ++j) 00279 { 00280 HexPoint cj = brd.GetGroups().CaptainOf(hits[j].moves1()[0]); 00281 if (cj == edge) 00282 { 00283 augment.set(*p); 00284 break; 00285 } 00286 } 00287 } 00288 00289 // add the edge's connections to all cells in augment 00290 for (BitsetIterator p(augment); p; ++p) { 00291 for (BoardIterator q(brd.Const().EdgesAndInterior()); q; ++q) { 00292 graph[*p][*q] = graph[*p][*q] || graph[edge][*q]; 00293 graph[*q][*p] = graph[*q][*p] || graph[edge][*q]; 00294 } 00295 } 00296 } 00297 00298 } // annonymous namespace 00299 00300 void ResistanceUtil::Initialize() 00301 { 00302 InitializeCapMiai(); 00303 g_ResistanceUtilInitialized = true; 00304 } 00305 00306 void ResistanceUtil::AddAdjacencies(const HexBoard& brd, 00307 AdjacencyGraph graph[BLACK_AND_WHITE]) 00308 { 00309 for (BWIterator c; c; ++c) 00310 AddAdjacent(*c, brd, graph[*c]); 00311 } 00312 00313 void ResistanceUtil::SimulateAndOverEdge(const HexBoard& brd, 00314 AdjacencyGraph g[BLACK_AND_WHITE]) 00315 { 00316 if (brd.Builder().Parameters().and_over_edge) 00317 { 00318 LogWarning() 00319 << "**** Simulating and-over-edge " 00320 << "while vcs are computed over edge!! ****" 00321 << '\n'; 00322 } 00323 00324 SimulateAndOverEdge1(brd,BLACK, HexPointUtil::colorEdge1(BLACK), g[BLACK]); 00325 SimulateAndOverEdge1(brd,BLACK, HexPointUtil::colorEdge2(BLACK), g[BLACK]); 00326 SimulateAndOverEdge1(brd,WHITE, HexPointUtil::colorEdge1(WHITE), g[WHITE]); 00327 SimulateAndOverEdge1(brd,WHITE, HexPointUtil::colorEdge2(WHITE), g[WHITE]); 00328 } 00329 00330 //----------------------------------------------------------------------------