简体中文 → · 中文文档目录 · Quick start · Algorithm tutorial · Changelog
中文用户请从 README.zh-CN.md 或 快速上手 开始;所有中文入口汇总在 docs/README.zh-CN.md。
Symbolic simplifier for graphs of Wigner 3j / 6j / 9j angular-momentum
symbols. Takes a coupling diagram as input — either a .3js file of 3j
tuples or a .braket file of nested bra-kets — applies bubble-cancellation
and cycle-breaking rewrites, and emits a minimal symbolic expression
together with an interactive step-by-step graph history.
A built-in Wigner symbol calculator evaluates 3j / 6j / 9j numerically to any sympy-precision rational form, with one-click "Copy as Symbolic / LaTeX / Numeric / MathML" for any field.
For an end-to-end walkthrough of the algorithm — what each frame of
the reduction tree means, which Wigner identity each rule represents,
and how to read the GraphPane sidebar — see
docs/algorithm.md. The walkthrough cites
Edmonds 1957, Varshalovich 1988, and Yutsis-Levinson-Vanagas 1962
for every rewrite.
| Platform | Asset | Notes |
|---|---|---|
| macOS arm64 | Wigsym-v2.0.7.dmg |
Open the dmg, drag Wigsym into Applications, eject. First launch may show "unidentified developer" — right-click → Open. |
| Windows x64 | Wigsym-v2.0.7-windows-x64.zip |
Unzip anywhere; double-click Wigsym.exe. The folder is self-contained — no installer. |
Latest releases: https://github.com/yilibinbin/Wigsym/releases.
- Structured input editor — type into
all_3j/powers/factor_sqrtrows directly, or open a.3js/.braketfile and edit in place. The source file is never written until you click Save as…. - Threaded solver — runs in a background thread; the UI stays responsive on heavy 9j and braket cases.
- Interactive reduction tree — Cytoscape-rendered graph with
Prev/Next stepping, Re-layout, and a sidebar where every frame is
labelled with the rewrite rule that produced it (
Bubble cancel,Break graph, etc.). - MathJax-rendered output — input and result both typeset as proper Wigner brackets. Right-click any block for "Copy as".
- Wigner 3j/6j/9j calculator — selectable symbol type, input grid shaped like the actual matrix (3j and 6j → 2×3, 9j → 3×3), exact sympy evaluation.
- Cross-platform dark mode — follows the OS theme by default, Ctrl+Shift+D toggles. MathJax math and Cytoscape graph respect the theme without a white flash.
- Bilingual UI — switch English / 简体中文 at runtime via View → Language; all Qt widgets and HTML shells reload immediately.
Requires Python 3.11 or newer (3.13 tested).
git clone https://github.com/yilibinbin/Wigsym
cd Wigsym
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
python -m wigsympython -m wigsym
# or, after install
wigsymOpen a testcases/*.3js or *.braket from the left pane, click Solve.
The result pane shows:
- the input rendered as a product of 3j matrices (top),
- the simplified expression typeset with MathJax (middle),
- the raw text form (a single-line ASCII expression),
- the step-by-step rewrite log.
The graph pane shows each intermediate graph G0 → G1 → … with zoom
and pan, and a navigable reduction tree in the sidebar where every
frame carries the rule that produced it (e.g. G2 Bubble cancel).
.3js — three labelled lines:
all_3j=(j1,j2,j3)(j1,j4,j5)(j2,j4,j6)(j3,j5,j6)
powers=-m1,-m2,j3,-m3,-m4,-m5,j6,-m6
factor_sqrt=j3,j6
Shorthand: (j1, j2, -j3) expands to (j1, j2, j3, mj1, mj2, -mj3).
Names starting with z or mz are reserved for synthetic intermediate
momenta the solver introduces during graph breaking.
.braket — a single nested bra-ket like
<((j1,j2)j5,(j3,j4)j6)j7|(j1,((j2,j3)j8,j4)j9)j7>.
The format-hint button (?) in the editor pane has these examples
available inline.
pytest # unit + regression suite
pytest wigsym/tests/test_regression.py # strict regression bar + drift xfails
ruff check wigsym/ # lintThe regression harness runs every testcases/*.3js / *.braket through
the solver and compares to the committed .out. Cases that permute 6j
label positions or synthetic-edge (z*) names compare equal via a
Weisfeiler-Lehman-style canonicalizer. A small number of complex
multi-stage decompositions produce algebraically-equivalent outputs
through a different decomposition path; these are xfail-annotated as
known drift — see CORRECTNESS.md for the per-case
status matrix and the four-level verification taxonomy
(structural / legacy parity / numerical equivalence / literature
parity).
# macOS — produces dist/Wigsym.app + a versioned dist/Wigsym-vX.Y.Z.dmg
VERSION=2.0.7
pip install -e ".[build]"
pyinstaller mac.spec --clean
dmgbuild -s dmg.py "Wigsym" "dist/Wigsym-v${VERSION}.dmg"
# Windows (run on Windows) — produces dist/Wigsym/Wigsym.exe
$version = "2.0.7"
pip install -e ".[build]"
pyinstaller win.spec --clean
Compress-Archive -Path dist/Wigsym/* -DestinationPath "dist/Wigsym-v$version-windows-x64.zip"See BUILD.md for the cross-machine workflow used to
package both platforms.
wigsym/
├── core/ # pure Python, no Qt — safe in worker threads
│ ├── state.py # SolverState
│ ├── parser.py # parse_3js_file / parse_braket / parse_text
│ ├── recognize.py # is_6j / is_9j / show_6j / show_9j
│ ├── layout.py # snapshot_graph / recognize_and_emit
│ ├── rewrites.py # cancel_bubbles / break_graph / simplified
│ ├── solver.py # solve() orchestrator
│ ├── canonical.py # SolveMode.CANONICAL: lex-smallest 6j/9j + phase mod 4
│ ├── yutsis.py # SolveMode.YUTSIS: Regge cycling outer loop
│ ├── be_rewriter.py # Biedenharn-Elliott pentagon detector
│ ├── rendering.py # Solution dataclass + render() — text + LaTeX + graph JSON
│ └── equivalence.py # canonicalize() for regression comparison
├── ui/ # PySide6 — never imports core globals
│ ├── app.py # QApplication bootstrap
│ ├── mainwindow.py # MainWindow with threaded solver + Help menu
│ ├── input_pane.py # Left: structured input form + Wigner calculator
│ ├── editor_backend.py
│ ├── calculator_backend.py
│ ├── result_pane.py # Top-right: MathJax via QWebEngineView
│ ├── result_backend.py
│ ├── graph_pane.py # Bottom-right: Cytoscape via QWebEngineView
│ ├── web_pane.py # Shared webview + QWebChannel setup
│ ├── bridge.py # UiBridge — fans JSON payloads to all three shells
│ └── theme.py # Light/dark QSS + system-theme follower
├── assets/
│ ├── editor.qml # QML editor + parsed-fields panel
│ ├── calculator_inputs.qml # QML symbol selector + j/m grid
│ ├── result_shell.html # MathJax shell — solutionReady subscriber
│ ├── calculator_shell.html # MathJax shell — calculatorPayloadChanged subscriber
│ ├── graph_shell.html # Cytoscape shell — graphHistoryReady subscriber
│ ├── shell_common.js # Shared HTML-shell helpers
│ ├── mathjax/ # vendored MathJax 3.2.2
│ ├── cytoscape/ # vendored Cytoscape.js 3.33 + cose-bilkent
│ └── icons/
└── tests/ # unit + regression + canonical
The core never imports Qt; the UI never touches core globals. This
means wigsym.core can be embedded in a Jupyter notebook or driven
from a non-GUI CLI without the PySide6 dependency.
- A subset of complex
.brakettestcases produces a symbolically equivalent output along a different decomposition path. The number and label-set of 6j/9j symbols is preserved; only phase simplifications that rely on(-1)^(2j+1)identities may differ. See thetest_regression_matches_canonicalxfail list. - UI smoke tests are skipped by default on Python 3.13 because PySide6 6.10 + QtTest segfaults at pytest fixture-setup. The app itself runs fine when launched directly.
MIT. Includes original code by Xiang, Shao-Hui (2019)
Mendeley dataset
under BSD-3-Clause; that attribution is preserved in LICENSE.