Skip to content

Commit b296816

Browse files
committed
Modernisation and efficiency improvements for polynomials.
This makes a number of updates to polynomials: * The component monomials are now stored as a std::vector rather than a Gambit::List. * All iteration over monomials is now iteration rather than index-based lookup (which was linear-time under List) * Algorithm improvements have been made to addition and multiplication of polynomials as well as evaluation at a point. * Revised: PartialDerivative is now valid for general polynomials (previously it was only correct for multinomials) * Revised: Normalisation is now done if the absolute value of the largest coefficient exceeds a threshold (previously it was the value not absolute value, which was surely not intended). * Move operations added to monomial and polynomial. With the exception of the notes above, there should be no changes in behaviour. Some rough benchmarking suggests this package of changes speeds up enumpoly around 30% on some small-to-mid-size games. LeadingCoefficient TranslateOfMono (and adds pow) MonoInNewCoordinates Max value of nonlinear part normalize normalize - norm by absolute value Adds IsZero, IsConstant Use std::vector instead of Array; add explicit move to polynomial
1 parent 1e75fea commit b296816

File tree

8 files changed

+486
-380
lines changed

8 files changed

+486
-380
lines changed

Makefile.am

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,6 @@ gambit_enumpoly_SOURCES = \
404404
src/solvers/enumpoly/rectangle.h \
405405
src/solvers/enumpoly/poly.cc \
406406
src/solvers/enumpoly/poly.h \
407-
src/solvers/enumpoly/poly.imp \
408407
src/solvers/enumpoly/polysystem.h \
409408
src/solvers/enumpoly/polypartial.h \
410409
src/solvers/enumpoly/polypartial.imp \

src/core/array.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ template <class T> class Array {
5151
using reference = typename std::vector<T>::reference;
5252
using const_reference = typename std::vector<T>::const_reference;
5353

54-
explicit Array(size_t p_length = 0) : m_offset(1), m_data(p_length) {}
54+
Array() : m_offset(1) {}
55+
explicit Array(size_t p_length) : m_offset(1), m_data(p_length) {}
5556
/// Construct an array with the given index bounds.
5657
/// If p_high is less than p_low, the resulting array will have
5758
/// front_index equal to p_low, and size 0.
@@ -116,7 +117,17 @@ template <class T> class Array {
116117
}
117118
erase(std::next(begin(), p_index - front_index()));
118119
}
120+
template <class Pred> void remove_if(Pred p)
121+
{
122+
auto it = std::remove_if(m_data.begin(), m_data.end(), p);
123+
m_data.erase(it, m_data.end());
124+
}
125+
119126
void push_back(const T &value) { m_data.push_back(value); }
127+
template <class... Args> T &emplace_back(Args &&...args)
128+
{
129+
return m_data.emplace_back(std::forward<Args>(args)...);
130+
}
120131
void pop_back() { m_data.pop_back(); }
121132
void reserve(size_t len) { m_data.reserve(len); }
122133
};

