Skip to content

type-based interface #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Jun 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4a0632b
new interface: spring layout
hexaeder May 17, 2021
76bc45d
iterator should not mutate the data
hexaeder May 17, 2021
51c7088
SPDF: new interface
hexaeder May 17, 2021
a2dd839
buchheim: new interface
hexaeder May 17, 2021
0ac914c
simplify interface
hexaeder May 19, 2021
b835b6d
Stress: new interface
hexaeder May 19, 2021
661393c
Spectral: new interface
hexaeder May 19, 2021
8d413b3
Cicular: new interface
hexaeder May 19, 2021
301e7e9
Shell: new layout + slightly more general
hexaeder May 19, 2021
dce2985
set julia compat
hexaeder May 19, 2021
c3e225a
start working on docs
hexaeder May 19, 2021
3a31ef2
use not-yet-published GraphMakie with NL0.4 compat
hexaeder May 20, 2021
deace72
coherent doc strings for new interface
hexaeder May 25, 2021
97d95d9
add docs for interface
hexaeder May 25, 2021
8dcf7ec
animations for iterative layouts
hexaeder May 25, 2021
0a9268f
update readme, rm travis ci
hexaeder May 25, 2021
6e190be
export layouts
hexaeder May 25, 2021
b645d82
update docstrings
hexaeder May 26, 2021
0fa0209
add SquareGrid layout
hexaeder May 26, 2021
43e1df9
use Requires.jl to define layout for AbstractGraph
hexaeder May 29, 2021
1dacc67
seed random positions
hexaeder Jun 1, 2021
609c6e6
Apply suggestions from code review
hexaeder Jun 7, 2021
84a99ec
assert squareness of adj_matrix
hexaeder Jun 10, 2021
9160c7e
update square grid example
hexaeder Jun 10, 2021
74279fd
bump used GraphMakie, add Spectral plots to docs
hexaeder Jun 10, 2021
45329b9
remove Circular layout, Shell() does the same
hexaeder Jun 11, 2021
9c61bc4
improve docs
hexaeder Jun 11, 2021
2c784af
remove `layout` from exports, add lowercase calls
hexaeder Jun 11, 2021
57586a1
add LG glue code for LayoutIterator
hexaeder Jun 11, 2021
26ce7d3
fix Spectral and add 2d option
hexaeder Jun 11, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .JuliaFormatter.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
always_for_in = true
always_use_return = true
always_use_return = false
import_to_using = true
margin = 110
pipe_to_function_call = true
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ jobs:
- run: |
julia --project=docs -e '
using Pkg
# XXX: temp solution to resolve deps
Pkg.add(url="https://github.com/JuliaPlots/GraphMakie.jl", rev="f9b8c18")
Pkg.develop(PackageSpec(path=pwd()))
Pkg.instantiate()'
- run: |
Expand Down
15 changes: 0 additions & 15 deletions .travis.yml

This file was deleted.

6 changes: 4 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
name = "NetworkLayout"
uuid = "46757867-2c16-5918-afeb-47bfcb05e46a"
version = "0.3.0"
version = "0.4.0"

[deps]
GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[compat]
julia = "1"
GeometryBasics = "0.3"
julia = "1"

[extras]
DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab"
Expand Down
325 changes: 15 additions & 310 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,324 +1,29 @@
# NetworkLayout.jl
Layout algorithms for graphs and trees in pure Julia.

