Add new Julia tutorial (continuing work from #5254) - #5557
Conversation
…5254) ### Summary Julia is a dynamic language designed for high-performance numerical and scientific computing, making it a natural fit for quantum simulation workflows. The tutorial shows how Julia is used for both classical pre- and post-processing (e.g., building Hamiltonians, running ODE solvers, computing expectation values) and for orchestrating quantum hardware jobs, eliminating the need to switch between languages or environments. The example used in this tutorial is simulating time evolution of the transverse-field Ising model. To interface with IBM Quantum hardware from Julia, this tutorial uses two packages from the Qiskit ecosystem: `Qiskit.jl` wraps the Qiskit C API and provides circuit construction and transpilation functionality in Julia; `QiskitIBMRuntime.jl` connects to IBM Quantum hardware through the Qiskit IBM Runtime service, enabling job submission and result retrieval directly from Julia. ### Files The PR adds the following files: - the notebook `docs/tutorials/time-evolution/time-evolution.ipynb` - the files `docs/tutorials/time-evolution/Project.toml` and `docs/tutorials/time-evolution/Manifest.toml`, which are needed to set up the Julia environment and install the dependencies to run the notebook. --------- Co-authored-by: ABBY CROSS <across@us.ibm.com> Co-authored-by: Jim Garrison <garrison@ibm.com>
|
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
|
One or more of the following people are relevant to this code:
|
Henry Zou (henryzou50)
left a comment
There was a problem hiding this comment.
Thanks for the updates haimeng-zhang, overall the tutorial looks great! I do have some minor suggestions:
-
Background text still has the old factor-of-2: the Background cell says each Trotter step applies
$R_x(h_i\Delta\tau/2)$ , but since$R_x(\theta) = e^{-i\theta X/2}$ , the half-step needs$\theta = h_i\Delta\tau$ , which is what the corrected code now does. Both mentions should read$R_x(h_i\Delta\tau)$ . (The$R_{ZZ}(2J_{ij}\Delta\tau)$ part is correct.) -
Two saved outputs are stale: the
printlnstrings in two cells were edited after the notebook was last run, so the saved outputs still show the old text. To fix, edit the saved output text to match the current code (no re-run needed, since only the print strings changed):- In the tensor-network fidelity cell (the loop printing
"fidelity at Trotter step $(r) was $(fidelity)"), the saved output still readsfidelity at trotter step ..., capitalize "trotter" -> "Trotter" in the output lines. - In the N=60 tensor-network reference cell, the code says
println("Tensor-network reference:")but the saved output still readsConverged tensor-network reference:- change the output line toTensor-network reference:.
- In the tensor-network fidelity cell (the loop printing
-
Small copyedits:
- Comma splice in Requirements: "developed with Julia 1.11, one can install…" -> "…Julia 1.11; install it with
juliaup add 1.11" - Doubled
# #comment marker in thesave_countscell - "Github" -> "GitHub" in Next steps
- Comma splice in Requirements: "developed with Julia 1.11, one can install…" -> "…Julia 1.11; install it with
abbycross one thing I noticed on the infra side: the two plot images don't show up in the PR preview, the "Output:" blocks for both plot cells render empty. The extracted SVGs are deployed correctly; the problem is in the notebook itself. IJulia/Plots.jl saves each plot output with two representations: a text/html entry (an <img> with a ~50 KB base64 data-URI PNG) alongside the text/plain entry that ./fix rewrites to the <Image src="...svg" /> tag. The site renderer prefers text/html when present, and the data-URI <img> gets sanitized away, so nothing renders. I tried tox -e fix as usual, but it doesn't help as it extracts the SVG and rewrites text/plain but leaves the stale text/html entry in place (Python notebooks never hit this since matplotlib emits image/png/image/svg+xml directly). I think a fix for this PR is to delete the text/html key from the two plot outputs (also shrinks the notebook by ~120 KB). Longer term, we could have the notebook normalizer strip text/html from outputs it extracts images from, since any future Julia notebook will hit the same issue.
|
Ah, we need to look more closely at how to handle the toml files, and the additional directory added here. We might need to rethink how to support that content. Kaelyn Ferris (@kaelynj) thoughts? |
| "* `JSON.jl`\n", | ||
| "* `Plots.jl`\n", | ||
| "\n", | ||
| "Note that `Qiskit.jl` is pinned to the 0.4 series. Version 0.5 tracks a newer release of the underlying Qiskit C library whose circuit-parameter representation `QiskitIBMRuntime.jl` does not yet read correctly, which causes gate rotation angles to be lost when a job is submitted. Because 0.4 predates the `Qiskit.Operations` submodule, this notebook builds circuits with the property-style API (`qc.rx(θ, i)`) rather than the `!`-suffixed functions (`rx!(qc, θ, i)`)." |
There was a problem hiding this comment.
I tried to understand this statement, but I was unable to reproduce a problem.
While investigating this, I discovered that the rzz hardware gate is currently not available to users of the C API. This results in each rzz gate being transpiled to two 2-qubit gates rather than one. I opened an upstream issue: Qiskit/qiskit-ibm-runtime-c#29
There was a problem hiding this comment.
More context, from Claude:
Following up on my earlier comment with what I found, since I think two things in the Requirements section should change before this merges.
I dug into the claim that 0.5 loses rotation angles and could not find a mechanism for it:
- QPY serialization happens inside
libqiskit_ibm_runtime, and it packs every instruction parameter unconditionally as an 8-byte little-endian float (generate_qpy.rs#L103-L110). There is no version-dependent path there, and nothing Julia-specific. - With unpinned packages the environment resolves to a single
libqiskitshared by bothQiskit.jlandQiskitIBMRuntime.jl(onelibqiskit.soinLibdl.dllist()), so there is no ABI skew between two copies. - Comparing the
v0.4.0tag withv0.5.1, the parameter path is unchanged: 0.5 moved the inline logic from the gate closure into_apply_gatewith identical semantics (collect(Float64, ...)params, thenInt32qubits, intoqk_circuit_gate).
So I would suggest dropping the pin, both in the prose and in PackageSpec(name="Qiskit", version="0.4"). As written it will strand readers on the 0.4 series as the ecosystem moves forward.
Following that, unless there is something I am missing, the note recommending the property-style API over the !-suffixed functions does not hold. Both call styles route through the same _apply_gate, and I verified they produce byte-identical instructions:
--- property-style qc.rx ---
rx qubits=[1] params=[0.7853981633974483]
rzz qubits=[1, 2] params=[1.2345]
--- bang-style rx! ---
rx qubits=[1] params=[0.7853981633974483]
rzz qubits=[1, 2] params=[1.2345]
Since Qiskit.Operations is the idiomatic Julia form, I would rather this tutorial not steer readers away from it.
If the original symptom was something real that I have not reproduced, I would like to know what it was — I only established that the stated cause has no supporting mechanism, not that nothing went wrong.
Separately, on the rzz issue I linked (Qiskit/qiskit-ibm-runtime-c#29), the cost is larger than I first said. Transpiling this notebook's Trotter circuit (N=20) against two targets differing only in whether rzz is present gives exactly 2x the two-qubit gates at every depth:
| Trotter steps | rzz in target |
rzz dropped |
|---|---|---|
| 1 | 19 | 38 |
| 2 | 38 | 76 |
| 5 | 95 | 190 |
| 10 | 190 | 380 |
At 10 steps: rzz->190 becomes cz->380, with rx->565 and rz->410.
Since ibm_boston supports rzz natively, the two-qubit depths reported here are about twice what the device could actually run — no fault of the notebook, just the C API limitation. The Trotter-error/noise trade-off you demonstrate is unaffected, and it might be nice to mention in passing that the depths will come down once fractional gates are available from C, so readers who revisit this later aren't puzzled by the difference.
None of this was discoverable from the notebook, incidentally: the only signal for the dropped gate is a log_warn gated behind QISKIT_IBM_RUNTIME_LOG_LEVEL.
This comment was generated by Claude Opus 5 under my guidance.
|
abbycross I looked into the TOML setup. I think we can keep Other tutorials already download supporting files directly from GitHub:
haimeng-zhang I’d also suggest consolidating the two setup options into one executable path. Currently, “Run All” executes both, and the second assumes the TOML files have already been downloaded beside the notebook. Downloading and activating the environment in one setup cell would let readers start with just the notebook. We should resolve Jim’s version-pin feedback before finalizing the environment files. The nested notebook page already renders in the preview, so flattening its location would be for consistency rather than fixing a rendering issue. abbycross Kaelyn Ferris (@kaelynj), do we have a preferred repository location for these companion environment files? |
No description provided.