Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Summary generated by alphavix

Open In Colab

For prices at which commodities are exchanged to approximately correspond to their values, nothing more is necessary than 1) for the exchange of the various commodities to cease being purely accidental or only occasional; 2) so far as direct exchange of commodities is concerned, for these commodities to be produced on both sides in approximately sufficient quantities to meet mutual requirements, something learned from mutual experience in trading and therefore a natural outgrowth of continued trading; and 3) so far as selling is concerned, for no natural or artificial monopoly to enable either of the contracting sides to sell commodities above their value or to compel them to undersell. By accidental monopoly we mean a monopoly which a buyer or seller acquires through an accidental state of supply or demand.

The assumption that the commodities of the various spheres of production are sold at their value merely implies, of course, that their value is the centre of gravity around which their prices fluctuate, and their continual rises and drops tend to equalise.

(Marx. Capital, Vol 3. 1971: 178)

Summary generated by alphavix

This paper investigates Marx’s theory of the law of value using a dynamic computational model. Here’s a summary of its key findings and contributions:

The paper aims to verify Marx’s claim that prices gravitate towards labor values in a simple commodity economy, where capitalist investment and profit are absent. This is a crucial aspect of Marx’s theory, as the law of value forms a fundamental building block. The author employs an agent-based computational model to simulate a simple commodity economy with NN economic actors and LL commodity types. The model incorporates production, consumption, exchange, and a market mechanism with subjective pricing, along with a rule for actors to switch production sectors.

The main conclusions drawn from the computational model are:

  • Labor value as an attractor for market price: The model demonstrates that the labor value of a commodity acts as an attractor for its market price, meaning that market prices tend to converge towards the labor values over time.

  • Market prices as error signals: Market prices serve as error signals that dynamically allocate social labor between different production sectors.

  • Deviations between prices and values indicate misallocations of labor. Efficient allocation of social labor: The tendency of prices to approach labor values is the monetary manifestation of a simple commodity economy efficiently allocating social labor. This suggests that the market mechanism, even with subjective pricing, can lead to an efficient division of labor.

  • Emergence of Marx’s law of value: The model shows that Marx’s law of value can spontaneously emerge from numerous local exchanges, operating through money flows that constrain actors’ subjective price evaluations. This happens “behind the backs” of individual actors, without their explicit awareness or intent.

The paper also introduces the concept of the Monetary Expression of Labour-time (MELT), which quantifies the constant of proportionality between mean prices and labor values. It highlights MELT as a measure of how much labor-time money represents and its role in connecting non-market phenomena (production times) with market phenomena (prices). Overall, the research supports the theoretical integrity of Marx’s law of value in the context of a simple commodity economy, providing a dynamic analysis that complements traditional static models. It suggests that the market, through price signals and money flows, effectively coordinates the allocation of social labor, leading to an equilibrium where prices reflect the underlying labor values.

Statistical Approximation of the Law of Value

This model is presented in chapter 9 of the book Classical Econophysics and also as the paper: The Emergence of the Law of Value in a Dynamic Simple Commodity Economy.
First, we must clearly establish that there is a distinction between value and price:

  • Value: determined by the prevailing technical conditions of production and measured by the socially necessary labor time to produce it.

  • Price: the amount of money that the commodity yields in the market.

In a theoretical simplification of capitalism (a “simple commodity economy”), prices tend to values, and this is what we want to demonstrate.

The Model

The essential characteristics of the model are:

  • It is composed of NN workers (identified by an integer ii between 1 and NN).

  • There are LL commodities (identified by an integer jj between 1 and LL).

  • It has a total and constant amount of money M=iNmiM=\sum_{i}^{N}m_{i}.

  • Each worker produces one commodity at a time.

  • Each commodity is simple: it does not require another commodity to be produced and can be produced by a single worker, and all workers produce the same commodity with the same efficiency.

  • The agents produce to satisfy their needs.

Production Rule P1P_{1}:

  • At the beginning of the simulation, each agent ii has a vector ei=0\boldsymbol{e}_{i}=\boldsymbol{0} that indicates the quantity of commodities agent ii possesses.

  • The commodity jj being produced by agent ii is given by A(i)=jA(i)=j. We can think of a vector A\boldsymbol{A} that tells us what each agent ii is producing at the moment.

  • Each commodity requires ljl_{j} steps to be produced. That is, at each step the agent produces L=1/ljL=1/l_{j} units of the commodity.
    We define a vector l=(1/l1,,1/lL)=(L1,,LL)\boldsymbol{l}=\left(1/l_{1},\dots,1/l_{L}\right)=\left(L_{1},\dots,L_{L}\right).

