Repository navigation
Formal Summary
Alex Creiner edited this page Feb 22, 2026
·
1 revision
This is an attempt to formally present our labor-time economy agent based model
- t <- Current discrete time step. Each time step represents the passing of an hour.
-
$T$ <- The length of the working day. One day is 24 hours. -
$D$ <- Current number of working days per week. -
$U$ <- Set of all unemployed persons.
The variables below are set equal to their currently chosen default values.
-
$N_c = 100$ <- Number of products -
$N_p = N_d = N_c$ <- Number of producers and distributors, respectively. -
$N_h = 1000$ <- Number of people. -
$N_a = 3$ <- Number of worker abilities -
$N_m = \frac{N_c}{5}$ <- Starting number of machines -
$\tau_d = 7$ <- Firm demand window (measured in days) -
$\delta_s$ = 1: <- Firm stockpile duration (measured in months) -
$\beta_f$ <- Pooled account? -
$\gamma = 0.75$ <- Deadline safety multiplier -
$\mathcal{T} = 7T$ <- Firm training time (measured in work-weeks?) -
$T_S = 1$ <- Person shopping period (measured in weeks) -
$\rho_R = 8.3\times 10^{-7}$ <- Retirement chance modifier. -
$R_m = 67$ <- Max retirement age (in years). -
$\sigma_s = 12$ <- Person shopping variance (in hours) -
$\sigma_C = 0.1$ <- Person consumption frequency variance -
$h_S = 0.01$ : <- Daily person sickness chance -
$h_R = 0.1$ : <- Daily recovery chance -
$T_0 = 8$ <- Initial working day's length (in hours) -
$D_0 = 5$ <- Initial number of working days per week -
$C_m$ <- Firm initial inventory multiplier
$t := 0$ $T := T_0$ $D := D_0$ -
$\vec{\lambda}_0 := (I-A^T)^{-1}\vec{l}_0$ <- Initial prices are equal to initial values - Initially
$U$ is set equal to the set of all people, i.e. everyone is initially unemployed.
As before, equalities denote initial settings
-
$A_n = 17$ years old <- Initial age of each person (measured in hours) (should probably be a distribution with some random variance) -
$H_n = 0$ healthy <- Initial health of each person (options are 0 = Healthy, 1 = Unhealthy) -
$F_n = {f_c}_{c=1}^{N_p}$ where$f_c$ is sampled from the distn$|F|$ where$F \sim \mathcal{N}(\mu_c, \sigma_C)$ <- Per-commodity consumption frequencies -
$\delta_p$ <- Shopping offset. Determined by$T_s$ According to an extremely wonky distribution that I think will tend to cluster around 0 and the period-1. Better suggestion: von Mises distribution with $\kappa = 10$ish -
$\alpha_n = {\eta_i}{i=1}^{N_a}$ where$\eta_i \sim \mathcal{N}(1, \sigma\alpha)$ <- Per-ability proficiency-
$\sigma_{\alpha}$ is set by default to$0.2$ .
-
- A random permutation
$\sigma(N_d)_n$ of all distributors <- Ranking from most to least preferred -
$m_n = 168T_S(\vec{\lambda}_0 \cdot \vec{\mu}_c)$ : Current worker savings (i.e. accumulated labor credits).- Here
$\vec{\mu}_c$ is the vector of consumption frequencies for each commodity, and$168T$ is the number of hours in a mean consumption period. Thus each person is given funds for a week's worth of normal shopping.
- Here
-
$\mathcal{M}C = {\mu_c}{c=1}^{N_c}$ where$\mu_c \sim DUnif(0,1)$ <- Mean consumption frequencies for each commodity type- (The 0 and 1 are also parameters within the code, but I see no reason that they should ever change.)
-
$o_p$ : <- Standard order size (the way it's implemented, this is really the minimal order size?) -
$Y_p$ : <- Set of required abilities for production of the product. -
$\lambda_p$ <- Price per unit (really, an ongoing approximation of the embodied labor time) Every commodity is registered either as a consumer good or a machine. Machines have two additional properties: -
$\mathcal{D}_p$ <- Lifetime of the machine. -
$d_p$ <- Hours of use remaining.
-
$C_f$ : A set of products, the firm's catalog. Each firm is initialized to an initial catalog consisting of a single commodity$C_f = {i}$ where each$i$ is possessed in an equal number of catalogs. -
$Q_f$ <- A queue of demand signals, initially empty. A demand signal consists of a product name, a quantity, and a timestep. -
$D_f$ <- A dict of mappings from products to quantities (representing the firm's inventory demands). Initially empty -
$I_f$ <- A dict of mappings from products to quantities, representing the firm's actual inventory. It will be convenient to see$I_f$ as a vector as well, which we denote$\vec{I}_f$ .- If the firm is a producer, then initial inventory is set to
$\vec{o} \circ (A + I)\vec{e}_i$ where$\vec{o}$ is the vector of standard order sizes for each commodity,$\vec{e}_i$ is the$i^{th}$ standard basis vector, and$\circ$ denotes entry-wise multiplication. - If the firm is a distributor, then initial inventory is set to
$\vec{o} \circ \vec{e}_i$ .
- If the firm is a producer, then initial inventory is set to
-
$O_f$ <- A dict of mappings from products to the set of all outbound orders for that product of the producer (i.e. these are orders which the firm has put out for a product which are still being waited on.) Initially empty -
$S_f$ <- A list of producer firms, which the firm sees as its suppliers. Initially firms possess a suppliers list of all producers (except for themselves if they are producers). -
$R_f$ : <- A dict of mappings from orders to drafted plans. (Internal) -
$W_f$ : <- A list of Person's object (workers who are employed by the firm and also not tied up in the maintaining of any order). Initialized to an empty list. -
$P_f$ <- A list of plans currently in progress. Initialized to an empty list -
$M_f$ <- A list of machines posessed by the firm. Initialized to the set of machines needed for the production of the firm's initial commodity type.
Our ABM is defined by the sequence of rules
- For each person
$p$ :- Execute age update rule
$PA$ . - Execute shopping rule
$PS$ . - Execute health update rule
$PH$ . - Execute consumption rule
$PC$ .
- Execute age update rule
- For each firm
$f$ :- Execute demand signal processing rule
$FS$ . - Execute product reordering rule
$FR$ . - If firm is a producer, execute production rule
$FP$ . - If firm is a distributor, execute distribution rule
$FD$ .
- Execute demand signal processing rule
- For each person
$n$ ,$A_n \mapsto A_n + 1$ . - If
$A_n > R_m$ or$\gamma < \rho_R$ where$\gamma \sim CUnif(0,1)$ , retire.- Retirement rule currently unimplemented.
- If
$t \mod T_s \neq \delta_p$ , do nothing. Otherwise... - Create a vector of ideal amounts
$\mathbf{i}$ of each commodity which the person wishes to purchase according to$i_c = \gamma(f_cT_s)$ where$\gamma \sim \mathcal{N}(1, \sigma_s)$ . - Compute the total price of this purchase:
$P = \sum_{c=1}^{N_c} \lambda_c i_c$ . - As it stands, the code is scaling this bundle to match the funds of the person: They are setting the affordable bundle
$\mathbf{a} = \mathbf{i} \frac{M_n}{\mathbf{i}\cdot \mathbf{\lambda}}$ (really, the floor of this number), and then purchasing these quantities. Thus if the person has more money than they need, they will hoard product and purchase more than they wanted.- This is a big issue. We should probably just have the people maximizing their utility.
- For each product type
$c$ in the bundle, attempt to purchase the computed quantity$a_c$ - Iterate over each distributor in the order given by
$\sigma(N_d)_n$ . Attempt to purchase the good from the first distributor which has the product in stock. The distributor will:- Push the new demand signal
$(c, a_c)$ . - If the product is not in the inventory, reject the sale.
- If the product is available but not in the quantity
$a_c$ , reject the sale. - If the product is not available as a consumer good, reject the sale. Weren't we doing away with this?
- If the person cannot afford the product in the quantity demanded, reject the sale.
- If all of these checks are passed, the distributor's plan is updated accordingly (TBA)
- No goods are actually transferred to the person because the person does not yet have an inventory or actual consumption going on.
- Push the new demand signal
- Iterate over each distributor in the order given by
- For each person
$n$ :- Sample
$h$ from a standard normal random variable$H \sim \mathcal{N}(0,1)$ . - If
$H_n = 0$ and$h < (1-h_S)^{\frac{1}{24}}$ , set$H_n = 1$ (person gets sick) - If
$H_n = 1$ and$h < (1-h_R)^{\frac{1}{24}}$ , set$H_n = 0$ (person recovers)
- Sample
- Peak at the demand signal
$(p,q,t_d)$ at the front of the queue$Q_f$ . While$t_d \leq t - \tau_d$ :- Decrement the inventory demands for product
$p$ by$\frac{q}{\tau_d}$ :$D_f[p] \mapsto D_f[p] - \frac{q}{\tau_d}$ - Remove the signal from the queue, and peak at the next one.
- Decrement the inventory demands for product
- Create a list of products to place new orders on
$R$ .- If firm is a distributor,
$R = C_f$ . - If firm is a producer,
$R = C_f \cup \bigcup_{j \in C_f} {i: a_{ij} > 0}$ .
- If firm is a distributor,
- For each product
$p \in R$ ,- Get the pending inventory for product
$p$ :$i_p = I[p] + \sum_{o \in O_f[p]} o[q]$ (where$q$ denotes the quantity attribute of the order). - Define the threshhold
$q_p = \max(672\delta_sD_f[p], o_p)$ - If
$i_p < q_p$ or$\beta_f = 0$ : reorder the product$p$ . - Create a desired deadline (time to order completion)
$$\Delta_o = \frac{i_p\delta_s\gamma}{q_p}$$ - Create a new order object:
$o = (p, q_p, f, \Delta_0, F)$ . (I.e. create an order for product$p$ for the amount$h_p$ (meeting the threshold), to be received by the firm$f$ within the time window$\Delta_0$ , currently marked unfinished ($F$ )). - For each
$s \in S_f$ : call on the supplier$s$ to draft a plan (see plan drafting rule below). After drafting the plan, the supplier firm sends back the expected turnover time in days. - Let
$\Sigma_o = {\sigma_s}_{s \in S_f}$ denote the set of all drafted plans. Then the supplier$s_o$ is chosen via so as to minimize the expected turnaround time. This supplier is sent a signal to indicate this. - Upon receiving the signal, the supplier
$s_o$ will:- Retrieve the draft plan from its internal map
$\sigma = R_{s_o}[o]$ . - For each worker assigned to the draft plan
$w \in \sigma[W]$ , if$w \in W_{s_o}$ , remove$w$ from$W_{s_o}$ . If$w \in U$ , remove$w$ from$U$ . - Remap
$O_{s_o}[o] \mapsto Null$ . - Add
$\sigma$ to the list of actively pursued plans$P_{s_o}$ .
- Retrieve the draft plan from its internal map
- Get the pending inventory for product
- For each plan
$\sigma \in P_f$ - Determine if plan has been started by checking if
$\sigma[\mathcal{L}] = \sigma[\mathcal{L}']$ . If these equal, plan has not been started.- If unstarted, then inventory has not yet been set aside. Let
$j$ denote the index of the commodity being produced and$q$ the order size i.e.$j = \sigma[o[p]]$ ,$q = \sigma[o[q]]$ . For simplicity we will let$\sigma[p] := \sigma[o[p]]$ . For each commodity type$i$ such that$a_{ip} > 0$ , remove$a_{ip}q$ from the firm's inventory$I_f$ , i.e.$$I_f[\sigma[p]] \mapsto I_f[\sigma[p]] - a_{ij}q$$
- If unstarted, then inventory has not yet been set aside. Let
- If plan is not yet finished and the current hour is within the working day's window and it is not the weekend (i.e.
$\sigma[\mathcal{L'}] > 0 \wedge t\mod 24 < T \wedge (t \mod 24)\mod 7 < D$ , continue the plan:- Compute the number of labor hours which will be completed during the next hour:
$$l_{\sigma} = \min(|\sigma[W]|,\sigma[L'])$$ - If plan is in a training phase, i.e.
$\sigma[\nu'] > 0$ :- Decrement the training time:
$\sigma[\nu'] \mapsto \sigma[\nu'] - 1$ . If this leads to$\sigma[\nu'] = 0$ :- Update the abilities of the workers. For all ability types, and for all workers, update the worker's aptitude for that ability to the maximum aptitude over all workers. I.e. for each ability type
$1 \leq i \leq N_a$ and for each worker$w \in \sigma[W]$ , set$\alpha_w[\eta_i] = \max{\eta_i^{w'}: w' \in \sigma[W]}$ where$\eta_i^{w'}$ denotes the aptitude of worker$w'$ for ability$i$ .
- Update the abilities of the workers. For all ability types, and for all workers, update the worker's aptitude for that ability to the maximum aptitude over all workers. I.e. for each ability type
- Set the hourly raw materials used
$r = 0$ .
- Decrement the training time:
- Otherwise, plan is in the production phase:
- Hourly raw materials used is computed as
$$r= \frac{\sigma[R]l_\sigma}{\sigma[L]-|\sigma[W]|\sigma[\nu]} $$ - To understand why this is correct, note first that the denominator here is the total living labor time needed in production specifically. For simplicity, assume that this is all labor time required, i.e.
$\sigma[\nu] = 0$ . In this case, substituting$\sigma[R]$ and$\sigma[L]$ for what they were computed as gives $$r= \beta l_{\sigma} \frac{(\vec{a}_j\cdot \vec{\lambda}_t)}{l_j}$$The right-most term here represents the hours of raw materials needed per production of a unit of commodity$p$ divided by the hours of living labor needed per production of a unit of commodity$p$ . These two denominators cancel out so that what it really represents is the number of hours worth of raw materials needed per hour of living labor employed in the production of a unit of commodity$p$ . By taking this, multiplying it by the number of hours of labor done, and then modifying it according to the overall proficiency of the workforce, we obtain the hours of raw materials consumed. Note this assumes that lower-proficiency workers are consumind less raw materials per hour.
- Hourly raw materials used is computed as
- Pay the workers, i.e. assign each worker an hour of labor credits: For each worker
$w \in \sigma[W]$ ,$m_w \mapsto m_w + 1$ . (The code registers labor_hours_done / plan.workers.size() but this is silly) - Update the plan:
$\sigma[L'] \mapsto \sigma[L'] - l_\sigma$ $\sigma[R'] \mapsto \sigma[R'] - r_{\sigma}$ $\sigma[\mathcal{L}'] \mapsto \sigma[\mathcal{L}'] - (l_{\sigma} + r_{\sigma})$
- Compute the number of labor hours which will be completed during the next hour:
- If
$\sigma[\mathcal{L}'] = 0$ , then plan is completed. To end a plan:- Mark order as finished:
$\sigma[o[Done]] \mapsto T$ - The quantity produced
$q$ of$p$ is added to the firm's inventory:$I_f[p] \mapsto I_f[p] + q$ . This is currently pointless but might be useful later. - The quantity produced is removed from the firm's inventory.
$I_f[p] \mapsto I_f[p] + q$ - The customer
$b = o[Firm]$ has the quantity produced added to their inventory:$I_b[p] \mapsto I_b[p] + q$ -
$\sigma[PRD] \mapsto \sigma[PRD] + q\lambda_p$ .
- Mark order as finished:
- Determine if plan has been started by checking if
- For each plan in progress, distributor removes the number of workers assigned to the plan from the plan's labor_hours_remaining, and pays its workers (1 hour per worker assigned)
Drafting of plans occurs at the request of a firm. We therefore assume a scenario in which an order
- Check if producer has enough supplies to meet the order: If
$\vec{I}_f - q_p\vec{a}_p$ is negative in any entry, return a rejection flag - firm will not be able to complete the order. Otherwise... - Add a demand signal
$(p, q_p, t)$ to the firm's demand signal queue$Q_f$ . Increment the inventory demands:$D_f[p] \mapsto D_f[p] + \frac{q_p}{\Delta_o}$ . - Initialize a draft plan
$\sigma$ . A plan consists of the order, a firm assigned to it, a collection of workers who will be assigned to it, and a required training time. Initially, the latter fields are empty or zero. - Assigning workers:
- For each free worker
$w \in W_s$ , overall suitability is defined$$\beta_w = H_w\frac{1}{N_a}\sum_{i \in Y_p}\eta_i$$ - Workers are sorted according to suitability. Number of workers needed is calculated as
$$n_o = \frac{24q_pl_p}{T\Delta_o}$$ . Is this correct? Is living labor per unit measured in days or hours? - Loop over the sorted list of free workers. For each worker in this sorted list
$w$ :- Remove
$w$ from$W_s$ :$W_s \mapsto W_s - [w]$ . - Append the worker to
$\sigma$ 's worker list:$\sigma[W] \mapsto \sigma[W] + [w]$ . - Currently, there is a suitability_threshold variable which the suitability of each worker is compared against. If the worker suitability is below this, then the loop breaks. However, this is effectively an unused feature at present because the function is only ever called with a suitability threshold of 0.
- Remove
- If, after this,
$|\sigma[W]| < n_o$ , then begin looping over the unemployed population. For each unemployed person$w \in U$ , do the same as above, stopping if and when we achieve$|\sigma[W]| < n_o$ . - There does not seem to be any kind of check to see if the number of workers has actually reached
$n_o$ . - Next, the total suitability
$\beta = \sum_{w \in P_o[W]} \beta_w$ of all assigned workers is computed. Then the total predicted labor hours required to complete the order is computed as $$\sigma[L] := \frac{q_pl_p}{\beta}$$Note that if all workers are perfectly suited, then$\epsilon$ equals the number of workers required for the job. Otherwise, the predicted hours are inflated. - Labor hours remaining is also initialized
$\sigma[L'] := \sigma[L]$ . - Finally, the total predicted turnover time in days is
$$\sigma[\Delta] := L_P\frac{24}{T}$$ Again, I don't think we should be multiplying by 24 at all here.
- For each free worker
- Let
$\vec{\lambda}_t$ denote the current price (value) estimates at time$t$ . Raw materials costs are calculated via the formula$$\sigma[R] := (\vec{a}_p \cdot \vec{\lambda}_t)q_p$$ - The plan's remaining dependent fields are entered:
- Total hours:
$\sigma[\mathcal{L}] := \sigma[L] + \sigma[R]$ - Total hours remaining:
$\sigma[\mathcal{L}'] = \sigma[\mathcal{L}]$ - PRD/Social Debt:
$\sigma[PRD] := -\sigma[\mathcal{L}]$
- Total hours:
- If
$\sigma[\Delta] \leq \Delta_o$ , the drafting is complete, and$\sigma$ is returned to the firm who requested it. Else, training is required, and we initiate a secondary drafting phase which allows for this. - A copy of the existing plan is created, call it
$\sigma'$ .$\sigma'$ has both its training time and training time remaining set to$\mathcal{T}$ (a constant parameter):$\sigma'[\nu] := \mathcal{T}$ ,$\sigma'[\nu'] := \mathcal{T}$ - No reassignment of workers is done. We assume that after this much training time, all workers will be as suited to the job as the most suitable worker currently assigned to the plan. Thus we define
$$\hat{\eta_i} = \max{\eta_i^w: w \in \sigma'[W]}$$ where$\eta_i^w$ is the proficiency toward ability$i$ of worker$w$ , and let$$\hat{\alpha} = {\hat{\eta_i}}_{i=1}^{N_a}$$ - Total suitability is then recalculated as
$$\beta' = \frac{1}{N_a}\sum_{w \in P'o[W]}H_w\sum{i \in Y_p}\hat{\eta_i} \geq \beta$$ - Predicted total labor hours are adjusted then via
$$\sigma'[L] = \sigma'[L'] = \mathcal{T}|\sigma'[W]|+ \frac{q_pl_p}{\beta}$$ (Note that this equation assumes that during a given hour, all workers will be occupied at once either with training or producing, but never both at once. It also assumes that$\mathcal{T}$ is measured in hours, which is problematic given the turnaround time calc below.) - Predicted turnaround time is then recomputed
$$\sigma'[\Delta] := \mathcal{T} + \frac{q_pl_p}{\beta'}\frac{24}{T}$$ Note that this is not necessarily smaller than$\sigma[\Delta]$ . In particular if the workers were already all equally suitable, then no increase will be earned in$\beta$ , and we will have added the training time on to the plan to no benefit. This might produce issues. There should be away to boost maximal suitability through training, or a total rethinking of this system. - Finally, the raw materials requirements
$\sigma'[R]$ , total hours remaining$\sigma'[\mathcal{L}]$ , and PRD$\sigma'[PRD]$ are all recomputed in the same manner as before. - We now have two partially drafted plans
$\sigma$ and$\sigma'$ , one of which goes through a training period and one of which does not. We discard the one with the shorter predicted turnaround time and continue to draft up machinery requirements. - Machinery cost per hours is calculated as
$$m_o = \sum_{m \in M_s} \frac{\lambda_p}{\mathcal{D}_p}$$ - Total machinery cost is then a function of the number of workers: $$\sigma[M] := \begin{cases} \frac{m_o\sigma[L]}{|\sigma[W]|} & \textrm{if } \sigma[W] \neq \varnothing \ 0 & \textrm{else} \end{cases}$$
- This completes the trafting of the plan. Once completed, the plan is added to an internal database
$R_s$ :$R_s[o] = \sigma$ . The turnover time$\sigma[\Delta]$ is sent to the firm who requested the plan.
- Let
$l_n$ be the system-defined living labor required to producec a unit of a fixed commodity type$i$ each period. Let$v_n$ be the living labor required per unit as reported by the previous order's output, and$\tau$ be a constant. - Current living labor updating system does not converge:
$l_n = \frac{v_n + (\tau-1)l_{n-1}}{\tau}$ is the current system. Can be shown that if$v_0 \neq l_0$ , and$v_n = v_0$ for all$n$ , then$l_n \to \infty$ . - A fix: let
$o$ denote the number of units of product produced by the previous order, and$q$ be the number of units produced on average over a period of$T$ many steps (where$T$ is some fixed constant long enough to reasonably include multiple completed orders). Then let$\alpha = \frac{o}{q} \leq 1$ .$l_n = l_{n-1} + \alpha(l-l_{n-1})$ . Can be shown that under the same conditions as above,$l_n \to v_0$ . - However, this does not resolve the issues with how values are updated. Let
$\lambda_n$ be the labor value of the same commodity type at time$n$ . The current rule is that$\lambda_n = \lambda_{n-1} + \Delta l_n$ , where$\Delta l_n = l_n - l_{n-1}$ . This will obviously update$\lambda_n$ properly. However:- If commodity
$i$ is an input to any other production method, those values must also be updated. - If any other savings are witnessed in the production process besides labor, the changed values will not reflect this.
- If commodity