From b7ace35334e5fc6d44458bdf420dd1e7fd70ff4a Mon Sep 17 00:00:00 2001 From: Andy Fingerhut Date: Fri, 31 Jan 2020 02:39:18 -0800 Subject: [PATCH] Add more extensive documentation strings for many functions --- src/ubergraph/core.clj | 127 ++++++++++++++++++++++++++++++++++++ src/ubergraph/protocols.clj | 72 ++++++++++++++++---- 2 files changed, 185 insertions(+), 14 deletions(-) diff --git a/src/ubergraph/core.clj b/src/ubergraph/core.clj index 2129806..0b9d0b5 100644 --- a/src/ubergraph/core.clj +++ b/src/ubergraph/core.clj @@ -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. diff --git a/src/ubergraph/protocols.clj b/src/ubergraph/protocols.clj index 31a2ee4..a980e88 100644 --- a/src/ubergraph/protocols.clj +++ b/src/ubergraph/protocols.clj @@ -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.