Thus, agent ii generates one unit of commodity A(i)A(i) every lA(i)l_{A(i)} steps, and as a consequence the element of the vector ei[A(i)]e_{i}[A(i)] is incremented by one unit.

Consumption Rule C1C_{1}

  • All agents have the same consumption desire given by a global vector
    c=(1/c1,,1/cL)=(C1,,CL)\boldsymbol{c}=\left(1/c_{1},\dots,1/c_{L}\right)=\left(C_{1},\dots,C_{L}\right).

  • Each agent ii has a consumption deficit vector initialized as di=0\boldsymbol{d}_{i}=0.

  • Analogous to production, every cjc_{j} steps the element di[j]\boldsymbol{d}_{i}[j] is incremented by 1, that is, at each step the agent’s desire increases by Cj=1/cjC_{j}=1/c_{j} unit.

Thus, at each step, agent ii consumes a quantity of commodities given by the vector oi=min(ei,di)\boldsymbol{o}_{i}=\text{min}\left(\boldsymbol{e}_{i},\boldsymbol{d}_{i}\right). For example, for commodity j=1j=1:

  • If the agent has none of it, ei[1]=0e_{i}[1]=0, then they cannot consume.

  • If they have no desire, di[1]=0d_{i}[1]=0, they also will not consume.

  • If both values are nonzero and the agent has more than they desire, ei[1]>di[1]e_{i}[1]>d_{i}[1], then they consume only what they desire, di[1]d_{i}[1].

  • If the agent has fewer goods than they desire, ei[1]<di[1]e_{i}[1]<d_{i}[1], they consume all they have, ei[1]e_{i}[1].

In all cases, consumption is given by the smaller value:
oi[1]=min(ei[1],di[1])o_{i}[1]=\text{min}\left(e_{i}[1],d_{i}[1]\right). Evidently, this consumed amount must be deducted from both the deficit and the commodities in possession. Thus, the vectors are updated as: ei=eioi\boldsymbol{e}'_{i}=\boldsymbol{e}_{i}-\boldsymbol{o}_{i} and di=dioi\boldsymbol{d}'_{i}=\boldsymbol{d}_{i}-\boldsymbol{o}_{i}.


Reproduction Coefficient η=ljcj=CjLj\eta=\sum\frac{l_{j}}{c_{j}}=\sum\frac{C_{j}}{L_{j}}:

  • η=1\eta=1 means that production equals consumption: the simulation will adopt this condition.

  • η>1\eta>1 means the economy is permanently in deficit.

  • η<1\eta<1 means the economy permanently produces a surplus.

Since under no circumstance do we have cj<0c_{j}<0 or lj<0l_{j}<0, to avoid η>1\eta>1 we must ensure that no term is ljcj>1\frac{l_{j}}{c_{j}}>1. If there is more than one commodity, then we must be even more restrictive and require ljcj<1\frac{l_{j}}{c_{j}}<1. In other words, it must take fewer steps to produce a commodity than to desire it. This is a necessary condition for stability, since each agent produces only one commodity at a time but consumes all. For example, if we have two commodities, two agents, and each produces one commodity with values lj=1l_{j}=1 and cj=2c_{j}=2, then η=0.5+0.5=1\eta=0.5+0.5=1.

Price Rule O1O_{1}

  • The price of commodity jj according to agent ii is a value pjip_{j}^{i}
    that is randomly drawn from the interval [0,mi]\left[0,m_{i}\right].

Market Rule M1M_{1}:

