FEAT: add Mermaid graph rendering - #347
Conversation
…tests and documentation
…mermaid in Jupyter notebooks - Updated the visualize_mermaid.ipynb to use asmermaid for generating Mermaid source directly. - Removed show_mermaid_markdown function and integrated its functionality into asmermaid. - Adjusted documentation and examples to reflect the new usage of asmermaid. - Cleaned up the io module by consolidating Mermaid-related functions and removing unnecessary code. - Enhanced tests to verify the new asmermaid functionality and removed tests related to the deprecated show_mermaid_markdown.
…ebook with few mermaid examples and fix references
…tic newline in visualize.ipynb
|
Found issue with rendering long lines eg. in graph edges. isospin_magnitude ∊ [0, 1/2, 1, 3/2 ] gets displayed as isospin_magnitude ∊ [0, 1/2, 1, 3/2 |
b024f39 to
4c59a19
Compare
redeboer
left a comment
There was a problem hiding this comment.
Good job! 💯
Some smaller comments below.
One general thought I had: it seems that Mermaid (as opposed to Graphviz) supports LaTeX, even if not for all backends. Rewriting the example of #159:
```mermaid
graph LR
A["$$J/\psi$$"] --> N0[ ]
N0 --> N1["$$f_0(980)$$"]
N0 --> 0[$$\gamma$$]
N1 --> 1[$$\pi^0$$]
N1 --> 2[$$\pi^0$$]
style A fill:#FFFFFF, stroke:#FFFFFF;
style N0 fill:#FFFFFF, stroke:#FFFFFF;
style N1 fill:#FFFFFF, stroke:#FFFFFF;
style 0 fill:#FFFFFF, stroke:#FFFFFF;
style 1 fill:#FFFFFF, stroke:#FFFFFF;
style 2 fill:#FFFFFF, stroke:#FFFFFF;
```Maybe worth posting a follow-up issue. Similarly to #348, one could select LaTeX as rendering with a flag, for instance.
Rendering on GitHub ❌
graph LR
A["$$J/\psi$$"] --> N0[ ]
N0 --> N1["$$f_0(980)$$"]
N0 --> 0[$$\gamma$$]
N1 --> 1[$$\pi^0$$]
N1 --> 2[$$\pi^0$$]
style A fill:#FFFFFF, stroke:#FFFFFF;
style N0 fill:#FFFFFF, stroke:#FFFFFF;
style N1 fill:#FFFFFF, stroke:#FFFFFF;
style 0 fill:#FFFFFF, stroke:#FFFFFF;
style 1 fill:#FFFFFF, stroke:#FFFFFF;
style 2 fill:#FFFFFF, stroke:#FFFFFF;
…bled and also switched edge style from arrows to lines
| def __init__( | ||
| self, | ||
| *, | ||
| render_node: bool | None = None, | ||
| render_final_state_id: bool = True, | ||
| render_resonance_id: bool = False, | ||
| render_initial_state_id: bool = False, | ||
| strip_spin: bool = False, | ||
| collapse_graphs: bool = False, | ||
| figure_style: dict[str, Any] | None = None, | ||
| edge_style: dict[str, Any] | None = None, | ||
| node_style: dict[str, Any] | None = None, | ||
| ) -> None: | ||
| self.render_node = render_node | ||
| self.render_final_state_id = render_final_state_id | ||
| self.render_resonance_id = render_resonance_id | ||
| self.render_initial_state_id = render_initial_state_id | ||
| self.strip_spin = strip_spin | ||
| self.collapse_graphs = collapse_graphs | ||
| self.figure_style = dict(figure_style) if figure_style else {} | ||
| self.edge_style = dict(edge_style) if edge_style else {} | ||
| self.node_style = dict(node_style) if node_style else {} |
There was a problem hiding this comment.
Better to declare this class with @attrs.define to reduce boilerplate code, just like GraphPrinter, which is declared with @define(on_setattr=_check_booleans).
There was a problem hiding this comment.
Does this definition satisfies this?
Also AI mentioned, that in the definition of the GraphvizPrinter class has the weakness, that it also accepts invalid constructor combinations.
Should this also be fixed? A shared attrs validator was a recommendation.
There was a problem hiding this comment.
Does this definition satisfies this?
Yes, looks good!
Also AI mentioned, that in the definition of the GraphvizPrinter class has the weakness, that it also accepts invalid constructor combinations.
You mean that GraphPrinter does not set kw_only=True? Or because its fields have different validators/converts?
Since it makes sense to rename GraphPrinter to GraphvizPrinter now, I am considering if this is something of a larger issue that should be handled separately. But these modules are private (due to the underscore), so renaming GraphPrinter is technically not a breaking change and fits well with the intentions of rest of this PR (an extension of the io module with a new printer).
So yeah, I think it's a good idea to do that additional cleanup in this PR.
…y into seperate _labels.py module
|
Except for checking the width of the edge labels, the issue might be ready to be closed. 🎊 |
redeboer
left a comment
There was a problem hiding this comment.
Looks good! But let's discuss your comment #347 (comment) about the GraphPrinter class next week. It may be worth fixing that in this PR too.
| def __init__( | ||
| self, | ||
| *, | ||
| render_node: bool | None = None, | ||
| render_final_state_id: bool = True, | ||
| render_resonance_id: bool = False, | ||
| render_initial_state_id: bool = False, | ||
| strip_spin: bool = False, | ||
| collapse_graphs: bool = False, | ||
| figure_style: dict[str, Any] | None = None, | ||
| edge_style: dict[str, Any] | None = None, | ||
| node_style: dict[str, Any] | None = None, | ||
| ) -> None: | ||
| self.render_node = render_node | ||
| self.render_final_state_id = render_final_state_id | ||
| self.render_resonance_id = render_resonance_id | ||
| self.render_initial_state_id = render_initial_state_id | ||
| self.strip_spin = strip_spin | ||
| self.collapse_graphs = collapse_graphs | ||
| self.figure_style = dict(figure_style) if figure_style else {} | ||
| self.edge_style = dict(edge_style) if edge_style else {} | ||
| self.node_style = dict(node_style) if node_style else {} |
There was a problem hiding this comment.
Does this definition satisfies this?
Yes, looks good!
Also AI mentioned, that in the definition of the GraphvizPrinter class has the weakness, that it also accepts invalid constructor combinations.
You mean that GraphPrinter does not set kw_only=True? Or because its fields have different validators/converts?
Since it makes sense to rename GraphPrinter to GraphvizPrinter now, I am considering if this is something of a larger issue that should be handled separately. But these modules are private (due to the underscore), so renaming GraphPrinter is technically not a breaking change and fits well with the intentions of rest of this PR (an extension of the io module with a new printer).
So yeah, I think it's a good idea to do that additional cleanup in this PR.



Closes #159
✨ New features
qrules.io.asmermaid()function that rendersTopology,MutableTransition,ReactionInfo,ProblemSet,QNProblemSet, andQNResultobjects as Mermaid flowchart source. It mirrors the signature ofasdot(), includingstrip_spin,collapse_graphs, therender_*_idflags, andfigure_style/edge_style/node_stylefor styling the diagram, its edges, and its nodes.qrules.io.write()now recognizes the.mmdfile extension and writes Mermaid source, either from a graph-like object or from a string produced byasmermaid().sphinxcontrib-mermaidis enabled indocs/conf.pyanddocs/usage/visualize.ipynbshows Mermaid examples next to the existing Graphviz ones.🐛 Bug fixes
ProblemSetandQNProblemSetlabels were sorted as strings, so9sorted above100and rules without a priority (NA) ended up at the top. Priorities are now parsed as numbers (negative values included) andNAsorts last.collapse_graphs()iterated over its input twice, which silently produced an empty result when given a generator instead of a list. The input is now materialized first.strip_projections()contained a deadfreeze()branch whose result was immediately overwritten.🔨 Maintenance
as_string(),create_edge_label(),collapse_graphs(),get_particle_graphs(), andstrip_projections()— moved out ofqrules.io._dotinto a newqrules.io._labelsmodule, so both backends produce identical labels from a single implementation.Squash commit messages