Library

User Friendly Interfaces

GraphPlayground.playgroundFunction
playground(g; 
  [link_options = (;iterations=1,distance=30),] 
  [center_options = NamedTuple(),]
  [charge_options = NamedTuple(),]
  [graphplot_options = NamedTuple(),]
  [initial_iterations = 10,]
  [labels = map(i->string(i), 1:nv(g)),]
  [verbose = false,]
  [kwargs...] )
playground(g, sim; kwargs...)

Create a graph playground window from the graph g.

The value that is returned is a Playground object, which is a thin wrapper around the window, the simulation, and the Makie axis.

Optional parameters

  • link_options: These options are passed to the LinkForceconstructor. The default is(;iterations=1,distance=30)`. This is generally good. For grid graphs, set iterations higher to get a better layout.
  • center_options: These options are passed to the PositionForce constructor.
  • charge_options: These options are passed to the ManyBodyForce constructor.
  • graphplot_options: These options are passed to the graphplot! function.
  • initial_iterations: The number of layout iteration to run before the first display. The default is 10.
  • labels: A list of strings to display for the node identifiers. By default these are the numeric node ids
  • verbose: If true, additional information is shown in the lower left corner of the plot.

Examples

g = wheel_graph(10)
playground(g)

For a grid, this often looks much better

g = grid([10,10])
playground(g; 
  link_options=(;iterations=10, strength=1, distance=20))

See also

ForceSimulation, LinkForce, ManyBodyForce, PositionForce

source

Force Simulator Interface

GraphPlayground.ForceSimulationType
ForceSimulation([PositionType,] nodes; [...]) 
ForceSimulation(positions, nodes; [rng,] [alpha,] [velocity_decay,] forcelist...)

Create a force simulator for a set of positions. This evaluates and evolves the positions based on the forces applied. It is designed to be used with evaluating a dynamic force directed graph layout including attractive forces for edges and repulsive forces for nodes. But it may have other uses as well. For instance, collision forces can be used to simulate packed bubble charts with various radii.

Arguments

  • nodes is any array of nodes. This can be very simple, i.e. 1:n, or a list of objects. The objects must be used consistent with other forces involved.
  • PositionType is the type of the positions. Using Point2f is recommended and the default
  • positions is an array of initial positions. The position type is determined by the elements of

the array.

  • forcelist is a trailing list of forces. The names of these forces do not matter. The order is the order in which they are applied. While forcelist is not syntactically required, it is semantically required as otherwise the simulation will not do anything.

Optional Arguments

  • rng is a random number generator. This is used for the initial positions and for any random perturbations if there are degeneracies. The default is to use a deterministic generator so that the results are reproducible.
  • alpha is the cooling stepper. This is used to control the rate of convergence. See GraphPlayground.CoolingStepper for more information.
  • velocity_decay is the factor by which the velocities are decayed each step. Setting this to 1 will not decay the velocities. Setting it to 0 will stop all motion. The default is 0.6.

Usage

Here is an example that packs balls of different sizes into a region around the point (0,0).

radii = 1:10
sim = ForceSimulation(1:10; 
  collide=CollisionForce(radius=radii, iterations=3),
  center=PositionForce(target=(0,0)))
initial_positions = copy(sim.positions) 
step!(sim, 100) # run 100 steps 
plot(sim.positions; markersize=(radii .- 0.5).*pi/1.11, 
  markerspace=:data, strokewidth=0.25, strokecolor=:white)  # weird 1.11 to get the right size, add 0.05 

Forces

The list of forces can have silly names if you wish. The names are not used other than for display. For example, this is entirely valid:

sim = ForceSimulation(1:10; 
  collide=CollisionForce(radius=radii, iterations=3),
  push_nodes_to_middle=PositionForce(target=(0,0))
  push_nodes_to_offset=PositionForce(target=(10,10)))

Of course, that generates a very useless simulator.

Forces

  • LinkForce: This force applies a spring force to all edges in the graph. The force is proportional to the distance between the nodes.
  • ManyBodyForce: This force applies a repulsive force between all nodes. The force is proportional to the inverse square of the distance between the nodes.
  • PositionForce: This force applies a force to all nodes to move them to a target position. This is useful for centering the graph or pushing nodes to the edge.
  • CollisionForce: This force applies a repulsive force between all positions. The force is proportional to the sum of the radii of the nodes.
  • CenterForce: This force directly centers all the positions.

Data

The simulator maintains the following data that are useful:

  • positions: The current positions of the nodes.
  • velocities: The current velocities of the nodes.

You can access these directly.

Methods

To fix a node in place, use fixnode!(sim, i, pos). To free a node, use freenode!(sim, i). To take a step, use step!(sim). To take multiple steps, use step!(sim, n).

See also

step!, fixnode!, freenode!, LinkForce, ManyBodyForce, PositionForce, CollisionForce, CenterForce

source
GraphPlayground.fixnode!Function
fixnode!(sim::ForceSimulation, i, pos)

Fix the position of a node in the simulation. This will prevent the node from moving. This importantly keeps the velocity of the node set to 0, which will prevent the node from updating other implicit positions.

source
GraphPlayground.freenode!Function
freenode!(sim::ForceSimulation, i)

Remove the fixed position of a node in the simulation. This will allow the node to move.

source
GraphPlayground.step!Function
step!(sim) # take one step 
step!(sim, n) # take n steps

Take a step of the force simulator. This will apply all forces in the order they were added to the simulator. The forces are applied to the positions and velocities. The velocities are then decayed by the velocity_decay factor.

See ForceSimulation for more information and an example.

source
GraphPlayground.LinkForceType
LinkForce(;edges)
LinkForce(;edges, [strength], [distance], [bias], [iterations], [random])

A link force computes forces between nodes that emulate a strong connection. This is useful for graphs where the edges represent strong connections between nodes.

The force applied between two nodes is based on the strength of the link, the distance, the strength of the edge. The bias of the edge is used to determine how much the nodes should move.

For an edge between src, dst, let $d$ be the difference vector between the position of dst and src with their velocity corrections included. The total force is $f = \alpha \cdot s \cdot (||d|| - l) / ||d||$ where $l$ is the ideal distance and $s$ is the strength of the link. The force is applied to the velocity of the nodes proportional to the bias of the edge $\beta$

  `vel[dst] -=` ``\beta f \cdot d``
  `vel[src] +=` ``(1-\beta) f \cdot d``

The bias is used to determine how much the nodes should move. If the bias is 0, then the update is exclusively provided to the src node. If the bias is 1, then the update is exclusively provided to the dst node.

Arguments

  • edges: An array of edge structures, where each edge structure contains src and dst fields or can be indexed like a tuple with e[1], e[2] as the source and destination nodes.

Optional Arguments

  • strength: A function or array of values that determine the strength of the link between two nodes. By default, this is based on the number of edges between the nodes: 1/(min(degree(src), degree(dst))).
  • distance: A function or array of values that determine the ideal distance between two nodes. By default, this is 30.0. But this can be a function that takes the edge index and returns a distance.
  • bias: A function or array of values that determine the bias of the link between two nodes. This is designed to weight how much the nodes should move. It's designed to make it harder to move high degree nodes.
  • iterations: The number of iterations to run the link force. The default is 1. Each iteration updates the velocity but not the positions. However, the velocity updates are included in the force calculations. So by running multiple iterations, the forces are more accurate. This is because we update the velocities in-place. Using large values here are most important for grids or graphs with a lot of structure.
  • random: A random number generator. This is used for the random perturbations. The default is to use a deterministic generator so that the results are reproducible. I can't imagine why you would need to use this, but it's here in case someone needs to reproduce something strange.

Function inputs

An exmaple of using it with a function is the following

val = randn(10)
f = LinkForce(;edges=edges, strength=(i,e,src,dst)->val[src]*val[dst])

This is called with

  • i: The index of the edge in the edges array.
  • e: The edge structure.
  • src: The source node.
  • dst: The destination node.

This same structure is used for all strength, bias, and distance.

Usage

LinkForce is usually used as part of a ForceSimulation. Here, we setup something simple with two nodes at distance 1. But that want to be at distance 10 given the edge between them.

nodes = [1,2]
edgelist = [(1, 2)]
positions = [Point2f(0.0, 0.0), Point2f(1.0, 0.0)]
sim = ForceSimulation(positions, nodes; 
  link=LinkForce(edges=edgelist, strength=10, distance=10.0, bias=0.25))
iforce = sim.forces.link
# iforce is an `InitializedLinkForce` that's been linked to the simulation
GraphPlayground.force!(0.1, sim, iforce)  # Assume alpha=0.1
sim.velocities

This example shows how the [LinkForce]computes the velocities of the nodes to move them away from each other. The reason the update is nonsymmetric is because of the bias. This says that we want to move node 1 more than node 2.

See also

ForceSimulation

source
GraphPlayground.ManyBodyForceType
ManyBodyForce()
ManyBodyForce(; [strength], [min_distance2], [max_distance2], [theta2], [random])

Create a force defined by multiple bodies. This force is used to simulate the repulsion or attraction between nodes of the simulation. If you wish to apply to only a subset of nodes, you can set the strength to zero for the nodes you wish to ignore.

This computation is implemented with a space partitioning data structure (current a KDTree) to approximate the impact of distance forces using a far-field approximation (this is often called a Barnes-Hut approximation, but that doesn't help understand what is going on). Setting theta2 to zero will cause it to discard the approximation and compute the exact force. Reasonable values for theta2 are between 0.5 (better approximation) and 1.5 (poor approximation). (This is the square of the $\theta$ value commonly used in Barnes-Hut approximations.)

Arguments

  • strength: A constant, a function or array of values. The repulsive strength to use, defaults to -30, which is a repulsive force between nodes.
  • min_distance2: A constant, that defines a minimum distance between nodes. If the distance between two nodes is less than this value, the force is increased a bit. The default is 1.0.
  • max_distance2: A constant, that defines a maximum distance between nodes. If the distance between two nodes is greater than this value, the force is ignored. The default is Inf.
  • theta2: A constant, that defines the accuracy of the approximation. The default is 0.81, which is a reasonable value for most simulations.
  • random: A random number generator. This is used for the random perturbations.
source
GraphPlayground.PositionForceType
PositionForce(;[target] [, strength])

PositionForce represents a force that directions nodes of the simulation towards specific target positions.

Arguments

  • target: The target position of each node. This can be a single value or an array of values. The default is (0,0), which tries to center the positions.
  • strength: The strength of the force, which is a real number. The default is 0.1.

See also

ForceSimulation

source
GraphPlayground.CenterForceType

CenterForce represents a centering adjustment in a force simulation. it has two parameters:

  • center: The center of the force, which can be anything resembling a point
  • strength: The strength of the force, which is a real number

Note that CenterForce directly applies the force to the positions of the nodes in the simulation instead of updating their velocities.

Use PositionForce to apply a force to the velocities of the nodes instead. (Also, please don't combine PositionForce and CenterForce.)

Example

rad = 10*rand(100) sim = ForceSimulation(Point2f, eachindex(rad); center=CenterForce(center, strength=1.0), collide=CollisionForce(radius=rad) ) p = scatter(sim.positions, markersize=rad) for i in 1:100 step!(sim) p[:node_pos][] = sim.positions sleep(0.5) end

See also

ForceSimulation

source
GraphPlayground.CoolingStepperType

A model of the cooling step in d3-force. The stepper allows dynamic retargeting of the cooling factor, which is useful in simulations where you want to adjust behavior for user interaction or for incoming data.

Once the stepper has reached it's minimum value, it will return zero for all subsequent steps.

Usage: ```julia alpha = CoolingStepper() for i=1:10 println(step!(alpha)) end alpha.alphatarget = 0.5 for i=1:10 println(step!(alpha)) end alpha.alphatarget = 0.0 for i=1:10 println(step!(alpha)) end

