Skip to main content
Convergence in Emergent means the agents have reached a sufficiently similar state — measured as the standard deviation of a node attribute falling at or below a threshold. You can let run_to_convergence() manage the loop automatically, or call is_converged() yourself to decide what to do at each step.

How convergence is measured

At the end of every timestep (when running automatically), Emergent collects the value of convergence_data_key from every node and computes the standard deviation across the population:
import numpy as np

nodes = np.array([node_data[data_key] for _, node_data in graph.nodes(data=True)])
is_converged = nodes.std() <= std_dev_threshold
When that standard deviation is at or below convergence_std_dev, the simulation is considered converged.
Convergence only makes sense for numeric node attributes. The standard deviation calculation will raise an error if the attribute values cannot be collected into a numpy array.

Parameters

convergence_data_key
string
default:"None"
The node attribute to monitor. Must be set before calling run_to_convergence(). The attribute must exist on every node after initialize_graph() completes.
convergence_std_dev
number
default:"100"
Standard deviation threshold. The model is converged when std(values) <= convergence_std_dev. A higher value makes convergence easier to reach; a lower value demands tighter agreement.

run_to_convergence

run_to_convergence() calls your timestep_function repeatedly until the population converges or the timestep limit is reached. It returns the number of timesteps executed.
model.update_parameters({
    "num_nodes": 30,
    "graph_type": "cycle",
    "convergence_data_key": "opinion",
    "convergence_std_dev": 0.05,
})
model.initialize_graph()

steps = model.run_to_convergence()
print(f"Converged in {steps} timesteps")
If the simulation reaches MAX_TIMESTEPS without converging, run_to_convergence() stops and returns MAX_TIMESTEPS. It does not raise an exception.
Always set convergence_data_key before calling run_to_convergence(). If it is None, the method raises an exception immediately.

is_converged

is_converged(data_key, std_dev) checks convergence at any point without advancing the simulation. Use it for manual control loops or conditional logic:
model.initialize_graph()

for step in range(10000):
    model.timestep()

    if step % 100 == 0:
        if model.is_converged("opinion", std_dev=0.05):
            print(f"Converged at step {step}")
            break
        else:
            current_std = ... # inspect graph manually if needed
            print(f"Step {step}: not yet converged")
is_converged takes the data key and threshold as explicit arguments rather than reading from model parameters, so you can check multiple attributes with different thresholds in the same loop.

Adjusting the timestep limit

The default maximum is 100,000 timesteps. Change it with change_max_timesteps():
# Fail fast during development
model.change_max_timesteps(500)

# Allow longer runs for slow-converging topologies
model.change_max_timesteps(1_000_000)
This only affects run_to_convergence(). Manual loops using timestep() and is_converged() are not subject to this limit.

Tuning convergence settings

If run_to_convergence() always returns MAX_TIMESTEPS, your threshold may be too tight for the dynamics of your model.
  • Raise convergence_std_dev: A larger threshold is easier to meet. Try doubling it and see whether the simulation converges earlier.
  • Check your timestep function: Make sure agents are actually moving toward agreement. Print the standard deviation every 1,000 steps to see if it is decreasing.
  • Check your graph topology: A cycle graph with many nodes can be very slow to converge. Try switching to "complete" as a sanity check.
# Quick diagnostic — print std dev every 1000 steps
import numpy as np

model.initialize_graph()
for step in range(50_000):
    model.timestep()
    if step % 1000 == 0:
        graph = model.get_graph()
        vals = [d["opinion"] for _, d in graph.nodes(data=True)]
        print(f"Step {step}: std={np.std(vals):.4f}")
If convergence happens in very few timesteps, convergence_std_dev may be too high — the initial node values might already satisfy the threshold.
  • Lower convergence_std_dev: Require tighter agreement between agents.
  • Check your initial data function: If all nodes start with the same value, the simulation is converged before it begins.
# Verify the initial state is not already converged
model.initialize_graph()
print(model.is_converged("opinion", std_dev=0.05))
# Should print False for a meaningful simulation
The right threshold depends on the range of values your node attribute can take:
  • Opinions in [0, 1]: A threshold of 0.01 to 0.1 gives precise convergence. 0.5 would mean agents with opinions of 0 and 1 are considered converged.
  • Integer counts: Use a threshold proportional to the expected final spread — e.g., 1.0 for counts in the hundreds.
  • Unbounded values: Consider normalizing values before convergence checks, or use a threshold relative to the initial standard deviation.
# Example: require opinions within 0.05 of each other
model.update_parameters({
    "convergence_data_key": "opinion",
    "convergence_std_dev": 0.05,
})
Use is_converged() with progressively tighter thresholds to understand how your simulation’s agreement evolves. Compare the timestep counts run_to_convergence() returns across different graph topologies to quantify how much topology affects the speed of consensus.
See Parameters for how to read and update convergence_data_key and convergence_std_dev, and Agent model for the full simulation lifecycle.

Build docs developers (and LLMs) love