The market for a given commodity is considered “cleared” when there are no more buyers or sellers for that commodity. In other words, if it is not cleared, it means there are still buyers and sellers. We begin with a set CC of commodities that have not yet been cleared in the market:

  1. Randomly select a commodity jj from the set CC.

  2. Randomly select a seller agent ss from the set of potential sellers: that is, agents who have more of commodity jj than they wish to consume, ei[j]>di[j]e_{i}[j]>d_{i}[j].

  3. Randomly select a buyer agent bb from the set of potential buyers: that is, agents who have fewer units of commodity jj than they wish to consume, ei[j]<di[j]e_{i}[j]<d_{i}[j].

  4. If there are no potential buyers or sellers, remove commodity jj from CC. If there are, apply the exchange rule E1\boldsymbol{E}_{1}.

  5. Repeat the previous steps until all commodities are cleared.

Exchange Rule E1E_{1}

The previous rule identifies buyers and sellers to carry out the conditional exchange defined here.

  • Once we have a buyer bb and a seller ss who estimate the prices pjbp_{j}^{b} and pjsp_{j}^{s} for commodity jj by O1O_1 rule, a transaction price is drawn from the discrete interval [pjb,pjs]\left[p_{j}^{b},p_{j}^{s}\right].

  • If the buyer has enough money, the exchange takes place:
    the buyer loses money and gains one unit of the commodity, while the seller gains money and loses one unit of the commodity.

Sector Rule S1S_{1}

After a fixed amount of time, considering we are at period nn, each agent calculates an error vector din\left\Vert \boldsymbol{d}_{i}^{n}\right\Vert and compares it with the same vector calculated in the previous period din1\left\Vert \boldsymbol{d}_{i}^{n-1}\right\Vert. If the error has increased, din>din1\left\Vert \boldsymbol{d}_{i}^{n}\right\Vert > \left\Vert \boldsymbol{d}_{i}^{n-1}\right\Vert, then the agent randomly switches the commodity being produced.

Simulation Rule R1R_{1}

The entire cycle of production, consumption, exchange, and reallocation in production follows this rule. Initially, we construct l\boldsymbol{l} and c\boldsymbol{c} such that η=1\eta=1, and allocate M/NM/N among all agents. Then:

  • Increase the simulation time by one step.

  • Invoke rule P1P_{1} for each agent.

  • Invoke rule C1C_{1} for each agent.

  • Invoke the market rule M1M_{1}.

  • Invoke rule S1S_{1} for each agent.

  • Repeat.

That is:

SCE={R1,P1,C1,O1{M1,E1},S1}SCE=\left\{ R_{1},P_{1},C_{1},O_{1}\left\{ M_{1},E_{1}\right\},S_{1}\right\}

The parameters are:

  • NN — number of agents.

  • LL — number of commodities.

  • MM — amount of money in the simulation.

  • RR — the maximum possible consumption time, used to constrain the construction of the vectors l\boldsymbol{l} and c\boldsymbol{c}.

  • CC — a constant multiple of RR, and a parameter that defines the duration of the period between applications of the sector-switching rule S1S_{1}.

Division of Labor

Definition 1: A division of labor is efficient when, for each commodity, the number of commodities being produced equals the demand. Thus, the total quantity of commodity jj demanded per step is NCjNC_{j}. If we have a fraction αN\alpha N of agents producing commodity jj, then ajNLja_{j}NL_{j} units are produced per unit of time.

To achieve an efficient division of labor:

ajNLj=NCjaj=CjLj=ljcja_{j}NL_{j}=NC_{j}\quad\rightarrow \quad a_{j} =\frac{C_{j}}{L_{j}}=\frac{l_{j}}{c_{j}}

In other words, aja_{j} represents an efficient division of labor.

Objective Prices

We begin with two definitions:

  • The average price of commodity jj is given by pj\left\langle p_{j}\right\rangle, and we have the vector p=(p1,,pL)\boldsymbol{p}=\left(\left\langle p_{1}\right\rangle ,\dots,\left\langle p_{L}\right\rangle \right).

  • The values, i.e., the amount of time embedded in the production of the commodities, are v=(l1,,lL)\boldsymbol{v}=\left(l_{1},\dots,l_{L}\right).

Thus, if the price gravitates around value, if it is proportional to value, we can write:

pλv\boldsymbol{p} \approx \lambda \boldsymbol{v}

where λ\lambda has units of ‘money per unit of labor time’. Since our economy is simple, we can then define the Monetary Expression of Labor Time (MELT) as the ratio between the measure of the total quantity of commodities exchanged over a time interval at current prices and the productive labor expended in producing these commodities.

In other words, MELT is simply:

