diff --git a/docs/source/conf.py b/docs/source/conf.py index b258a3ba..31c3fc05 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -41,6 +41,7 @@ "sphinx_copybutton", "sphinx_pyrunblock", "sphinx_codeautolink", + "matplotlib.sphinxext.plot_directive", ] autosummary_generate = True diff --git a/src/pgraph/PGraph.py b/src/pgraph/PGraph.py index c1be5956..8fcc9fe7 100644 --- a/src/pgraph/PGraph.py +++ b/src/pgraph/PGraph.py @@ -108,6 +108,13 @@ def Dict(cls, d: dict, reverse: bool = False) -> UGraph | DGraph: By default parent vertices are linked their children. If ``reverse`` is True then children are linked to their parents. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> d = {'b': 'a', 'c': 'a', 'd': 'b'} + >>> g = UGraph.Dict(d) + >>> print(g) + :seealso: :meth:`Adjacency` """ @@ -165,6 +172,14 @@ def Adjacency( .. warning:: For undirected graph ``A`` should be symmetric but this is not checked. Only the upper triangular part is used. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> import numpy as np + >>> A = np.array([[0, 1, 0], [1, 0, 1], [0, 1, 0]]) + >>> g = UGraph.Adjacency(A) + >>> print(g) + :seealso: :meth:`Dict` :meth:`adjacency` """ @@ -209,6 +224,15 @@ def copy(self) -> _BaseGraph: :return: deep copy :rtype: _BaseGraph + + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> g2 = g.copy() + >>> g2 is g + >>> g2[0] is g[0] """ return copy.deepcopy(self) @@ -243,6 +267,15 @@ def add_vertex( :attr:`_vertex_cls` rather than duplicated per subclass -- see :doc:`policy` for why that matters. + .. runblock:: pycon + + >>> from pgraph import UGraph, UVertex + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0]) + >>> print(v1.name) + >>> v2 = g.add_vertex(UVertex(coord=[1,1], name='v2')) + >>> print(v2.name) + :seealso: :meth:`vertex_copy` """ if isinstance(coord, self._vertex_cls): @@ -282,6 +315,14 @@ def vertex_copy(cls, vertex: BaseVertex) -> BaseVertex: create a new vertex with the same name and coordinates for inclusion in a new graph -- of ``cls``'s own vertex kind, per :attr:`_vertex_cls`. + .. runblock:: pycon + + >>> from pgraph import UGraph, DGraph + >>> g = UGraph() + >>> v = g.add_vertex(coord=[1,2], name='v1') + >>> newv = DGraph.vertex_copy(v) + >>> print(newv) + :seealso: :meth:`BaseVertex.copy` """ return cls._vertex_cls(coord=vertex.coord, name=vertex.name) @@ -359,6 +400,18 @@ def add_edge(self, v1: BaseVertex | str, v2: BaseVertex | str, **kwargs: Any) -> This is a graph centric way of creating an edge. The alternative is the ``connect`` method of a vertex. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> v3 = g.add_vertex(coord=[2,2], name='v3') + >>> e = g.add_edge(v1, v2) + >>> print(e) + >>> e2 = g.add_edge('v2', 'v3', cost=99) + >>> print(e2) + :seealso: :meth:`Edge.connect` :meth:`BaseVertex.connect` """ v1 = self._resolve_vertex(v1, "v1") @@ -388,6 +441,16 @@ def remove_edge(self, edge: Edge) -> None: implementation would otherwise raise ``ValueError`` on the target side. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> e = g.add_edge(v1, v2) + >>> g.remove_edge(e) + >>> print(g) + :seealso: :meth:`remove_vertex` :meth:`Edge.remove` """ if edge not in self._edgelist: @@ -426,6 +489,16 @@ def remove_vertex(self, vertex: BaseVertex) -> None: (see :attr:`BaseVertex.edges`), so incoming edges would otherwise be missed and left dangling. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> g.add_edge(v1, v2) + >>> g.remove_vertex(v2) + >>> print(g) + :seealso: :meth:`remove_edge` :meth:`BaseVertex.remove` """ if vertex._graph is not self: @@ -469,6 +542,15 @@ def show(self) -> None: """ Print a summary of all vertices and edges to stdout + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> g.add_edge(v1, v2) + >>> g.show() + :seealso: :meth:`__str__` """ print("vertices:") @@ -485,6 +567,14 @@ def n(self) -> int: :return: Number of vertices :rtype: int + + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> g.add_vertex(name='v1') + >>> g.add_vertex(name='v2') + >>> print(g.n) """ return len(self._vertexdict) @@ -495,6 +585,15 @@ def ne(self) -> int: :return: Number of vertices :rtype: int + + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(name='v1') + >>> v2 = g.add_vertex(name='v2') + >>> g.add_edge(v1, v2) + >>> print(g.ne) """ return len(self._edgelist) @@ -526,6 +625,16 @@ def nc(self) -> int: operation has been performed since the last call, the graph coloring algorithm is run which is potentially expensive for a large graph. + + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(name='v1') + >>> v2 = g.add_vertex(name='v2') + >>> v3 = g.add_vertex(name='v3') + >>> g.add_edge(v1, v2) + >>> print(g.nc) """ n = self._graphcolor() if n is not None: @@ -621,6 +730,14 @@ def metric(self, metric: Callable[[NDArray], float] | str) -> None: exactly 3 elements; raises :exc:`ValueError` otherwise. The metric is used by :meth:`closest` and :meth:`distance` + + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> import numpy as np + >>> g = UGraph() + >>> g.metric = 'L1' + >>> print(g.metric(np.r_[3, -4])) """ self._metric = self._metricfunc(metric) @@ -659,6 +776,14 @@ def heuristic(self, heuristic: Callable[[NDArray], float] | str) -> None: exactly 3 elements; raises :exc:`ValueError` otherwise. The heuristic distance is only used by the A* planner :meth:`path_Astar`. + + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> import numpy as np + >>> g = UGraph() + >>> g.heuristic = 'L2' + >>> print(g.heuristic(np.r_[3, 4])) """ self._heuristic = self._metricfunc(heuristic) @@ -773,6 +898,15 @@ def closest(self, coord: ArrayLike) -> tuple[BaseVertex, float]: Returns the vertex closest to the given point. Distance is computed according to the graph's metric. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[10,10], name='v2') + >>> vertex, d = g.closest([1, 1]) + >>> print(vertex, d) + :seealso: :meth:`metric` """ min_dist = np.inf @@ -802,6 +936,15 @@ def edges(self) -> set[Edge]: connection order, this returns a ``set`` with no defined iteration order. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> g.add_edge(v1, v2) + >>> print(g.edges()) + :seealso: :meth:`BaseVertex.edges` """ return self._edgelist @@ -840,6 +983,24 @@ def plot( which are the vertex names. If ``text`` is None default formatting is used. If ``text`` is False no labels are added. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> g.add_edge(v1, v2) + >>> g.plot(block=None) + + .. plot:: + + from pgraph import UGraph + g = UGraph() + v1 = g.add_vertex(coord=[0,0], name='v1') + v2 = g.add_vertex(coord=[1,1], name='v2') + g.add_edge(v1, v2) + g.plot(block=None) + :seealso: :meth:`highlight_path` """ vopt = {**dict(marker="o", markersize=12), **vopt} @@ -924,6 +1085,30 @@ def highlight_path(self, path: list[BaseVertex], block: bool = False, **kwargs: The vertices and edges along the path are overwritten with a different size/width and color. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> v3 = g.add_vertex(coord=[2,2], name='v3') + >>> g.add_edge(v1, v2) + >>> g.add_edge(v2, v3) + >>> g.plot(block=None) + >>> g.highlight_path([v1, v2, v3], block=None) + + .. plot:: + + from pgraph import UGraph + g = UGraph() + v1 = g.add_vertex(coord=[0,0], name='v1') + v2 = g.add_vertex(coord=[1,1], name='v2') + v3 = g.add_vertex(coord=[2,2], name='v3') + g.add_edge(v1, v2) + g.add_edge(v2, v3) + g.plot(block=None) + g.highlight_path([v1, v2, v3], block=None) + :seealso: :meth:`highlight_vertex` :meth:`highlight_edge` """ for i in range(len(path)): @@ -947,6 +1132,33 @@ def highlight_edge( :type scale: float, optional :param color: Overwrite with a line in this color, defaults to 'r' :type color: str, optional + :param alpha: Transparency of the highlight, defaults to 0.5 + :type alpha: float, optional + :rtype: None + + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> e = g.add_edge(v1, v2) + >>> g.plot(block=None) + >>> g.highlight_edge(e) + + The plot produced looks like: + + .. plot:: + + from pgraph import UGraph + g = UGraph() + v1 = g.add_vertex(coord=[0,0], name='v1') + v2 = g.add_vertex(coord=[1,1], name='v2') + e = g.add_edge(v1, v2) + g.plot(block=None) + g.highlight_edge(e) + + :seealso: :meth:`highlight_vertex` :meth:`highlight_path` """ p1 = edge.v1 p2 = edge.v2 @@ -964,13 +1176,37 @@ def highlight_vertex( """ Highlight a vertex in the graph - :param edge: The vertex to highlight - :type edge: BaseVertex subclass + :param vertex: The vertex (or vertices, or vertex names) to highlight + :type vertex: BaseVertex subclass, or iterable of BaseVertex subclass or str :param scale: Overwrite with a line this much bigger than the original, defaults to 1.5 :type scale: float, optional :param color: Overwrite with a line in this color, defaults to 'r' :type color: str, optional + :param alpha: Transparency of the highlight, defaults to 0.5 + :type alpha: float, optional + :rtype: None + + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> g.add_edge(v1, v2) + >>> g.plot(block=None) + >>> g.highlight_vertex(v1) + + .. plot:: + + from pgraph import UGraph + g = UGraph() + v1 = g.add_vertex(coord=[0,0], name='v1') + v2 = g.add_vertex(coord=[1,1], name='v2') + g.add_edge(v1, v2) + g.plot(block=None) + g.highlight_vertex(v1) + """ if isinstance(vertex, Iterable): for n in vertex: @@ -1006,6 +1242,15 @@ def dotfile(self, filename: str | Any | None = None, direction: str | None = Non .. note:: If ``filename`` is a file object then the file will *not* be closed after the GraphViz model is written. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> g.add_edge(v1, v2) + >>> g.dotfile() + :seealso: :func:`showgraph` """ @@ -1045,7 +1290,7 @@ def dotfile(self, filename: str | Any | None = None, direction: str | None = Non print("}", file=f) - if filename is None or isinstance(filename, str): + if isinstance(filename, str): f.close() # noqa def showgraph(self, **kwargs: Any) -> None: @@ -1103,6 +1348,19 @@ def iscyclic(self) -> bool: question Kahn's algorithm answers exactly with integers in O(V+E). Not used here for that reason. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(name='v1') + >>> v2 = g.add_vertex(name='v2') + >>> v3 = g.add_vertex(name='v3') + >>> g.add_edge(v1, v2) + >>> g.add_edge(v2, v3) + >>> print(g.iscyclic()) + >>> g.add_edge(v3, v1) + >>> print(g.iscyclic()) + :seealso: :meth:`adjacency` """ if isinstance(self, UGraph): @@ -1137,6 +1395,17 @@ def average_degree(self) -> float: :math:`E / N` for a directed graph where :math:`E` is the total number of edges and :math:`N` is the number of vertices. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(name='v1') + >>> v2 = g.add_vertex(name='v2') + >>> v3 = g.add_vertex(name='v3') + >>> g.add_edge(v1, v2) + >>> g.add_edge(v2, v3) + >>> print(g.average_degree()) + :seealso: :meth:`degree` """ if isinstance(self, DGraph): @@ -1417,6 +1686,16 @@ def component(self, c: int) -> list[BaseVertex]: ``graph.component(c)`` is a list of all vertices in graph component ``c``. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(name='v1') + >>> v2 = g.add_vertex(name='v2') + >>> v3 = g.add_vertex(name='v3') + >>> g.add_edge(v1, v2) + >>> print(g.component(0)) + :seealso: :meth:`nc` :meth:`samecomponent` """ self._graphcolor() # ensure labels are uptodate @@ -1438,6 +1717,17 @@ def samecomponent(self, v1: BaseVertex, v2: BaseVertex) -> bool: - directed graph this implies a path between them - undirected graph there is not necessarily a path between them + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(name='v1') + >>> v2 = g.add_vertex(name='v2') + >>> v3 = g.add_vertex(name='v3') + >>> g.add_edge(v1, v2) + >>> print(g.samecomponent(v1, v2)) + >>> print(g.samecomponent(v1, v3)) + :seealso: :meth:`component` """ self._graphcolor() # ensure labels are uptodate @@ -1464,6 +1754,19 @@ def path_BFS( Returns a list of vertices that form a path from vertex ``S`` to vertex ``G`` if possible, otherwise return None. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,0], name='v2') + >>> v3 = g.add_vertex(coord=[2,0], name='v3') + >>> g.add_edge(v1, v2) + >>> g.add_edge(v2, v3) + >>> path, length = g.path_BFS(v1, v3) + >>> print(path) + >>> print(length) + :seealso: :meth:`path_UCS` :meth:`path_Astar` """ S = self._resolve_vertex(S, "start") @@ -1550,6 +1853,19 @@ def path_UCS( The heuristic is the distance metric of the graph, which defaults to Euclidean distance. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,0], name='v2') + >>> v3 = g.add_vertex(coord=[2,0], name='v3') + >>> g.add_edge(v1, v2) + >>> g.add_edge(v2, v3) + >>> path, length, tree = g.path_UCS(v1, v3) + >>> print(path) + >>> print(length) + :seealso: :meth:`path_BFS` :meth:`path_Astar` """ S = self._resolve_vertex(S, "start") @@ -1649,6 +1965,19 @@ def path_Astar( The heuristic is the distance metric of the graph, which defaults to Euclidean distance. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,0], name='v2') + >>> v3 = g.add_vertex(coord=[2,0], name='v3') + >>> g.add_edge(v1, v2) + >>> g.add_edge(v2, v3) + >>> path, length, tree = g.path_Astar(v1, v3) + >>> print(path) + >>> print(length) + :seealso: :meth:`heuristic` :meth:`path_BFS` :meth:`path_UCS` """ S = self._resolve_vertex(S, "start") @@ -2119,6 +2448,18 @@ def remove(self) -> None: .. note:: This is a thin convenience wrapper around :meth:`_BaseGraph.remove_edge`. + :rtype: None + + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> e = g.add_edge(v1, v2) + >>> e.remove() + >>> print(g) + :seealso: :meth:`_BaseGraph.remove_edge` :meth:`BaseVertex.remove` """ if self.v1 is None or self.v1._graph is None: @@ -2222,6 +2563,13 @@ def copy(self, cls: type | None = None) -> BaseVertex: vertex of the appropriate subclass for that graph type, otherwise a vertex of the same class as ``self`` is created directly. + .. runblock:: pycon + + >>> from pgraph import UVertex, DGraph + >>> v = UVertex(coord=[1,2], name='v1') + >>> v2 = v.copy(cls=DGraph) + >>> print(v2) + :seealso: :meth:`UGraph.vertex_copy` :meth:`DGraph.vertex_copy` """ if cls is not None: @@ -2237,6 +2585,17 @@ def neighbours(self) -> list[BaseVertex]: .. note:: For a directed graph the neighbours are those on edges leaving this vertex + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> v3 = g.add_vertex(coord=[2,2], name='v3') + >>> g.add_edge(v1, v2) + >>> g.add_edge(v1, v3) + >>> print(v1.neighbours()) + :seealso: :meth:`neighbors` :meth:`incidences` """ return [e.next(self) for e in self._edgelist] @@ -2249,6 +2608,15 @@ def neighbors(self) -> list[BaseVertex]: .. note:: For a directed graph the neighbours are those on edges leaving this vertex + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> g.add_edge(v1, v2) + >>> print(v1.neighbors()) + :seealso: :meth:`neighbours` """ return [e.next(self) for e in self._edgelist] @@ -2281,6 +2649,17 @@ def isneighbour(self, vertex: BaseVertex) -> bool: For a directed graph this is true only if the edge is from ``self`` to ``vertex``. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> v3 = g.add_vertex(coord=[2,2], name='v3') + >>> g.add_edge(v1, v2) + >>> print(v1.isneighbour(v2)) + >>> print(v1.isneighbour(v3)) + :seealso: :meth:`neighbours` """ return vertex in [e.next(self) for e in self._edgelist] @@ -2294,6 +2673,15 @@ def incidences(self) -> list[tuple[BaseVertex, Edge]]: .. note:: For a directed graph the edges are those leaving this vertex + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> g.add_edge(v1, v2) + >>> print(v1.incidences()) + :seealso: :meth:`neighbours` :meth:`edges` """ return [(e.next(self), e) for e in self._edgelist] @@ -2336,6 +2724,15 @@ def connect( :meth:`DGraph.add_vertex`), this also rules out connecting a ``UVertex`` to a ``DVertex``. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> e = v1.connect(v2, cost=1.414) + >>> print(e) + :seealso: :meth:`Edge` :meth:`Edge.connect` """ @@ -2369,6 +2766,15 @@ def edgeto(self, dest: BaseVertex) -> Edge: .. note:: - For a directed graph ``dest`` must be at the arrow end of the edge + + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> g.add_edge(v1, v2, cost=1.414) + >>> print(v1.edgeto(v2)) """ for n, e in self.incidences(): if n is dest: @@ -2387,6 +2793,15 @@ def edges(self) -> list[Edge]: - For a directed graph the edges are those leaving this vertex - For a non-directed graph the edges are those leaving or entering this vertex + + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> g.add_edge(v1, v2) + >>> print(v1.edges()) """ return self._edgelist @@ -2430,6 +2845,14 @@ def distance(self, coord: ArrayLike | BaseVertex) -> float: must belong to a graph, since the metric is a property of the graph, not the vertex. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[3,4], name='v2') + >>> print(v1.distance(v2)) + :seealso: :meth:`metric` """ if self._graph is None: @@ -2450,6 +2873,17 @@ def degree(self) -> int: .. note:: For a ``DGraph`` only outgoing edges are considered. + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> v3 = g.add_vertex(coord=[2,2], name='v3') + >>> g.add_edge(v1, v2) + >>> g.add_edge(v1, v3) + >>> print(v1.degree) + :seealso: :meth:`edges` """ return len(self.edges()) @@ -2461,6 +2895,12 @@ def x(self) -> float: :return: The x-coordinate :rtype: float + + .. runblock:: pycon + + >>> from pgraph import UVertex + >>> v = UVertex(coord=[1,2], name='v1') + >>> print(v.x) """ return self.coord[0] @@ -2471,6 +2911,12 @@ def y(self) -> float: :return: The y-coordinate :rtype: float + + .. runblock:: pycon + + >>> from pgraph import UVertex + >>> v = UVertex(coord=[1,2], name='v1') + >>> print(v.y) """ return self.coord[1] @@ -2481,6 +2927,12 @@ def z(self) -> float: :return: The z-coordinate :rtype: float + + .. runblock:: pycon + + >>> from pgraph import UVertex + >>> v = UVertex(coord=[1,2,3], name='v1') + >>> print(v.z) """ return self.coord[2] @@ -2520,6 +2972,18 @@ def remove(self) -> None: .. note:: This is a thin convenience wrapper around :meth:`_BaseGraph.remove_vertex`. + :rtype: None + + .. runblock:: pycon + + >>> from pgraph import UGraph + >>> g = UGraph() + >>> v1 = g.add_vertex(coord=[0,0], name='v1') + >>> v2 = g.add_vertex(coord=[1,1], name='v2') + >>> g.add_edge(v1, v2) + >>> v2.remove() + >>> print(g) + :seealso: :meth:`_BaseGraph.remove_vertex` :meth:`Edge.remove` """ if self._graph is None: