diff --git a/Makefile.am b/Makefile.am index d698afb1c..eccc92c12 100644 --- a/Makefile.am +++ b/Makefile.am @@ -247,6 +247,7 @@ core_SOURCES = \ src/core/util.h \ src/core/array.h \ src/core/vector.h \ + src/core/segment.h \ src/core/recarray.h \ src/core/matrix.h \ src/core/integer.cc \ diff --git a/src/core/core.h b/src/core/core.h index 409e3281a..409d3536e 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -28,6 +28,7 @@ #include "recarray.h" #include "vector.h" #include "matrix.h" +#include "segment.h" #include "rational.h" #endif // GAMBIT_CORE_CORE_H diff --git a/src/core/segment.h b/src/core/segment.h new file mode 100644 index 000000000..84f8f7abc --- /dev/null +++ b/src/core/segment.h @@ -0,0 +1,109 @@ +// +// This file is part of Gambit +// Copyright (c) 1994-2026, The Gambit Project (https://www.gambit-project.org) +// +// FILE: src/core/segment.h +// A container segmenter, with versions for array and vector +// +// 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. +// + +#ifndef GAMBIT_CORE_SEGMENT_H +#define GAMBIT_CORE_SEGMENT_H + +#include "vector.h" + +namespace Gambit { + +template class Segmented { + Storage m_values; + Array m_offsets; + Array m_shape; + +public: + using value_type = typename Storage::value_type; + + template class SegmentView { + U *m_data{nullptr}; + size_t m_size{0}; + + public: + using value_type = U; + using pointer = U *; + using reference = U &; + using iterator = U *; + using const_iterator = const U *; + + SegmentView() = default; + SegmentView(U *data, const size_t size) : m_data(data), m_size(size) {} + + size_t size() const { return m_size; } + bool empty() const { return m_size == 0; } + + U *data() { return m_data; } + const U *data() const { return m_data; } + + U &operator[](const size_t i) { return m_data[i - 1]; } + const U &operator[](const size_t i) const { return m_data[i - 1]; } + + iterator begin() { return m_data; } + iterator end() { return m_data + m_size; } + const_iterator begin() const { return m_data; } + const_iterator end() const { return m_data + m_size; } + }; + + Segmented() = delete; + explicit Segmented(const Array &p_shape) + : m_values(std::accumulate(p_shape.begin(), p_shape.end(), 0)), m_offsets(p_shape.size()), + m_shape(p_shape) + { + for (size_t index = 0, i = 1; i <= m_shape.size(); i++) { + m_offsets[i] = index; + index += m_shape[i]; + } + } + Segmented(const Segmented &) = default; + Segmented(Segmented &&) noexcept = default; + ~Segmented() = default; + + Segmented &operator=(const Segmented &) = default; + Segmented &operator=(Segmented &&) noexcept = default; + Segmented &operator=(const value_type &c) + { + m_values = c; + return *this; + } + + const Array &GetShape() const { return m_shape; } + + SegmentView segment(const size_t a) + { + return SegmentView(&m_values[m_offsets[a] + 1], m_shape[a]); + } + SegmentView segment(const size_t a) const + { + return SegmentView(&m_values[m_offsets[a] + 1], m_shape[a]); + } + + void SetFlattened(const Storage &v) { m_values = v; } + const Storage &GetFlattened() const { return m_values; } +}; + +template using SegmentedArray = Segmented>; +template using SegmentedVector = Segmented>; + +} // namespace Gambit + +#endif // GAMBIT_CORE_SEGMENT_H diff --git a/src/core/vector.h b/src/core/vector.h index e5ae2d488..c35ee3a3a 100644 --- a/src/core/vector.h +++ b/src/core/vector.h @@ -41,6 +41,7 @@ template class Vector { } public: + using value_type = typename Array::value_type; using iterator = typename Array::iterator; using const_iterator = typename Array::const_iterator; diff --git a/src/solvers/simpdiv/simpdiv.cc b/src/solvers/simpdiv/simpdiv.cc index a09a02fd3..135898254 100644 --- a/src/solvers/simpdiv/simpdiv.cc +++ b/src/solvers/simpdiv/simpdiv.cc @@ -26,46 +26,6 @@ namespace Gambit::Nash { -template class PVector { -private: - Vector m_values; - Array m_offsets; - Array m_shape; - -public: - explicit PVector(const Array &p_shape) - : m_values(std::accumulate(p_shape.begin(), p_shape.end(), 0)), m_offsets(p_shape.size()), - m_shape(p_shape) - { - for (size_t index = 0, i = 1; i <= m_shape.size(); i++) { - m_offsets[i] = index; - index += m_shape[i]; - } - } - PVector(const PVector &v) = default; - ~PVector() = default; - - size_t size() const { return m_values.size(); } - T &operator[](const int a) { return m_values[a]; } - T &operator()(const int a, const int b) { return m_values[m_offsets[a] + b]; } - const T &operator()(const int a, const int b) const { return m_values[m_offsets[a] + b]; } - - PVector &operator=(const PVector &v) = default; - PVector &operator=(const Vector &v) - { - m_values = v; - return *this; - } - PVector &operator=(const T &c) - { - m_values = c; - return *this; - } - - const Array &GetShape() const { return m_shape; } - explicit operator const Vector &() const { return m_values; } -}; - class NashSimpdivStrategySolver { public: explicit NashSimpdivStrategySolver( @@ -90,15 +50,15 @@ class NashSimpdivStrategySolver { class State; Rational Simplex(MixedStrategyProfile &, const Rational &d) const; - static void update(State &, RectArray &, RectArray &, PVector &, - const PVector &, int j, int i); - static void getY(const State &, MixedStrategyProfile &x, PVector &, - const PVector &, const PVector &, const PVector &, - const RectArray &, int k); + static void update(State &, RectArray &, RectArray &, SegmentedVector &, + const SegmentedVector &, int j, int i); + static void getY(const State &, MixedStrategyProfile &x, SegmentedVector &, + const SegmentedVector &, const SegmentedVector &, + const SegmentedVector &, const RectArray &, int k); static void getnexty(const State &, MixedStrategyProfile &x, const RectArray &, - const PVector &, int i); - static int get_c(int j, int h, int nstrats, const PVector &); - static int get_b(int j, int h, int nstrats, const PVector &); + const SegmentedVector &, int i); + static int get_c(int j, int h, int nstrats, const SegmentedVector &); + static int get_b(int j, int h, int nstrats, const SegmentedVector &); }; //------------------------------------------------------------------------- @@ -117,7 +77,7 @@ class NashSimpdivStrategySolver::State { Rational d, pay, maxz, bestz; explicit State(int p_leashLength) : m_leashLength(p_leashLength), bestz(1.0e30) {} - Rational getlabel(MixedStrategyProfile &yy, Array &, PVector &); + Rational getlabel(MixedStrategyProfile &yy, Array &, SegmentedVector &); /* Check whether the distance p_dist is "too far" given the leash length, if set. */ bool CheckLeashOK(const Rational &p_dist) const @@ -140,12 +100,10 @@ Rational NashSimpdivStrategySolver::Simplex(MixedStrategyProfile &y, Array nstrats(game->GetStrategies().shape_array()); Array ylabel(2); RectArray labels(y.MixedProfileLength(), 2), pi(y.MixedProfileLength(), 2); - PVector U(nstrats), TT(nstrats); - PVector ab(nstrats), besty(nstrats), v(nstrats); - for (size_t i = 1; i <= v.size(); i++) { - v[i] = y[i]; - } - besty = y.GetProbVector(); + SegmentedVector U(nstrats), TT(nstrats); + SegmentedVector ab(nstrats), besty(nstrats), v(nstrats); + v.SetFlattened(y.GetProbVector()); + besty.SetFlattened(y.GetProbVector()); int i = 0; int j, k, h, jj, hh, ii, kk, tot; Rational maxz; @@ -158,10 +116,10 @@ Rational NashSimpdivStrategySolver::Simplex(MixedStrategyProfile &y, for (j = 1; j <= static_cast(game->NumPlayers()); j++) { const GamePlayer player = game->GetPlayer(j); for (h = 1; h <= nstrats[j]; h++) { - if (v(j, h) == Rational(0)) { - U(j, h) = 1; + if (v.segment(j)[h] == Rational(0)) { + U.segment(j)[h] = 1; } - y[player->GetStrategy(h)] = v(j, h); + y[player->GetStrategy(h)] = v.segment(j)[h]; } } @@ -174,9 +132,9 @@ Rational NashSimpdivStrategySolver::Simplex(MixedStrategyProfile &y, // Label case1a not currently used, hence commented // case1a: - if (TT(j, h) == 0 && U(j, h) == 0) { + if (TT.segment(j)[h] == 0 && U.segment(j)[h] == 0) { for (hh = 1, tot = 0; hh <= nstrats[j]; hh++) { - if (TT(j, hh) == 1 || U(j, hh) == 1) { + if (TT.segment(j)[hh] == 1 || U.segment(j)[hh] == 1) { tot++; } } @@ -189,7 +147,7 @@ Rational NashSimpdivStrategySolver::Simplex(MixedStrategyProfile &y, } } /* case1b */ - else if (TT(j, h)) { + else if (TT.segment(j)[h]) { i = 1; while (labels(i, 1) != j || labels(i, 2) != h || i == state.ibar) { i++; @@ -197,15 +155,15 @@ Rational NashSimpdivStrategySolver::Simplex(MixedStrategyProfile &y, goto step3; } /* case1c */ - else if (U(j, h)) { + else if (U.segment(j)[h]) { k = h; - while (U(j, k)) { + while (U.segment(j)[k]) { k++; if (k > nstrats[j]) { k = 1; } } - if (TT(j, k) == 0) { + if (TT.segment(j)[k] == 0) { i = state.t + 1; } else { @@ -226,8 +184,8 @@ Rational NashSimpdivStrategySolver::Simplex(MixedStrategyProfile &y, state.ibar = i + 1; state.t++; getnexty(state, y, pi, U, i); - TT(j, h) = 1; - U(j, h) = 0; + TT.segment(j)[h] = 1; + U.segment(j)[h] = 0; goto step1; step3: @@ -252,19 +210,19 @@ Rational NashSimpdivStrategySolver::Simplex(MixedStrategyProfile &y, /* case3a */ if (i == 1 && (y[GetStrategy(game, j, k)] <= Rational(0) || - !state.CheckLeashOK(v(j, k) - y[GetStrategy(game, j, k)]))) { + !state.CheckLeashOK(v.segment(j)[k] - y[GetStrategy(game, j, k)]))) { for (hh = 1, tot = 0; hh <= nstrats[j]; hh++) { - if (TT(j, hh) == 1 || U(j, hh) == 1) { + if (TT.segment(j)[hh] == 1 || U.segment(j)[hh] == 1) { tot++; } } if (tot == nstrats[j] - 1) { - U(j, k) = 1; + U.segment(j)[k] = 1; goto end; } else { update(state, pi, labels, ab, U, j, i); - U(j, k) = 1; + U.segment(j)[k] = 1; getnexty(state, y, pi, U, state.t); goto step1; } @@ -272,18 +230,18 @@ Rational NashSimpdivStrategySolver::Simplex(MixedStrategyProfile &y, /* case3b */ else if (i >= 2 && i <= state.t && (y[GetStrategy(game, j, k)] <= Rational(0) || - !state.CheckLeashOK(v(j, k) - y[GetStrategy(game, j, k)]))) { + !state.CheckLeashOK(v.segment(j)[k] - y[GetStrategy(game, j, k)]))) { goto step4; } /* case3c */ - else if (i == state.t + 1 && ab(j, kk) == Rational(0)) { + else if (i == state.t + 1 && ab.segment(j)[kk] == Rational(0)) { if (y[GetStrategy(game, j, h)] <= Rational(0) || - !state.CheckLeashOK(v(j, h) - y[GetStrategy(game, j, h)])) { + !state.CheckLeashOK(v.segment(j)[h] - y[GetStrategy(game, j, h)])) { goto step4; } else { k = 0; - while (ab(j, kk) == Rational(0) && k == 0) { + while (ab.segment(j)[kk] == Rational(0) && k == 0) { if (kk == h) { k = 1; } @@ -326,10 +284,10 @@ Rational NashSimpdivStrategySolver::Simplex(MixedStrategyProfile &y, getY(state, y, v, U, TT, ab, pi, 1); j = pi(i - 1, 1); h = pi(i - 1, 2); - TT(j, h) = 0; + TT.segment(j)[h] = 0; if (y[GetStrategy(game, j, h)] <= Rational(0) || - !state.CheckLeashOK(v(j, h) - y[GetStrategy(game, j, h)])) { - U(j, h) = 1; + !state.CheckLeashOK(v.segment(j)[h] - y[GetStrategy(game, j, h)])) { + U.segment(j)[h] = 1; } labels.RotateUp(i, state.t + 1); pi.RotateUp(i - 1, state.t); @@ -346,7 +304,7 @@ Rational NashSimpdivStrategySolver::Simplex(MixedStrategyProfile &y, labels.RotateDown(1, state.t + 1); state.ibar = 1; pi.RotateDown(1, state.t); - U(j, k) = 0; + U.segment(j)[k] = 0; jj = pi(1, 1); hh = pi(1, 2); kk = get_b(jj, hh, nstrats[jj], U); @@ -359,7 +317,7 @@ Rational NashSimpdivStrategySolver::Simplex(MixedStrategyProfile &y, if (k == h) { kk = 0; } - ab(j, k) -= Rational(1); + ab.segment(j)[k] -= Rational(1); k++; if (k > nstrats[j]) { k = 1; @@ -371,14 +329,15 @@ Rational NashSimpdivStrategySolver::Simplex(MixedStrategyProfile &y, maxz = state.bestz; for (i = 1; i <= static_cast(game->NumPlayers()); i++) { for (j = 1; j <= nstrats[i]; j++) { - y[GetStrategy(game, i, j)] = besty(i, j); + y[GetStrategy(game, i, j)] = besty.segment(i)[j]; } } return maxz; } void NashSimpdivStrategySolver::update(State &state, RectArray &pi, RectArray &labels, - PVector &ab, const PVector &U, int j, int i) + SegmentedVector &ab, + const SegmentedVector &U, int j, int i) { int jj, hh, k, f = 1; @@ -397,7 +356,7 @@ void NashSimpdivStrategySolver::update(State &state, RectArray &pi, RectArr if (k == hh) { f = 0; } - ab(j, k) += Rational(1); + ab.segment(j)[k] += Rational(1); k++; if (k > ab.GetShape()[jj]) { k = 1; @@ -417,7 +376,7 @@ void NashSimpdivStrategySolver::update(State &state, RectArray &pi, RectArr if (k == hh) { f = 0; } - ab(j, k) -= Rational(1); + ab.segment(j)[k] -= Rational(1); k++; if (k > ab.GetShape()[jj]) { k = 1; @@ -429,18 +388,19 @@ void NashSimpdivStrategySolver::update(State &state, RectArray &pi, RectArr } void NashSimpdivStrategySolver::getY(const State &state, MixedStrategyProfile &x, - PVector &v, const PVector &U, - const PVector &TT, const PVector &ab, - const RectArray &pi, int k) + SegmentedVector &v, const SegmentedVector &U, + const SegmentedVector &TT, + const SegmentedVector &ab, const RectArray &pi, + int k) { - x = static_cast &>(v); + x = v.GetFlattened(); for (int j = 1; j <= static_cast(x.GetGame()->NumPlayers()); j++) { const GamePlayer player = x.GetGame()->GetPlayer(j); for (size_t h = 1; h <= player->GetStrategies().size(); h++) { - if (TT(j, h) == 1 || U(j, h) == 1) { - x[player->GetStrategy(h)] += state.d * ab(j, h); + if (TT.segment(j)[h] == 1 || U.segment(j)[h] == 1) { + x[player->GetStrategy(h)] += state.d * ab.segment(j)[h]; const int hh = (h > 1) ? h - 1 : player->GetStrategies().size(); - x[player->GetStrategy(hh)] -= state.d * ab(j, h); + x[player->GetStrategy(hh)] -= state.d * ab.segment(j)[h]; } } } @@ -450,7 +410,8 @@ void NashSimpdivStrategySolver::getY(const State &state, MixedStrategyProfile &x, - const RectArray &pi, const PVector &U, int i) + const RectArray &pi, const SegmentedVector &U, + int i) { const int j = pi(i, 1); const GamePlayer player = x.GetGame()->GetPlayer(j); @@ -460,10 +421,10 @@ void NashSimpdivStrategySolver::getnexty(const State &state, MixedStrategyProfil x[player->GetStrategy(hh)] -= state.d; } -int NashSimpdivStrategySolver::get_b(int j, int h, int nstrats, const PVector &U) +int NashSimpdivStrategySolver::get_b(int j, int h, int nstrats, const SegmentedVector &U) { int hh = (h > 1) ? h - 1 : nstrats; - while (U(j, hh)) { + while (U.segment(j)[hh]) { hh--; if (hh == 0) { hh = nstrats; @@ -472,14 +433,15 @@ int NashSimpdivStrategySolver::get_b(int j, int h, int nstrats, const PVector &U) +int NashSimpdivStrategySolver::get_c(int j, int h, int nstrats, const SegmentedVector &U) { const int hh = get_b(j, h, nstrats, U) + 1; return (hh > nstrats) ? 1 : hh; } Rational NashSimpdivStrategySolver::State::getlabel(MixedStrategyProfile &yy, - Array &ylabel, PVector &besty) + Array &ylabel, + SegmentedVector &besty) { Rational maxz(-1000000); ylabel[1] = 1; @@ -509,7 +471,7 @@ Rational NashSimpdivStrategySolver::State::getlabel(MixedStrategyProfile(yy.GetGame()->NumPlayers()); i++) { const GamePlayer player = yy.GetGame()->GetPlayer(i); for (size_t j = 1; j <= player->GetStrategies().size(); j++) { - besty(i, j) = yy[player->GetStrategy(j)]; + besty.segment(i)[j] = yy[player->GetStrategy(j)]; } } }