λ=γMlivi\lambda = \frac{\gamma M}{\sum l_{i}v_{i}}

Analyzing:

  • The denominator is the labor expended on the commodities that are exchanged over a given time interval. Here, viv_{i} is the average exchange rate of commodity jj. Thus, we have a summation where each term corresponds to the labor involved in the exchanges of each type of commodity.

  • The numerator is the amount of money exchanged over a given time interval through commodity exchange. Here, γ\gamma is simply the proportion of money exchanged per unit of time, so γM\gamma M is the velocity of money, i.e., how much money is exchanged within the time interval under consideration.

In summary, we have:

λ=money exchanged per unit of timelabor time exchanged per unit of time=γMlivi\lambda = \frac{\text{money exchanged per unit of time}}{\text{labor time exchanged per unit of time}} = \frac{\gamma M}{\sum l_{i}v_{i}}

Evidently, we only focus on commodities that are exchanged, not on production as a whole; we are analyzing the market. It is worth noting that in this model commodities have a price only at the moment of exchange, while their value is given by the technical production characteristics of the entire society.

Simulation

Neither the book nor the paper provide the exact implementation code of the model, so I am trying to be as faithful as possible. However, after several weeks working on this model, some comments need to be made:

Comment 1: At no point is it specified whether money is integer or not. It is only mentioned that the interval from which prices are drawn is discrete, which is a requirement for any computational model, no matter how small the intervals are.

The exchange rule can make execution excessively slow if continuous (we will use “continuous” to indicate that we are using variables such as float or double) money is used, because the model does not advance until buyers and sellers reach an agreement to clear the market. However, if a buyer has wealth close to ma0m_a \approx 0, the commodity price defined by rule O1O_1 needs to be close to 0 for the agent to be able to buy. The lower the probability of this happening, the higher the wealth of the seller.

In the appendix, the book mentions implementing a limit on the number of times each agent can enter the market, but it does not detail exactly how this is done. I opted to use decimal money; tests with integers produced faster but less precise results. I also limited the agents’ visits to the market, regardless of success or failure, or which commodity was involved. In my tests, increasing the number of market visits up to the tested limit made the model more precise but slower. However, I am not sure whether allowing unlimited attempts necessarily improves the results. It would require testing to verify whether failing to obtain the desired commodity due to insufficient funds is an important part of the system’s dynamics.

Comment 2: In the implementation of rule S1S_1 for sector switching, it is not clear how the vector di(n)\boldsymbol{d}_{i}^{(n)} is constructed. Is it simply the vector di\boldsymbol{d}_{i} at the last step of the period, or is it some distinct vector? I opted for this simpler implementation mentioned.

Also, in this same rule, it is stated that if there was an error increment, the agent should move to a new sector, but it says that the new sector is drawn uniformly among the LL sectors. It is ambiguous whether the agent can end up remaining in the same sector. I implemented it so that the agent is forced to switch sectors. Some tests also suggest that this dynamic of agents switching between sectors plays an important role in distributing money among agents and sectors within the system, even when the system is in statistical equilibrium.

Comment 3: It was not clear to me what happens if, when applying rule E1E_1, the seller’s offer pj(s)p_j^{(s)} is greater than the buyer’s offer pj(b)p_j^{(b)}. I assume that we always take both the buyer’s and seller’s offers and draw the final price between the smaller and the larger of the two, regardless of who is making the offer.

In the application of rule O1O_1, it is allowed for a commodity to have a price p=0p=0. Depending on the total amount of money, it is highly possible that a commodity could be exchanged at no price if we work with integers, which seems undesirable to me.

Comment 4: To conclude, I must admit that I had difficulty obtaining the results presented for L>2L>2. I initially tried to implement the code in Python, but then migrated to C# in search of higher execution speed. My results show a clear tendency for prices to be proportional to value, but not always with as strong a correlation as presented in both the book and the paper.

I believe that, besides some possible implementation differences, there are certain distributions of production rates and deficits for which the model can exhibit a higher correlation rate, and some parameter choices can make the model more accurate while at the same time increasing computational cost. In my tests, as mentioned, increasing the number of market visits per agent had a positive effect on the final result. Increasing the number of agents itself also helped, as well as using continuous money instead of integer. All these decisions improve the result at the expense of higher computational cost.

