Commit 4aa57a90 authored by Jason Rhinelander's avatar Jason Rhinelander

Consumer::Differentiable: take MemberID, not eris_id_t

Taking a MemberID allows SharedMember<T>s to be passed, which is no
longer allowed now that the eris_id_t operator in SharedMember<T> is
gone.
parent 58127c8a
......@@ -34,9 +34,9 @@ class Consumer : public Agent {
class Consumer::Differentiable : public Consumer {
public:
/// Returns \f$\frac{\partial u(\mathbf{g})}{\partial g_i}\f$
virtual double d(const BundleNegative &b, eris_id_t gid) const = 0;
virtual double d(const BundleNegative &b, MemberID gid) const = 0;
/// Returns \f$\frac{\partial^2 u(\mathbf{g})}{\partial g_i \partial g_j}\f$
virtual double d2(const BundleNegative &b, eris_id_t g1, eris_id_t g2) const = 0;
virtual double d2(const BundleNegative &b, MemberID g1, MemberID g2) const = 0;
/** Returns the gradient for the given goods g evaluated at Bundle b. These must be passed
* separately because the Bundle need not contain quantities for all valid goods. The
* default implementation simply calls d() for each good, but subclasses may override that
......
......@@ -29,10 +29,10 @@ double CobbDouglas::coef() const {
return constant;
}
// Linear term
double& CobbDouglas::exp(eris_id_t g) {
double& CobbDouglas::exp(MemberID g) {
return exponents[g];
}
double CobbDouglas::exp(eris_id_t g) const {
double CobbDouglas::exp(MemberID g) const {
return exponents.count(g) ? exponents.at(g) : 0.0;
}
......@@ -64,7 +64,7 @@ double CobbDouglas::utility(const BundleNegative &b) const {
return u;
}
double CobbDouglas::d(const BundleNegative &b, eris_id_t g) const {
double CobbDouglas::d(const BundleNegative &b, MemberID g) const {
// Short-circuit cases resulting in a 0 derivative
if (!exponents.count(g) or exponents.at(g) == 0)
return 0.0;
......@@ -85,7 +85,7 @@ double CobbDouglas::d(const BundleNegative &b, eris_id_t g) const {
}
return grad;
}
double CobbDouglas::d2(const BundleNegative &b, eris_id_t g1, eris_id_t g2) const {
double CobbDouglas::d2(const BundleNegative &b, MemberID g1, MemberID g2) const {
// Short-circuit all the things that always result in a 0 second derivative
if (!exponents.count(g1) or !exponents.count(g2) or exponents.at(g1) == 0 or exponents.at(g2) == 0)
return 0.0;
......
......@@ -49,21 +49,21 @@ class CobbDouglas : public Consumer::Differentiable {
/** Accesses the exponent on good \f$g\f$. Creates it (initially set to 0) if it is not yet
* set.
*/
double& exp(eris_id_t g);
double& exp(MemberID g);
/** `const` accessor for the exponent on good \f$g\f$. Returns 0 (without creating it) if
* not yet set.
*/
double exp(eris_id_t g) const;
double exp(MemberID g) const;
/// Evaluates the utility given the current coefficients at bundle \f$b\f$.
virtual double utility(const BundleNegative &b) const;
double utility(const BundleNegative &b) const override;
/// Returns the first derivative w.r.t. good g, evaluated at bundle b.
virtual double d(const BundleNegative &b, eris_id_t g) const;
double d(const BundleNegative &b, MemberID g) const override;
/// Returns the second derivative w.r.t. goods g1, g2, evaluated at bundle b.
virtual double d2(const BundleNegative &b, eris_id_t g1, eris_id_t g2) const;
virtual double d2(const BundleNegative &b, MemberID g1, MemberID g2) const override;
protected:
/// The constant offset. \sa coef()
double constant = 0.0;
......
......@@ -79,12 +79,12 @@ class CompoundSum::Differentiable : public Consumer::Differentiable {
}
/// Returns the derivative, which is the sum of consumer derivatives.
double d(const BundleNegative &bundle, eris_id_t g) const override {
double d(const BundleNegative &bundle, MemberID g) const override {
return first->d(bundle, g) + second->d(bundle, g);
}
/// Returns the second derivative, which is the sum of consumer second derivatives.
double d2(const BundleNegative &bundle, eris_id_t g1, eris_id_t g2) const override {
double d2(const BundleNegative &bundle, MemberID g1, MemberID g2) const override {
return first->d2(bundle, g1, g2) + second->d2(bundle, g1, g2);
}
......@@ -162,7 +162,7 @@ class CompoundProduct::Differentiable : public Consumer::Differentiable {
/** Returns the derivative of \f$ u_1(\hdots) u_2(\hdots) \f$, which is, by the product rule,
* \f$ \frac{\partial u_1}{\partial g} u_2 + u_1 \frac{\partial u_2}{\partial g} \f$.
*/
double d(const BundleNegative &bundle, eris_id_t g) const override {
double d(const BundleNegative &bundle, MemberID g) const override {
double grad = 0.0;
double aprime = first->d(bundle, g);
if (aprime != 0) grad += aprime * second->utility(bundle);
......@@ -181,7 +181,7 @@ class CompoundProduct::Differentiable : public Consumer::Differentiable {
* u_1 \frac{\partial^2 u_2}{\partial g_1 \partial g_2}
* \f$.
*/
double d2(const BundleNegative &bundle, eris_id_t g1, eris_id_t g2) const override {
double d2(const BundleNegative &bundle, MemberID g1, MemberID g2) const override {
double h = 0.0;
double a12 = first->d2(bundle, g1, g2);
if (a12 != 0) h += a12 * second->utility(bundle);
......
......@@ -14,7 +14,7 @@ Polynomial::Polynomial(double offset) : offset(offset) {}
Polynomial::Polynomial(std::map<eris_id_t, std::vector<double>> coef, double offset) : offset(offset), coefficients(coef) {}
double& Polynomial::coef(eris_id_t g, unsigned int n) {
double& Polynomial::coef(MemberID g, unsigned int n) {
if (n == 0) return coef();
if (coefficients[g].size() < n)
......@@ -23,7 +23,7 @@ double& Polynomial::coef(eris_id_t g, unsigned int n) {
return coefficients[g][n-1];
}
double Polynomial::coef(eris_id_t g, unsigned int n) const {
double Polynomial::coef(MemberID g, unsigned int n) const {
if (n == 0) return coef();
return coefficients.count(g) and coefficients.at(g).size() > n
......@@ -57,7 +57,7 @@ double Polynomial::utility(const BundleNegative &b) const {
// Calculate the derivative for good g. Since utility is separable, we only
// need to use the coefficients for good g to get the value of the derivative
double Polynomial::d(const BundleNegative &b, eris_id_t g) const {
double Polynomial::d(const BundleNegative &b, MemberID g) const {
if (!coefficients.count(g)) return 0.0;
auto c = coefficients.at(g);
double up = 0.0;
......@@ -72,7 +72,7 @@ double Polynomial::d(const BundleNegative &b, eris_id_t g) const {
return up;
}
double Polynomial::d2(const BundleNegative &b, eris_id_t g1, eris_id_t g2) const {
double Polynomial::d2(const BundleNegative &b, MemberID g1, MemberID g2) const {
double upp = 0.0;
// Separable polynomial utility has no iteration terms, so Hessian is diagonal; thus we can just
......
......@@ -36,12 +36,12 @@ class Polynomial : public Consumer::Differentiable {
* such access does not include any auto-vivification, and the nth element there actually
* applies to the (n+1)th power.
*/
virtual double& coef(eris_id_t g, unsigned int n);
virtual double& coef(MemberID g, unsigned int n);
/** `const` version of `coef` that does not auto-vivify elements, but still returns 0 when
* accessing non-existing elements.
*/
virtual double coef(eris_id_t g, unsigned int n) const;
virtual double coef(MemberID g, unsigned int n) const;
/** Returns a reference to the constant offset term. Identical to accessing the `offset`
* member directly.
......@@ -62,7 +62,7 @@ class Polynomial : public Consumer::Differentiable {
* \frac{\partial u(b)}{\partial g}
* \f]
*/
virtual double d(const BundleNegative &b, eris_id_t g) const override;
virtual double d(const BundleNegative &b, MemberID g) const override;
/** Returns the second derivate with respect to \f$g_1\f$ then \f$g_2\f$, evaluated at
* Bundle \f$b\f$. Mathematically:
* \f[
......@@ -71,7 +71,7 @@ class Polynomial : public Consumer::Differentiable {
* Note that for the utility functions implementable by this class the second derivative is
* 0 whenever \f$g_1 \neq g_2\f$.
*/
virtual double d2(const BundleNegative &b, eris_id_t g1, eris_id_t g2) const override;
virtual double d2(const BundleNegative &b, MemberID g1, MemberID g2) const override;
/** Returns the consumer's hessian. This overrides the implementation in
* Consumer::Differentiable with a more efficient version, since only diagonal elements need
* to be calculated (off-diagonal elements are always 0).
......
......@@ -15,19 +15,19 @@ double Quadratic::coef() const {
return offset;
}
// Linear term
double& Quadratic::coef(eris_id_t g) {
double& Quadratic::coef(MemberID g) {
return linear[g];
}
double Quadratic::coef(eris_id_t g) const {
double Quadratic::coef(MemberID g) const {
return linear.count(g) ? linear.at(g) : 0.0;
}
// Quadratic term
double& Quadratic::coef(eris_id_t g1, eris_id_t g2) {
double& Quadratic::coef(MemberID g1, MemberID g2) {
// quad only stores elements with g1 <= g2, so swap if necessary
if (g1 > g2) std::swap(g1, g2);
return quad[g1][g2];
}
double Quadratic::coef(eris_id_t g1, eris_id_t g2) const {
double Quadratic::coef(MemberID g1, MemberID g2) const {
if (g1 > g2) std::swap(g1, g2);
return quad.count(g1) && quad.at(g1).count(g2) ? quad.at(g1).at(g2) : 0.0;
}
......@@ -48,7 +48,7 @@ double Quadratic::utility(const BundleNegative &b) const {
return u;
}
double Quadratic::d(const BundleNegative &b, eris_id_t g) const {
double Quadratic::d(const BundleNegative &b, MemberID g) const {
double up = linear.count(g) ? linear.at(g) : 0.0;
for (auto g2 : b) {
......@@ -65,7 +65,7 @@ double Quadratic::d(const BundleNegative &b, eris_id_t g) const {
// The second derivative is really easy: it's just the coefficient for off-diagonals, and double the
// coefficient for diagonals; it doesn't depend on the bundle at all.
double Quadratic::d2(const BundleNegative&, eris_id_t g1, eris_id_t g2) const {
double Quadratic::d2(const BundleNegative&, MemberID g1, MemberID g2) const {
double upp = coef(g1, g2);
if (g1 == g2) upp *= 2.0;
return upp;
......
......@@ -21,7 +21,8 @@ class Quadratic : public Consumer::Differentiable {
/// Initialize with no coefficients with an optional constant offset.
Quadratic(double offset = 0.0);
/** Initialize with linear coefficients and (optionally) a constant offset. %Quadratic
* coefficients have to be set individually after construction using &coef(eris_id_t, eris_id_t).
* coefficients have to be set individually after construction using &coef(MemberID,
* MemberID).
*/
Quadratic(std::map<eris_id_t, double> linear, double offset = 0.0);
......@@ -32,39 +33,39 @@ class Quadratic : public Consumer::Differentiable {
/** Accesses the coefficient on the linear term for good \f$g\f$. Creates it first (set
* to 0) if it is not yet set.
*/
double& coef(eris_id_t g);
double& coef(MemberID g);
/** `const` accessor for the coefficient on the linear term for good \f$g\f$. Returns 0
* (without creating it) if not yet set.
*/
double coef(eris_id_t g) const;
double coef(MemberID g) const;
/** Access the coefficient on the quadratic term \f$g_1 g_2\f$. When \f$g_1 = g_2\f$, this
* accesses the coefficient on the squared term. Note that `coef(a, b)` and `coef(b, a)` access
* the same coefficient. Creates and initializes the value to 0.0 if it does not yet exist.
*
* Example: `consumer.coef(good1, good2) = 2;`
*/
double& coef(eris_id_t g1, eris_id_t g2);
double& coef(MemberID g1, MemberID g2);
/** `const` accessor for the coefficient on quadratic term \f$g_1 g_2\f$. Returns 0
* (without creating the coefficient) if it does not yet exist. Note that `coef(a, b)` and
* `coef(b, a)` access the same coefficient.
*/
double coef(eris_id_t g1, eris_id_t g2) const;
double coef(MemberID g1, MemberID g2) const;
/// Evaluates the utility given the current coefficients at bundle \f$b\f$.
virtual double utility(const BundleNegative &b) const;
double utility(const BundleNegative &b) const override;
/// Returns the first derivative w.r.t. good g, evaluated at bundle b.
virtual double d(const BundleNegative &b, eris_id_t g) const;
double d(const BundleNegative &b, MemberID g) const override;
/// Returns the second derivative w.r.t. goods g1, g2, evaluated at bundle b.
virtual double d2(const BundleNegative &b, eris_id_t g1, eris_id_t g2) const;
double d2(const BundleNegative &b, MemberID g1, MemberID g2) const override;
protected:
/// The constant offset. \sa coef()
double offset = 0.0;
/// The map of coefficients on linear terms. \sa coef(eris_id_t)
/// The map of coefficients on linear terms. \sa coef(MemberID)
std::map<eris_id_t, double> linear;
/** The nested map of coefficients on quadratic terms. \sa coef(eris_id_t, eris_id_t) Note
/** The nested map of coefficients on quadratic terms. \sa coef(MemberID, MemberID) Note
* that we only store values for which the outer key is less than or equal to the inner key.
* That is, we store `quad[3][4]` but not `quad[4][3]`. `coef(eris_id_t, eris_id_t)` handles this
* That is, we store `quad[3][4]` but not `quad[4][3]`. `coef(MemberID, MemberID)` handles this
* argument reordering so that both `coef(3, 4)` and `coef(4, 3)` access `quad[3][4]`.
*/
std::map<eris_id_t, std::map<eris_id_t, double>> quad;
......
#include <eris/intraopt/MUPD.hpp>
#include <eris/Consumer.hpp>
#include <eris/Market.hpp>
#include <eris/Good.hpp>
#include <limits>
#include <unordered_map>
#include <utility>
......@@ -9,8 +10,8 @@ using std::unordered_map;
namespace eris { namespace intraopt {
MUPD::MUPD(const Consumer::Differentiable &consumer, eris_id_t money, double tolerance) :
tolerance(tolerance), con_id(consumer.id()), money(money), money_unit(Bundle {{ money, 1 }})
MUPD::MUPD(SharedMember<Consumer::Differentiable> c, SharedMember<Good> m, double tolerance) :
tolerance(tolerance), con(std::move(c)), money(std::move(m)), money_unit{money, 1}
{}
double MUPD::price_ratio(const SharedMember<Market> &m) const {
......@@ -91,13 +92,12 @@ double MUPD::calc_mu_per_d(
void MUPD::intraOptimize() {
auto sim = simulation();
auto consumer = sim->agent<Consumer::Differentiable>(con_id);
// Before bothering with anything else, make sure the consumer actually has some money to spend
{
auto lock = consumer->readLock();
if (consumer->assets[money] <= 0)
auto lock = con->readLock();
if (con->assets[money] <= 0)
return;
}
......@@ -134,12 +134,12 @@ void MUPD::intraOptimize() {
if (markets == 0) return;
// Now hold a write lock on this optimizer and the consumer. We'll add and remove market locks to this as needed.
auto big_lock = writeLock(consumer);
auto big_lock = writeLock(con);
markets = spending.size()-1; // -1 for the id=0 cash pseudo-market
if (markets == 0) return;
Bundle &a = consumer->assets;
Bundle &a = con->assets;
Bundle a_no_money = a;
double cash = a_no_money.remove(money);
if (cash <= 0) {
......@@ -165,7 +165,7 @@ void MUPD::intraOptimize() {
Bundle tryout = a_no_money + alloc.bundle;
for (auto m : spending) {
mu_per_d[m.first] = calc_mu_per_d(consumer, big_lock, m.first, alloc, tryout);
mu_per_d[m.first] = calc_mu_per_d(con, big_lock, m.first, alloc, tryout);
}
eris_id_t highest = 0, lowest = 0;
......@@ -192,7 +192,7 @@ void MUPD::intraOptimize() {
break; // Nothing more to optimize
}
double baseU = consumer->utility(tryout);
double baseU = con->utility(tryout);
// Attempt to transfer all of the low utility spending to the high-utility market. If MU/$
// are equal, we're done; if the lower utility is still lower, transfer 3/4, otherwise
// transfer 1/4. Repeat.
......@@ -208,8 +208,8 @@ void MUPD::intraOptimize() {
alloc = spending_allocation(try_spending);
tryout = a_no_money + alloc.bundle;
if (consumer->utility(tryout) < baseU or
calc_mu_per_d(consumer, big_lock, highest, alloc, tryout) < calc_mu_per_d(consumer, big_lock, lowest, alloc, tryout)) {
if (con->utility(tryout) < baseU or
calc_mu_per_d(con, big_lock, highest, alloc, tryout) < calc_mu_per_d(con, big_lock, lowest, alloc, tryout)) {
// Transferring *everything* from lowest to highest is too much (MU/$ for the highest
// good would end up lower than the lowest good, post-transfer, or else overall utility
// goes down entirely).
......@@ -244,7 +244,7 @@ void MUPD::intraOptimize() {
alloc = spending_allocation(try_spending);
tryout = a_no_money + alloc.bundle;
double delta = calc_mu_per_d(consumer, big_lock, highest, alloc, tryout) - calc_mu_per_d(consumer, big_lock, lowest, alloc, tryout);
double delta = calc_mu_per_d(con, big_lock, highest, alloc, tryout) - calc_mu_per_d(con, big_lock, lowest, alloc, tryout);
if (delta == 0)
// We equalized MU/$. Done.
break;
......@@ -286,7 +286,7 @@ void MUPD::intraOptimize() {
}
// Safety check: make sure we're actually increasing utility; if not, don't do anything.
if (consumer->utility(a_no_money + final_alloc.bundle) <= consumer->currUtility()) {
if (con->utility(a_no_money + final_alloc.bundle) <= con->currUtility()) {
return;
}
......@@ -306,7 +306,7 @@ void MUPD::intraOptimize() {
auto market = sim->market(m.first);
big_lock.add(market);
try {
reservations.push_front(market->reserve(consumer, m.second));
reservations.push_front(market->reserve(con, m.second));
}
catch (Market::output_infeasible &e) {
restart = true;
......@@ -337,13 +337,13 @@ void MUPD::intraOptimize() {
}
void MUPD::intraReset() {
auto lock = writeLock(simAgent<Consumer::Differentiable>(con_id));
auto lock = writeLock(con);
reservations.clear();
}
void MUPD::intraApply() {
auto lock = writeLock(simAgent<Consumer::Differentiable>(con_id));
auto lock = writeLock(con);
for (auto &res: reservations) {
res.buy();
......@@ -353,7 +353,7 @@ void MUPD::intraApply() {
}
void MUPD::added() {
dependsOn(con_id);
dependsOn(con);
dependsOn(money);
}
......
......@@ -32,7 +32,7 @@ class MUPD : public Member, public virtual OptApplyReset {
* difference between highest and lowest MU/$ values (calculated as \f$ \frac{highest -
* lowest}{highest} \f$ is smaller than this value. Defaults to 1.0e-10.
*/
MUPD(const Consumer::Differentiable &consumer, eris_id_t money, double tolerance = default_tolerance);
MUPD(SharedMember<Consumer::Differentiable> consumer, SharedMember<Good> money, double tolerance = default_tolerance);
/** Attempts to spend any available money optimally. Returns false is no money was spent;
* typically, with no other changes to the economy between calls, optimize() will return
......@@ -65,10 +65,10 @@ class MUPD : public Member, public virtual OptApplyReset {
};
protected:
/// The id of the consumer this optimizer applies to
const eris_id_t con_id;
/// The id of the money good that the consumer spends
const eris_id_t money;
/// The consumer this optimizer applies to
const SharedMember<Consumer::Differentiable> con;
/// The money good that the consumer spends
const SharedMember<Good> money;
/// A fixed bundle of exactly 1 unit of the money good
const Bundle money_unit;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment