Commit 370dc2f6 authored by Jason Rhinelander's avatar Jason Rhinelander

Declare defaults as constexprs rather than inline

parent 07dac9d7
......@@ -150,6 +150,9 @@ class BundleNegative {
BundleNegative& operator *= (const double &m);
BundleNegative& operator /= (const double &d);
/// The default epsilon for transferApprox(), if not specified.
static constexpr double default_transfer_epsilon = 1.0e-14;
/** Transfers (approximately) the given amount between two Bundles. Positive quantities in
* `amount` are transferred from the invoked object to the `to` Bundle; negative quantities
* are transferred from the `to` Bundle to the invoked object. The error parameter is a
......@@ -158,7 +161,7 @@ class BundleNegative {
*
* Calling
*
* from.transfer(amount, to);
* from.transferApprox(amount, to);
*
* is roughly equivalent to
*
......@@ -180,7 +183,7 @@ class BundleNegative {
* possible when the destination is a BundleNegative with a negative quantity, since the
* destination bundle is always the one being added to).
*/
void transferApprox(const BundleNegative &amount, BundleNegative &to, double epsilon = 1e-14);
void transferApprox(const BundleNegative &amount, BundleNegative &to, double epsilon = default_transfer_epsilon);
BundleNegative operator + (const BundleNegative &b) const noexcept;
BundleNegative operator - (const BundleNegative &b) const noexcept;
......
......@@ -109,9 +109,13 @@ range_<Iter> range(std::pair<Iter, Iter> const &pair) {
*/
class Stepper final {
public:
static constexpr double default_initial_step = 1.0/32.0;
static constexpr int default_increase_count = 4;
static constexpr double default_min_step = std::numeric_limits<double>::epsilon();
/** Constructs a new Stepper object.
*
* \param step the initial size of a step, relative to the current value. Defaults to 1/32
* \param initial_step the initial size of a step, relative to the current value. Defaults to 1/32
* (about 3.1%). Over time the step size will change based on the following options. When
* increasing, the new value is \f$(1 + step)\f$ times the current value; when decreasing
* the value is \f$\frac{1}{1 + step}\f$ times the current value. This ensures that an
......@@ -130,7 +134,9 @@ class Stepper final {
* machine epsilon (i.e. the smallest value v such that 1 + v is a value distinct from 1),
* which is the smallest value possible for a step.
*/
Stepper(double step = 1.0/32.0, int increase_count = 4, double min_step = std::numeric_limits<double>::epsilon());
Stepper(double initial_step = default_initial_step,
int increase_count = default_increase_count,
double min_step = default_min_step);
/** Called to signal that a step increase (`up=true`) or decrease (`up=false`) should be
* taken. Returns the relative step value, where 1 indicates no change, 1.2 indicates a 20%
......
#pragma once
#include <eris/firm/PriceFirm.hpp>
#include <eris/interopt/Stepper.hpp>
#include <eris/interopt/ProfitStepper.hpp>
namespace eris { namespace interopt {
......@@ -11,7 +11,7 @@ using eris::firm::PriceFirm;
* change increased profits, the next period price moves in the same direction; if the previous move
* decreased profits, the next period moves in the opposite direction.
*/
class PriceStepper : public Stepper {
class PriceStepper : public ProfitStepper {
public:
/** Constructs a new PriceStepper optimization object for a given PriceFirm. The algorithm
* always starts with a price increase the first time optimization occurs; subsequent rounds
......@@ -25,47 +25,15 @@ class PriceStepper : public Stepper {
*/
PriceStepper(
const PriceFirm &firm,
double step = 1.0/32.0,
int increase_count = 4
double step = default_initial_step,
int increase_count = default_increase_count
);
protected:
/** Calculates whether or not the price should be increase or decreased based on what
* happened to profits in the previous period.
*
* "Profits" in each period are determined from the multiple of the firm's price bundle that
* exists in the firm's assets. When price is a single good bundle, this is
* straightforward; when price is a multi-good bundle, profits are determined by Bundle
* division. In particular, if price is 1 right shoe and 2 left shoes, assets of 50 right
* shoes and 60 left shoes would count as the same profit as assets of 30 right shoes and 60
* left shoes.
*
* If profits in a period increased from the previous period, the price for the next period
* will change by a step in the same direction as the previous period. If profits
* decreased, the next period's price will change by a step in the opposite direction. If
* profits stay exactly the same (often at 0), prices will take a step downward, regardless
* of the previous direction.
*/
virtual bool should_increase() const override;
/** Takes a step, setting the new firm price to the given multiple of the current firm
* price.
*/
virtual void take_step(double relative) override;
/** Declares a dependency on the handled firm when added to the simulation.
*/
virtual void added() override;
private:
const eris_id_t firm_;
// We need a fixed Bundle telling us what money is so that we can compare profits. Thus
// this stores the *initial* firm price, since that will be denominated in money. We can't
// use the current firm price since the whole point of this InterOpt is to change that.
const Bundle price_;
double prev_profit_ = 0;
mutable double curr_profit_ = 0;
};
}}
......@@ -21,13 +21,16 @@ namespace eris { namespace intraopt {
*/
class MUPD : public IntraOptimizer {
public:
/// The default value of the constructor's tolerance parameter
static constexpr double default_tolerance = 1.0e-10;
/** Constructors a MUPD optimizer given a differentiable consumer instance and a money good.
*
* \param tolerance the relative tolerance of the algorithm; optimization will stop when the
* 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, const eris_id_t &money, double tolerance = 1.0e-10);
MUPD(const Consumer::Differentiable &consumer, const eris_id_t &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
......
#pragma once
#include <eris/algorithms.hpp>
#include <eris/IntraOptimizer.hpp>
#include <eris/market/Quantity.hpp>
namespace eris { namespace intraopt {
namespace eris {
namespace market { class QMarket; }
namespace intraopt {
using eris::market::Quantity;
using eris::market::QMarket;
/** Intra-period pricing mechanism for a Quantity-based market. This optimizer takes a configurable
/** Intra-period pricing mechanism for a QMarket-based market. This optimizer takes a configurable
* number of steps to try to find a (roughly) correct market price. It relies on the quantity of
* market reservations made in other intraopt optimize() to try to determine the price that just
* sells out the market.
*
* \sa eris::market::Quantity
* \sa eris::market::QMarket
*/
class QMPricer : public IntraOptimizer {
public:
/** Creates a new Quantity market pricer.
/// Default number of tries if not specified in constructor
static constexpr int default_tries = 5;
/// Default initial step if not specified in constructor
static constexpr double default_initial_step = Stepper::default_initial_step;
/// Default increase_count if not specified in constructor
static constexpr int default_increase_count = Stepper::default_increase_count;
/** Creates a new QMarket market pricer.
*
* \param qm the Quantity market this optimizer works on
* \param qm the QMarket object (or subclass) that this optimizer works on
* \param tries the number of steps to take in each period. Each step triggers a restart of
* the optimization routine in eris::Simulation, and so making this large may be very
* computationally costly.
......@@ -29,10 +37,10 @@ class QMPricer : public IntraOptimizer {
* \sa eris::Stepper
*/
QMPricer(
const Quantity &qm,
int tries = 5,
double initial_step = 1.0/32.0,
int increase_count = 4
const QMarket &qm,
int tries = default_tries,
double initial_step = default_initial_step,
int increase_count = default_increase_count
);
/// Resets the number of tries used up for this period to 0.
......@@ -42,9 +50,13 @@ class QMPricer : public IntraOptimizer {
* relative step (based on the current step size) up (if the market sold out) or down (if the
* market had a surplus).
*
* If the most recent step got a different result than the previous (or initial) price, cut
* the step size in half and reverse direction. If we see `increase_count` steps in the
* same direction, double the step size.
* If the most recent step got a different result (i.e. excess capacity versus no excess
* capacity) than the previous (or initial) price, cut the step size in half and reverse
* direction. If we see `increase_count` steps in the same direction, double the step size.
*
* If the previous step was a price decrease, but excess capacity either increased or stayed
* the same, assume we've hit some sort of market saturation, and so increase the price
* (contradicting the rule above, saying we should decrease it further).
*
* If this has already been called `tries` times in this period, it does nothing and simply
* returns false.
......@@ -63,6 +75,8 @@ class QMPricer : public IntraOptimizer {
int tries_ = 0;
/// The number of times we have already tried to adjust in the current period
int tried_ = 0;
/// The excess capacity we found in the previous iteration
double last_excess_;
};
} }
......@@ -15,6 +15,9 @@ namespace eris { namespace market {
*/
class Bertrand : public Market {
public:
/// The default value of the constructor's randomize parameter
static constexpr bool default_randomize = false;
/** Constructs the market.
*
* \param output the output Bundle for the firm
......@@ -24,7 +27,7 @@ class Bertrand : public Market {
* \param randomize if true, randomly selects a lowest-price firm in the event of ties; the
* default, false, evenly divides among lowest-price firms in the event of ties.
*/
Bertrand(Bundle output, Bundle price_unit, bool randomize = false);
Bertrand(Bundle output, Bundle price_unit, bool randomize = default_randomize);
/// Returns the pricing information for purchasing q units in this market.
virtual price_info price(double q) const override;
/// Returns the quantity (in terms of multiples of the output Bundle) that p units of the
......@@ -45,7 +48,7 @@ class Bertrand : public Market {
/** Allocation information. */
struct allocation {
price_info p;
std::map<eris_id_t, share> shares;
std::unordered_map<eris_id_t, share> shares;
};
/** Calculates the allocations across firms for a purchase of q units of the good.
* Lower-priced firms get priority, with ties as decided by the randomize parameter
......
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