-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Change style of DAG generated in dag_drawer() #13253
base: main
Are you sure you want to change the base?
Conversation
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:
|
Pull Request Test Coverage Report for Build 12821657503Details
💛 - Coveralls |
There was a problem hiding this 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
* 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
There was a problem hiding this 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.
else: | ||
style_name = style | ||
else: | ||
raise exceptions.VisualizationError("Invalid style {style}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this might have been an f-string?
raise exceptions.VisualizationError("Invalid style {style}") | |
raise exceptions.VisualizationError(f"Invalid style {style}") |
self.style = StyleDict(**default_style) | ||
|
||
|
||
def load_style(style: dict | str = "color") -> StyleDict: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we instead be able to provide a file or directory here too? Since whenever we provide a string it searches for one. Perhaps we should be able to specify a specific Path or file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, that seems a lot cleaner. This function definition was taken from the load_style()
function in qiskit/visualization/circuit/qcstyle.py
, should that be changed too or is that outside of the scope for this PR?
(I also noticed that the docstring here needs updating, I'll fix that too)
if style_name in ["color"]: | ||
current_style = DefaultStyle().style |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe compere the strings directly here?
if style_name in ["color"]: | |
current_style = DefaultStyle().style | |
if style_name == "color": | |
current_style = DefaultStyle().style |
else: | ||
# Search for file in 'styles' dir, and then the current directory | ||
style_name = style_name + ".json" | ||
style_paths = [] | ||
|
||
default_path = Path(__file__).parent / "styles" / style_name | ||
style_paths.append(default_path) | ||
|
||
# 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 = StyleDict(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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This here can get a little convoluted. While providing a string name for a style that might be located at the styles directory is a good idea. We should be able to directly provide an instance of File
or Path
so that we don't need to perform this search actively and the user can just provide the Path themselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, similar to before this code uses the same logic as the other instance of load_style() in qiskit/visualization/circuit/qcstyle.py
, but with small changes to accommodate for the different use case.
if node.name == "barrier": | ||
n["style"] = "filled" | ||
n["fillcolor"] = "grey" | ||
elif getattr(node.op, "_directive", False): | ||
n["style"] = "filled" | ||
n["fillcolor"] = "red" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason why these two conditions were removed here?
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 howcircuit_drawer
does.Here is an example:
The code above generates this DAG: