Qiskit / rustworkx

A high performance Python graph library implemented in Rust.
https://www.rustworkx.org
Apache License 2.0
1.13k stars 154 forks source link

Serializing data with node_link_json returns "data": null #1298

Open ethanporcaro opened 1 month ago

ethanporcaro commented 1 month ago

Information

What is the current behavior?

It returns a JSON string with null for the data field:

{"directed":false,"multigraph":true,"attrs":null,"nodes":[{"id":0,"data":null},{"id":1,"data":null},{"id":2,"data":null}],"links":[{"source":0,"target":1,"id":0,"data":null},{"source":0,"target":2,"id":1,"data":null},{"source":1,"target":2,"id":2,"data":null}]}

What is the expected behavior?

I should see somthing like [{"id":0,"data":"A"}

Steps to reproduce the problem

This uses the quickstart example:

import rustworkx

G = rustworkx.PyGraph()
a = G.add_node("A")
b = G.add_node("B")
c = G.add_node("C")
G.add_edges_from([(a, b, 1.5), (a, c, 5.0), (b, c, 2.5)])
print(rustworkx.node_link_json(G))

I wasn't sure if I was supposed to pass anything to node_attrs or edge_attrs. Thanks

IvanIsCoding commented 1 month ago

Indeed, node_attrs and edge_attrs are necessary to get a more useful output. For example:

import rustworkx

G = rustworkx.PyGraph()
a = G.add_node("A")
b = G.add_node("B")
c = G.add_node("C")
G.add_edges_from([(a, b, 1.5), (a, c, 5.0), (b, c, 2.5)])

def node_attrs_fn(weight):
  return {"name": weight}

def edge_attrs_fn(weight):
  return {"cost": str(weight)}

graph_json = rustworkx.node_link_json(G, node_attrs=node_attrs_fn, edge_attrs=edge_attrs_fn)
print(graph_json)

Outputs:

{"directed":false,"multigraph":true,"attrs":null,"nodes":[{"id":0,"data":{"name":"A"}},{"id":1,"data":{"name":"B"}},{"id":2,"data":{"name":"C"}}],"links":[{"source":0,"target":1,"id":0,"data":{"cost":"1.5"}},{"source":0,"target":2,"id":1,"data":{"cost":"5.0"}},{"source":1,"target":2,"id":2,"data":{"cost":"2.5"}}]}

Which is probably closer to what you want. I think the way forward with this issue is to have a better example on the documentation itself.

Also, this decision may seem not the best when the node weights are strings. But it makes sense for general Python objects, I don't think anyone would like a JSON with {data: "<object object at 0x7fdbbeb78740>"} by default.