Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update plot_gate_map (and related visualization functions) to leverage rustworkx.visualization.graphviz_draw for unknown coupling maps #9031

Closed
mtreinish opened this issue Oct 28, 2022 · 7 comments · Fixed by #10208
Assignees
Labels
help wanted community contributions welcome. For filters like http://github-help-wanted.com/ mod: visualization qiskit.visualization type: feature request New feature or request

Comments

@mtreinish
Copy link
Member

What should we add?

Right now the plot_gate_map() visualization function (and similar functions like plot_circuit_layout() use rustworkx's spring_layout() function when there a backend is passed in and there isn't a hardcoded layout available and passes that to a mpl visualization. For moderate numbers of qubits this works reasonably well, but for large numbers of qubits the layout doesn't scale positions appropriately and the output is typically cluttered and not a useful visualization. Graphviz is particularly well suited for doing this type of graph visualization (as it is a specialized tool for doing graph visualization). So instead of trying to rebuild what we get from graphviz we should just call out to graphviz if it's available and leverage the rustwork function for graph visualization using graphivz to generate the visualization. The only potential issue is potentially plot_error_map which has histograms and color bars built using matplotlib and integrating the two different visualization tools might prove difficult.

Here is some example code I used to leverage graphviz_draw() to build a view like plot_circuit_layout():

from rustworkx.visualization import graphviz_draw

graph = target.build_coupling_map().graph
for node in graph.node_indices():
    graph[node] = node
    
for edge, triple in graph.edge_index_map().items():
    graph.update_edge_by_index(edge, (triple[0], triple[1]))

physical_bits = layout.get_physical_bits()
qubit_indices = {bit: index for index, bit in enumerate(linear_circuit.qubits)}
    
def color_node(node):
    if node in physical_bits:
        out_dict = {
            "label": str(qubit_indices[physical_bits[node]]),
            "color": "red",
            "fillcolor": "red",
            "style": "filled",
            "shape": "circle",
        }
    else:
        out_dict = {
            "label": "",
            "color": "blue",
            "fillcolor": "blue",
            "style": "filled",
            "shape": "circle",
        }
    return out_dict

def color_edge(edge):
    if all(x in physical_bits for x in edge):
        out_dict = {
            "color": "red",
            "fillcolor": "red",
        }
    else:
        out_dict = {
            "color": "blue",
            "fillcolor": "blue",
        }
    return out_dict
        
graphviz_draw(graph, method="neato", node_attr_fn=color_node, edge_attr_fn=color_edge)

which in this case output something like
plot_circuit_layout_graphviz

@mtreinish mtreinish added type: feature request New feature or request help wanted community contributions welcome. For filters like http://github-help-wanted.com/ mod: visualization qiskit.visualization labels Oct 28, 2022
@javabster javabster moved this to Tagged but unassigned in Contributor Monitoring Oct 29, 2022
@1ucian0 1ucian0 added the unitaryhack Issues/PR participating (now or in the past) in the UnitaryHack event see https://unitaryhack.dev/ label Apr 28, 2023
@1ucian0
Copy link
Member

1ucian0 commented May 5, 2023

For reference, the current plot_gate_map and plot_error_map problems with larger backends:

from qiskit_ibm_provider import IBMProvider
from qiskit.visualization import plot_gate_map

backend = IBMProvider().get_backend('ibm_washington')
print(backend.num_qubits)  # 127
plot_gate_map(backend)

image

from qiskit.visualization import plot_error_map
plot_error_map(backend)

image

@1ucian0 1ucian0 removed the unitaryhack Issues/PR participating (now or in the past) in the UnitaryHack event see https://unitaryhack.dev/ label May 5, 2023
@husayngokal
Copy link

Hey! Is this still available for UnitaryHack @1ucian0? I'd love to take a shot 💪

@maxwell04-wq
Copy link
Contributor

@husayngokal can collaborate with you, if you'd like?

@husayngokal
Copy link

Sure! PM me on LinkedIn and we can work on this together:
https://www.linkedin.com/in/husayn-gokal/

@maxwell04-wq
Copy link
Contributor

maxwell04-wq commented Jun 4, 2023

@1ucian0 @mtreinish I have a few questions:

  • Should we remove matplotlib from the functions entirely or provide the user with the choice b/w graphviz and matplotlib?
  • A comment in the source code asks to replace the spring_layout() function with planar_layout(). The graph is getting rendered with planar_layout(). However, it's still not available in the documentation and the output graphs have overlapping edges (as per my understanding, the planar_layout() is meant to avoid just this). Should I stick with spring_layout()?
  • Does this issue require us to modify only the functions in the qiskit/visualization/gate_map.py file:
    • plot_gate_map
    • plot_coupling_map
    • plot_circuit_layout
    • plot_error_map ?
  • Is there a way to add custom colors to rusworkx graphs? In the existing code, the default node and edge color is '#648fff'; I am unable to use it in rx graphs.
    • This is especially important for error maps. If nothing is available, I am thinking of using webcolors to get the names of the colors in the colormap.

@maxwell04-wq
Copy link
Contributor

@mtreinish can you please assign me to this issue?

maxwell04-wq added a commit to maxwell04-wq/qiskit-terra-plot-gate-map that referenced this issue Jun 8, 2023
@mtreinish
Copy link
Member Author

@1ucian0 @mtreinish I have a few questions:

* Should we remove matplotlib from the functions entirely or provide the user with the choice b/w graphviz and matplotlib?

I think for the visualization just always using graphviz is probably easiest. I don't think we'll be able to fully remove matplotlib from the function though mostly because it will probably be needed to build a color map and also the graphs for plot_error_map(). So considering that maybe giving the option is nice just in case people don't have graphviz installed locally. But it's not really a requirement as we can just do everything in graphviz (except for what I noted before).

* A comment in the source code asks to replace the `spring_layout()` function with `planar_layout()`. The graph is getting rendered with `planar_layout()`. However, it's still not available in the documentation and the output graphs have overlapping edges (as per my understanding, the `planar_layout()` is meant to avoid just this). Should I stick with `spring_layout()`?

That comment is out of date, at the time I wrote it my thinking was that planar_layout() (once implemented in rustworkx) would be a better default especially at large graph sizes. But in reality it's not suitable for this application (you can see the in progress PR for this here: Qiskit/rustworkx#645). Using graphviz_draw() side steps the issues associated with using rustworkx's graph layout function.

* Does this issue require us to modify only the functions in the `qiskit/visualization/gate_map.py` file:
  
  * plot_gate_map
  * plot_coupling_map
  * plot_circuit_layout
  * plot_error_map ?

Yeah, the only place this comes into play is the visualization functions in gate_map.py. This issue is specifically for updating the default behavior to leverage graphviz (via rustworkx's graphviz_draw()) to do the graph layout. Algorithmic graph layout is a complex problem and graphviz being a dedicated tool for it is the best solution.

* Is there a way to add custom colors to rusworkx graphs? In the existing code, the default node and edge color is '#648fff'; I am unable to use it in `rx` graphs.

Using rustworkx's graphviz draw the way you set custom colors is via node or edge attributes via the callback functions. In my example code in the OP these are the color_node() and color_edge() functions. In the return dictionary from those you can set the dictionary key "color" to any valid graphviz color string, which is either a name from: https://graphviz.org/doc/info/colors.html or you can use a custom color code using: https://graphviz.org/docs/attr-types/color/

For all the values in the output from color_node() and color_edge() you can refer to the graphviz documentation for valid fields. Those output dictionaries get passed directly to the intermediate .dot files that the function passes to graphviz. So that's how you can control the visualization at the per node and edge level.

  * This is especially important for error maps. If nothing is available, I am thinking of using webcolors to get the names of the colors in the colormap.

The way I've typically done quantitative color maps with graphviz_draw() is using matplotlib to build a separate color map and convert that to a color code string that graphviz understands. There is an example I put in the documentation for how to do this here: https://qiskit.org/ecosystem/rustworkx/tutorial/betweenness_centrality.html#visualize-the-betweenness-centrality

@github-project-automation github-project-automation bot moved this from Tagged but unassigned to Done in Contributor Monitoring Sep 22, 2023
github-merge-queue bot pushed a commit that referenced this issue Sep 22, 2023
…10208)

* Update gate_map.py

Update the gate_map.py to migrate the visualization modules from matplotlib to rustworkx.graphviz

* Removed has_rustworkx instances

* Added release notes

* Update test_gate_map.py

Updated tests for the modified gate_map.py file

* Formatted gate_map.py

* Format test_gate_map.py

* Added release notes for the fix of #9031

* Update gate_map.py

* Update test_gate_map.py

Test file updated so that all tests can be passed.

* Update test_gate_map.py

* Update gate_map.py for rerunning tests.

* Update test_clifford.py

* Update gate_map.py to reuse rx.draw_graphiz

* Update test_gate_map.py to omit qubit_visualization

* Update test_gate_map.py to fix formatting changes

* Update gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update test_gate_map.py

* Update test_gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update test_gate_map.py

* Update test_gate_map.py to add rx import

* Update test_gate_map.py

* Update test_gate_map.py

* Update test_gate_map.py to fix imports

* Update test_gate_map.py to add HAS_GRAPHVIZ to all tests

* Update test_graph_matplotlib_drawer.py to have HAS_GRAPHVIZ

* Update test_graph_matplotlib_drawer.py to add HAS_GRAPHVIZ import to test_font_color function

* Update test_graph_matplotlib_drawer.py to add GRAPHVIZ check to the class

* Update test_graph_matplotlib_drawer.py to add HAS_GRAPHVIZ to both gate_plot_map and test_gate_plot_map import to test_font_color function

* Update test_graph_matplotlib_drawer.py

* Update test_graph_matplotlib_drawer.py

* Update test_graph_matplotlib_drawer.py

* Fixed the `font_color` parameter in `gate_map.py/plot_gate_map()`

The font_color parameter can now accept hex values as well.

* Modify color_edge function to increase speed

* Update gate_map.py to fix formatting

* Update gate_map.py to shift seaborn import to `plot_error_map`

* Add HAS_SEABORN to test_plot_error_map

* Reformat gate_map.py

* Fixed node autoscaling in `gate_map.py` graphs

* Add return type to `test_from_gate_with_cyclic_definition ` in `test_clifford.py`

* Update update-gate_map-visualizations-6ea907a0502fdc1a.yaml

* Rename update-gate_map-visualizations-6ea907a0502fdc1a.yaml to update-gate-map-visualizations-6ea907a0502fdc1a.yaml

* Update update-gate-map-visualizations-6ea907a0502fdc1a.yaml

* Update update-gate-map-visualizations-6ea907a0502fdc1a.yaml

* Update pauli_op.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update operator.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update pauli_sum_op.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update random.py to use np.product

`np.product` is deprecated and is causing test fails.

* Update chi.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Replace np.product with np.prod

`np.product` is deprecated and is causing test fails.

* Update ptm.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update stinespring.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update superop.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update transformations.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update densitymatrix.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update random.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update statevector.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update local_readout_mitigator.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update unitary_synthesis.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update test_random.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update test_scalar_op.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update test_random.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update test_clifford.py to remove debugging code

* Move matplotlib imports to occur at run time

* Apply suggestions from code review

* Move matplotlib_close_if_inline to runtime import too

* Flatten parallel edges

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

* Fix 1 qubit backend handling

* Fix qubit label and font sizes

* new reference images

* Adjust font scaling

* Update reference images

* Remove unrelated reference file update

* Improve formatting

* Adjust pixel scaling factor

* Update reference images with formatting changes

---------

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
Co-authored-by: Luciano Bello <bel@zurich.ibm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted community contributions welcome. For filters like http://github-help-wanted.com/ mod: visualization qiskit.visualization type: feature request New feature or request
Projects
Status: Done
4 participants