<!-- [![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://juliagraphs.org/NetworkLayout.jl/stable) -->
<!-- [![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://juliagraphs.org/NetworkLayout.jl/dev/) -->
[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://juliagraphs.org/NetworkLayout.jl/stable)
[![Build Status](https://github.com/JuliaGraphs/NetworkLayout.jl/workflows/CI/badge.svg)](https://github.com/JuliaGraphs/NetworkLayout.jl/actions)
[![Coverage](https://codecov.io/gh/JuliaGraphs/NetworkLayout.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaGraphs/NetworkLayout.jl)

## Algorithms

### Scalable Force Directed Placement

Spring-Electric Force Directed Placement algorithm as explained in [Efficient and High Quality Force-Directed Graph Drawing](http://yifanhu.net/PUB/graph_draw_small.pdf) by Yifan Hu.

Module Name : `SFDP`

#### Usage

```julia
layout(adjacency_matrix,dimension;startpostitions,tol,C,K,iterations)
```
##### arguments
* `adjacency_matrix` - sparse/full adjacency matrix that represents the graph
* `dimension` - dimension in which the layouting code has to be generated. `dimension` can be an integer specifying
the dimension or a `Point` type, eg. `Point3f0` which denotes 3D.
* `startpositions` - co-ordinates of the layout to start with. By default, a random layout is used (kwarg)
* `tol` - permitted distance between current and calculated co-ordinate. Lower the tolerance, more the number of iterations (kwarg)
* `C, K` - used to scale the layout (kwarg)
* `iterations` - Number of iterations we apply the forces (kwarg)

##### returns
`positions` - co-ordinates of nodes in the layout

##### iterator

A user can move between iterations using a `Layout` object.


#### Example

```julia
using LightGraphs
using NetworkLayout:SFDP
g = WheelGraph(10)
a = adjacency_matrix(g) # generates a sparse adjacency matrix
network = layout(a,Point2f0,tol=0.1,C=1,K=1,iterations=10) # generate 2D layout
```
Using Iterator :

```julia
g = WheelGraph(10)
a = adjacency_matrix(g)
tol = 0.1
C = 0.2
K = 1
iterations = 100
network = Layout(a,locs,tol,C,K,iterations)
state = start(network)
while !done(network,state)
network, state = next(network,state)
end
return network.positions
```
![sfdp](https://cloud.githubusercontent.com/assets/8404278/17638280/a9671850-6106-11e6-912f-be94477f5ecd.png)

The image shows a `LightGraphs.WheelGraph(10)` object layout generated by SFDP Algorithm.

### Buchheim Tree Drawing

Buchheim Tree Drawing as explained in [Improving Walker's Algorithm to Run in Linear Time](http://dirk.jivas.de/papers/buchheim02improving.pdf) by Christoph Buchheim, Michael Junger and Sebastian Leipert.

Module Name : `Buchheim`

#### Usage

```julia
layout(adjacency_list; nodesize)
```

##### arguments
* `adjacency_list` - adjacency list that represents the tree
* `nodesize` - sizes of nodes (used to position the nodes) (kwarg)

##### returns
* `positions` - co-ordinates of the layout

#### Example

```julia
using NetworkLayout:Buchheim
adj_list = Vector{Int}[ # adjacency list
[2,3,4],
[5,6],
[7],
[],
[],
[],
[]
]
nodesize = [1,2.3,1.2,2,3,1.4,0.8]
locs = layout(adj_list,nodesize=nodesize) # generating the layout for the tree
```
![tree](https://cloud.githubusercontent.com/assets/8404278/17638844/afd280a4-610a-11e6-8fea-5c99808bd740.png)

The image shows a `LightGraphs.BinaryTree(4)` object layout by Buchheim Algorithm.

### Spring/Repulsion Model

Spring/Repulsion model of Fruchterman and Reingold (1991). Original code taken from [GraphLayout.jl](https://github.com/IainNZ/GraphLayout.jl)

Module Name : `Spring`

#### Usage

```julia
layout(adjacency_matrix,dimension;startpositions,C,iterations,initialtemp)
```
##### arguments
* `adjacency_matrix` - sparse/full adjacency matrix that represents the graph
* `dimension` - dimension in which the layouting code has to be generated. `dimension` can be an integer specifying
the dimension or a `Point` type, eg. `Point3f0` which denotes 3D.
* `startpositions` - co-ordinates of the layout to start with. By default, a random layout is used (kwarg)
* `iterations` - Number of iterations we apply the forces (kwarg)
* `C` - Constant to fiddle with density of resulting layout (kwarg)
* `initialtemp` - Initial "temperature", controls movement per iteration (kwarg)

##### returns
`positions` - co-ordinates of nodes in the layout

##### iterator

A user can move between iterations using a `Layout` object.


#### Example

```julia
using LightGraphs
using NetworkLayout:Spring
g = WheelGraph(30)
a = adjacency_matrix(g) # generates a sparse adjacency matrix
network = layout(a,Point2f0,C=2.0,iterations=100,K=2.0) # generate 2D layout
```
Using Iterator :

```julia
g = WheelGraph(30)
a = adjacency_matrix(g)
iterations = 200
C = 2.0
initialtemp = 2.0
network = Layout(a,locs,C,iterations,initialtemp)
state = start(network)
while !done(network,state)
network, state = next(network,state)
end
return network.positions
```
![spring](https://cloud.githubusercontent.com/assets/8404278/17638354/1c20cc56-6107-11e6-82ed-8873431d8d33.png)

The image shows a `LightGraphs.WheelGraph(10)` object layout generated by Spring Algorithm.

### Stress Majorization

Based on the algorithm explained in "Graph Drawing by Stress Majorization" by Emden R Gansner, Yehuda Koren and Stephen North. Original code taken from [GraphLayout.jl](https://github.com/IainNZ/GraphLayout.jl)

Module Name : `Stress`

#### Usage

```julia
layout(δ,dimension;startpositions,weights,iterations,abstols,reltols,abstolx)
```
##### arguments
* `δ` - Matrix of pairwise distances (Adjacency Matrix can be used)
* `dimension` - dimension in which the layouting code has to be generated. `dimension` can be an integer specifying
the dimension or a `Point` type, eg. `Point3f0` which denotes 3D.
* `weights` - Matrix of weights (kwarg)
* `startpositions` - co-ordinates of the layout to start with. By default, a random layout is used (kwarg)
* `iterations` - Number of iterations we apply the forces (kwarg)
* `abstols` - Absolute tolerance for convergence of stress (kwarg)
* `reltols` - Relative tolerance for convergence of stress (kwarg)
* `abstolx` - Absolute tolerance for convergence of layout (kwarg)

##### returns
`positions` - co-ordinates of nodes in the layout

##### iterator

A user can move between iterations using a `Layout` object.


#### Example

```julia
using LightGraphs
using NetworkLayout:Stress
g = CompleteGraph(10)
a = adjacency_matrix(g) # generates a sparse adjacency matrix
network = layout(a,2) # generate 2D layout
```
Using Iterator :

```julia
g = CompleteGraph(10)
δ = adjacency_matrix(g)
startpositions=rand(Point{3, Float64}, size(δ,1))
iter = Layout(δ, Point{3,Float64}; startpositions=startpositions)
state = start(iter)
while !done(iter, state)
iter, state = next(iter, state)
end
iter.positions
```

![stress](https://cloud.githubusercontent.com/assets/8404278/17638554/5e65e26c-6108-11e6-9522-30e6fa044d26.png)

The image shows a `LightGraphs.CompleteGraph(10)` object layout using Stress Algorithm.

### Spectral Layout Algorithm

Uses the technique of Spectral Graph Drawing, which is an under-appreciated method of graph layouts; easier, simpler, and faster than the more common spring-based methods. Original code taken from [PlotRecipes.jl](https://github.com/JuliaPlots/PlotRecipes.jl)

Module Name : `Spectral`

#### Usage

```julia
layout(adjacency_matrix; node_weights, kw...)
```
##### arguments
* `adjacency_matrix` - Adjacency Matrix in dense/sparse format
* `node_weights` - weights for different nodes (kwarg)

##### returns
`positions` - co-ordinates of nodes in the layout

#### Example

```julia
using LightGraphs
using NetworkLayout:Spectral
g = CompleteGraph(10)
a = adjacency_matrix(g) # generates a sparse adjacency matrix
network = layout(a) # generate 3D layout
```
![spectral](https://cloud.githubusercontent.com/assets/8404278/17638718/a0b451ca-6109-11e6-9a66-fd22332b8541.png)

The image shows a `LightGraphs.CompleteGraph(10)` object layout by Spectral Algorithm.

### Circular Layout Algorithm

Position nodes on a circle. Original code taken from [GraphPlot.jl](https://github.com/afternone/GraphPlot.jl)

Module Name : `Circular`

#### Usage

```julia
layout(adjacency_matrix)
## Installation
``` julia
pkg> add NetworkLayout.jl
```
##### arguments
* `adjacency_matrix` - Adjacency Matrix in dense/sparse format
## Algorithms
The available algorithms and their parameters can be found in the
[docs](https://juliagraphs.org/NetworkLayout.jl/stable).

##### returns
`positions` - co-ordinates of nodes in the layout

#### Example
All of the algorithms represent mappings `adjacency matrix ↦ vector of
positions` where the positions are represented by the `Point` datatype from
[`GeometryBasics.jl](https://github.com/JuliaGeometry/GeometryBasics.jl)

```julia
``` julia
using NetworkLayout
using LightGraphs
using NetworkLayout:Circular
g = CompleteGraph(30)
a = adjacency_matrix(g) # generates a sparse adjacency matrix
network = layout(a) # generate 2D layout
```

![circular](https://cloud.githubusercontent.com/assets/8404278/17638609/d8eb4428-6108-11e6-934b-f326f07cf044.png)

The image shows a `LightGraphs.CompleteGraph(10)` object layout using Circular Algorithm.

### Shell Layout Algorithm

Position nodes in concentric circles. Original code taken from [GraphPlot.jl](https://github.com/afternone/GraphPlot.jl)

Module Name : `Shell`

#### Usage
adj_matrix = adjacency_matrix(wheel_graph(10))

```julia
layout(adjacency_matrix;nlist)
algorithm = NetworkLayout.Spring(; iterations=20)
pos = algorithm(adj_matrix)
```
##### arguments
* `adjacency_matrix` - Adjacency Matrix in dense/sparse format
* `nlist` - Shell-wise separation of nodes (kwarg)

##### returns
`positions` - co-ordinates of nodes in the layout

#### Example

```julia
using LightGraphs
using NetworkLayout:Shell
g = CompleteGraph(30)
n = Array(Vector{Int},2)
n[1] = [1:15]
n[2] = [16:30]
a = adjacency_matrix(g) # generates a sparse adjacency matrix
network = layout(a,nlist=n) # generate 2D layout
```
![shell](https://cloud.githubusercontent.com/assets/8404278/17638171/efac921e-6105-11e6-9e48-33471bf3b27e.png)

This figure shows a `LightGraphs.CompleteGraph(30)` object in 2 shells.

## Benchmarks

The iterative algorithms have been benchmarked using 3 different graphs: `LightGraphs.WheelGraph(10)`, `LightGraphs.WheelGraph(100)` and `jagmesh1`. The number of iterations is fixed on 100. The following graph is obtained which shows SFDP to be the fastest in a general scenario, but Stress Algorithm is faster when the number of edges per graph is comparatively less, as in `jagmesh1`.

![bench](https://cloud.githubusercontent.com/assets/8404278/17642254/fd6f1718-615b-11e6-9a30-8c1a362aead7.png)



*NOTE* : All screenshots are generated using [NetworkViz.jl](https://github.com/abhijithanilkumar/NetworkViz.jl), [ThreeJS.jl](https://github.com/rohitvarkey/ThreeJS.jl) and [Escher.jl](https://github.com/shashi/Escher.jlhttps://github.com/rohitvarkey/ThreeJS.jl). The plot used is generated using [Gadfly.jl](https://github.com/dcjones/Gadfly.jl)
4 changes: 4 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[deps]
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
GraphMakie = "1ecd5474-83a3-4783-bb4f-06765db800d2"
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
NetworkLayout = "46757867-2c16-5918-afeb-47bfcb05e46a"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Loading