Liking cljdoc? Tell your friends :D

Loom logo

Clojars Project cljdoc test

Maintenance fork (2026). The original aysylu/loom is no longer maintained. This fork supports current Clojure / ClojureScript and fixes correctness bugs (see CHANGELOG). It is published to Clojars as net.clojars.savya/loom.

Stack

Clojure ClojureScript deps.edn tools.build

Video and Slides

Watch the Loom talk at Clojure/West 2014. View the slides. Watch the talk at LispNYC. View the slides.

Usage

Install

deps.edn:

net.clojars.savya/loom {:mvn/version "1.3.2"}

Leiningen:

[net.clojars.savya/loom "1.3.2"]

Or use the maintained fork directly as a git dependency (deps.edn):

io.github.jsavyasachi/loom {:git/tag "1.3.0" :git/sha "c666221b9c3ad9e600a48fe4853d9cfbf17c87e5"}

Namespaces

loom.graph   - records & constructors
loom.alg     - algorithms (see also loom.alg-generic)
loom.gen     - graph generators
loom.attr    - graph attributes
loom.label   - graph labels
loom.io      - read, write, and view graphs in external formats
loom.derived - derive graphs from existing graphs using maps and filters

Documentation

API Reference

Frequently Asked Questions

Join the Loom mailing list to ask questions.

Basics

Create a graph:

;; Initialize with any of: edges, adacency lists, nodes, other graphs
(def g (graph [1 2] [2 3] {3 [4] 5 [6 7]} 7 8 9))
(def dg (digraph g))
(def wg (weighted-graph {:a {:b 10 :c 20} :c {:d 30} :e {:b 5 :d 5}}))
(def wdg (weighted-digraph [:a :b 10] [:a :c 20] [:c :d 30] [:d :b 10]))
(def rwg (gen-rand (weighted-graph) 10 20 :max-weight 100))
(def fg (fly-graph :successors range :weight (constantly 77)))

If you have GraphViz installed, and its binaries are in the path, you can view graphs with loom.io/view:

(view wdg) ;opens image in default image viewer

Inspect:

(nodes g)
=> #{1 2 3 4 5 6 7 8 9}

(edges wdg)
=> ([:a :c] [:a :b] [:c :d] [:d :b])

(successors g 3)
=> #{2 4}

(predecessors wdg :b)
=> #{:a :d}

(out-degree g 3)
=> 2

(in-degree wdg :b)
=> 2

(weight wg :a :c)
=> 20

(map (juxt graph? directed? weighted?) [g wdg])
=> ([true false false] [true true true])

Add or remove items. Graphs are immutable, so these functions return new graphs:

(add-nodes g "foobar" {:name "baz"} [1 2 3])

(add-edges g [10 11] ["foobar" {:name "baz"}])

(add-edges wg [:e :f 40] [:f :g 50]) ;weighted edges

(remove-nodes g 1 2 3)

(remove-edges g [1 2] [2 3])

(subgraph g [5 6 7])

Traverse a graph:

(bf-traverse g) ;lazy
=> (9 8 5 6 7 1 2 3 4)

(bf-traverse g 1)
=> (1 2 3 4)

(pre-traverse wdg) ;lazy
=> (:a :b :c :d)

(post-traverse wdg) ;not lazy
=> (:b :d :c :a)

(topsort wdg)
=> (:a :c :d :b)

Pathfinding:

(bf-path g 1 4)
=> (1 2 3 4)

(bf-path-bi g 1 4) ;bidirectional, parallel
=> (1 2 3 4)

(dijkstra-path wg :a :d)
=> (:a :b :e :d)

(dijkstra-path-dist wg :a :d)
=> [(:a :b :e :d) 20]

Other stuff:

(connected-components g)
=> [[1 2 3 4] [5 6 7] [8] [9]]

(bf-span wg :a)
=> {:c [:d], :b [:e], :a [:b :c]}

(pre-span wg :a)
=> {:a [:b], :b [:e], :e [:d], :d [:c]}

(dijkstra-span wg :a)
=> {:a {:b 10, :c 20}, :b {:e 15}, :e {:d 20}}

Attributes on nodes and edges:

(def attr-graph (-> g
                (add-attr 1 :label "node 1")
                (add-attr 4 :label "node 4")
                (add-attr-to-nodes :parity "even" [2 4])
                (add-attr-to-edges :label "edge from node 5" [[5 6] [5 7]])))

; Return attribute value on node 1 with key :label
(attr attr-graph 1 :label)
=> "node 1"

; Return attribute value on node 2 with key :parity
(attr attr-graph 2 :parity)
=> "even"

; Getting an attribute that doesn't exist returns nil
(attr attr-graph 3 :label)
=> nil

; Return all attributes for node 4
; Two attributes found
(attrs attr-graph 4)
=> {:parity "even", :label "node 4"}

; Return attribute value for edge between nodes 5 and 6 with key :label
(attr attr-graph 5 6 :label)
=> "edge from node 5"

; Return all attributes for edge between nodes 5 and 7
(attrs attr-graph 5 7)
=> {:label "edge from node 5"}

; Getting an attribute that doesn't exist returns nil
(attrs attr-graph 3 4)
=> nil

; Remove the attribute of node 4 with key :label
(def attr-graph (remove-attr attr-graph 4 :label))

; Return all attributes for node 4
; One attribute found because the other has been removed
(attrs attr-graph 4)
=> {:parity "even"}

Derived graphs:

; Build a derived graph using a node mapping
(nodes (mapped-by #(+ 10 %) g))
=> #{11 12 13 14 15 16 17 18 19}

; Subgraphs of g
(edges (nodes-filtered-by #{1 2 3 5} dg))
=> ([1 2] [2 1] [2 3] [3 2])

(edges (subgraph-reachable-from dg 1))
=> ([1 2] [2 1] [2 3] [3 2] [3 4] [4 3])

Dependencies

Loom uses Clojure. It can use GraphViz for visualization.

TODO

See Loom TODO board.

Testing

clojure -M:test
clojure -T:build jar
clojure -T:build deploy

Contributors

Names are in no order:

Namespaces

The dependency graph of Loom namespaces. lein-ns-dep-graph generates it.

Loom namespace dependency graph

License

Copyright © 2010-2016 Aysylu Greenberg & Justin Kramer (jkkramer@gmail.com).

Savyasachi maintains this fork (2026). Original: https://github.com/aysylu/loom. The project uses the Eclipse Public License 1.0. It keeps the original license.

Can you improve this documentation? These fine people already did:
Aysylu, Savyasachi, Aysylu Greenberg, Justin Kramer, aysylu, Horst Duchene, Andrea Crotti, Jim Berlage, bpringe, Ertuğrul Çetin, Francois Rey, Paul L. Snyder, Robert Lachlan, Daniel Compton & Kevin Downey
Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close