Increasing the data capture window—for example, averaging over 50,000 steps instead of 5,000—also had a positive effect, again at the cost of increasing the time required for the simulation to run. This is why I ultimately implemented the code in C# (I apologize for the lack of comments. I spent so much time trying to reproduce this model that I got exhausted. In the future, I may revisit it and organize the code properly).

However, I must warn that I still believe there may be some subtle differences compared to the original proposal due to the difficulty of faithfully reproducing the results. The correlation originally reported, even for L=5L=5, was around 9.8 over 10 runs, and in one case even reached 1. Nonetheless, I believe that this version of the model is already sufficient to investigate the issue. Suggestions for improvement are welcome.

One of the main problems I notice is that the commodity with the longest production time seems to struggle to achieve the highest price. If we have L=5L=5, the other four commodities follow a very strong linear trend, while this last commodity has difficulty showing the expected results. Additionally, this commodity also exhibits an extremely low number of transactions, which I believe is a result of the deficit accumulated in previous steps and the fact that the model prioritizes consumption over exchange. Thus, I assume (though further investigation is needed) that agents who start working in this sector largely end up producing more for consumption than for exchange, even more so than in the other sectors.

C# Code


using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp2
{
    internal class Program
    {
        static void Main()
        {
            int numAgents = 500;
            int numGoods = 10;
            double totalMoney = 100.0 * numAgents;
            double[,] endowment = new double[numAgents, numGoods];
            var agentChoice = new int[numAgents];
            var error = new double[numAgents];
            double[,] demand = new double[numAgents, numGoods];
            var money = new double[numAgents];
            double[] productionRate = { 1.0 / 1.0, 1.0 / 1.2, 1.0 / 1.4, 1.0 / 1.6, 1.0 / 1.8, 1.0 / 2.0, 1.0 / 2.2, 1.0 / 2.4, 1.0 / 2.6, 1.0 / 3.8 };
            double[] consumptionRate = { 1.0 / 20, 1.0 / 20, 1.0 / 20, 1.0 / 20, 1.0 / 20, 1.0 / 20, 1.0 / 20, 1.0 / 20, 1.0 / 20, 1.0 / 20 };
            List<double>[] trades = new List<double>[numGoods];
            for (int i = 0; i < trades.Length; i++)
            { trades[i] = new List<double>(); }
            Random rnd = new Random(0);
            int[] workersCount = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

            // Initialize agents
            for (int i = 0; i < numAgents; i++)
            {
                for (int j = 0; j < numGoods; j++)
                {
                    demand[i, j] = 0.0;
                    endowment[i, j] = 0.0;
                }
                money[i] = totalMoney / numAgents;
                agentChoice[i] = rnd.Next(numGoods);
            }

            // Main simulation loop
            for (int step = 0; step < 500000; step++)
            {
                for (int i = 0; i < numAgents; i++)
                {
                    // Production
                    int goodProduced = agentChoice[i];
                    workersCount[goodProduced]++;
                    endowment[i, goodProduced] += productionRate[goodProduced];

                    // Consumption
                    for (int j = 0; j < numGoods; j++)
                    {
                        demand[i, j] += consumptionRate[j];
                        int consumed = Math.Min((int)demand[i, j], (int)endowment[i, j]);
                        demand[i, j] -= consumed;
                        endowment[i, j] -= consumed;
                    }
                }

                // Trade
                List<int> remainingGoods = Enumerable.Range(0, numGoods).ToList();
                var attempts = new int[numAgents];
                for (int i = 0; i < numAgents; i++)
                { attempts[i] = numGoods * 250; }

                while (remainingGoods.Count > 0)
                {
                    int randIndex = rnd.Next(remainingGoods.Count);
                    int good = remainingGoods[randIndex];
                    List<int> buyers = new List<int>();
                    List<int> sellers = new List<int>();

                    for (int i = 0; i < numAgents; i++)
                    {
                        if ((int)endowment[i, good] > (int)demand[i, good] && attempts[i] > 0)
                            sellers.Add(i);
                        if ((int)endowment[i, good] < (int)demand[i, good] && attempts[i] > 0)
                            buyers.Add(i);
                    }

                    if (sellers.Count == 0 || buyers.Count == 0)
                        remainingGoods.Remove(good);
                    else
                    {
                        int buyer = buyers[rnd.Next(buyers.Count)];
                        int seller = sellers[rnd.Next(sellers.Count)];
                        double buyerOffer = rnd.NextDouble() * money[buyer];
                        double sellerOffer = rnd.NextDouble() * money[seller];
                        double minOffer = Math.Min(buyerOffer, sellerOffer);
                        double maxOffer = Math.Max(buyerOffer, sellerOffer);
                        double price = rnd.NextDouble() * (maxOffer - minOffer) + minOffer;
                        attempts[buyer]--;
                        attempts[seller]--;

                        if (money[buyer] >= price)
                        {
                            money[buyer] -= price;
                            money[seller] += price;
                            endowment[buyer, good]++;
                            endowment[seller, good]--;
                            trades[good].Add(price);
                        }
                    }
                }

                // Strategy adaptation
                if (step % 40 == 0)
                {
                    for (int i = 0; i < numAgents; i++)
                    {
                        double errorValue = 0;
                        for (int j = 0; j < numGoods; j++)
                        { errorValue += demand[i, j] * demand[i, j]; }
                        errorValue = Math.Sqrt(errorValue);

                        if (errorValue > error[i])
                        {
                            int newChoice = agentChoice[i];
                            while (newChoice == agentChoice[i])
                                newChoice = rnd.Next(numGoods);
                            agentChoice[i] = newChoice;
                        }
                        error[i] = errorValue;
                    }
                }

                // Output every 50,000 steps
                if (step % 50000 == 0)
                {
                    for (int j = 0; j < numGoods; j++)
                    {
                        double avgPrice = trades[j].Count > 0 ? trades[j].Average() : 0.0;
                        double avgWorkers = workersCount[j] / 50000.0;
                        Console.WriteLine($"{step} | Avg Price {j}: {avgPrice} | Workers {avgWorkers}");
                        trades[j].Clear();
                        workersCount[j] = 0;
                    }
                    Console.WriteLine("");
                }
            }
        }
    }
}

