Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
127 changes: 127 additions & 0 deletions src/ubergraph/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,133 @@
(alter-meta! #'add-edges assoc
:doc "Adds edges to graph g of the form [n1 n2], [n1 n2 weight], or [n1 n2 attr-map].")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Make many doc strings more informative
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(alter-meta!
#'nodes assoc :doc
"Return a sequence of the nodes in graph g.

For the implementation on type Ubergraph, returns in O(1) time, with
effectively O(n) time to traverse all n nodes.")

(alter-meta!
#'edges assoc :doc
"Return a sequence of the edges of graph g. Any undirected edges in
the graph appear twice in this sequence, one as an edge e1
with (mirror-edge? e1) equal to true, the other as an edge e2
with (mirror-edge? e2) equal to false. This is so even for
undirected self loop edges, i.e. with both node endpoints equal to
each other.

For the implementation on type Ubergraph, returns in O(1) time, with
effectively O(m) time to traverse all m edges.")

(alter-meta!
#'has-node? assoc :doc
"Return true if node is in g, otherwise false.

For the implementation on type Ubergraph, returns in effectively
O(1) time, the same as looking up a value as a key in a map.")

(alter-meta!
#'has-edge? assoc :doc
"Return true if there is an edge from node n1 to n2 in g, otherwise
false. There is an edge from node n1 to n2 if there is a directed
edge from n1 to n2, or if there is an undirected edge between those
two nodes.

For the implementation on type Ubergraph, returns in effectively
O(1) time.")

(alter-meta!
#'out-degree assoc :doc
"Return the number of outgoing edges of node. This is the same value
as (count (ubergraph.core/out-edges g node)).

For the implementation on type Ubergraph, returns the answer from a
precalculated stored value, in effectively O(1) time.")

(alter-meta!
#'out-edges assoc :doc
"Return a sequence of all of the outgoing edges of node. If the
graph has multiple parallel edges between nodes, all of them are
included.

If the node has any directed 'self loop' edges to itself, they are
included once. Undirected self loop edges are included twice, one
with mirror-edge? equal to true, the other with mirror-edge? equal
to false.

For the implementation on type Ubergraph, returns in effectively
O(1) time, with effectively O(k) time to traverse the sequence of k
edges.")

(alter-meta!
#'in-degree assoc :doc
"Return the number of incoming edges of node. This is the same value
as (count (ubergraph.core/in-edges g node)).

For the implementation on type Ubergraph, returns the answer from a
precalculated stored value, in effectively O(1) time.")

(alter-meta!
#'in-edges assoc :doc
"Return a sequence of all of the incoming edges of node. If the
graph has multiple parallel edges between nodes, all of them are
included.

If the node has any directed 'self loop' edges to itself, they are
included once. Undirected self loop edges are included twice, one
with mirror-edge? equal to true, the other with mirror-edge? equal
to false.

For the implementation on type Ubergraph, returns in effectively
O(1) time, with effectively O(k) time to traverse the sequence of k
edges.")

(alter-meta!
#'src assoc :doc
"Returns the source node of the edge.

For the implementation on type Ubergraph, returns in O(1) time.")

(alter-meta!
#'dest assoc :doc
"Returns the dest node of the edge.

For the implementation on type Ubergraph, returns in O(1) time.")

(alter-meta!
#'successors assoc :doc
"Return a sequence of nodes in the graph g that are direct
successors of the given node. Node v is a direct successor of node
u if there is a directed edge from u to v, or an undirected edge
between them. A node u is returned as a successor of itself if
there is a 'self loop' edge from u to u. Every successor node is
guaranteed to appear exactly once in the returned sequence, even if
there are multiple parallel edges that make it a successor.

For the implementation on type Ubergraph, returns in effectively
O(1) time to return the sequence, with effectively O(k) time to
traverse all k nodes returned.")

(alter-meta!
#'predecessors assoc :doc
"Return a sequence of nodes in the graph g that are direct
predecessors of the given node. Node v is a direct predecessor of
node u if there is a directed edge from v to u, or an undirected
edge between them. A node u is returned as a predecessor of itself
if there is a 'self loop' edge from u to u. Every predecessor node
is guaranteed to appear exactly once in the returned sequence, even
if there are multiple parallel edges that make it a predecessor.

For the implementation on type Ubergraph, returns in effectively
O(1) time to return the sequence, with effectively O(k) time to
traverse all k nodes returned.")


;; This namespace provides a concrete implementation of ubergraph.protocols, which is
;; a conservative extension to Loom's protocols.

Expand Down
72 changes: 58 additions & 14 deletions src/ubergraph/protocols.clj
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,68 @@
; two sides of an undirected edge, so we can keep attributes and weight in sync
; and add/remove or otherwise mark them in sync.
(defprotocol UndirectedGraph
(other-direction [g edge] "Returns the other direction of this edge in graph g"))
(other-direction [g edge]
"Returns the other direction of an undirected edge in graph g. If
edge is a directed edge, returns nil."))

; We need a way to retrieve edges that is friendly to both regular and multi-graphs,
; but especially important for multi-graphs.
(defprotocol QueryableGraph
(find-edges [g src dest] [g query] "Returns all edges that match the query")
(find-edge [g src dest] [g query] "Returns first edge that matches the query"))

; Sample queries
; (find-edges g {:src 1, :dest 3, :color :red})
; finds all edges from 1 to 3 with :color :red in edge attributes
;
; (find-edges g {:src 1, :weight 5})
; finds all edges from 1 with a weight of 5
;
; (find-edges g {:dest :Chicago, :airline :Delta, :time :afternoon})
; finds all edges leading to :Chicago with :airline :Delta and
; :time :afternoon in edge attributes
(find-edges [g src dest] [g query]
"Returns all edges that match the query.

(find-edges g src dest) and (find-edges g {:src src :dest dest})
return a sequence of edges in the graph g that are from node src to
node dest. In graphs where parallel edges are allowed, there can be
more than one edge in this sequence.

If src and dest are different nodes, the sequence includes all
directed edges from src to dest once each, and all undirected edges
between src and dest once each.

If src and dest are the same node, directed 'self loop' edges are
included once each, but undirected 'self loop' edges are included
twice, one with mirror-edge? returning true, the other false.

(find-edges g {:src src}) without :dest is the same as (out-edges g
src), (find-edges g {:dest dest}) without :src is the same
as (in-edges g dest). (find-edges g {}) is the same as (edges g).

Any keys other than :src or :dest in the map will be used to filter
the sequence of edges to only those that have at least those keys in
their attribute map, and equal associated values as those in the
query map.

Examples:

(find-edges g {:src 1, :dest 3, :color :red})
finds all edges from node 1 to node 3 with :color :red in edge
attributes.

(find-edges g {:src 1, :weight 5})
finds all edges from node 1 with a weight of 5.

(find-edges g {:dest :Chicago, :airline :Delta, :time :afternoon})
finds all edges leading to node :Chicago with :airline :Delta and
:time :afternoon in edge attributes.

For the implementation on type Ubergraph, returns in effectively
O(1) time, with effectively O(k) time to traverse all k edges in the
sequence. The implementation traverses all k edges, internally
filtering for the ones that match the attribute part of a query, so
must traverse all edges that do not satisfy the attribute
conditions, even though they are not included in the returned
sequence.")
(find-edge [g src dest] [g query]
"Returns first edge that matches the query, same
as (first (find-edges ...)). Returns nil if no edges match the
query.

For the implementation on type Ubergraph, returns in effectively
O(1) time when there are no attributes to match. When there are
attributes to match, returns in effectively O(k) time, where k is
the number of edges traversed until the first edge matching the
query is found."))

; It would be nice to have a way to incorporate both undirected and directed edges
; into the same graph structure.
Expand Down