diff --git a/Makefile.am b/Makefile.am index f64b1b628e..ec5d107a5d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -226,6 +226,7 @@ game_SOURCES = \ src/games/stratspt.h \ src/games/stratpure.h \ src/games/stratmixed.h \ + src/games/stratmixed.cc \ src/games/file.cc \ src/games/workspace.cc \ src/games/workspace.h \ @@ -244,7 +245,6 @@ game_SOURCES = \ src/games/agg/bagg.h \ src/games/agg/proj_func.h \ src/games/agg/trie_map.h \ - src/games/agg/trie_map.imp \ src/games/agg/trie_map.cc linalg_SOURCES = \ @@ -318,7 +318,7 @@ path_SOURCES = \ logit_SOURCES = \ src/solvers/logit/logbehav.h \ - src/solvers/logit/logbehav.imp \ + src/solvers/logit/logbehav.cc \ src/solvers/logit/logit.h \ src/solvers/logit/efglogit.cc \ src/solvers/logit/nfglogit.cc @@ -342,7 +342,7 @@ enumpoly_SOURCES = \ src/solvers/enumpoly/poly.h \ src/solvers/enumpoly/polysystem.h \ src/solvers/enumpoly/polypartial.h \ - src/solvers/enumpoly/polypartial.imp \ + src/solvers/enumpoly/polypartial.cc \ src/solvers/enumpoly/polysolver.cc \ src/solvers/enumpoly/polysolver.h \ src/solvers/enumpoly/efgpoly.cc \ diff --git a/src/games/agg/trie_map.cc b/src/games/agg/trie_map.cc index bb951fe346..23ef09317c 100644 --- a/src/games/agg/trie_map.cc +++ b/src/games/agg/trie_map.cc @@ -22,10 +22,446 @@ // #include "agg.h" -#include "trie_map.imp" +#include "trie_map.h" namespace Gambit::agg { +template +std::pair::iterator, bool> +trie_map::insert(const trie_map::value_type &x) +{ + + static size_t ind; + static std::vector::const_iterator p; //,s; + // s=x.first.end(); + TrieNode *ptr = root; + + for (p = x.first.begin(); p != x.first.end();) { + // ind=x.first[i]; + ind = *(p++); + if (ind >= ptr->children.size()) { + ptr->children.resize(ind + 1, nullptr); + } + if (ptr->children[ind] == nullptr) { + ptr->children[ind] = new TrieNode(initBranches, data.end()); + } + ptr = ptr->children[ind]; + } + if (ptr->val != data.end()) { + return std::pair::iterator, bool>(ptr->val, false); + } + + // now insert the item + leaves.push_back(ptr); + return std::pair::iterator, bool>((ptr->val = data.insert(data.begin(), x)), + true); +} + +template +typename trie_map::iterator &trie_map::find(const trie_map::key_type &k) const +{ + size_t i = 0; + TrieNode *ptr = root; + for (; i < k.size() && k[i] < static_cast(ptr->children.size()) && ptr->children[k[i]]; + ptr = ptr->children[k[i++]]) + ; + + return ptr->val; +} + +template +typename trie_map::iterator trie_map::findExact(const trie_map::key_type &k) +{ + size_t i = 0; + TrieNode *ptr = root; + for (; i < k.size() && k[i] < static_cast(ptr->children.size()) && ptr->children[k[i]]; + ptr = ptr->children[k[i++]]) + ; + + if (i >= k.size() && ptr->val != data.end()) { + return ptr->val; + } + else { + return data.end(); + } +} + +template +trie_map::trie_map(const trie_map &other) + : initBranches(other.initBranches), root(new TrieNode(other.initBranches, data.end())) +{ + typename trie_map::const_iterator p; + for (p = other.begin(); p != other.end(); ++p) { + insert(*p); + } +} + +template trie_map &trie_map::operator=(const trie_map &other) +{ + if (this != &other) { + reset(); + // initBranches = other.initBranches; + for (auto p = other.begin(); p != other.end(); ++p) { + insert(*p); + } + } + return *this; +} + +template void trie_map::swap(trie_map &other) +{ + if (this != &other) { + const trie_map temp = *this; + *this = other; + other = temp; + } +} + +template +void trie_map::multiply(const trie_map &t1, const trie_map &t2, size_t keylen, + std::vector &f) +{ + static size_t i; + static std::pair, V> v; + const_iterator p1, p2; + v.first.resize(keylen); + reset(); + for (p1 = t1.begin(); p1 != t1.end(); ++p1) { + if ((*p1).second > static_cast(0)) { + for (p2 = t2.begin(); p2 != t2.end(); ++p2) { + if ((*p2).second > static_cast(0)) { + for (i = 0; i < keylen; ++i) { + v.first[i] = (*(f[i]))((*p1).first[i], (*p2).first[i]); + } + v.second = (*p1).second * (*p2).second; + (*this) += v; + } + } // end for(p2 + } + } // end for(p1 +} + +template +void trie_map::multiply(const trie_map &other, size_t keylen, std::vector &f) +{ + static typename std::list::value_type>::iterator p1; + static size_t i; + + if (&other == this) { + throw std::runtime_error( + "Error: (in-place) multiply: other should not be the same object as self"); + } + std::list::value_type> data2; + data2 = data; + reset(); + + static std::pair, V> v; + v.first.resize(keylen); + TrieNode *ptr; + + for (p1 = data2.begin(); p1 != data2.end(); ++p1) { + if ((*p1).second > static_cast(0.0)) { + for (auto p2 = other.begin(); p2 != other.end(); ++p2) { + if ((*p2).second > static_cast(0.0)) { + ptr = root; + for (i = 0; i < keylen; ++i) { + v.first[i] = (*(f[i]))((*p1).first[i], (*p2).first[i]); + if (v.first[i] >= static_cast(ptr->children.size())) { + ptr->children.resize(v.first[i] + 1, nullptr); + } + if (ptr->children[v.first[i]] == nullptr) { + ptr->children[v.first[i]] = new TrieNode(initBranches, data.end()); + } + ptr = ptr->children[v.first[i]]; + } + v.second = (*p1).second * (*p2).second; + if (ptr->val != data.end()) { + ptr->val->second += v.second; + } + else { + // insert the item + leaves.push_back(ptr); + ptr->val = data.insert(data.begin(), v); + } + } // end for(p2 + } + } // end for(p1 + } +} + +template +void trie_map::square(trie_map &dest, size_t keylen, std::vector &f) const +{ + static std::pair, V> v; + v.first.resize(keylen); + dest.reset(); + for (auto p1 = begin(); p1 != end(); ++p1) { + if ((*p1).second > static_cast(0)) { + for (auto p2 = p1; p2 != end(); ++p2) { + if ((*p2).second > static_cast(0)) { + for (size_t i = 0; i < keylen; ++i) { + v.first[i] = (*(f[i]))((*p1).first[i], (*p2).first[i]); + } + v.second = (*p1).second * (*p2).second; + if (p1 != p2) { + v.second *= static_cast(2); + } + dest += v; + } + } // end for(p2 + } + } // end for(p1 +} + +template void trie_map::square(size_t keylen, std::vector &f) +{ + static typename std::list::value_type>::iterator p1, p2; + static std::pair, V> v; + v.first.resize(keylen); + std::list::value_type> data2; + data2 = data; + reset(); + for (p1 = data2.begin(); p1 != end(); ++p1) { + if ((*p1).second > static_cast(0)) { + for (p2 = p1; p2 != data2.end(); ++p2) { + if ((*p2).second > static_cast(0)) { + for (size_t i = 0; i < keylen; ++i) { + v.first[i] = (*(f[i]))((*p1).first[i], (*p2).first[i]); + } + v.second = (*p1).second * (*p2).second; + if (p1 != p2) { + v.second *= static_cast(2); + } + (*this) += v; + } + } // end for(p2 + } + } // end for(p1 +} + +template +void trie_map::power(size_t p, trie_map &dest, trie_map &scratch, size_t keylen, + std::vector &f) +{ + if (p == 1) { + dest = *this; + return; + } + square(dest, keylen, f); + p -= 2; + if (p > 1) { + scratch = dest; + } + while (p > 0) { + if (p == 1) { + dest.multiply(*this, keylen, f); + return; + } + dest.multiply(scratch, keylen, f); + p -= 2; + } +} + +template V trie_map::inner_prod(trie_map &other, V init /*= (V) (0)*/) const +{ + V result(init); + for (auto p = begin(); p != end(); ++p) { + if ((*p).second > static_cast(0)) { + auto p2 = other.find((*p).first); + if (p2 == other.end()) { + if (p->second > static_cast(THRESH)) { + std::stringstream str; + str << "inner_prod WARNING: discarding ["; + copy(p->first.begin(), p->first.end(), std::ostream_iterator(str, " ")); + str << "] " << p->second; + throw std::runtime_error(str.str()); + } + } + else { + result += (*p).second * (*p2).second; + } + } + } + return result; +} + +template +V trie_map::inner_prod(const std::vector &x, size_t keylen, std::vector &f, + trie_map &other, V init /* = (V) (0)*/) const +{ + V result(init); + V th(THRESH); + static iterator p2; + // V s(-1); + for (auto p = begin(); p != end(); ++p) { + if ((*p).second > static_cast(0)) { + value_type y = *p; + for (size_t i = 0; i < keylen; ++i) { + y.first[i] = (*(f[i]))(y.first[i], x[i]); + } + p2 = other.find(y.first); + if (p2 == other.end()) { + if (y.second > th) { + std::stringstream str; + str << "inner_prod WARNING: discarding ["; + copy(y.first.begin(), y.first.end(), std::ostream_iterator(str, " ")); + str << "] " << y.second; + throw std::runtime_error(str.str()); + } + } + else { + result += y.second * (*p2).second; + } + } + } + return result; +} + +template trie_map &trie_map::operator/=(const std::vector &denom) +{ + // first, find the pivot: the first nonzero element of denom + V th(std::sqrt(THRESH)); + int piv = -1; + for (size_t i = 0; i < denom.size(); ++i) { + if (denom[i] > th) { + piv = static_cast(i); + break; + } + } + if (piv == -1) { + return *this; + } + + // get rid of 0th order terms at the pivot level + std::list::value_type> data2; + // data.swap(data2); + data2 = data; + clear(); + typename std::list::value_type>::iterator itr; + for (itr = data2.begin(); itr != data2.end(); ++itr) { + if (itr->first[piv] > 0) { + insert(*itr); + } + } + + div(denom, root, 0, piv); + return *this; +} + +template +void trie_map::div(const std::vector &denom, TrieNode *n, int current, int pivot) +{ + int i; + if (pivot != current) { + const int s = n->children.size(); + for (i = 0; i < s; i++) { + if (n->children[i]) { + div(denom, n->children[i], current + 1, pivot); + } + } + return; + } + + for (i = 0; i < static_cast(n->children.size()) - 1; i++) { + n->children[i] = n->children[i + 1]; + } + n->children.pop_back(); + + typename trie_map::div_helper f(denom, pivot, data.end()); + for (i = n->children.size() - 1; i >= 0; i--) { + if (i < static_cast(n->children.size()) - 1 && n->children[i + 1]) { + // if(!n->children[i])n->children[i]=new TrieNode(initBranches, data.end()); + typename trie_map::div_helper_mul g(denom, pivot, n->children[i], data.end()); + in_order_subtree(g, n->children[i + 1]); + } + in_order_subtree(f, n->children[i]); + } +} + +template std::ostream &operator<<(std::ostream &s, const trie_map &t) +{ + for (auto p = t.begin(); p != t.end(); ++p) { + s << "[ "; + copy(p->first.begin(), p->first.end(), std::ostream_iterator(s, " ")); + s << "] " << p->second << std::endl; + } + return s; +} + +template void trie_map::deleteNodes(TrieNode *n) +{ + if (!n) { + return; + } + const size_type s = n->children.size(); + for (size_type i = 0; i < s; ++i) { + if (n->children[i]) { + deleteNodes(n->children[i]); + } + } + delete n; +} + +template void trie_map::div_helper_mul::add(const std::vector &conf, V y) +{ + const size_t keylen = conf.size(); + const double th(THRESH / static_cast(denom[pivot])); + // if (y<=th&&y>=-th) return; + + if (!dest) { + if (static_cast(y) > th || static_cast(y) < -th) { + std::stringstream str; + str << "division (pivot=" << denom[pivot] << ") WARNING: discarding " << y; + throw std::runtime_error(str.str()); + } + return; + } + TrieNode *ptr = dest; + for (size_t i = pivot + 1; i < keylen; ++i) { + if ((*ptr)[conf[i]] == nullptr) { + if (static_cast(y) > th || static_cast(y) < -th) { + std::stringstream str; + str << "division (pivot=" << denom[pivot] << ") WARNING: discarding " << y; + throw std::runtime_error(str.str()); + } + return; + } + ptr = (*ptr)[conf[i]]; + } + ptr->val->second += y; + if (static_cast(ptr->val->second) <= -th) { + std::stringstream str; + str << "division (pivot=" << denom[pivot] << ") WARNING: discarding " << ptr->val->second + << std::endl; + throw std::runtime_error(str.str()); + } + if (ptr->val->second < static_cast(0)) { + ptr->val->second = static_cast(0); + } +} + +template void trie_map::div_helper_mul::operator()(iterator p) +{ + if (p == endp) { + return; + } + const size_t keylen = p->first.size(); + V null_prob(static_cast(1) - denom[pivot]); + // V th(THRESH); + for (size_t i = pivot + 1; i < keylen; ++i) { + if (denom[i] > static_cast(0)) { + p->first[i]++; + add(p->first, -denom[i] * p->second); + p->first[i]--; + null_prob -= denom[i]; + } + } + + if (null_prob > static_cast(0)) { + add(p->first, -null_prob * p->second); + } +} + template class trie_map; template std::ostream &operator<<(std::ostream &s, const trie_map &t); diff --git a/src/games/agg/trie_map.imp b/src/games/agg/trie_map.imp deleted file mode 100644 index df6d9418c7..0000000000 --- a/src/games/agg/trie_map.imp +++ /dev/null @@ -1,465 +0,0 @@ -// -// This file is part of Gambit -// Copyright (c) 1994-2026, The Gambit Project (https://www.gambit-project.org) -// Albert Xin Jiang -// -// FILE: src/games/agg/trie_map.imp -// Trie with STL-like interfaces -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// - -#include -#include "trie_map.h" - -namespace Gambit::agg { - -template -std::pair::iterator, bool> -trie_map::insert(const trie_map::value_type &x) -{ - - static size_t ind; - static std::vector::const_iterator p; //,s; - // s=x.first.end(); - TrieNode *ptr = root; - - for (p = x.first.begin(); p != x.first.end();) { - // ind=x.first[i]; - ind = *(p++); - if (ind >= ptr->children.size()) { - ptr->children.resize(ind + 1, nullptr); - } - if (ptr->children[ind] == nullptr) { - ptr->children[ind] = new TrieNode(initBranches, data.end()); - } - ptr = ptr->children[ind]; - } - if (ptr->val != data.end()) { - return std::pair::iterator, bool>(ptr->val, false); - } - - // now insert the item - leaves.push_back(ptr); - return std::pair::iterator, bool>((ptr->val = data.insert(data.begin(), x)), - true); -} - -template -typename trie_map::iterator &trie_map::find(const trie_map::key_type &k) const -{ - size_t i = 0; - TrieNode *ptr = root; - for (; i < k.size() && k[i] < static_cast(ptr->children.size()) && ptr->children[k[i]]; - ptr = ptr->children[k[i++]]) - ; - - return ptr->val; -} - -template -typename trie_map::iterator trie_map::findExact(const trie_map::key_type &k) -{ - size_t i = 0; - TrieNode *ptr = root; - for (; i < k.size() && k[i] < static_cast(ptr->children.size()) && ptr->children[k[i]]; - ptr = ptr->children[k[i++]]) - ; - - if (i >= k.size() && ptr->val != data.end()) { - return ptr->val; - } - else { - return data.end(); - } -} - -template -trie_map::trie_map(const trie_map &other) - : initBranches(other.initBranches), root(new TrieNode(other.initBranches, data.end())) -{ - typename trie_map::const_iterator p; - for (p = other.begin(); p != other.end(); ++p) { - insert(*p); - } -} - -template trie_map &trie_map::operator=(const trie_map &other) -{ - if (this != &other) { - reset(); - // initBranches = other.initBranches; - for (auto p = other.begin(); p != other.end(); ++p) { - insert(*p); - } - } - return *this; -} - -template void trie_map::swap(trie_map &other) -{ - if (this != &other) { - const trie_map temp = *this; - *this = other; - other = temp; - } -} - -template -void trie_map::multiply(const trie_map &t1, const trie_map &t2, size_t keylen, - std::vector &f) -{ - static size_t i; - static std::pair, V> v; - const_iterator p1, p2; - v.first.resize(keylen); - reset(); - for (p1 = t1.begin(); p1 != t1.end(); ++p1) { - if ((*p1).second > static_cast(0)) { - for (p2 = t2.begin(); p2 != t2.end(); ++p2) { - if ((*p2).second > static_cast(0)) { - for (i = 0; i < keylen; ++i) { - v.first[i] = (*(f[i]))((*p1).first[i], (*p2).first[i]); - } - v.second = (*p1).second * (*p2).second; - (*this) += v; - } - } // end for(p2 - } - } // end for(p1 -} - -template -void trie_map::multiply(const trie_map &other, size_t keylen, std::vector &f) -{ - static typename std::list::value_type>::iterator p1; - static size_t i; - - if (&other == this) { - throw std::runtime_error( - "Error: (in-place) multiply: other should not be the same object as self"); - } - std::list::value_type> data2; - data2 = data; - reset(); - - static std::pair, V> v; - v.first.resize(keylen); - TrieNode *ptr; - - for (p1 = data2.begin(); p1 != data2.end(); ++p1) { - if ((*p1).second > static_cast(0.0)) { - for (auto p2 = other.begin(); p2 != other.end(); ++p2) { - if ((*p2).second > static_cast(0.0)) { - ptr = root; - for (i = 0; i < keylen; ++i) { - v.first[i] = (*(f[i]))((*p1).first[i], (*p2).first[i]); - if (v.first[i] >= static_cast(ptr->children.size())) { - ptr->children.resize(v.first[i] + 1, nullptr); - } - if (ptr->children[v.first[i]] == nullptr) { - ptr->children[v.first[i]] = new TrieNode(initBranches, data.end()); - } - ptr = ptr->children[v.first[i]]; - } - v.second = (*p1).second * (*p2).second; - if (ptr->val != data.end()) { - ptr->val->second += v.second; - } - else { - // insert the item - leaves.push_back(ptr); - ptr->val = data.insert(data.begin(), v); - } - } // end for(p2 - } - } // end for(p1 - } -} - -template -void trie_map::square(trie_map &dest, size_t keylen, std::vector &f) const -{ - static std::pair, V> v; - v.first.resize(keylen); - dest.reset(); - for (auto p1 = begin(); p1 != end(); ++p1) { - if ((*p1).second > static_cast(0)) { - for (auto p2 = p1; p2 != end(); ++p2) { - if ((*p2).second > static_cast(0)) { - for (size_t i = 0; i < keylen; ++i) { - v.first[i] = (*(f[i]))((*p1).first[i], (*p2).first[i]); - } - v.second = (*p1).second * (*p2).second; - if (p1 != p2) { - v.second *= static_cast(2); - } - dest += v; - } - } // end for(p2 - } - } // end for(p1 -} - -template void trie_map::square(size_t keylen, std::vector &f) -{ - static typename std::list::value_type>::iterator p1, p2; - static std::pair, V> v; - v.first.resize(keylen); - std::list::value_type> data2; - data2 = data; - reset(); - for (p1 = data2.begin(); p1 != end(); ++p1) { - if ((*p1).second > static_cast(0)) { - for (p2 = p1; p2 != data2.end(); ++p2) { - if ((*p2).second > static_cast(0)) { - for (size_t i = 0; i < keylen; ++i) { - v.first[i] = (*(f[i]))((*p1).first[i], (*p2).first[i]); - } - v.second = (*p1).second * (*p2).second; - if (p1 != p2) { - v.second *= static_cast(2); - } - (*this) += v; - } - } // end for(p2 - } - } // end for(p1 -} - -template -void trie_map::power(size_t p, trie_map &dest, trie_map &scratch, size_t keylen, - std::vector &f) -{ - if (p == 1) { - dest = *this; - return; - } - square(dest, keylen, f); - p -= 2; - if (p > 1) { - scratch = dest; - } - while (p > 0) { - if (p == 1) { - dest.multiply(*this, keylen, f); - return; - } - dest.multiply(scratch, keylen, f); - p -= 2; - } -} - -template V trie_map::inner_prod(trie_map &other, V init /*= (V) (0)*/) const -{ - V result(init); - for (auto p = begin(); p != end(); ++p) { - if ((*p).second > static_cast(0)) { - auto p2 = other.find((*p).first); - if (p2 == other.end()) { - if (p->second > static_cast(THRESH)) { - std::stringstream str; - str << "inner_prod WARNING: discarding ["; - copy(p->first.begin(), p->first.end(), std::ostream_iterator(str, " ")); - str << "] " << p->second; - throw std::runtime_error(str.str()); - } - } - else { - result += (*p).second * (*p2).second; - } - } - } - return result; -} - -template -V trie_map::inner_prod(const std::vector &x, size_t keylen, std::vector &f, - trie_map &other, V init /* = (V) (0)*/) const -{ - V result(init); - V th(THRESH); - static iterator p2; - // V s(-1); - for (auto p = begin(); p != end(); ++p) { - if ((*p).second > static_cast(0)) { - value_type y = *p; - for (size_t i = 0; i < keylen; ++i) { - y.first[i] = (*(f[i]))(y.first[i], x[i]); - } - p2 = other.find(y.first); - if (p2 == other.end()) { - if (y.second > th) { - std::stringstream str; - str << "inner_prod WARNING: discarding ["; - copy(y.first.begin(), y.first.end(), std::ostream_iterator(str, " ")); - str << "] " << y.second; - throw std::runtime_error(str.str()); - } - } - else { - result += y.second * (*p2).second; - } - } - } - return result; -} - -template trie_map &trie_map::operator/=(const std::vector &denom) -{ - // first, find the pivot: the first nonzero element of denom - V th(std::sqrt(THRESH)); - int piv = -1; - for (size_t i = 0; i < denom.size(); ++i) { - if (denom[i] > th) { - piv = static_cast(i); - break; - } - } - if (piv == -1) { - return *this; - } - - // get rid of 0th order terms at the pivot level - std::list::value_type> data2; - // data.swap(data2); - data2 = data; - clear(); - typename std::list::value_type>::iterator itr; - for (itr = data2.begin(); itr != data2.end(); ++itr) { - if (itr->first[piv] > 0) { - insert(*itr); - } - } - - div(denom, root, 0, piv); - return *this; -} - -template -void trie_map::div(const std::vector &denom, TrieNode *n, int current, int pivot) -{ - int i; - if (pivot != current) { - const int s = n->children.size(); - for (i = 0; i < s; i++) { - if (n->children[i]) { - div(denom, n->children[i], current + 1, pivot); - } - } - return; - } - - for (i = 0; i < static_cast(n->children.size()) - 1; i++) { - n->children[i] = n->children[i + 1]; - } - n->children.pop_back(); - - typename trie_map::div_helper f(denom, pivot, data.end()); - for (i = n->children.size() - 1; i >= 0; i--) { - if (i < static_cast(n->children.size()) - 1 && n->children[i + 1]) { - // if(!n->children[i])n->children[i]=new TrieNode(initBranches, data.end()); - typename trie_map::div_helper_mul g(denom, pivot, n->children[i], data.end()); - in_order_subtree(g, n->children[i + 1]); - } - in_order_subtree(f, n->children[i]); - } -} - -template std::ostream &operator<<(std::ostream &s, const trie_map &t) -{ - for (auto p = t.begin(); p != t.end(); ++p) { - s << "[ "; - copy(p->first.begin(), p->first.end(), std::ostream_iterator(s, " ")); - s << "] " << p->second << std::endl; - } - return s; -} - -template void trie_map::deleteNodes(TrieNode *n) -{ - if (!n) { - return; - } - const size_type s = n->children.size(); - for (size_type i = 0; i < s; ++i) { - if (n->children[i]) { - deleteNodes(n->children[i]); - } - } - delete n; -} - -template void trie_map::div_helper_mul::add(const std::vector &conf, V y) -{ - const size_t keylen = conf.size(); - const double th(THRESH / static_cast(denom[pivot])); - // if (y<=th&&y>=-th) return; - - if (!dest) { - if (static_cast(y) > th || static_cast(y) < -th) { - std::stringstream str; - str << "division (pivot=" << denom[pivot] << ") WARNING: discarding " << y; - throw std::runtime_error(str.str()); - } - return; - } - TrieNode *ptr = dest; - for (size_t i = pivot + 1; i < keylen; ++i) { - if ((*ptr)[conf[i]] == nullptr) { - if (static_cast(y) > th || static_cast(y) < -th) { - std::stringstream str; - str << "division (pivot=" << denom[pivot] << ") WARNING: discarding " << y; - throw std::runtime_error(str.str()); - } - return; - } - ptr = (*ptr)[conf[i]]; - } - ptr->val->second += y; - if (static_cast(ptr->val->second) <= -th) { - std::stringstream str; - str << "division (pivot=" << denom[pivot] << ") WARNING: discarding " << ptr->val->second - << std::endl; - throw std::runtime_error(str.str()); - } - if (ptr->val->second < static_cast(0)) { - ptr->val->second = static_cast(0); - } -} - -template void trie_map::div_helper_mul::operator()(iterator p) -{ - if (p == endp) { - return; - } - const size_t keylen = p->first.size(); - V null_prob(static_cast(1) - denom[pivot]); - // V th(THRESH); - for (size_t i = pivot + 1; i < keylen; ++i) { - if (denom[i] > static_cast(0)) { - p->first[i]++; - add(p->first, -denom[i] * p->second); - p->first[i]--; - null_prob -= denom[i]; - } - } - - if (null_prob > static_cast(0)) { - add(p->first, -null_prob * p->second); - } -} - -} // end namespace Gambit::agg diff --git a/src/games/game.cc b/src/games/game.cc index e7dab4ad65..e1ef620651 100644 --- a/src/games/game.cc +++ b/src/games/game.cc @@ -333,221 +333,4 @@ void GameRep::RelabelOutcomes(const std::map &p_labels } } -//======================================================================== -// MixedStrategyProfileRep -//======================================================================== - -template -MixedStrategyProfileRep::MixedStrategyProfileRep(const StrategySupportProfile &p_support) - : m_probs(p_support.GetShape()), m_offsets(p_support.GetShape()), m_support(p_support), - m_gameversion(p_support.GetGame()->GetVersion()) -{ - int index = 1; - for (auto player : p_support.GetGame()->GetPlayers()) { - for (auto strategy : player->GetStrategies()) { - if (p_support.Contains(strategy)) { - m_offsets.GetFlattened()[index] = StrategyOffset(strategy); - m_profileIndex[strategy] = index; - index++; - } - else { - m_profileIndex[strategy] = -1; - } - } - } - SetCentroid(); -} - -template void MixedStrategyProfileRep::SetCentroid() -{ - for (auto player : m_support.GetGame()->GetPlayers()) { - T center = static_cast(1) / static_cast(m_support.GetStrategies(player).size()); - for (auto strategy : m_support.GetStrategies(player)) { - (*this)[strategy] = center; - } - } -} - -template -std::unique_ptr> MixedStrategyProfileRep::Normalize() const -{ - auto norm = Copy(); - for (auto player : m_support.GetGame()->GetPlayers()) { - T sum = static_cast(0); - for (auto strategy : m_support.GetStrategies(player)) { - sum += (*this)[strategy]; - } - if (sum == static_cast(0)) { - continue; - } - for (auto strategy : m_support.GetStrategies(player)) { - (*norm)[strategy] /= sum; - } - } - return norm; -} - -//======================================================================== -// MixedStrategyProfile: Lifecycle -//======================================================================== - -template -MixedStrategyProfile::MixedStrategyProfile(const MixedBehaviorProfile &p_profile) - : m_rep(std::make_unique>(p_profile)) -{ - auto *efg = dynamic_cast(p_profile.GetGame().operator->()); - for (const auto &player : efg->m_players) { - for (const auto &strategy : player->m_strategies) { - auto prob = static_cast(1); - for (const auto &infoset : player->m_infosets) { - if (strategy->m_behav.contains(infoset.get()) && strategy->m_behav.at(infoset.get()) > 0) { - prob *= p_profile[infoset->GetAction(strategy->m_behav.at(infoset.get()))]; - } - } - (*m_rep)[strategy] = prob; - } - } -} - -template -MixedStrategyProfile &MixedStrategyProfile::operator=(const MixedStrategyProfile &p_profile) -{ - if (this != &p_profile) { - InvalidateCache(); - m_rep = p_profile.m_rep->Copy(); - } - return *this; -} - -//======================================================================== -// MixedStrategyProfile: General data access -//======================================================================== - -template Vector MixedStrategyProfile::GetStrategy(const GamePlayer &p_player) const -{ - CheckVersion(); - auto strategies = m_rep->GetSupport().GetStrategies(p_player); - Vector probs(strategies.size()); - std::transform(strategies.begin(), strategies.end(), probs.begin(), - [this](const GameStrategy &s) { return (*m_rep)[s]; }); - return probs; -} - -template MixedStrategyProfile MixedStrategyProfile::ToFullSupport() const -{ - CheckVersion(); - MixedStrategyProfile full(m_rep->GetSupport().GetGame()->NewMixedStrategyProfile(T(0))); - - for (const auto &player : m_rep->GetSupport().GetGame()->GetPlayers()) { - for (const auto &strategy : player->GetStrategies()) { - full[strategy] = - (m_rep->GetSupport().Contains(strategy)) ? (*m_rep)[strategy] : static_cast(0); - } - } - return full; -} - -//======================================================================== -// MixedStrategyProfile: Computation of interesting quantities -//======================================================================== - -template void MixedStrategyProfile::ComputePayoffs() const -{ - if (m_cache.m_valid) { - return; - } - Cache newCache; - for (const auto &player : m_rep->GetSupport().GetPlayers()) { - newCache.m_payoffs[player] = GetPayoff(player); - const auto &strategies = m_rep->GetSupport().GetStrategies(player); - Vector values(strategies.size()); - if (m_rep->GetPayoffDerivs(player->GetNumber(), values)) { - auto value_it = values.begin(); - for (const auto &strategy : strategies) { - newCache.m_strategyValues[player][strategy] = *value_it; - ++value_it; - } - } - else { - for (const auto &strategy : m_rep->GetSupport().GetStrategies(player)) { - newCache.m_strategyValues[player][strategy] = GetPayoff(strategy); - } - } - } - newCache.m_valid = true; - m_cache = std::move(newCache); -} - -template T MixedStrategyProfile::GetLiapValue() const -{ - CheckVersion(); - ComputePayoffs(); - - auto liapValue = static_cast(0); - for (const auto &p : m_cache.m_payoffs) { - const auto &values = m_cache.m_strategyValues.at(p.first); - liapValue += sum_function(values, [&](const auto &v) { - return sqr(std::max(v.second - p.second, static_cast(0))); - }); - } - return liapValue; -} - -template T MixedStrategyProfile::GetRegret(const GameStrategy &p_strategy) const -{ - CheckVersion(); - ComputePayoffs(); - - auto player = p_strategy->GetPlayer(); - if (player->m_strategies.size() == 1) { - return T{0}; - } - T best_other_payoff = maximize_function( - filter_if(player->GetStrategies(), [&](const auto &s) { return s != p_strategy; }), - [this, &player](const auto &strategy) -> T { - return m_cache.m_strategyValues.at(player).at(strategy); - }); - return std::max(best_other_payoff - m_cache.m_strategyValues.at(player).at(p_strategy), T{0}); -} - -template T MixedStrategyProfile::GetRegret(const GamePlayer &p_player) const -{ - CheckVersion(); - ComputePayoffs(); - auto strategies = p_player->GetStrategies(); - if (strategies.size() == 0) { - return T{0}; - } - auto br_payoff = maximize_function(strategies, [this, p_player](const auto &strategy) -> T { - return m_cache.m_strategyValues.at(p_player).at(strategy); - }); - return br_payoff - m_cache.m_payoffs.at(p_player); -} - -template T MixedStrategyProfile::GetMaxRegret() const -{ - CheckVersion(); - if (GetGame()->GetPlayers().size() == 0) { - return T{0}; - } - return maximize_function(GetGame()->GetPlayers(), - [this](const auto &player) -> T { return this->GetRegret(player); }); -} - -MixedStrategyProfile PureStrategyProfileRep::ToMixedStrategyProfile() const -{ - auto temp = m_game->NewMixedStrategyProfile(Rational(0)); - temp = Rational(0); - for (const auto &player : m_game->GetPlayers()) { - temp[GetStrategy(player)] = Rational(1); - } - return temp; -} - -template class MixedStrategyProfileRep; -template class MixedStrategyProfileRep; - -template class MixedStrategyProfile; -template class MixedStrategyProfile; - } // end namespace Gambit diff --git a/src/games/stratmixed.cc b/src/games/stratmixed.cc new file mode 100644 index 0000000000..99660b9f64 --- /dev/null +++ b/src/games/stratmixed.cc @@ -0,0 +1,250 @@ +// +// This file is part of Gambit +// Copyright (c) 1994-2026, The Gambit Project (https://www.gambit-project.org) +// +// FILE: src/games/stratmixed.cc +// Implementation of mixed strategy profile classes +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// + +#include "games.h" + +// The reference to the tree representation violates the logic +// of separating implementation types. This will be fixed when we move +// editing operations into the game itself instead of in the member-object +// classes. +#include "gametree.h" + +namespace Gambit { + +//======================================================================== +// MixedStrategyProfileRep +//======================================================================== + +template +MixedStrategyProfileRep::MixedStrategyProfileRep(const StrategySupportProfile &p_support) + : m_probs(p_support.GetShape()), m_offsets(p_support.GetShape()), m_support(p_support), + m_gameversion(p_support.GetGame()->GetVersion()) +{ + int index = 1; + for (auto player : p_support.GetGame()->GetPlayers()) { + for (auto strategy : player->GetStrategies()) { + if (p_support.Contains(strategy)) { + m_offsets.GetFlattened()[index] = StrategyOffset(strategy); + m_profileIndex[strategy] = index; + index++; + } + else { + m_profileIndex[strategy] = -1; + } + } + } + SetCentroid(); +} + +template void MixedStrategyProfileRep::SetCentroid() +{ + for (auto player : m_support.GetGame()->GetPlayers()) { + T center = static_cast(1) / static_cast(m_support.GetStrategies(player).size()); + for (auto strategy : m_support.GetStrategies(player)) { + (*this)[strategy] = center; + } + } +} + +template +std::unique_ptr> MixedStrategyProfileRep::Normalize() const +{ + auto norm = Copy(); + for (auto player : m_support.GetGame()->GetPlayers()) { + T sum = static_cast(0); + for (auto strategy : m_support.GetStrategies(player)) { + sum += (*this)[strategy]; + } + if (sum == static_cast(0)) { + continue; + } + for (auto strategy : m_support.GetStrategies(player)) { + (*norm)[strategy] /= sum; + } + } + return norm; +} + +//======================================================================== +// MixedStrategyProfile: Lifecycle +//======================================================================== + +template +MixedStrategyProfile::MixedStrategyProfile(const MixedBehaviorProfile &p_profile) + : m_rep(std::make_unique>(p_profile)) +{ + auto *efg = dynamic_cast(p_profile.GetGame().operator->()); + for (const auto &player : efg->m_players) { + for (const auto &strategy : player->m_strategies) { + auto prob = static_cast(1); + for (const auto &infoset : player->m_infosets) { + if (strategy->m_behav.contains(infoset.get()) && strategy->m_behav.at(infoset.get()) > 0) { + prob *= p_profile[infoset->GetAction(strategy->m_behav.at(infoset.get()))]; + } + } + (*m_rep)[strategy] = prob; + } + } +} + +template +MixedStrategyProfile &MixedStrategyProfile::operator=(const MixedStrategyProfile &p_profile) +{ + if (this != &p_profile) { + InvalidateCache(); + m_rep = p_profile.m_rep->Copy(); + } + return *this; +} + +//======================================================================== +// MixedStrategyProfile: General data access +//======================================================================== + +template Vector MixedStrategyProfile::GetStrategy(const GamePlayer &p_player) const +{ + CheckVersion(); + auto strategies = m_rep->GetSupport().GetStrategies(p_player); + Vector probs(strategies.size()); + std::transform(strategies.begin(), strategies.end(), probs.begin(), + [this](const GameStrategy &s) { return (*m_rep)[s]; }); + return probs; +} + +template MixedStrategyProfile MixedStrategyProfile::ToFullSupport() const +{ + CheckVersion(); + MixedStrategyProfile full(m_rep->GetSupport().GetGame()->NewMixedStrategyProfile(T(0))); + + for (const auto &player : m_rep->GetSupport().GetGame()->GetPlayers()) { + for (const auto &strategy : player->GetStrategies()) { + full[strategy] = + (m_rep->GetSupport().Contains(strategy)) ? (*m_rep)[strategy] : static_cast(0); + } + } + return full; +} + +//======================================================================== +// MixedStrategyProfile: Computation of interesting quantities +//======================================================================== + +template void MixedStrategyProfile::ComputePayoffs() const +{ + if (m_cache.m_valid) { + return; + } + Cache newCache; + for (const auto &player : m_rep->GetSupport().GetPlayers()) { + newCache.m_payoffs[player] = GetPayoff(player); + const auto &strategies = m_rep->GetSupport().GetStrategies(player); + Vector values(strategies.size()); + if (m_rep->GetPayoffDerivs(player->GetNumber(), values)) { + auto value_it = values.begin(); + for (const auto &strategy : strategies) { + newCache.m_strategyValues[player][strategy] = *value_it; + ++value_it; + } + } + else { + for (const auto &strategy : m_rep->GetSupport().GetStrategies(player)) { + newCache.m_strategyValues[player][strategy] = GetPayoff(strategy); + } + } + } + newCache.m_valid = true; + m_cache = std::move(newCache); +} + +template T MixedStrategyProfile::GetLiapValue() const +{ + CheckVersion(); + ComputePayoffs(); + + auto liapValue = static_cast(0); + for (const auto &p : m_cache.m_payoffs) { + const auto &values = m_cache.m_strategyValues.at(p.first); + liapValue += sum_function(values, [&](const auto &v) { + return sqr(std::max(v.second - p.second, static_cast(0))); + }); + } + return liapValue; +} + +template T MixedStrategyProfile::GetRegret(const GameStrategy &p_strategy) const +{ + CheckVersion(); + ComputePayoffs(); + + auto player = p_strategy->GetPlayer(); + if (player->m_strategies.size() == 1) { + return T{0}; + } + T best_other_payoff = maximize_function( + filter_if(player->GetStrategies(), [&](const auto &s) { return s != p_strategy; }), + [this, &player](const auto &strategy) -> T { + return m_cache.m_strategyValues.at(player).at(strategy); + }); + return std::max(best_other_payoff - m_cache.m_strategyValues.at(player).at(p_strategy), T{0}); +} + +template T MixedStrategyProfile::GetRegret(const GamePlayer &p_player) const +{ + CheckVersion(); + ComputePayoffs(); + auto strategies = p_player->GetStrategies(); + if (strategies.size() == 0) { + return T{0}; + } + auto br_payoff = maximize_function(strategies, [this, p_player](const auto &strategy) -> T { + return m_cache.m_strategyValues.at(p_player).at(strategy); + }); + return br_payoff - m_cache.m_payoffs.at(p_player); +} + +template T MixedStrategyProfile::GetMaxRegret() const +{ + CheckVersion(); + if (GetGame()->GetPlayers().size() == 0) { + return T{0}; + } + return maximize_function(GetGame()->GetPlayers(), + [this](const auto &player) -> T { return this->GetRegret(player); }); +} + +MixedStrategyProfile PureStrategyProfileRep::ToMixedStrategyProfile() const +{ + auto temp = m_game->NewMixedStrategyProfile(Rational(0)); + temp = Rational(0); + for (const auto &player : m_game->GetPlayers()) { + temp[GetStrategy(player)] = Rational(1); + } + return temp; +} + +template class MixedStrategyProfileRep; +template class MixedStrategyProfileRep; + +template class MixedStrategyProfile; +template class MixedStrategyProfile; + +} // end namespace Gambit diff --git a/src/solvers/enumpoly/poly.cc b/src/solvers/enumpoly/poly.cc index d4cddb2a8c..cbe57ee29f 100644 --- a/src/solvers/enumpoly/poly.cc +++ b/src/solvers/enumpoly/poly.cc @@ -21,7 +21,6 @@ // #include "poly.h" -#include "polypartial.imp" namespace Gambit { @@ -331,7 +330,5 @@ template Polynomial Polynomial::Normalize() const } template class Polynomial; -template class PolynomialDerivatives; -template class PolynomialSystemDerivatives; } // end namespace Gambit diff --git a/src/solvers/enumpoly/polypartial.imp b/src/solvers/enumpoly/polypartial.cc similarity index 97% rename from src/solvers/enumpoly/polypartial.imp rename to src/solvers/enumpoly/polypartial.cc index 434dbb04b2..e20df77962 100644 --- a/src/solvers/enumpoly/polypartial.imp +++ b/src/solvers/enumpoly/polypartial.cc @@ -2,7 +2,7 @@ // This file is part of Gambit // Copyright (c) 1994-2026, The Gambit Project (http://www.gambit-project.org) // -// FILE: src/solvers/enumpoly/polypartial.imp +// FILE: src/solvers/enumpoly/polypartial.cc // Implementation of partial derivatives of polynomials // // This program is free software; you can redistribute it and/or modify @@ -196,4 +196,7 @@ Vector PolynomialSystemDerivatives::ValuesOfRootPolys(const Vector &poi return answer; } +template class PolynomialDerivatives; +template class PolynomialSystemDerivatives; + } // end namespace Gambit diff --git a/src/solvers/logit/efglogit.cc b/src/solvers/logit/efglogit.cc index 823ae57993..3af5a9ce47 100644 --- a/src/solvers/logit/efglogit.cc +++ b/src/solvers/logit/efglogit.cc @@ -26,7 +26,7 @@ #include "games.h" #include "games/gametree.h" #include "logit.h" -#include "logbehav.imp" +#include "logbehav.h" #include "solvers/path/path.h" namespace { diff --git a/src/solvers/logit/logbehav.imp b/src/solvers/logit/logbehav.cc similarity index 98% rename from src/solvers/logit/logbehav.imp rename to src/solvers/logit/logbehav.cc index 0cad1825cc..433026952c 100644 --- a/src/solvers/logit/logbehav.imp +++ b/src/solvers/logit/logbehav.cc @@ -2,7 +2,7 @@ // This file is part of Gambit // Copyright (c) 1994-2026, The Gambit Project (https://www.gambit-project.org) // -// FILE: src/solvers/logit/logbehav.imp +// FILE: src/solvers/logit/logbehav.cc // Behavior strategy profile where action probabilities are represented using // logarithms. // @@ -21,8 +21,9 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // -#include "logbehav.h" +#include "games.h" #include "games/gametree.h" +#include "logbehav.h" //======================================================================== // LogBehavProfile: Lifecycle @@ -260,3 +261,5 @@ template void LogBehavProfile::ComputeSolutionData() const ComputeSolutionDataPass2(m_game->GetRoot()); m_cacheValid = true; } + +template class LogBehavProfile;