Skip to content

Broken graph state after removing node #322

@ktaletsk

Description

@ktaletsk

Bug report

cytoscapeobj.graph.remove_node(node) removes more nodes than just the node passed as the parameter. This also affects cytoscapeobj.graph.remove_node_by_id(node_id) which uses it.

def remove_node(self, node):
"""
Removes node from the end of the list. Equivalent to Python's remove method.
Parameters
----------
node : ipycytoscape.Node
"""
try:
self.nodes.remove(node)
for target in list(self._adj[node.data["id"]]):
self.remove_edge_by_id(node.data["id"], target)
for source in list(self._adj):
for target in list(self._adj[source]):
if target == node.data["id"]:
self.remove_edge_by_id(source, node.data["id"])
del self._adj[node.data["id"]]
except ValueError:
raise ValueError(f'{node.data["id"]} is not present in the graph.')

Issues seems to lie specificly in this call:

self.nodes.remove(node)

Code for reproduction

  1. Open 'DAG example' notebook. Binder

  2. Execute all existing cells in order. The graph would look like this
    image

  3. Observe the list of existing nodes

cytoscapeobj.graph.nodes
_______________________
[Node(data={'id': 'n0'}, position={}),
 Node(data={'id': 'n1'}, position={}),
 Node(data={'id': 'n2'}, position={}),
 Node(data={'id': 'n3'}, position={}),
 Node(data={'id': 'n4'}, position={}),
 Node(data={'id': 'n5'}, position={}),
 Node(data={'id': 'n6'}, position={}),
 Node(data={'id': 'n7'}, position={}),
 Node(data={'id': 'n8'}, position={}),
 Node(data={'id': 'n9'}, position={}),
 Node(data={'id': 'n10'}, position={}),
 Node(data={'id': 'n11'}, position={}),
 Node(data={'id': 'n12'}, position={}),
 Node(data={'id': 'n13'}, position={}),
 Node(data={'id': 'n14'}, position={}),
 Node(data={'id': 'n15'}, position={}),
 Node(data={'id': 'n16'}, position={})]
  1. Remove node with id n3 (the bottom-right child node in the left-most graph)
cytoscapeobj.graph.remove_node_by_id('n3')

The graph now looks like this:
image

Alternatively, you can remove the node from the list of nodes (which is what cytoscapeobj.graph.remove_node does internally) to the same effect:

cytoscapeobj.graph.nodes.remove(cytoscapeobj.graph.nodes[3])
  1. Inspect the list of nodes
cytoscapeobj.graph.nodes
_______________________
[Node(data={'id': 'n0'}, position={}),
 Node(data={'id': 'n1'}, position={}),
 Node(data={'id': 'n2'}, position={}),
 Node(data={'id': 'n4'}, position={}, removed=True),
 Node(data={'id': 'n5'}, position={}, removed=True),
 Node(data={'id': 'n6'}, position={}, removed=True),
 Node(data={'id': 'n7'}, position={}, removed=True),
 Node(data={'id': 'n8'}, position={}, removed=True),
 Node(data={'id': 'n9'}, position={}, removed=True),
 Node(data={'id': 'n10'}, position={}, removed=True),
 Node(data={'id': 'n11'}, position={}, removed=True),
 Node(data={'id': 'n12'}, position={}, removed=True),
 Node(data={'id': 'n13'}, position={}, removed=True),
 Node(data={'id': 'n14'}, position={}, removed=True),
 Node(data={'id': 'n15'}, position={}, removed=True),
 Node(data={'id': 'n16'}, position={}, removed=True)]
...
  1. JS console logs indicate an error immediately after running cytoscapeobj.graph.remove_node_by_id('n3'):
Uncaught (in promise) Error: Can not create second element with ID `n4`
Uncaught (in promise) Error: Can not create second element with ID `n4`
Uncaught (in promise) Error: Can not create edge `a1b4ae96-fe81-4363-ba59-23298b1a10e3` with nonexistant source `n4`

Is this the intended behavior?

Actual outcome

Nodes with ids "higher" than node requested to be deleted are getting removed=True on them and disappear from the graph.

Expected outcome

Maybe it would make more sense to make this optional, and only delete a single node by default?

Version Info

  • runtime environment: Binder (link above)
    • ipycytoscape version : 1.3.2
    • Python version: 3.7.12
    • JupyterLab version: 3.3.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions