Skip to content

Add pretty printing for statevectors and density matrices#523

Closed
simsaidan wants to merge 6 commits into
TeamGraphix:masterfrom
simsaidan:aidans/prettyprint
Closed

Add pretty printing for statevectors and density matrices#523
simsaidan wants to merge 6 commits into
TeamGraphix:masterfrom
simsaidan:aidans/prettyprint

Conversation

@simsaidan

Copy link
Copy Markdown

First, implements function that pretty prints a complex number. Then, adds to statevecotr and density matrix. The density matrix is printed in matrix form.

Resolves #501 as part of UnitaryHack.

Before submitting, please check the following:

  • Make sure you have tests for the new code and that test passes (run nox)
  • If applicable, add a line to the [unreleased] part of CHANGELOG.md, following keep-a-changelog.
  • Format added code by ruff
    • See CONTRIBUTING.md for more details
  • Make sure the checks (github actions) pass.

Then, please fill in below:

Context (if applicable):

Description of the change:

Related issue:

First, implements function that pretty prints a complex number. Then,
adds to statevecotr and density matrix. The density matrix is printed in matrix form.
@codecov

codecov Bot commented Jun 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.46939% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.17%. Comparing base (fcdb8f4) to head (7df4907).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
graphix/pretty_print.py 98.42% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #523      +/-   ##
==========================================
+ Coverage   88.85%   89.17%   +0.31%     
==========================================
  Files          49       49              
  Lines        7135     7325     +190     
==========================================
+ Hits         6340     6532     +192     
+ Misses        795      793       -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@thierry-martinez thierry-martinez left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thank you very much for your contribution!

Codecov has detected that some parts of your code are not tested.

As you may have noticed, PR #503 and PR #524 address the same issue (#501). We invite you to cross‑review each other's work: PR reviews are an inherent part of software development, just as important as writing code. Moreover, this review process will help you position your PR relative to the others, allowing us to determine which contribution best addresses the issue.

Comment thread graphix/pretty_print.py Outdated
def _exp_i_to_str(angle_str: str, output: OutputFormat) -> str:
match output:
case OutputFormat.LaTeX:
return rf"e^{{i{angle_str}}}"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The issue description indicates that e and i should be typeset with \mathrm.

Comment thread tests/test_pretty_print.py Outdated
(-1, OutputFormat.Unicode, "-1"),
(1j, OutputFormat.Unicode, "i"),
(-1j, OutputFormat.Unicode, "-i"),
(0.25 + 0.25j, OutputFormat.ASCII, "1/4 + 1/4 i"),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Shouldn't we write: « sqrt(2)/4e^(ipi/4)} »?
It would look better without a space between the coefficient and the imaginary unit.

@simsaidan

Copy link
Copy Markdown
Author

Hey @thierry-martinez ! Thank you for the feedback. I added in \mathrm for e and i. For imaginary numbers, I now format them as 2i or .43i (without the space for integer or decimal factors), or 3i/4 for fractional. Would that work?

I also added tests to improve coverage.

@Vinny010

Vinny010 commented Jun 5, 2026

Copy link
Copy Markdown

Hi @simsaidan — looking through this per @thierry-martinez's nudge. Some observations:

Nice to see the docs additions in visualization.rst and the _ENCODING = Literal["LSB", "MSB"] alias — both worth adopting regardless of which PR lands.

Two technical notes:

  • Performance of _real_scalar_to_str: it does a nested loop over (1..max_radicand) × (1..max_denominator) (with the defaults, ~10k iterations per call, and per amplitude). A square-then-rationalize approach — approximate as Fraction(x*x).limit_denominator(max_denominator) and decompose the result into a squarefree inner and a rationalized outer — is O(1) in the matching and avoids the brute-force scan.
  • _validate_output_format uses assert; under python -O asserts are stripped, so input validation should ideally use an explicit if/raise.

@simsaidan

Copy link
Copy Markdown
Author

@Vinny010 Thanks for the feedback! I switched the assert to a raise, since you are right about that. I am leaving the for loop as is for now since I don't think it's a huge bottleneck; @thierry-martinez let me know if you disagree.

@thierry-martinez

Copy link
Copy Markdown
Collaborator

Thank you, @simsaidan, for your contribution.

At this stage, #524 is closer to being merged than yours: the logic of complex_to_str is clearer, and the _recognize_sqrt approach is more general than _real_scalar_to_str. I agree that this is not a bottleneck, but your implementation imposes a hard limit (_MAX_RADICAND), whereas _recognize_sqrt works for any value.

I am closing your PR to prevent you from spending time on an improvement that has little chance of being merged. You are welcome to open other Unitary Hack PRs for different “bouties” if you wish.

For the record, here are a few test cases that would still need fixing. I recommend focusing your efforts on other PRs instead:

>>> complex_to_str(cmath.exp(-1j * math.pi / 3), OutputFormat.Unicode)
'e^(i-π/3)'          # should be 'e^(-iπ/3)'

>>> bell = Statevec([2**-0.5, 0, 0, 2**-0.5])
>>> bell.draw(OutputFormat.ASCII)
'√2/2|00⟩ + √2/2|11⟩'   # unexpected Unicode characters in ASCII output

>>> complex_to_str(math.sqrt(12), OutputFormat.Unicode)
'3.4641'                # limited by _MAX_RADICAND

>>> binomial = Statevec(np.array([0.5 + 0.25j,
...                               (1 - abs(0.5 + 0.25j)**2)**0.5]))
>>> binomial.draw(OutputFormat.Unicode)
'(1/2 + i/4)|0⟩ + 0.829156|1⟩'   # should be '√11/4'

Feel free to let me know if you have any questions or need further clarification.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pretty-print for Statevec and DensityMatrix

4 participants