Below is a result for L=5L=5, with N=200N=200 agents, M=100NM=100 \cdot N, and L500L \cdot 500 market visits per step for each agent, data obtained over 50,000 steps (50,000-100,000):

Commodity 0 | Price: 38.33  | Exchanges: 1975   | Workers: 20
Commodity 1 | Price: 116.84 | Exchanges: 154118 | Workers: 30
Commodity 2 | Price: 553.42 | Exchanges: 22738  | Workers: 40
Commodity 3 | Price: 747.60 | Exchanges: 2207   | Workers: 50
Commodity 4 | Price: 541.66 | Exchanges: 148    | Workers: 60

The price and worker values are averages. We can note that the distribution of workers in each sector follows the efficient distribution, and that the price increases with value, except for the highest-value commodity. However, it has an exchange volume 13 times lower than the second-lowest traded commodity. One commodity was exchanged roughly every 500 steps. If we take into account the number of transactions per agent, the figure becomes even lower. The number of exchanges per agent, for each commodity, is: [98.75,5137.26,568.45,44.14,2.46][98.75, 5137.26, 568.45, 44.14, 2.46].

It is worth remembering that there is a passage in the book discussing the requirements for the law of value to operate:

[if] the probability that a seller of j will find a buyer in the marketplace is low; hence exchange becomes occasional, failing a requirement for the law of value to operate.

I believe this is what happens in this model. Despite everything, the relationship between value and price remains clear: If you increase the average production time of a commodity, the average price at which this commodity will be traded tends to increase. The correlation considering all commodities is 0.84, and disregarding the highest-value commodity it is 0.97. The production time for each commodity is [1,1.5,2,2.5,3][1,1.5,2,2.5,3], and the time required to increase a unit of deficit for all commodities is 10.

Due to the statistical nature of the model, when there are more exchanges, the law of value is respected. For almost the same case, but now allowing only L100L \cdot 100 market visits per agent, and taking the averages between steps 950,000 and 1,000,000, we have:

Commodity 0 | Price: 36.69  | Exchanges: 7151   | Workers: 20
Commodity 1 | Price: 101.88 | Exchanges: 139728 | Workers: 30
Commodity 2 | Price: 547.72 | Exchanges: 17750  | Workers: 40
Commodity 3 | Price: 666.68 | Exchanges: 1226   | Workers: 50
Commodity 4 | Price: 891.73 | Exchanges: 142    | Workers: 60

