An ABM in the box

18 July 13. [link] PDF version

Part of a series of posts that started here

I promised you agent-based models, so here's a model of the demand-side of an economy:

$\def\Re{{\mathbb R}} \def\datas{{\mathbb D}} \def\params{{\mathbb P}} \def\models{{\mathbb M}} \def\mod#1{M_{#1}} \def\muv{\boldsymbol{\mu}} \def\Sigmav{\boldsymbol{\Sigmav}}$

We could readily add to this simple framework agent interactions, a more interesting utility function, agent irrationality, or other bits of added realism.

Is this mechanical sequence of events a statistical model? Yes. All of the entries in this series to this point have been aimed at allaying any concerns about this question.

Does it fit the basic form of a model? Yes: given a set of parameters $\mu_b, \mu_\alpha, p_1$, and a observed mean consumption $Q_1, Q_2$, the model specifies the likelihood $L_m(\mu_b, \mu_\alpha, \sigma_{b\alpha}, p, Q_1, Q_2)$.

Can it be wrapped in the same black box as a Normal distribution or regression? Yes: we can use this the same way we use all of the models to this point, making random draws, estimating the parameters given data, transforming or chaining it with other models, et cetera. The only restant technical issue is that a likelihood with a built-in random number generator is stochastic, so we have to be careful about consistency (i.e., as draws $\to\infty$, parameter estimates converge), and there are practical difficulties of using a stochastic function.

Can this model be tested? Yes, but only in the same way that OLS and its parameters can be tested, meaning that we use the likelihood function asserted by the model to test the odds that statistics are significant.

OLS is much easier to state: as in earlier entries, we could write down the entire model in six symbols, $L_{\cal N}(Y, X\beta, \sigma)$, versus the text exposition for the model above. But that means that the difficult subtleties are implications, not assumptions. I'd go over them, but there are entire textbooks devoted to the implications of the OLS model. The random-demand model above is more explicit about its moving parts, but it doesn't have bookshelves devoted to probing further implications.

The modeling world tends to break down into social subgroups based on which assumptions are most plausible to whom. Agent-based modelers often find the linearity assumptions underlying OLS to be entirely implausible; people accustomed to simple linear models often suspect that ABMs have hidden assumptions or undesirable and unanticipated characteristics.

I'll have a few more points on this in a few entries.

Meanwhile, here's today's sample code to implement the above model, delivering on the promise that we can make draws, get likelihoods, and estimate parameters. As usual, it's easiest to read the code from the bottom up.


#include <apop.h>

typedef struct {
    double b, alpha, q1, q2;
} an_agent;

void draw(double *qs, gsl_rng *r, apop_model *m){
    double m1 = apop_data_get(m->parameters, 0);
    double m2 = apop_data_get(m->parameters, 1);
    double p1 = apop_data_get(m->parameters, 2);

    apop_model *ba_model = apop_model_stack(
            apop_model_set_parameters(apop_normal, m1, 1),
            apop_model_set_parameters(apop_normal, m2, 1)
            ); //leaks; don't care.

    //set up agents
    int agent_count=1000;
    an_agent a_list[agent_count];
    for(int i=0; i< agent_count; i++){
        double out[2];
        do {
            apop_draw(out, r, ba_model);
            a_list[i] = (an_agent){.b=out[0], .alpha=out[1]};
        } while (a_list[i].alpha <=0);
    }

    //agents decide
    qs[0]=qs[1]=0;
    for (int i=0; i< agent_count; i++){
        qs[0] += a_list[i].q1 = GSL_MIN(
                        pow(p1/a_list[i].alpha, 1./(1-a_list[i].alpha)),
                        a_list[i].b/p1);
        qs[1] += a_list[i].q2 = (a_list[i].b - p1*a_list[i].q1);
        //printf("%g %g\n", a_list[i].q1,a_list[i].q2);
    }
    qs[0] /= agent_count;
    qs[1] /= agent_count;
    apop_model_free(ba_model);
}

//for the Kernel density: center a Uniform distribution around a datum
void set_fn(apop_data *d, apop_model *m){
    Apop_matrix_row(d->matrix, 0, onerow);
    gsl_vector_memcpy(m->parameters->vector, onerow);
}

long double p(apop_data *d, apop_model *m){
    apop_multivariate_normal.vsize =
    apop_multivariate_normal.msize1=
    apop_multivariate_normal.msize2= 2;
    apop_model *kernel = apop_model_set_parameters(apop_multivariate_normal,
            0,   1, 0,
            0,   0, 1);

    apop_model *smoothed = apop_model_copy_set(apop_kernel_density, apop_kernel_density,
            .base_data = apop_model_draws(m, 500), .kernel=kernel, .set_fn=set_fn);
    double out = apop_p(d, smoothed);
    apop_data_free(smoothed->data);
    apop_model_free(smoothed);
    return out;
}

apop_model demandside = {"Demand given price", .vsize=3, .dsize=2, .draw=draw, .p=p};

int main(){
    apop_data *draws = apop_model_draws(apop_model_set_parameters(demandside, 2.6, 1.6, 1.2), .count=20);
    apop_data_show(draws);

    Apop_settings_add_group(&demandside, apop_mle, .tolerance=1e-5, .dim_cycle_tolerance=1e-3);
    apop_model_show(apop_estimate(draws, demandside));
}

[Previous entry: "RNG-based models"]
[Next entry: "Bayesians and ABMs"]