Changes
2.0.0 (2026-10-14)
This version keeps every function and argument of version 1.0, though some defaults changed (listed under New functions and options). Alignments run much faster, several bugs are fixed, and LDDMM and LDDMM_3D_to_slice choose more of their settings from the data (the new defaults below), so results differ from earlier ones. legacy_defaults=True turns the new defaults off.
Faster
LDDMMandLDDMM_3D_to_slicehave a new optional C++ engine, compiled bypipwhen a C++17 compiler is available. Without one, they use a new puretorchimplementation. Both compute the same iterations up to rounding. Time of the tutorials’ alignments (LDDMMorLDDMM_3D_to_slice) with their own data, parameters and numbers of iterations, on a Mac / on Linux (end to end adds rasterizing the cells and transforming them):tutorial
iterations
version 1.0
C++ engine
torchengineC++ engine,
legacy_defaults=TrueMERFISH to MERFISH
10,000
9.2 min / 12.1 min
10 s / 45 s
1.5 min / 1.7 min
7.9 s / 42 s
MERFISH, simulated deformation
1,000
2.3 min / 3.8 min
4.2 s / 14 s
19 s / 31 s
2.1 s / 11 s
Xenium to Xenium
300
10 s / 15 s
0.18 s / 0.65 s
1.8 s / 2.3 s
0.17 s / 0.64 s
Xenium to H&E image
2,000
1.3 min / 1.9 min
1.5 s / 6.5 s
14 s / 24 s
1.4 s / 6.3 s
Heart sections
1,000
2.9 min / 3.8 min
7.2 s / 31 s
29 s / 1.1 min
3.6 s / 26 s
MERFISH to Visium, with landmarks
200
4.2 min / 12.5 min
4.4 s / 30 s
35 s / 2.0 min
4.3 s / 29 s
MERFISH to Allen atlas (3D)
2,000
21.6 min / 48.4 min
34 s / 3.0 min
1.8 min / 8.1 min
28 s / 2.8 min
STARmap to Allen atlas (3D)
800
16.3 min / 32.8 min
30 s / 2.8 min
2.0 min / 7.6 min
24 s / 2.7 min
Visium to Visium, affine only
1,000
16 s / 30 s
0.92 s / 1.1 s
1.3 s / 1.9 s
0.06 s / 0.03 s
Xenium to STARmap
4,000
2.4 min / 3.3 min
6.2 s / 14 s
25 s / 31 s
2.7 s / 9.3 s
all 10 tutorials
60.6 min / 119.3 min
1.6 min / 8.2 min
7.5 min / 22.0 min
1.2 min / 7.5 min
all 10, end to end
61.1 min / 120.2 min
1.7 min / 8.2 min
7.5 min / 22.1 min
1.3 min / 7.6 min
Measured on an Apple M1 Ultra (macOS, 16 threads) and an AMD Ryzen 5 5560U (Linux, 8 threads,
nice -n 19), one alignment at a time, on the CPU in float64 without figures, with replicas of the tutorials (this version: best of 2 runs); version 1.0 ran with 2 threads or with 16 (Linux: 8), whichever was faster, for 300 iterations (100 for MERFISH to Visium and the 3D tutorials, all of them for Xenium to Xenium and Visium to Visium), extrapolated to the tutorial’s number from its steady time per iteration, which includes redrawing its figures every 10 iterations as it always does (three full-length runs were within 2.5% of the extrapolation). In total, the C++ engine runs the ten fits 37 times faster than version 1.0 on the Mac and 15 times faster on Linux (17–58 and 7–27 times per tutorial), and thetorchengine 8 and 5 times faster. The tutorials pass their own numbers of iterations, so these times do not include the shorter default of 2000 iterations in 2D (below). The other new defaults add up to 6 s per fit on the Mac and 12 s on Linux, mostly for the affine pre-fit; without them (legacy_defaults=True) the C++ engine is 49 and 16 times faster in total. On the Linux machine the C++ engine gains less: with one thread it is 1.6–2.1 times slower than on the Mac, and 8 threads make it 3.4–4.2 times faster than one thread, where 16 threads on the Mac make it 11–13 times faster (MERFISH to MERFISH and MERFISH to Allen atlas,legacy_defaults=True).LDDMMandLDDMM_3D_to_slicedraw their figures once, after the last iteration, instead of every 10 iterations (draw=Truerestores the old behavior).rasterizeis about 60 times faster on the Mac and 45 times on Linux (85,958 cells: 1.8 s to 31 ms and 3.3 s to 75 ms; 2 million cells: 45 s to 0.72 s and 78 s to 1.6 s), andrasterize_with_signalas much with one signal column and about 20 times with 16 columns. Both give the same images up to rounding (1e-12 relative). Peak memory is bounded.The transform functions split large point sets into chunks and use several threads. Transforming all cells of a tutorial takes at most 0.07 s on the Mac and 0.3 s on Linux;
analyze3Daligntakes 0.07 s instead of 1.9 s in the STARmap tutorial (Linux: 0.27 s instead of 5.4 s). In 2D the exact inverse (below) makes source-to-target transforms slower than the previous approximation, though still fast: 0.025 s instead of 0.005 s for the cells of the MERFISH tutorial (Mac).
More accurate default alignments
LDDMM and LDDMM_3D_to_slice now start with a coarse affine pre-alignment, and choose the regularization weight sigmaR and (in 2D) the step sizes from the images. The accuracy comparisons in this section are with legacy_defaults=True (below): this version, with the corrections under Changes that affect results, but with the previous defaults, which are those of version 1.0 except sigmaR=2e6 in 3D. On the held-out problems of the evaluation suite (synthetic sections with a known deformation, never used for tuning, all run on the same machine type and each paired with its fit under legacy_defaults=True), the geometric mean error of the alignment fell from 27.5 to 11.9 µm in 2D (ratio 0.43, ±2 SE 0.27–0.70; better on 21 of 27 problems, worse on 2; median ratio 0.87) and from 123.9 to 25.8 µm in 3D (ratio 0.21, 0.08–0.51; better on 4 of 4). On 15 further held-out problems with other combinations of deformation size, realism and pixel size, the ratios were 0.37 in 2D (0.18–0.76; 10 better, 2 worse of 13) and 0.28 in 3D (2 problems; not significant). On the suite’s main set, whose problems were also used to choose the new settings, they were 0.36 in 2D (0.21–0.63) and 0.13 in 3D (0.08–0.21). The 2D fits took 0.43–0.52 times as long as with legacy_defaults=True (about half), mostly because of the new default of 2000 iterations. The large gains come from alignments that failed with the old defaults: rotated sections, slices tilted by a few degrees, and the synthetic Xenium sections, on which the old step sizes were unstable. Version 2.0 also matches landmarks under the fitted map, and adds align_cells for two sets of cells and a mixture model for targets with missing tissue (an option of LDDMM, on by default in align_cells).
Affine pre-fit (
prefit='auto'). LDDMM’s gradient descent does not correct a large initial misalignment: it failed on test sections rotated by 20–30° and never corrected atlas planes tilted by a few degrees. Now, before LDDMM, many starts are fitted as affine maps on coarse (about 80 pixels across) images, and the best one is refined and handed to LDDMM. In 2D the starts are the initial affine rotated in 12 steps of 30° about the target’s tissue centroid; in 3D the initial plane tilted by -6, 0 and 6° about both in-plane axes and shifted by -300, 0 and 300 µm along its normal. Ties (energies within 1%) go to the start closest to the initial affine, so nearly symmetric tissue keeps its orientation. In 2D the pre-fit is skipped when landmarks are given, and in both from a nonzero initial velocityv(a warm start);prefit=TrueorFalseforces it. A target without tissue (a constant image) skips it with a warning. It takes about 1–4 s per 2D tutorial and 5 s per 3D tutorial on the Mac (4 or 16 threads), and 1–4 s and 8.5–10 s on Linux (8 threads), is deterministic, and is reported in the result’s'prefit'entry.sigmaR from a displacement scale (
sigmaR=None, newsigma_disp=94.03). A fixedsigmaRmeans a different deformation prior at every pixel size, intensity scale and smoothnessa: on the evaluation suite the bestsigmaRmoved with the pixel size. The defaultsigmaRis nowsigma_disp · sqrt(C π) · a^(d/2) · sqrt(nt) · (dx / 30 µm) · sigmaM / s_J(C = 12 in 2D, 64 in 3D; dx the target pixel size in source units; s_J the typical tissue intensity of the target), so the prior allows displacements of aboutsigma_dispµm whatever the images. At the defaults, a 30 µm target withs_J = sigmaMgets about the previous 2D default 5e5. It assumes coordinates in µm. A givensigmaRis used as is. In 3D it replaces the previous defaultsigmaR=1e8, which regularizes much less with the corrected 3D regularizer (below): the 3D tutorials get about 1.1e7 (MERFISH) and 1.85e6 (STARmap).2D step sizes from the data (
epL,epT,epV= None). The previous defaults are scaled bysigmaM² · 1.1 / G, where G estimates the curvature of the image term from the target’s variance and the source’s gradients after the pre-fit. Fixed steps were unstable on some images and needlessly small on others. G sums over the target’s pixels, but the largest stable step of the velocity field does not grow with the tissue area, so on small images these steps alone were far too large (a 1.2 mm image fitted to itself diverged, its cells moving by over 100 µm), and they did not account for landmarks, a smallsigma_dispor, for the affine steps, coordinates far from the origin. Each default step is therefore lowered where needed, from the curvature of the energy at the starting affine (the image term under the same assumptions as G, the landmarks and the regularizer):epVto at most half the step at which gradient descent diverges,1 / (λ + DV / sigmaR²)with λ the largest eigenvalue of the kernel-smoothed curvature in the velocity field, andepLandepTby a common factor to at most a quarter of theirs. λ is found by subspace iteration, whose time grows with the number of points of the velocity grid: for a 17 × 21 mm target with 25 µm pixels it took 1.9 s at the defaulta=500and 15 s ata=200(2 threads). On 351 test fits (crops and rescales of the MERFISH, Xenium and heart sections to 1–16 mm with 15–60 µm pixels, fitted to themselves or to a rotated, deformed copy, with and without landmarks, atsigma_dispfrom 10 to 300, and with landmarks 20–50 mm from the origin), 142 were unstable with the steps scaled by G alone and none are with the caps; sections fitted to themselves stay within 0.04 µm of the identity (within 0.74 µm over 99% of the tissue when the pre-fit, accurate to a few µm, starts them). On the evaluation suite only the steps of the sparse heart sections are lowered (epV by 0.50–0.68; the uncapped ones were at 73–100% of the divergence limit), plus the affine steps of the heart and Xenium-to-H&E tutorials: the serial heart sections moved by less than 0.1 µm, the error of two partial ones fell from 105.8 to 84.1 µm and from 82.8 to 67.5 µm (the first started at 42.5 µm, so it fails with or without the cap), and the tutorials’ results are unchanged except Xenium-to-H&E (landmark error 111.7 to 108.4 µm, within its sensitivity to rounding).align_cellswithout the mixture model (mixture=None), with its 500 iterations per level, converges more slowly on the serial heart sections (up to 9% more error; the same error with 1500 iterations per level) and improves on the partial ones. Given values are used as is.3D step sizes.
LDDMM_3D_to_slicekeeps its steps (epL=1e-6,epT=1e1), but itsepVdefault is nowNone: 1e3 as before, lowered where the regularizer alone would make gradient descent diverge (toepV · DV / sigmaR² ≤ 1, DV the volume of a velocity grid cell). That happens only for a very smallsigma_dispwithsigmaMwell below the target’s intensity. A given value is used as is.2000 iterations by default in 2D (
niter=None, previously 5000). With the new steps, stopping at 2000 instead of 5000 iterations changed the error by 0.4% (±2 SE −0.2 to +1.0%) and halved the time of the 2D fits.Landmarks are matched under the fitted map (
LDDMMwithpointsIandpointsJ). The landmark term movedpointsIby forward Euler steps, which invert the fitted target-to-source map only approximately, so the fit matched landmark positions that differ from thosetransform_points_source_to_targetgives after the fit. Each time step is now solved exactly (fixed-point iteration and a Newton step; the gradient follows from the implicit function theorem), in both engines. With the other new defaults, on the MERFISH-to-Visium tutorial problem the error of its 12 landmarks fell from 75.4 to 70.1 µm, and when 4 of them were held out of the fit, their error fell from 272.8 to 268.1 µm. The Xenium-to-Xenium tutorial is unchanged (28.5 µm), and on Xenium-to-H&E the change (114.3 to 111.7 µm) is within that problem’s sensitivity to rounding. ARuntimeWarningreports steps that did not converge, and steps whose Newton matrix is singular, which take the forward Euler step instead.Caveat: targets with missing tissue. Where the target lacks tissue that the source has (partial sections), the new defaults did not help: of 8 such test problems, 3 improved on
legacy_defaults=True(by 14–78%) and 5 got worse (by 9–29%). Check such alignments, and compare withlegacy_defaults=Trueorprefit=False, or trymixture='em'(below), whichalign_cellsuses by default.legacy_defaults=True(keyword-only) restores the previous defaults: no pre-fit (unlessprefit=True), forward Euler steps in the landmark term,niter=5000,epL=2e-8,epT=2e-1,epV=2e3andsigmaR=5e5in 2D, andsigmaR=2e6andepV=1e3without the cap in 3D. The results are then bit for bit those without these changes (the corrections below still apply).The result has two new entries:
'prefit'(whether the pre-fit ran, the affine'A'the fit started from, which is the initial affine when the pre-fit did not run, and if it ran the energy of every start) and'settings'(theniter,sigmaR,epL,epTandepVthe fit used). Passing them back, withA=out['prefit']['A']in place ofL,TorAandprefit=False(andlegacy_defaults=Trueif the fit used it), repeats the fit exactly, for example to align several sections with the same settings.New function
align_cells(xI, yI, xJ, yJ)aligns two sets of cells in one call. It rasterizes both at 15 µm with a Gaussian kernel of 30 µm standard deviation and runsLDDMMcoarse to fine on mean-pooled copies of these images (500 iterations at 60 µm pixels, then 500 at 30 µm), each level starting from the previous level’s affine and velocity field, withLDDMM’s defaults at each level except for the mixture model (mixture='em', below), whichalign_cellsuses by default andmixture=Noneturns off. It returnsLDDMM’s result, so the transform functions work on it as usual, plus the two rasters. On the 27 held-out synthetic cell-to-cell problems of our evaluation suite (known deformations of real MERFISH, Xenium and heart cell sets), it had less than half the error ofLDDMMwith the new defaults on the suite’s own rasters (15, 30 or 60 µm pixels with a kernel tied to the pixel size): geometric mean 4.6 instead of 11.9 µm (ratio 0.39, ±2 SE 0.29–0.51; better on 24 problems, worse on 3), in less than half the fitting time, and 0.17 times the error ofLDDMMwithlegacy_defaults=True(27.5 µm; 0.10–0.27). On the suite’s main set and on the 13 further held-out problems, its ratios toLDDMMwere 0.38 (0.30–0.50) and 0.34 (0.21–0.55). It was less accurate thanLDDMMonly on problems whose rasters in the suite already had 15 µm pixels: on 9 of these 11 problems, by 5–14% (10–14% on the 3 held-out ones; 3.3 instead of 3.0 µm, for example). On the other 2, whose rasters had a kernel twice as wide, it had 0.42–0.44 times the error ofLDDMM. Images from other sources (H&E, Visium, an atlas) still go toLDDMMdirectly.align_cellsuses the mixture model by default, because it fixes most cases of partial tissue: over the 64 distinct synthetic cell-to-cell problems of the three sets, it cut the geometric mean error of the 8 partial sections from 15.2 to 4.3 µm (ratio 0.29, ±2 SE 0.18–0.45; all 8 better). Without it, a sparse heart section missing 20% of its cells barely improved on the initial affine (49.9 instead of 50.9 µm; 4.5 µm with it). Where the target has all the tissue but uneven cell density, it does harm: the error of the 6 such sections rose from 7.9 to 12.8 µm (ratio 1.62, 1.31–2.00; up to 1.99 times), though it stays well below the 46.7 µm ofLDDMM, and 5 of the 8 serial heart sections got 10–57% worse (ratio 1.14, 1.02–1.26). The other 42 problems changed by less than 2%. Over all 64 the ratio was 0.91 (0.80–1.03; 0.91 on the main set and 0.88 and 1.00 on the two held-out sets, none significant). Passmixture=Nonewhere the target section is complete.New option
LDDMM(mixture='em')(keyword-only, 2D, off by default): a mixture model that estimates the noise of the matching term from the residual and gives a low matching weight to target pixels that look like background where the deformed source has tissue, as where the target section is incomplete. From iteration 50, every 5 iterations, each target pixel is weighted between the matching class and a background class whose mean is the median of the target’s border (ormuB); a dict changes these choices (see the docstring). On the main set of the evaluation suite, with the other new defaults, it cut the error of the 3 partial-section problems to 0.36 times that without it (all 3 better) and left the 21 serial sections unchanged on average (ratio 1.01), but one of the 2 problems with uneven cell density got 10% worse (the other 23% better). Overall the ratio was 0.89 (±2 SE 0.78–1.02, not significant), so it is notLDDMM’s default: try it where the target lacks tissue.align_cellsuses it by default (above). Both engines implement it natively, at no measurable cost per iteration.LDDMM_3D_to_slicedoes not implement it (anymixturebutNoneraisesNotImplementedError).
New functions and options
align_cellsandLDDMM(mixture=...)(above).import STalignalso gives the functions ofSTalign.STalign(STalign.LDDMMand so on), so the tutorials that importSTalign.pyfrom the repository withsys.path.appendalso work when STalign is installed.from STalign import STalignworks as before.LDDMMandLDDMM_3D_to_slicetake the keyword-only argumentsprefit,sigma_dispandlegacy_defaults(above),backend('auto','cpp'or'torch'),num_threads,drawandaffine_step_control(defaultFalseinLDDMMandTrueinLDDMM_3D_to_slice, see below). The newbackend_info()reports which engine is used and, if the C++ engine is unavailable, why.The number of threads is at most
num_threads, else the environment variableSTALIGN_NUM_THREADS, elsetorch.get_num_threads(). Results do not depend on it.transform_points_source_to_target,build_transform(direction='f'),build_transform3D(direction='f')andtransform_image_target_to_sourcetakeexact_inverse(defaultTrue, see below).rasterizeandrasterize_with_signaltake a keyword-onlyblur_um: the kernel standard deviation in the units of the cell coordinates, instead ofblurin pixels (blur = blur_um/(2*dx)), so the kernel keeps its physical width at any pixel size. Giving bothblurandblur_umis an error. Calls withoutblur_umgive the same images as before.Changed defaults (each explained above):
Noneforniter(was 5000),sigmaR(5e5),epL(2e-8),epT(2e-1) andepV(2e3) inLDDMM, forsigmaR(1e8) andepV(1e3) inLDDMM_3D_to_slice, and forblur(1.0) inrasterizeandrasterize_with_signal, where it means 1.0 unlessblur_umis given.
Changes that affect results
These corrections apply whatever the defaults. Their effects below were measured with the previous defaults (those of legacy_defaults=True).
Source-to-target transforms use the exact inverse of the fitted mapping.
LDDMMfits a target-to-source map; mapping cells the other way previously used a forward Euler approximation that is not its inverse. On the tutorial fits, cells move by 1–13 µm on average and up to ~100 µm. The ground-truth error of the simulation tutorial drops from 9.68 to 6.68 µm (6.38 µm with the new defaults).exact_inverse=Falsegives the previous approximation.transform_points_target_to_sourceintegrates the velocity in the correct time order (t = nt-1 down to 0, asLDDMMandbuild_transformdo). Changes are small: at most 0.26 raster pixels on the tutorials.The 3D regularizer is corrected.
LDDMM_3D_to_slicetransformed the velocity over only two of its three axes, so it applied no smoothness left-to-right and a position-dependent penalty up to 83,521 times stronger at the midline. It now computes the documented 0.5/sigmaR² Σ_t ‖L v_t‖². At the old defaultsigmaR=1e8the corrected term regularizes much less than the old one did (in the 3D tutorials the rms velocity became up to twice as large).sigmaR=2e6, the valuelegacy_defaults=Trueuses, gives deformations of about the previous size (rms velocity 16.7 and 17.4 µm in the MERFISH and STARmap tutorials, against 15.7 and 14.7 µm before); the default is now chosen from the data (above).sigmaR=1e8gives the old value.LDDMM_3D_to_slicecontrols the step of each affine parameter (affine_step_control=True, the new default). With the defaultepT, the translation found by plain gradient descent never settled: it jumped back and forth by 45–165 µm per iteration until the last one, so the result depended on rounding. Now the step of each entry ofLandTis multiplied by its own factor between 0.001 and 1, which halves when that parameter’s gradient changes sign from one iteration to the next and grows by 5% per iteration while it does not, so steps are never larger thanepLandepT. The affine parameters converge (the translation moves by 0.001–0.3 µm per iteration at the end), and changing the initial translation in its last digit moves the cells by about 1e-12 µm instead of 30–84 µm. WithsigmaR=2e6, the 3D tutorial alignments match the atlas about as well as version 1.0 did:matching energy
cells in brain
Dice (tissue vs brain)
correlation with the atlas
MERFISH, version 1.0
29,095
97.1%
0.885
0.394
MERFISH, this version,
legacy_defaults=True30,169
96.9%
0.882
0.400
STARmap, version 1.0
92,564
97.5%
0.804
0.516
STARmap, this version,
legacy_defaults=True77,782
97.7%
0.805
0.522
(Matching energy: EM over the last 20 iterations, lower is better. Correlation: the target with the deformed atlas Nissl image over the tissue.) With the new defaults, 97.3% of the cells are in the brain in the MERFISH tutorial, with Dice 0.885 and correlation 0.632, and 98.8%, 0.806 and 0.666 in the STARmap tutorial.
affine_step_control=Falsegives the plain gradient steps. The control is off by default inLDDMM: on the 2D tutorials the plain steps either already converge (MERFISH: the cells move by 0.03 µm with the control) or the control made the landmark error worse (the noisy H&E target of the Xenium tutorial: 34.2 instead of 30.3 pixels of the H&E image).analyze3Dalignreturns correct atlas coordinates. It indexed a 10-unit grid with indices computed fromdx, so coordinates were wrong wheneverdxwas not 10 (by 4.3 mm on average in the STARmap tutorial,dx=50) and biased by up to 19 µm otherwise. Region labels were mostly right and change for under 3% of cells at region boundaries.LDDMMandLDDMM_3D_to_slicereturn the affineA, velocityvand (3D) sample pointsXsof the same, last evaluated iteration.vpreviously came from one update later thanA. The difference is one velocity step.muAandmuBare matched to the number of target channels. A 3-entry mean against a 1-channel target (as in the 3D tutorials) previously counted the squared distance three times; it now warns and uses the single value. Integer, float, list, numpy and torch values are accepted.rasterize_with_signalkeeps the sign of a signed signal (it returned the absolute value).normalizemaps a constant array tot_mininstead of NaN, sorasterizewith a constant gene gives a zero image instead of NaN.
Long 3D fits with plain affine steps are very sensitive to rounding: a change in the last digit of the input moved version 1.0’s result by 30–84 µm on average in the 3D tutorials. Small differences from earlier versions are therefore expected even where nothing was fixed. With the affine step control, the new default in 3D, the same change moves the result by about 1e-12 µm, and the C++ and torch engines give the same 3D tutorial alignments to within 1e-11 µm. 2D alignments of noisy images can still be sensitive: the same change moves the cells of the Xenium-to-H&E tutorial by 8 µm on average.
Bug fixes
rasterizePCAworks (it raisedNameError: name 'tools' is not defined) and tolerates genes without counts.build_transform3D(direction='f')works (it always raised an error) and acceptsv=None.v_to_phiiandv_to_phii_3Daccept the velocity layout thatLDDMMreturns.calculate_treand the transform functions accept numpy arrays, torch tensors and lists of integers or floats.point_annotator.pyandcurve_annotator.pywork with output file names (they raisedNameError).saveRasterswrites into the folderpath(it wrote next to it without a trailing slash).download_aba_ontologyanddownload_aba_image_labelsraise on HTTP errors instead of saving the error page.plot_brain_regionsgives every region its own color with a readable legend; both plot helpers return(fig, ax).LDDMM_3D_to_slicewarns that it ignorespointsIandpointsJ(it has no landmark term).Inputs are checked before fitting, with error messages that name the problem. A drawing error warns instead of stopping the fit.
Tutorials
All 17 tutorial notebooks were re-run with this version, and the aligned cell positions they write into
docs/*_datawere regenerated. On the Mac with the C++ engine, their alignment cells take 0.5–36 s; the exception is the curve-annotator tutorial, whose 10,000 iterations on the full H&E image take 3.8 min. (The comparison with version 1.0 is under Faster.)The MERFISH-to-MERFISH, Xenium-to-Xenium, Xenium-to-STARmap and heart tutorials keep their step-by-step walkthrough and end with a section on
align_cells. The section aligns the same cells in one call and compares the result with the step-by-step one: the median distance between the two is 48, 14, 98 and 69 µm. The sections explain when to turn off the mixture model (mixture=None). The heart tutorial, whose complete serial sections are such a case, turns it off.The 3D tutorials start the fit from the atlas slice the user picks. Before, the starting plane was off by
xI[0][slice]*(1/scale_z - 1): 253 µm in the MERFISH tutorial and 47 µm in the STARmap tutorial. They also pass a one-entrymuAandmuBfor their one-channel target. The STARmap tutorial reads its data by a relative path and draws inline figures.The Xenium-to-Xenium tutorial reads the matching weights
WMon the target grid instead of the source grid. This has no visible effect there, but it matters whenever the two grids differ.Tutorials that no longer ran were fixed:
The simulation tutorial uses the current API and scikit-learn’s
root_mean_squared_error. Its ground-truth error is now 6.38 µm instead of 9.68 µm.The MERFISH-to-Xenium tutorial had wrong data paths and a commented-out import.
The curve-annotator tutorial resamples each pair of curves to the same number of points.
The L/T tutorial no longer unpacks
LDDMM’s result as a tuple.The Xenium-to-Xenium, Xenium-to-H&E, Xenium-to-STARmap and heart tutorials save their results as tables: the target’s own columns plus
aligned_xandaligned_y(their save cells calledto_csvon numpy arrays and raised an error). The Xenium-to-H&E tutorial also saved an undefined variable, and the heart tutorial’s result file,CN73_E2_STalign_to_CN73_E1.csv.gz(the cells of CN73_E2 in the coordinates of CN73_E1), is new, as its save cell never ran before.
The point-annotator tutorial writes the filtered cells into its
..._filtered.csv.gzfile (it wrote all cells). The tutorials convert between torch tensors and numpy arrays explicitly and pass no unused color limits toscatter, so they run without warnings, and the L/T tutorial no longer has a leftover cell that reloadedSTalign.The uncompressed
Xenium_Breast_Cancer_Rep1_STalign_to_HE.csvandXenium_Breast_Cancer_Rep2_STalign_to_Rep1.csvindocs/xenium_dataandstarmap_STalign_to_xenium.csvindocs/starmap_datawere written by earlier versions of the tutorials, which now write.csv.gzfiles. They are left as they were, so they do not match the results of this version.
Packaging and testing
The version is 2.0.0. It is defined once, as
STalign.__version__, whichsetup.pyand the documentation read.requirements.txtlists only the runtime dependencies, unpinned: matplotlib, numpy, pandas, pynrrd, requests and torch. pandas, pynrrd and requests are needed only by the Allen atlas helpers. The tutorials also use plotly, scipy, scikit-learn and scikit-image.pyproject.tomldeclares the build requirements. pybind11 is needed only to compile the C++ engine; if compilation fails, installation still succeeds and thetorchengine is used. Building the C++ engine in a clone (python setup.py build_ext --inplace) also needs setuptools, which Python 3.12 and later no longer include in new virtual environments.Wheels include the license of pocketfft (BSD-3-Clause), which the C++ engine compiles in, next to STalign’s own.
A
pytestsuite intests/checks both engines against a plain-autograd reference implementation of the iteration (tests/reference.py), and the rasterization, the transforms and the function signatures against version 1.0, whoseSTalign.pyit takes from the environment variableSTALIGN_ORIGINAL.A GitHub Actions workflow (
.github/workflows/tests.yaml) builds STalign with its C++ engine and runs the test suite on Linux and macOS (Apple silicon) with Python 3.10, 3.12 and 3.14, with version 1.0’sSTalign.pytaken from git for the comparisons. It also installs STalign without a compiler and checks that thetorchengine is used. The documentation workflow now usesactions/setup-python@v7andpeaceiris/actions-gh-pages@v4(the previous versions needed Node 16) andactions/checkout@v7, and installsdocs/requirements.txtwith the CPU build of torch. The documentation renders the numpy-style docstrings (with napoleon) and includes this file.docs/requirements.txtcan be installed with pip (it listed the standard-library modulesos,collectionsandcopy, andnrrdinstead ofpynrrd) and adds ipython, which highlights the notebooks’ code. ThePipfileadds scikit-image and scikit-learn, which the tutorials use, and pybind11 and setuptools, which building the C++ engine in place needs.pipenv-requirements.txtis regenerated from it.