A correlation of 0.97. Another indication that our model is not entirely faithful to the book is that more than half of the commodities have an average price up to 5 times the per capita wealth. In the graph presented in the book (or paper), no wealth exceeds the per capita wealth. However, I believe we are still able to demonstrate the relationship between price and value.

It should also be considered that, although this result is proportional, it is not necessarily directly proportional. That is, we have a kind of base price bb for the commodity, to which more price is added according to the labor time required to produce it: pj=λlj+b\left\langle p_{j}\right\rangle = \lambda l_{j} + b. Alternatively, we can think of it as a kind of added value nj=bλn_{j} = \frac{b}{\lambda} in the commodity, which is not directly defined but emerges from the dynamics of the model. Like an extra time it takes on average to be exchanged after being produced: pj=λ(lj+nj)\left\langle p_{j}\right\rangle = \lambda \left(l_{j} + n_{j}\right).

We can try to describe the relationship between production and price with a line given by:

import matplotlib.pyplot as plt
import numpy as np
preco=[36.69,101.88,547.72,666.68,891.73]
valor=[1,1.5,2,2.5,3]
a, b = np.polyfit(valor, preco, 1)
print(f"The relationship between price and value can be given by the line equation:  = {a:.2f}v + {b:.2f} with a ratio of {b/a:.2f}")
plt.plot([0.75,3.25],[0.75*a+b,3.25*a+b])
plt.plot(valor,preco,'ok')
plt.xlabel('Value')
plt.ylabel('Price')
plt.show()
The relationship between price and value can be given by the line equation:  = 454.98v + -461.01 with a ratio of -1.01
<Figure size 640x480 with 1 Axes>

We could spend time interpreting what this increase (or decrease) in the production times of each commodity means, or the existence of a base price for the commodity, but the most relevant point, for me, is the undeniable linear relationship between price and value. Furthermore, other modifications of this model can be explored with the aim of correcting and/or improving the results. Examples include: testing different rules for constructing the deficit vector to apply to the S1S_1 rule, or even an entirely different rule for S1S_1; alternative ways to limit the number of market visits per step; limiting each agent’s consumption per step; and so on. To conclude, we can analyze a simple special case with two commodities, in which it is easier to achieve equilibrium and we can efficiently allocate workers between the two sectors without major dynamic problems by disabling the switch rule.

import numpy as np
import random
from scipy.stats import pearsonr
import matplotlib.pyplot as plt


## SETTING THE INITIAL CONDITION

# Parameters
N = 100         # Number of workers
L = 2           # Number of commodities
M = 200       # Coins in the simulation
R = 20          # Maximum possible consumption time
C = 2           # Sector exchange factor
p = 2001        # Simulation duration

# % R1
# For calculations
E = np.zeros((N, L))                               # Commodities held by each agent, each row is a vector ei
A = np.random.randint(0, L, N)                     # Vector with the commodity produced by agent i
D = np.zeros((N, L))                               # Matrix with consumption deficit of each agent for each commodity
m = np.full(N, int(M/N))                           # Money of each agent

continue_simulation = True
while continue_simulation:
    l = np.random.random(L)                        # Increment per step in production of each commodity
    c = np.random.random(L)                        # Increase in desire per step for each commodity
    F = np.sum(c/l)                                # Calculating the rescaling factor
    c = c/F                                        # Obtain vector c respecting the sum
    T = int(C*max(1/c))+1                          # Calculating the period for sector exchange
    continue_simulation = False if (max(1/c) <= R) else True # Check if maximum consumption time is acceptable

Consumption = 1/c                                  # Time needed to consume
Production = 1/l                                   # Time needed to produce

# Distributing workers in an ideal way
A = []; a=c/l
for x in range(L):
    A = A + round(a[x] * N) * [x]

# %% Vectors used for different measures
exchanges = [[] for x in range(L)]

