vecwire 0.3.0__py3-none-any.whl

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,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
@@ -0,0 +1,13 @@
1
+ vecwire/__init__.py,sha256=FGC1YR_TdJFQAOuLvlv9CyCvk8kCB5lOJcRXR9Hyeho,415
2
+ vecwire/core.py,sha256=gHjVBZkAouX8bHs-0IsvjA3oC2t-Wvqp0Et4MIDwKGU,21653
3
+ vecwire/geometry.py,sha256=CzvOYTQc8zUbkv3aKESEkPYFThMXes63SAlQTnUjwDA,4282
4
+ vecwire/node.py,sha256=58pQEMjBYgmVTeyB77CT8nfGMlvRYtb845AkbCDXcaE,533
5
+ vecwire/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ vecwire/elements/__init__.py,sha256=KuwOA1khbuF2hO6v2W4-CQSvBXyiV4251S1JTxF9t7I,830
7
+ vecwire/elements/ground.py,sha256=fOismJRirtf5eU0pba9DQhOPDPbkOtaQPOOfDM2_acM,1261
8
+ vecwire/elements/junctions.py,sha256=8iL9kELAHXi4LNw1AzfxRBrCzSlqporhKxFYzBgtxb0,10795
9
+ vecwire/elements/passive.py,sha256=oEAuK0MmG70WJc82EZUE-aSpF-QiOm3ViaSIW8TJlGg,2052
10
+ vecwire-0.3.0.dist-info/METADATA,sha256=xiFNBl4uXCHUd5RIIjL9oKihGpkfGCPPYw-Owz9RdcE,12178
11
+ vecwire-0.3.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
12
+ vecwire-0.3.0.dist-info/licenses/LICENSE,sha256=OOjR7-6Sm14E-UnnnW_l_pL_BJTQzyoYOrglXbUHYhE,1070
13
+ vecwire-0.3.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -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.