Skip to content

Change style of DAG generated in dag_drawer() #13253

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

Merged
merged 41 commits into from
May 29, 2025
Merged

Conversation

emilkovacev
Copy link
Contributor

@emilkovacev emilkovacev commented Oct 1, 2024

Summary

Closes #13106

Adds non-breaking interface to dag_drawer() to allow Qiskit users to customize the colors of DAGs.

Details and comments

I created stylesheets for dag_drawer(), that works similarly to how circuit_drawer does.

Here is an example:

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.converters import circuit_to_dag
from qiskit.visualization import dag_drawer
 

q = QuantumRegister(3, 'q')
c = ClassicalRegister(3, 'c')
circ = QuantumCircuit(q, c)
circ.h(q[0])
circ.cx(q[0], q[1])
circ.measure(q[0], c[0])
circ.rz(0.5, q[1])

dag = circuit_to_dag(circ)

style = {
    # "fontsize": 12,
    # "bgcolor": "white",
    # "dpi": 10,
    # "pad": 0,
    # "nodecolor": "green",
    "inputnodecolor": "pink",
    # "inputnodefontcolor": "white",
    "outputnodecolor": "lightblue",
    # "outputnodefontcolor": "white",
    "opnodecolor": "red",
    # "opnodefontcolor": "white",
    # "edgecolor": "black",
    # "qubitedgecolor": "red",
    # "clbitedgecolor": "black",
}

dag_drawer(dag, style=style)

The code above generates this DAG:

Screenshot 2024-10-09 at 11 39 46 AM

@emilkovacev emilkovacev requested review from nonhermitian and a team as code owners October 1, 2024 21:25
@qiskit-bot qiskit-bot added the Community PR PRs from contributors that are not 'members' of the Qiskit repo label Oct 1, 2024
@qiskit-bot
Copy link
Collaborator

Thank you for opening a new pull request.

Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient.

While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone.

One or more of the following people are relevant to this code:

@coveralls
Copy link

coveralls commented Oct 1, 2024

Pull Request Test Coverage Report for Build 15331899554

Details

  • 87 of 205 (42.44%) changed or added relevant lines in 6 files are covered.
  • 11 unchanged lines in 4 files lost coverage.
  • Overall coverage decreased (-0.01%) to 87.85%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/visualization/dag/dagstyle.py 11 15 73.33%
qiskit/visualization/style.py 59 81 72.84%
qiskit/visualization/dag_visualization.py 3 95 3.16%
Files with Coverage Reduction New Missed Lines %
crates/circuit/src/symbol_expr.rs 1 75.39%
crates/qasm2/src/expr.rs 1 94.23%
crates/qasm2/src/lex.rs 2 92.73%
qiskit/visualization/dag_visualization.py 7 10.69%
Totals Coverage Status
Change from base Build 15330118071: -0.01%
Covered Lines: 79656
Relevant Lines: 90673

💛 - Coveralls

Copy link
Member

@mtreinish mtreinish left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for getting this started. I left an inline comment about the high level interface. I'm a bit concerned with how low level the current interface is and that it would require understanding how graphviz is used internally by dag_drawer

@emilkovacev emilkovacev requested a review from mtreinish October 13, 2024 03:06
@raynelfss raynelfss added this to the 1.3.0 milestone Oct 31, 2024
@raynelfss raynelfss added Changelog: New Feature Include in the "Added" section of the changelog mod: visualization qiskit.visualization mod: circuit Related to the core of the `QuantumCircuit` class or the circuit library and removed Community PR PRs from contributors that are not 'members' of the Qiskit repo mod: circuit Related to the core of the `QuantumCircuit` class or the circuit library labels Oct 31, 2024
@raynelfss raynelfss self-assigned this Oct 31, 2024
Copy link
Contributor

@raynelfss raynelfss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great, thank you for these additions. I wonder whether we should consider using FileSpecifier (such as File, Path, etc) as an alternative input for load_style(). I feel like it might benefit from not having to check whether the file exists or not. See the comments in my review.

@raynelfss raynelfss modified the milestones: 1.3.0, 2.0.0 Nov 7, 2024
@emilkovacev emilkovacev requested a review from raynelfss February 3, 2025 03:46
Copy link
Contributor

@raynelfss raynelfss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks better so far, I do have a concern about the reuse of code for the dagstyle.py file and some methods, so I left some tips to consider.

@1ucian0 1ucian0 modified the milestones: 2.0.0, 2.1.0 Feb 27, 2025
@emilkovacev
Copy link
Contributor Author

emilkovacev commented Mar 23, 2025

@raynelfss I attempted to resolve the code duplication issues in 9d6b0ec

  • Moved load_style, StyleDict, and DefaultStyle to qiskit/visualization/style.py
  • Formatted code that previously called load_style to inherit StyleDict and DefaultStyle, and passed the inherited type/object to load_style