# %% SIMULATION
for step in range(p):
    # % P1
    for i in range(N):  # Loop over agents
        j = A[i]        # Commodity the agent is producing
        E[i][j] += l[j] # Increase production

    # % C1
    for i in range(N):      # For each agent i
        for j in range(L):  # And commodity j
            D[i][j] += c[j] # Increase deficit
            # Calculate the minimum value
            consumption = int(D[i][j]) if (int(D[i][j]) < int(E[i][j])) else int(E[i][j])
            D[i][j] -= consumption # Reduce deficit
            E[i][j] -= consumption # Reduce commodities

    # % M1
    unresolved = [mer for mer in range(L)]             # Commodities still unresolved
    limits = np.full(N, 1000, dtype=float)

    while len(unresolved) > 0:                          # Resolve all commodities
        a = random.randint(0, len(unresolved)-1)       # Select a commodity randomly
        j = unresolved[a]                              # Selected commodity
        sellers = []                                   # Build list of potential sellers
        buyers = []                                    # Build list of potential buyers
        for i in range(N):
            if int(E[i][j]) > int(D[i][j]) and limits[i] > 0: # If agent has more commodity than desired
                sellers.append(i)
            if int(D[i][j]) > int(E[i][j]) and limits[i] > 0: # If agent desires more commodity than has
                buyers.append(i)
        if len(buyers) == 0 or len(sellers) == 0:      # If none available
            unresolved.pop(a)                          # Remove commodity from list
        else:                                           # Otherwise, pick buyer and seller and call E1
            a = random.randint(0, len(buyers)-1)
            buyer = buyers[a]
            a = random.randint(0, len(sellers)-1)
            seller = sellers[a]
            buyer_price = random.randint(0, m[buyer])   # Price estimated by buyer
            seller_price = random.randint(0, m[seller]) # Price estimated by seller
            min_price = min(buyer_price, seller_price)
            max_price = max(buyer_price, seller_price)
            price = random.randint(min_price, max_price)
            if m[buyer] >= price:                       # If buyer has enough money
                m[buyer] -= price                       # Buyer pays
                m[seller] += price                      # Seller earns
                E[buyer][j] += 1                        # Buyer gains item
                E[seller][j] -= 1                       # Seller loses item
                exchanges[j].append(price)

            limits[buyer] -= 1
            limits[seller] -= 1

    if ((step+1) % 1000 == 0):
        bkup = exchanges.copy()
averages = []
for x in range(L):
  averages.append(np.array(bkup[x]).mean())  # Calculate average price of each commodity
a=0;
b=0
for x in range(L):
    a += sum(bkup[x])
    b += len(bkup[x])*Production[x]
MELT=a/b

plt.plot([0,1.3],[0,1.3])
plt.plot(averages,MELT*Production,'ok')
plt.xlabel('Average Price')
plt.ylabel('MELT x Value')
print(f"The MELT is = {MELT:.2f} coins per unit of labour-time.")
The MELT is   = 0.20 coins per unit of labour-time.
<Figure size 640x480 with 1 Axes>

This line is not the result of interpolating the points, but rather a line y=xy=x constructed to pass through the origin.Evidently, there is no merit in constructing a line that passes through two points, but nothing requires the line to pass through the origin. This is a result of the model.

Extra:

A possible algorithm for constructing the vectors ll and cc is as follows: First, generate two random vectors l=(L1,,LL)\boldsymbol{l} = \left(L_{1}, \dots, L_{L}\right) and u=(u1,,uL)\boldsymbol{u} = \left(u_{1}, \dots, u_{L}\right). Compute the sum iLujLj=F\sum_{i}^{L} \frac{u_{j}}{L_{j}} = F, then divide both sides by FF:

iL1FujLj=FFiLuj/FLj=1=iLCjLj\sum_{i}^{L}\frac{1}{F}\frac{u_{j}}{L_{j}}=\frac{F}{F}\rightarrow\sum_{i}^{L}\frac{u_{j}/F}{L_{j}}=1=\sum_{i}^{L}\frac{C_{j}}{L_{j}}

That is, the elements of the vector C\boldsymbol{C} are Cj=ujFC_{j} = \frac{u_{j}}{F}, or equivalently, C=uF\boldsymbol{C} = \frac{\boldsymbol{u}}{F}.

References
  1. Wright, I. (2008). The Emergence of the Law of Value in a Dynamic Simple Commodity Economy. Review of Political Economy, 20(3), 367–391. 10.1080/09538250701661889