source

Extra window help

GraphPlayground.WindowFunction
Window(loop::Function, scene; [title="GraphPlayground", size=(800,800), kwargs...])

Create a window based on a scene. The window will run the provided loop function ever frame. The loop function should take a single argument, which is the time since the window was opened. This function is a fairly thin wrapper around GLMakie.Screen and GLMakie.display_scene!, but makes it easier to abstract in the future.

Parameters

  • loop: A function that will be called every frame. The function should take a single argument, which is the time since the window was opened.
  • scene: The scene to display in the window.
  • title: The title of the window. Default is "GraphPlayground".
  • size: The size of the window. Default is (800,800).
  • kwargs: Additional keyword arguments to pass to the GLMakie.Screen constructor.

Example

This example shows a bunch of points that are going to be pushed away from each other in a simulation of a collision.

using GeometryBasics, GraphPlayground, GLMakie
scenesize = 500 
n = 100
scene = Scene(camera=campixel!, size=(scenesize, scenesize))
pts = Observable((scenesize/2*rand(Point2f0, n)) .+ (scenesize/4)*Point2f(1,1))
radius = rand(10:20, n)
sim = ForceSimulation(pts[], eachindex(pts[]);
  collide = CollisionForce(radius=radius .+ 2, iterations=3))
scatter!(scene, pts, markersize=pi*radius/1.11)
GraphPlayground.Window(scene; 
  title="Collision Simulation", size=(scenesize, scenesize),
  focus_on_show = true) do _ 
  step!(sim)
  pts[] = sim.positions
end 
source

Index