These changes decouple the load_style code from qcstyle.py, which should also make it easier to consistently make features that load styles within the qiskit/visualization directory.

@emilkovacev emilkovacev requested a review from raynelfss March 24, 2025 20:32
emilkovacev and others added 6 commits April 7, 2025 16:28
Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>
* Adds functionality to load_style to support throwing errors if style
  is not identified
* Standardizes load_style input to ingest StyleDict as a type, and
  DefaultStyle as an object
raynelfss
raynelfss previously approved these changes Apr 10, 2025
Copy link
Contributor

@raynelfss raynelfss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, thank you for your work. I will look for a secondary reviewer to make sure visualization is in order :)


def load_style(
style: Union[dict, str, None],
style_dict: type[StyleDict],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you'd want to rename this to style_dict_type? It makes more sense to have it in the name that this argument is type based.

@1ucian0 1ucian0 self-assigned this Apr 10, 2025
@raynelfss raynelfss requested a review from 1ucian0 May 5, 2025 12:07
Co-authored-by: Luciano Bello <766693+1ucian0@users.noreply.github.com>
Copy link
Contributor

@raynelfss raynelfss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I quickly breezed through the code once again, just found one minor detail.

Comment on lines +157 to +199
# Search for file in 'styles' dir, then config_path, and finally the current directory
style_name = style_name + ".json"
style_paths = []

default_path = default_style.DEFAULT_STYLE_PATH / style_name
style_paths.append(default_path)

# check configured paths, if there are any
if config:
config_path = config.get(user_config_path_opt, "")
if config_path:
for path in config_path:
style_paths.append(Path(path) / style_name)

# check current directory
cwd_path = Path("") / style_name
style_paths.append(cwd_path)

for path in style_paths:
# expand ~ to the user directory and check if the file exists
exp_user = path.expanduser()
if os.path.isfile(exp_user):
try:
with open(exp_user) as infile:
json_style = json.load(infile)

current_style = style_dict(json_style)
break
except json.JSONDecodeError as err:
warn(
f"Could not decode JSON in file '{path}': {str(err)}. "
"Will use default style.",
UserWarning,
2,
)
break
except (OSError, FileNotFoundError):
warn(
f"Error loading JSON file '{path}'. Will use default style.",
UserWarning,
2,
)
break
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we gain any benefit from pre-computing the paths here, we might be able to fast-track it by performing each step separately and only continue if the next one hasn't been found. But I also understand this wasn't introduced by you, so it should stay as it is for now.

@@ -0,0 +1,223 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2019.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this copyright comment change as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's a new file that you added, it should have the current year.

Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>
raynelfss
raynelfss previously approved these changes May 29, 2025
Copy link
Contributor

@raynelfss raynelfss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to the best of my abilities!

@raynelfss raynelfss added this pull request to the merge queue May 29, 2025
Merged via the queue into Qiskit:main with commit c214e1c May 29, 2025
26 checks passed
rahaman-quantum pushed a commit to rahaman-quantum/qiskit that referenced this pull request Jun 20, 2025
* Added 'custom' style that exposes graphviz customization functions for maximum customizability

* Added type hints to function calls

* Added docs

* Adds tests for dag_drawer

* Fixed invalid exceptions caught by test

* ran formatter and linter on code

* Formatted code and test to abide by pylint standards

* Fix typo in docs for graph_attr kwarg

* placed functionality into stylesheets

* Added error correction to dag visualization

* rewrote tests to work with new stylesheet method

* Added color templates for DAGs

* Added common style templates for DAGs in
  qiskit/visualization/dag/styles
* Loaded styles from stylesheets to color DAG
* Fixed bug where 'plain' DAGs did not label nodes

* Re-format docs for dag_visualization

* reformatted dag_visualization.py

* Ran linting

* Added artifacts for docs to recognize stylesheets

* Fixed errors to match original invocation of dag_drawer()

* Fixed small import formatting issue

* Adds release notes for PR

* Modified DagDependency code to match previous impl

* Apply suggestions from code review

Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>

* merge load_style implementations

* Ran linting

* removed unused imports

* Added docs to new load_style function

* Ran linting

* Refactored style classes to add types

* moved load_style function to qiskit/visualization/style.py

* Fixed linting issues

* resolves dependency cycle

* Apply suggestions from code review

Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>

* merge load_style implementations

* fixed edge case with failing default style initialization

* Fixes type init issues

* Adds functionality to load_style to support throwing errors if style
  is not identified
* Standardizes load_style input to ingest StyleDict as a type, and
  DefaultStyle as an object

* ran black formatting

* Updated documentation for load_style

* Ran linting

* Apply suggestions from code review

Co-authored-by: Luciano Bello <766693+1ucian0@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>

* Update style.py

---------

Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>
Co-authored-by: Luciano Bello <766693+1ucian0@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: New Feature Include in the "Added" section of the changelog mod: visualization qiskit.visualization
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Allow for changing colors in dag circuit drawer
6 participants