src/core/list.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,18 @@ template <class T> class List {
9696
/// Inserts the value at the specified position
9797
void insert(iterator pos, const T &value) { m_list.insert(pos, value); }
9898
/// Erases the element at the specified position
99-
void erase(iterator pos) { m_list.erase(pos); }
99+
iterator erase(iterator pos) { return m_list.erase(pos); }
100+
template <class Predicate> void remove_if(Predicate pred)
101+
{
102+
for (auto it = begin(); it != end();) {
103+
if (pred(*it)) {
104+
it = erase(it);
105+
}
106+
else {
107+
++it;
108+
}
109+
}
110+
}
100111

101112
/// Removes all elements from the list container (which are destroyed),
102113
/// leaving the container with a size of 0.

src/solvers/enumpoly/efgpoly.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ Polynomial<double> BuildSequenceVariable(ProblemData &p_data, const GameSequence
6060
const std::map<GameSequence, int> &var)
6161
{
6262
if (!p_sequence->action) {
63-
return {p_data.space, 1};
63+
return Polynomial<double>(p_data.space, 1);
6464
}
6565
if (p_sequence->action != p_data.m_support.GetActions(p_sequence->GetInfoset()).back()) {
66-
return {p_data.space, var.at(p_sequence), 1};
66+
return Polynomial<double>(p_data.space, var.at(p_sequence), 1);
6767
}
6868

6969
Polynomial<double> equation(p_data.space);

src/solvers/enumpoly/poly.cc

Lines changed: 306 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,316 @@
2020
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2121
//
2222

23-
#include "poly.imp"
23+
#include "poly.h"
2424
#include "polypartial.imp"
2525

2626
namespace Gambit {
2727

28+
template <class T>
29+
std::vector<Monomial<T>> Polynomial<T>::Adder(const std::vector<Monomial<T>> &p_one,
30+
const std::vector<Monomial<T>> &p_two) const
31+
{
32+
if (p_one.empty()) {
33+
return p_two;
34+
}
35+
if (p_two.empty()) {
36+
return p_one;
37+
}
38+
std::vector<Monomial<T>> answer;
39+
answer.reserve(p_one.size() + p_two.size());
40+
41+
auto it1 = p_one.begin();
42+
auto it2 = p_two.begin();
43+
auto end1 = p_one.end();
44+
auto end2 = p_two.end();
45+
46+
while (it1 != end1 && it2 != end2) {
47+
const auto &e1 = it1->ExpV();
48+
const auto &e2 = it2->ExpV();
49+
50+
if (e1 < e2) {
51+
answer.push_back(*it1++);
52+
}
53+
else if (e2 < e1) {
54+
answer.push_back(*it2++);
55+
}
56+
else {
57+
T merged = it1->Coef() + it2->Coef();
58+
if (merged != static_cast<T>(0)) {
59+
answer.emplace_back(merged, e1); // more efficient than *it1 + *it2
60+
}
61+
++it1;
62+
++it2;
63+
}
64+
}
65+
66+
while (it1 != end1) {
67+
answer.push_back(*it1++);
68+
}
69+
while (it2 != end2) {
70+
answer.push_back(*it2++);
71+
}
72+
return answer;
73+
}
74+
75+
template <class T>
76+
std::vector<Monomial<T>> Polynomial<T>::Mult(const std::vector<Monomial<T>> &p_one,
77+
const std::vector<Monomial<T>> &p_two) const
78+
{
79+
std::vector<Monomial<T>> result;
80+
81+
if (p_one.empty() || p_two.empty()) {
82+
return result;
83+
}
84+
85+
result.reserve(p_one.size() * p_two.size());
86+
for (const auto &m1 : p_one) {
87+
for (const auto &m2 : p_two) {
88+
result.emplace_back(m1 * m2);
89+
}
90+
}
91+
std::sort(result.begin(), result.end(),
92+
[](const Monomial<T> &a, const Monomial<T> &b) { return a.ExpV() < b.ExpV(); });
93+
std::vector<Monomial<T>> merged;
94+
merged.reserve(result.size());
95+
96+
auto it = result.begin();
97+
auto end = result.end();
98+
99+
while (it != end) {
100+
Monomial<T> current = *it;
101+
++it;
102+
while (it != end && it->ExpV() == current.ExpV()) {
103+
current += *it;
104+
++it;
105+
}
106+
if (!current.IsZero()) {
107+
merged.emplace_back(std::move(current));
108+
}
109+
}
110+
return merged;
111+
}
112+
113+
template <class T> Polynomial<T> Polynomial<T>::DivideByPolynomial(const Polynomial &den) const
114+
{
115+
if (den.IsZero()) {
116+
throw ZeroDivideException();
117+
}
118+
119+
// assumes exact divisibility!
120+
Polynomial result(m_space);
121+
if (IsZero()) {
122+
return result;
123+
}
124+
125+
if (den.Degree() == 0) {
126+
return *this / den.NumLeadCoeff();
127+
}
128+
129+
int last = GetDimension();
130+
while (last > 1 && den.DegreeOfVar(last) == 0) {
131+
--last;
132+
}
133+
134+
Polynomial remainder(*this);
135+
while (!remainder.IsZero()) {
136+
const int deg_rem = remainder.DegreeOfVar(last);
137+
const int deg_den = den.DegreeOfVar(last);
138+
139+
const Polynomial lead_rem = remainder.LeadingCoefficient(last);
140+
const Polynomial lead_den = den.LeadingCoefficient(last);
141+
const Polynomial quot = lead_rem / lead_den;
142+
143+
// x_last^(deg_rem - deg_den)
144+
const int pow = deg_rem - deg_den;
145+
const Polynomial mono_pow(m_space, last, pow);
146+
147+
result += quot * mono_pow;
148+
remainder -= quot * mono_pow * den;
149+
}
150+
return result;
151+
}
152+
153+
template <class T> Polynomial<T> Polynomial<T>::PartialDerivative(int varnumber) const
154+
{
155+
std::vector<Monomial<T>> terms;
156+
terms.reserve(m_terms.size());
157+
158+
for (const auto &term : m_terms) {
159+
const auto &expv = term.ExpV();
160+
const int exponent = expv[varnumber];
161+
if (exponent == 0) {
162+
continue;
163+
}
164+
terms.emplace_back(term.Coef() * static_cast<T>(exponent), expv.DecrementExponent(varnumber));
165+
}
166+
167+
Polynomial result(m_space);
168+
result.m_terms = std::move(terms);
169+
return result;
170+
}
171+
172+
template <class T> Polynomial<T> Polynomial<T>::LeadingCoefficient(int varnumber) const
173+
{
174+
Polynomial result(m_space);
175+
result.m_terms.reserve(m_terms.size());
176+
177+
const int degree = DegreeOfVar(varnumber);
178+
if (degree == 0) {
179+
return result;
180+
}
181+
182+
for (const auto &term : m_terms) {
183+
const auto &expv = term.ExpV();
184+
if (expv[varnumber] == degree) {
185+
result.m_terms.emplace_back(term.Coef(), expv.WithZeroExponent(varnumber));
186+
}
187+
}
188+
return result;
189+
}
190+
191+
template <class T> Polynomial<T> Polynomial<T>::pow(int exponent) const
192+
{
193+
if (exponent < 0) {
194+
throw std::domain_error("Polynomial::pow: negative exponent not supported.");
195+
}
196+
197+
if (exponent == 0) {
198+
return Polynomial<T>(m_space, static_cast<T>(1));
199+
}
200+
if (exponent == 1) {
201+
return *this;
202+
}
203+
204+
Polynomial base(*this);
205+
Polynomial result(m_space, static_cast<T>(1));
206+
while (exponent > 0) {
207+
if (exponent & 1) {
208+
result *= base;
209+
}
210+
exponent >>= 1;
211+
if (exponent > 0) {
212+
base *= base;
213+
}
214+
}
215+
return result;
216+
}
217+
218+
template <class T>
219+
Polynomial<T> Polynomial<T>::TranslateOfMono(const Monomial<T> &m,
220+
const Vector<T> &new_origin) const
221+
{
222+
Polynomial result(m_space, static_cast<T>(1));
223+
224+
const auto &expv = m.ExpV();
225+
const int dim = GetDimension();
226+
227+
for (int i = 1; i <= dim; ++i) {
228+
const int exponent = expv[i];
229+
if (exponent == 0) {
230+
continue;
231+
}
232+
Polynomial shifted(m_space, i, 1);
233+
shifted += Polynomial<T>(m_space, new_origin[i]);
234+
result *= shifted.pow(exponent);
235+
}
236+
result *= m.Coef();
237+
return result;
238+
}
239+
240+
template <class T> Polynomial<T> Polynomial<T>::TranslateOfPoly(const Vector<T> &new_origin) const
241+
{
242+
Polynomial answer(m_space);
243+
answer.m_terms.reserve(m_terms.size());
244+
for (const auto &mono : m_terms) {
245+
answer += TranslateOfMono(mono, new_origin);
246+
}
247+
return answer;
248+
}
249+
250+
template <class T>
251+
Polynomial<T> Polynomial<T>::MonoInNewCoordinates(const Monomial<T> &m, const Matrix<T> &M) const
252+
{
253+
Polynomial result(m_space, static_cast<T>(1));
254+
const auto &expv = m.ExpV();
255+
const int dim = GetDimension();
256+
257+
int var_index = 1;
258+
259+
for (auto exp_it = expv.begin(); exp_it != expv.end(); ++exp_it, ++var_index) {
260+
const int exponent = *exp_it;
261+
if (exponent == 0) {
262+
continue;
263+
}
264+
265+
Polynomial linearform(m_space);
266+
linearform.m_terms.reserve(GetDimension());
267+
for (int j = 1; j <= dim; ++j) {
268+
const T &coeff = M(var_index, j);
269+
if (coeff != static_cast<T>(0)) {
270+
linearform.m_terms.emplace_back(coeff, ExponentVector(m_space, j, 1));
271+
}
272+
}
273+
result *= linearform.pow(exponent);
274+
}
275+
result *= m.Coef();
276+
return result;
277+
}
278+
279+
template <class T> Polynomial<T> Polynomial<T>::PolyInNewCoordinates(const Matrix<T> &M) const
280+
{
281+
Polynomial answer(m_space);
282+
answer.m_terms.reserve(m_terms.size());
283+
for (const auto &term : m_terms) {
284+
answer += MonoInNewCoordinates(term, M);
285+
}
286+
return answer;
287+
}
288+
289+
template <class T> T Polynomial<T>::MaximalValueOfNonlinearPart(const T &radius) const
290+
{
291+
T result = static_cast<T>(0);
292+
293+
for (const auto &term : m_terms) {
294+
if (const int deg = term.TotalDegree(); deg > 1) {
295+
T rpow = static_cast<T>(1);
296+
for (int k = 0; k < deg; ++k) {
297+
rpow *= radius;
298+
}
299+
result += term.Coef() * rpow;
300+
}
301+
}
302+
return result;
303+
}
304+
305+
template <class T> Polynomial<T> Polynomial<T>::Normalize() const
306+
{
307+
if (m_terms.empty()) {
308+
return *this;
309+
}
310+
const T eps = std::numeric_limits<T>::epsilon();
311+
312+
if (m_terms.size() == 1 && m_terms.front().TotalDegree() == 0) {
313+
T c = m_terms.front().Coef();
314+
if (std::abs(c) <= eps) {
315+
return *this;
316+
}
317+
return Polynomial(m_space, static_cast<T>(c > 0 ? 1 : -1));
318+
}
319+
320+
auto it = std::max_element(m_terms.begin(), m_terms.end(),
321+
[](const Monomial<T> &a, const Monomial<T> &b) {
322+
return std::abs(a.Coef()) < std::abs(b.Coef());
323+
});
324+
325+
const T maxc = it->Coef();
326+
const T absmax = std::abs(maxc);
327+
if (absmax <= eps) {
328+
return *this;
329+
}
330+
return *this / maxc;
331+
}
332+
28333
template class Polynomial<double>;
29334
template class PolynomialDerivatives<double>;
30335
template class PolynomialSystemDerivatives<double>;

0 commit comments

Comments
 (0)