vecwire 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .venv/
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ .vectex-cache/
8
+ dist/
9
+ build/
10
+ examples/output/
@@ -0,0 +1,45 @@
1
+ # VecWire agent guidance
2
+
3
+ VecWire is a focused Python package for readable, deterministic SVG circuit
4
+ schematics, especially circuit-QED publication figures. Keep its public API
5
+ small and grounded in the Fig. 1(a) example.
6
+
7
+ - Connect components through named, positioned `Node` objects. A node is a placed
8
+ point; a net may contain several nodes joined by wires. Via points affect only
9
+ routing and do not establish connectivity.
10
+ - Accept node names as concise references in examples. A wire may pass through
11
+ several connected nodes, and its centerline must pass through each of them.
12
+ - Preserve semantic SVG IDs and `data-from`/`data-to` endpoint metadata.
13
+ - Render each routed wire as one SVG path so line joins and corner radii work.
14
+ - Keep output deterministic and editable; use `to_svg_document()` for FigWorks integration.
15
+ - Treat VecTeX as optional. Plain SVG labels must work without TeX.
16
+ - Add symbols or routing features when a real figure needs them; avoid a circuit
17
+ simulation or netlist abstraction without an explicit requirement.
18
+ - Keep symbols in `vecwire.elements` as plain functions of the circuit, drawn
19
+ only through the public drawing API of `vecwire.core` (`branch`, `group`,
20
+ `line`, `rect`, `dot`, `path`) and `vecwire.geometry`. The library must not
21
+ import the core at run time; built-in symbols are bound as `Circuit` methods
22
+ by one class attribute each. No registries or plugin hooks.
23
+ - Leave number formatting to the core so output stays byte-identical; check
24
+ `examples/fig1a.svg` is unchanged after refactors.
25
+ - Preserve user-authored examples and unrelated changes.
26
+
27
+ ## Development
28
+
29
+ - Supported Python: 3.12 and newer.
30
+ - `uv venv && uv pip install -e '.[dev]'` for a development environment; add
31
+ `.[tex]` (and a TeX installation) to exercise VecTeX labels.
32
+ - Run `ruff format --check .`, `ruff check .`, `ty check`, and `pytest` before
33
+ reporting a change complete. ty checks `src`; type the
34
+ duck-typed inputs with a `Protocol`, not `Any`.
35
+ - Typing serves the code, not the other way round. Type public signatures and
36
+ what types naturally, and keep `ty check` passing; where precise types would
37
+ contort the code -- a decorator that changes what a parameter accepts, say --
38
+ use a looser annotation or a targeted `# ty: ignore[code]`. Do not raise the
39
+ Python floor for typing features.
40
+ - Run `python examples/fig1a_cqed.py` and `python examples/harmonic_junctions.py`
41
+ after touching drawing code, and diff what they write against
42
+ `examples/fig1a.svg` and `examples/harmonic_junctions.svg`. Any byte that
43
+ changes must be one the change intended.
44
+ - Keep `CHANGELOG.md` current in Keep a Changelog 1.1.0 format.
45
+ - Do not commit, tag, upload, or publish unless the user explicitly asks.
@@ -0,0 +1,78 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.3.0] - 2026-10-16
11
+
12
+ ### Added
13
+
14
+ - A publish workflow: a pushed `v<version>` tag is checked against the package
15
+ version, tested on the full CI matrix, built, checked with `twine check
16
+ --strict`, and uploaded to PyPI by trusted publishing. `docs/development.md`
17
+ describes the release steps.
18
+ - Optional `font_family` on plain-text `Circuit.label` calls, so labels can
19
+ use the same typeface as other figure sources.
20
+ - `Circuit.to_svg_document()` credits VecWire: the document opens with Dublin
21
+ Core metadata naming "VecWire vX, https://github.com/maiani/vecwire" as its
22
+ creator, as Matplotlib and Inkscape do. The circuit's own tree is unchanged.
23
+
24
+ ### Changed
25
+
26
+ - Use ty for type checking in development and CI instead of mypy.
27
+
28
+ ### Fixed
29
+
30
+ - `label(..., color=...)` colours VecTeX fragments again. Since VecTeX 0.3 a
31
+ fragment paints in `currentColor`, which follows the CSS `color`, not the
32
+ `fill` the label group set, so every TeX label came out black: the grey
33
+ `C_J` of `fig1a_cqed.py --tex` among them. A fragment label's group now sets
34
+ `color` too, to the label colour or the circuit's stroke; a fragment rendered
35
+ with a colour of its own keeps it. Plain-text labels are unchanged.
36
+
37
+ ## [0.2.0] - 2026-10-05
38
+
39
+ ### Changed
40
+
41
+ - **Renamed from cirquit to VecWire.** The distribution and import name is now
42
+ `vecwire` (was `cirquit`), because `cirquit` on PyPI belongs to an unrelated
43
+ quantum-computing platform; the new name joins the `vec` family of its
44
+ siblings. Output is unchanged.
45
+
46
+ ## [0.1.0] - 2026-10-05
47
+
48
+ ### Added
49
+
50
+ - `Circuit`, an SVG canvas of named, positioned nodes joined by wires, with
51
+ `row()`, `column()` and `nodes()` to declare them, `via` routing, a
52
+ per-circuit or per-wire `corner_radius`, labels from plain text or a VecTeX
53
+ fragment, dashed `box()` enclosures, and `to_svg_document()`, the only
54
+ interface FigWorks needs to place a circuit.
55
+ - The symbol library: `capacitor`, `inductor`, `ground`, the bare-X
56
+ `junction`, the boxed `harmonic_junction` for one `cos(mφ)` or `sin(mφ)`
57
+ term, and `harmonic_sum` for a multiharmonic junction.
58
+ - Byte-identical output: the core owns number formatting (`.12g`) and
59
+ attribute order.
60
+ - Examples reproducing a circuit-QED sketch (`fig1a_cqed.py`) and the junction
61
+ symbol legend (`harmonic_junctions.py`).
62
+ - Type information: the package ships `py.typed` and passes `mypy --strict`.
63
+ Label fragments are typed by the structural `vecwire.core.Fragment`
64
+ protocol (`width`, `height`, `to_svg()`), so VecTeX stays optional.
65
+ - `vecwire.__version__`, kept equal to the distribution version by a test.
66
+ - Package metadata: MIT licence (with a `LICENSE` file), keywords,
67
+ classifiers, and project URLs.
68
+
69
+ ### Changed
70
+
71
+ - The bare-X `junction` draws its leads on to the centre of the X, so the wire
72
+ runs continuously through the symbol instead of stopping at the square's
73
+ edge.
74
+ - Python 3.12 is the minimum supported version, matching VecTeX and VecView.
75
+ - The build backend is hatchling, as in the sibling projects; the wheel ships
76
+ `py.typed`.
77
+ - Ruff checks the `B`, `E`, `F`, `I`, `N`, `RUF`, `S` and `UP` rule sets, and
78
+ pytest runs with `--strict-config --strict-markers`.
vecwire-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andrea Maiani
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
vecwire-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,281 @@
1
+ Metadata-Version: 2.5
2
+ Name: vecwire
3
+ Version: 0.3.0
4
+ Summary: Readable, editable SVG schematics for electrical and circuit-QED circuits
5
+ Project-URL: Source, https://github.com/maiani/vecwire
6
+ Project-URL: Issues, https://github.com/maiani/vecwire/issues
7
+ Author-email: Andrea Maiani <dev@andreamaiani.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: circuit,circuit-qed,figures,schematic,svg,vector-graphics
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Scientific/Engineering :: Physics
20
+ Classifier: Topic :: Scientific/Engineering :: Visualization
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.12
23
+ Requires-Dist: lxml>=4.9
24
+ Provides-Extra: dev
25
+ Requires-Dist: lxml-stubs>=0.5; extra == 'dev'
26
+ Requires-Dist: pytest>=8; extra == 'dev'
27
+ Requires-Dist: ruff>=0.12; extra == 'dev'
28
+ Requires-Dist: ty>=0.0.84; extra == 'dev'
29
+ Provides-Extra: tex
30
+ Requires-Dist: vectex>=0.1; extra == 'tex'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # VecWire
34
+
35
+ VecWire draws electrical circuit schematics as SVG, with circuit-QED figures as
36
+ the first use case. The schematic is generated from code and stays editable
37
+ afterwards: each component becomes a named SVG group, the same script writes
38
+ byte-identical SVG, and the output opens in Inkscape.
39
+
40
+ <p align="center">
41
+ <img src="https://raw.githubusercontent.com/maiani/vecwire/v0.3.0/docs/images/readme.svg" alt="A circuit-QED schematic: capacitor, inductor and a Josephson junction with its capacitance, labels typeset by TeX" width="340">
42
+ </p>
43
+
44
+ <p align="center"><sub>A circuit-QED schematic with labels typeset by VecTeX, from <a href="https://github.com/maiani/vecwire/blob/v0.3.0/examples/fig1a_cqed.py"><code>examples/fig1a_cqed.py</code></a> with <code>--tex</code>.</sub></p>
45
+
46
+ The symbol set covers wires, capacitors, inductors, Josephson junctions (the
47
+ bare X, boxed harmonic junctions, and multiharmonic junctions), ground symbols,
48
+ connection nodes, dashed enclosures, and labels. Components connect named,
49
+ positioned nodes and draw their own leads.
50
+
51
+ VecWire is alpha: an early version, with an API that will still change.
52
+
53
+ ## Install
54
+
55
+ VecWire needs Python 3.12 or newer:
56
+
57
+ ```bash
58
+ python -m pip install vecwire
59
+ ```
60
+
61
+ The only runtime dependency is lxml. For labels typeset by TeX, install the
62
+ optional VecTeX extra; VecTeX also needs `pdflatex` and `dvisvgm` on `PATH`:
63
+
64
+ ```bash
65
+ python -m pip install "vecwire[tex]"
66
+ ```
67
+
68
+ ## Quick start
69
+
70
+ A capacitively shunted Josephson junction, grounded at the bottom:
71
+
72
+ ```python
73
+ from vecwire import Circuit
74
+
75
+ circuit = Circuit(70, 96)
76
+ circuit.row(14, top_left=18, phi=35, top_right=52, dots={"phi"})
77
+ circuit.row(78, bottom_left=18, gnd=35, bottom_right=52)
78
+
79
+ circuit.wire("top_left", "phi", "top_right")
80
+ circuit.wire("bottom_left", "gnd", "bottom_right")
81
+ circuit.capacitor("top_left", "bottom_left", id="C")
82
+ circuit.junction("top_right", "bottom_right", id="JJ")
83
+ circuit.ground("gnd", id="ground")
84
+
85
+ circuit.label("C", (11, 46), anchor="rc")
86
+ circuit.label("φ", "phi", offset=(0, -3), anchor="cb")
87
+ circuit.write_svg("transmon.svg")
88
+ ```
89
+
90
+ Coordinates are SVG user units, points by default (`Circuit(..., unit="pt")`;
91
+ `"px"` and `"mm"` are also accepted). `to_svg_document()` returns the same
92
+ document as a string.
93
+
94
+ ## Examples
95
+
96
+ ```bash
97
+ python examples/fig1a_cqed.py # writes examples/output/fig1a.svg
98
+ python examples/fig1a_cqed.py --tex # editable TeX labels through Vectex
99
+ python examples/harmonic_junctions.py # the junction symbol legend
100
+ ```
101
+
102
+ `fig1a_cqed.py` draws a shunt capacitance `C` across an inductor `L` in series
103
+ with a parallel junction branch of `V_J` and `C_J`, marking the node phases `φ`
104
+ and `δ`. `harmonic_junctions.py` draws the bare X beside the boxed
105
+ `m = 1, 2, 3` and `sin` symbols, and an inductor in series with a sinusoidal
106
+ junction next to the multiharmonic junction it acts like. Both previews are
107
+ checked in: [fig1a.svg](https://github.com/maiani/vecwire/blob/v0.3.0/examples/fig1a.svg) and
108
+ [harmonic_junctions.svg](https://github.com/maiani/vecwire/blob/v0.3.0/examples/harmonic_junctions.svg). Without `--tex`
109
+ the examples use plain SVG text and need no TeX installation.
110
+
111
+ ## Nodes and wires
112
+
113
+ A `Node` is a placed connection point, and wires join nodes into an electrical
114
+ net. `row()` and `column()` declare several nodes sharing one coordinate;
115
+ `nodes()` takes arbitrary positions. None of them draws anything. Methods
116
+ accept node names or the `Node` values these calls return.
117
+
118
+ A wire visits its nodes in the order given and may pass through several of
119
+ them; a route that doubles back is rejected. Crossing or overlapping in the
120
+ drawing does not make a connection. Optional `via` points shape a route without
121
+ adding connections:
122
+
123
+ ```python
124
+ circuit.wire("left", "right", via=((18, 30), (52, 30)), corner_radius=2)
125
+ ```
126
+
127
+ Each routed wire is one SVG path, so `linejoin="round"` softens a bend and
128
+ `corner_radius` curves the centerline; both can be set on the `Circuit` or per
129
+ wire. A route still passes exactly through every named node it visits, even
130
+ with a corner radius. Two-terminal symbols are placed horizontally or
131
+ vertically.
132
+
133
+ Labels take plain text or a VecTeX fragment; a fragment's SVG group is embedded
134
+ as is, so it stays editable through TexText.
135
+
136
+ The `Circuit` records drawing geometry only. It does not infer electrical
137
+ topology or compute circuit parameters; those belong to the scientific model
138
+ that produces the figure.
139
+
140
+ Plain-text labels accept `font_family` and `size` (in the circuit's user units):
141
+
142
+ ```python
143
+ circuit.label("Capacitance", (40, 20), size=8, font_family="TeX Gyre Heros")
144
+ ```
145
+
146
+ Omitting `font_family` preserves inherited SVG typography. For fragment labels,
147
+ choose typography when rendering the fragment; these text options do not
148
+ rescale or replace its glyphs.
149
+
150
+ ## Josephson junctions
151
+
152
+ `junction()` draws the bare X, the standard Josephson-junction symbol, which
153
+ leaves the current-phase relation unspecified; its leads meet at the centre of
154
+ the X, so the wire runs through it. `harmonic_junction()` draws the boxed
155
+ symbol of one tunnelling harmonic: its square holds `order` crossed diagonals
156
+ each way for a `cos(mφ)` term, in which `m` Cooper pairs tunnel together, and
157
+ `kind="sin"` adds two dots for the odd `sin(mφ)` term of a φ0 junction. The
158
+ `m = 1` box is the bare X inside its square, so the two symbols line up. In
159
+ both, `color` sets the glyph and `lead_color` keeps the leads in the circuit
160
+ ink.
161
+
162
+ ```python
163
+ circuit.junction("top", "bottom", id="JJ") # bare X
164
+ circuit.harmonic_junction("top", "bottom", 2, color="#8075c1", lead_color="#262626")
165
+ circuit.harmonic_junction("top", "bottom", kind="sin") # sin φ, m = 1
166
+ ```
167
+
168
+ Both groups carry `data-component="josephson-junction"`; the boxed one also
169
+ records `data-harmonic` and `data-kind`, so a stylesheet or selector can tell
170
+ them apart.
171
+
172
+ `harmonic_sum()` draws a multiharmonic junction: a dashed box holding
173
+ `⊠ + ⊠⊠ + ⊠⊠⊠ + ⋯` between two terminals. The harmonics of one junction add as
174
+ energies, so the terms hang in parallel between two rails, which the branch
175
+ leads join at the centre of the box. `rails=False` ends each term in terminal
176
+ dots instead, a shorthand notation. The sum reads left to right on a vertical
177
+ branch and top to bottom on a horizontal one. `colors` and `kinds` set each
178
+ term, and `ellipsis=False` drops the trailing `+ ⋯`.
179
+
180
+ `box()` draws a dashed enclosure around a subcircuit that acts as one element;
181
+ it is decoration only and creates no connection. Both return a `Box` with the
182
+ bounds: `Box.at("rb")` uses the label anchors, and `Box.terms` holds the
183
+ centres of the summed glyphs for per-term labels:
184
+
185
+ ```python
186
+ box = circuit.harmonic_sum("top", "bottom", (1, 2, 3), colors=shades)
187
+ for order, centre in zip((1, 2, 3), box.terms):
188
+ circuit.label(f"E{order}", (centre[0], box.bottom + 2), anchor="ct")
189
+ circuit.box((10, 18), (60, 95), id="effective-junction")
190
+ ```
191
+
192
+ ## Architecture
193
+
194
+ | Module | Contents |
195
+ | --- | --- |
196
+ | `vecwire.core` | `Circuit`: the SVG root and unique ids, nodes, wire routing, labels, `box()`, `to_svg_document()`, and the drawing API symbols are built from |
197
+ | `vecwire.elements` | the symbol library: `capacitor`, `inductor`, `junction`, `harmonic_junction`, `harmonic_glyph`, `harmonic_sum`, `ground` |
198
+ | `vecwire.geometry` | value types and checks shared by both: `Point`, `Box`, `Branch`, `positive`, `offset`, … |
199
+ | `vecwire.node` | `Node`, a named, placed connection point |
200
+
201
+ A symbol is a plain function whose first argument is the circuit, and it draws
202
+ only through the public drawing API: `circuit.branch(start, end)` resolves the
203
+ terminals into a `Branch`, `circuit.group(kind, id, branch=...)` opens a
204
+ semantic group with `data-from`/`data-to`, and `line`, `rect`, `dot`, and
205
+ `path` add strokes in the circuit ink. Symbols are written in branch
206
+ coordinates (`branch.at(along, across)`), so one definition serves every
207
+ orientation. A new symbol is a new function, in `vecwire.elements` or your own
208
+ script, with no change to the core:
209
+
210
+ ```python
211
+ def resistor(circuit, start, end, *, id=None):
212
+ branch = circuit.branch(start, end)
213
+ group = circuit.group("resistor", id, branch=branch)
214
+ lead, step = branch.length / 4, branch.length / 12
215
+ points = [branch.at(lead + (i + 0.5) * step, 3 * (-1) ** i) for i in range(6)]
216
+ circuit.path(
217
+ group,
218
+ [
219
+ ("M", *branch.start.position),
220
+ ("L", *branch.at(lead)),
221
+ *(("L", *p) for p in points),
222
+ ("L", *branch.at(3 * lead)),
223
+ ("L", *branch.end.position),
224
+ ],
225
+ )
226
+
227
+
228
+ resistor(circuit, "top", "bottom", id="R")
229
+ ```
230
+
231
+ The built-in symbols are also `Circuit` methods, bound by one class attribute
232
+ each (`capacitor = elements.capacitor`), so `circuit.capacitor(a, b)` is the
233
+ same call as `elements.capacitor(circuit, a, b)`, with the same signature and
234
+ docstring. There is no registry or plugin hook. The core imports the library
235
+ for these bindings, and the library never imports the core at run time
236
+ (`tests/test_elements.py` checks this), so the dependency points one way.
237
+
238
+ The core owns number formatting (`.12g`) and attribute order, so every symbol
239
+ serializes coordinates the same way and output is byte-identical from run to
240
+ run.
241
+
242
+ **Why not svg.py.** [svg.py](https://github.com/orsinium-labs/svg) was
243
+ evaluated for the symbols and is not used. It writes floats with `repr`
244
+ (`0.30000000000000004`, `1e-07`) and fixes its own attribute order, so adopting
245
+ it would change every byte of existing figures, or mean passing preformatted
246
+ strings and losing its typed fields. A symbol's size depends on the circuit's
247
+ ink, stroke width, and line caps, which svg.py has no notion of, so element
248
+ code would get longer, not shorter. The canvas needs lxml anyway, to embed
249
+ VecTeX fragments and reorder connection dots. (Its `svg.A` is also the `<a>`
250
+ element, not an arc.)
251
+
252
+ ## Development
253
+
254
+ ```bash
255
+ python -m pip install -e '.[dev]'
256
+ ruff format --check .
257
+ ruff check .
258
+ ty check
259
+ pytest
260
+ ```
261
+
262
+ After touching drawing code, rerun both examples and diff their output against
263
+ `examples/fig1a.svg` and `examples/harmonic_junctions.svg`; every changed byte
264
+ should be one the change intended. Releases are described in
265
+ [docs/development.md](https://github.com/maiani/vecwire/blob/main/docs/development.md).
266
+
267
+ ## Related projects
268
+
269
+ VecWire is developed alongside [FigWorks](https://github.com/maiani/figworks),
270
+ which composes multi-panel figures, and two other producers of editable SVG:
271
+ [VecTeX](https://github.com/maiani/vectex) (TeX equations) and
272
+ [VecView](https://github.com/maiani/vecview) (layered 3D schematics). All four
273
+ share one premise: figures generated from code, with stable ids and
274
+ byte-identical output, that stay editable in Inkscape.
275
+
276
+ VecWire does not depend on FigWorks. FigWorks places a `Circuit` in a panel
277
+ through `to_svg_document()` alone; see its README.
278
+
279
+ ## License
280
+
281
+ MIT