Skip to content
chromosomix

chromosomix 0.17.0

What changed in chromosomix 0.17.0, released on 26 October 2026.

  • The viewer in a Jupyter notebook (§21.5). pip install chromosomix (from a clone, pip install -e widgets/python) gives Chromosomix, the app itself in a cell — the map, its tracks, its side panels and its gestures — as an anywidget for JupyterLab, Notebook 7, VS Code, Colab and marimo: Chromosomix('sample.mcool::/resolutions/5000', region='chr2:10-14Mb', tracks=['ATAC.bw'], transform='log10'). goto, navigate and add drive it from Python, and state — a traitlet a cell can observe — follows where the reader takes it. A file on the kernel’s disk is served to the viewer by byte range from a server on 127.0.0.1, its sidecars beside it; behind JupyterHub through jupyter-server-proxy, in Colab through Colab’s proxy. The frame holds the site, a release by its permanent address (viewer='0.17.0'), any build, or the npm package fetched once by install_viewer() so the notebook works offline. widgets/python/demo.ipynb drives the yeast demo, and demo_marimo.py the same in marimo, where the wrapper is read and the widget driven; scripts/ui-jupyter-widget.mjs runs it in JupyterLab and shoots every frame. The local file server serves no dot-named file, follows a link by its own name so a DataLad or git-annex file finds its index, ignores a Range it cannot read, and prints nothing when a reader hangs up; behind JupyterHub, where only a viewer the kernel serves can read through the hub’s login, the widget warns when its frame is the site.

  • The viewer in R: an htmlwidget for R Markdown, Quarto, RStudio and Shiny (§21.5). The chromosomix R package (widgets/r/, install.packages("widgets/r", repos = NULL) from a clone) draws the app on a page — chromosomix(map, region = "IV:1000000-1400000", tracks = c("ATAC.bw", "genes.gtf"), transform = "log10") — and chromosomix_goto(), chromosomix_navigate() and chromosomix_add() move it: piped onto a widget before it is drawn, which is how a static page moves its views, or of one on screen in Shiny through chromosomixProxy(), where input$<id>_state follows the reader. Files on this machine are served to the viewer by byte range from the R session, their sidecars beside them; behind RStudio Server through RStudio’s proxy. The examples are an R Markdown page of the yeast demo and a Shiny app driving it (system.file("examples", package = "chromosomix")), whose controls follow the viewer without echoing back into it; scripts/ui-r-widget.mjs renders and runs both and shoots every widget. The local file server serves no dot-named file, follows a link by its own name so a DataLad or git-annex file finds its index, streams a whole file rather than read it into R, and ignores a Range it cannot read; while knitting, an installed viewer is framed from the site rather than from a session that ends with the render.

  • chromosomix:add, a host page putting files in an embed (§21.4). The sixth message of the embed’s channel: a list of addresses, each put in the tab as the header’s + puts one — a track beside the map, another map or a model beside it, a workspace — once the map the src= named is drawn, and answered with the state once they are in. The view state has no word for a track; this is how a page that frames a map puts its tracks beside it. A host’s messages are now carried out in the order they came, so a navigate sent after an add lands on what the add opened, and chromosomix:ready lists the messages the frame takes (accepts), so a host can tell a frame that takes files from one built before this. The Jupyter and R widgets read it: a viewer before 0.17.0 draws the map without the tracks, and the widget says above the frame how many it could not add.

  • Pairs QC: a library’s quality from its .pairs, and its restriction fragments (§18.8). A new task in the 2D card for a map binned from a .pairs — or any map, given the .pairs it came from. It draws the four orientations’ P(s) (moved here from P(s)), the pair types, cis and trans past 1–40 kb and the pairtools stats table, from the import’s own counts at no cost. With a FASTA or .2bit and an enzyme it adds the restriction-fragment statistics HiCUP readers look for: pair classes by orientation (dangling end, self-circle, same strand, re-ligation, valid, trans), the distance from each read to the next cut, the fragments seen, and coverage against sites per bin with Spearman ρ. Each read end is placed by the cut on its own strand — a − read’s 5′ end sits L − 2k past the top-strand cut once an overhang is filled in, 3 bp for DpnII — so a dangling end is counted as one. chromosomix pairsqc sample.pairs.gz --bin-size 10000 --sequence ref.fa --enzyme DpnII --out qc/. Held to pairtools stats, pairtools restrict (once per strand) and cooltools.coverage on a yeast library and its R64 reference in notebooks/validation.qmd §27: every count identical. Its row’s ? (§18.1) names pairtools’ paper.

  • Restriction sites: a reference digested by an enzyme, a kit or typed sites (§18.8). DpnII (MboI, Sau3AI), HindIII, NcoI, EcoRI, BglII, HinfI, DdeI, MseI, NlaIII, CviQI and AluI, the Arima kit, or names and IUPAC sites with the cut marked (G^ANTC). A lane of the cuts and the cuts per bin, read by range from a large reference as find a motif is. Its cuts are bioframe.digest’s and cooler digest’s less two: both add 1 to Biopython’s already 1-based position, so every fragment boundary they write is 2 bp late. Held to bioframe.digest in notebooks/validation.qmd §26, every cut and every bin’s count the same, and its row’s ? (§18.1) names bioframe’s paper.

  • pairtools stats for every .pairs the app imports, and a command that writes it (§3.7). The import counts, in the pass that bins the map, everything pairtools stats counts — records mapped on both sides, on one and on neither, duplicates, cis and trans and cis past 1 to 40 kb, every pair type, the four orientations by separation and where they converge, and the library’s size — and the General column shows it as a Pairs statistics card, each row a count and the share pairtools and MultiQC give it, with save .stats writing the whole table in pairtools’ own format. chromosomix stats sample.pairs.gz --out qc/ writes the same file from a shell. A port of pairtools 1.1.2’s PairCounter, held to pairtools’ own output on a committed fixture with every pair type in it (test/unit/pairs-stats.test.ts, npm run fixtures:pairstats): every key and every count and fraction the same, the library estimate to 1e-12. The pair type is counted and nothing is decided from it — the map is the same with the column or without it. The card draws three small charts from the same counts, with no run: the pair types as one bar, cis and cis past 1–40 kb as columns, and P(s) by orientation with where the four meet — in the theme’s colours, dark included.

  • The lower triangle in a reading of its own (§5.10). On a map of one file, the Map card has a second tab, ◣ lower: switched on, the triangle below the diagonal is drawn with its own score, smoothing, O/E, transform, colours and clip — balanced counts above the diagonal and their O/E below it, on two ramps and two keys. Everything else — the window, the resolution, the turn, the overlays, the tracks — stays the map’s. Switching it on starts it from the map’s own reading, so the press changes nothing until a field does; each triangle is pooled and coloured apart, and each scale measured on its own reading’s whole square, so the upper’s colours do not move. The second key is editable, the readout names the reading under the pointer, an exported figure’s caption says both, and a turned map is two bands, each cut from its own reading. A lower score that is another column or the balancing the other way reads the block a second time from the same file. In the link as lower, score2, oe2, smooth2, transform2, colormap2, vmin2 and vmax2, carried across a navigation but for the clip, and dropped when a second file or a deck is opened.

  • The paper’s GM12878 panels in the lab store (§24.2, §24.3). The Chromosomix paper opens on two new tabs over 4DN’s in situ Hi-C of GM12878 (Rao et al. 2014, 4DNFIXP4QG5B: 4,010,478,943 contacts, 27 GB, read in place). Fig. 1a: chr14:88.55–90.10 Mb at 5 kb, three cells across — the map, with ENCODE’s CTCF ChIA-PET of GM12878 in its lower triangle (Ruan lab, ENCSR184YZV, a Juicer .hic: two formats out of two consortia in one square, §5.7), both as O/E smoothed adaptively, with its insulation and borders above it, its compartments and E1 at 250 kb at its left, the 4C profile from chr14:89,150,001-89,180,000 under it, and the TAD and its borders marked; a tracks cell with ENCODE’s CTCF, ATAC-seq and its peaks, both strands of total RNA-seq, CTCF’s motif, UCSC’s refGene and hg38 sequence; and the pastis-NB model of the window, drawn as sticks, its TAD borders as beads and every bead coloured by its distance to one pinned inside the central sub-TAD. Fig. 2: the map on chr19 at 10 kb with two labs’ CTCF ChIP-seq (Broad’s ENCSR000AKB and UW’s ENCSR000DRZ, fold change over control) and ATAC-seq, and nothing computed: each task of the figure’s chain opens on its recipe — insulation at four scales, the peaks on the two CTCF tracks as replicates, the APA between each peak and the next at 2 kb, the metaplots — so the figure’s chromosomix panels are made in the browser from the map and the tracks alone. Everything else not a consortium’s file is the app’s own output — and genome-wide, the peaks (39,075) and the insulation at four scales — made by scripts/make_rsg_store.py derive --steps gm12878 on the map’s rungs read by range (notebooks/scripts/cool_subset.py), never the 27 GB.

  • Fig. 2’s chain, held to its references (§18.10). The validation notebook’s § 24 runs what the paper’s Fig. 2 runs on GM12878, each link by chromosomix and by the package it reproduces, each fed what the one before it wrote in chromosomix: insulation at four scales against cooltools.insulation, CTCF’s peaks on two labs’ ChIP-seq as replicates against yapc 0.1 with IDR 2.0.3, APA between consecutive peaks against coolpuppy, and the metaplot of CTCF, ATAC-seq and the insulation about the peaks against deepTools’ computeMatrix, plotProfile and plotHeatmap. It reads chr19 of 4DN’s 27-GB map by range (notebooks/scripts/cool_subset.py), ENCODE’s tracks held to their MD5s, and its commands are timed with the rest.

  • APA between consecutive features (§18.8). A fourth choice of anchors, consecutive features (--anchors consecutive), pairs every feature of a set with the next one along its sequence — consecutive CTCF peaks — and piles the pairs up off the diagonal, held to the nearest loop as a loaded loop set is. Its pileup is coolpuppy’s over a BEDPE of the same pairs within 5·10⁻⁶ (validation notebook § 24), so Fig. 2’s chain — insulation, peaks, the APA between them and the metaplots — runs in the app from the map and the tracks alone, with no pair file made outside it.

  • What a task re-implements, on its row (§18.1). A ? in a circle at the right of a task’s title in the Analysis column names the published method it follows, how it came out against it in the validation notebook, and the paper’s DOI as a link, in a small tip shown on hover. Green where the validation found it identical or equivalent — 22 tasks, from coverage and insulation to pastis-NB and the metaplot — and in the text’s colour where it is untested: the scalogram, the HiC score and per-cell contact QC. A mark, not a button: a press on it opens no task. Tasks that follow no published method have none.

  • The notebooks, in About (§18.10). A Notebooks tab names the four published notebooks — the validation, the timings and the parity, which GitHub Actions renders with every release, and the laptop, rendered by hand — each linking to its page, with a sentence on what it holds chromosomix to and, read from the rendered.json published beside it, the one line of what it found and when, from which build, on what machine and by which run, or by hand, it was rendered. The stamps are read when the tab is chosen, never when About opens; one that cannot be read leaves the link alone. The desktop app, the single file, the package’s copy and a dev server link the deployed site’s pages; the single file reads no stamp.

  • A remembered local file is one click away after a restart (§5.6, §8.6). Chromium keeps a dropped or chosen file’s grant across a restart but asks again before handing the file over, and asking needs a click the restore path does not have — so a restored tab landed on drop the same file below for a file the browser still knew, under a red note saying no page could re-open it. In that note’s place the picker now says This browser remembers rep1.cool and 4 other files, and only needs your permission to read them again beside allow access to all 5 files, and a detached track reads remembered file — allow access to draw it again with allow access before or re-open this file. One press asks for every file the browser remembers, the one the tab is waiting on first, and Chromium’s prompt offers allow on every visit: the tab’s map, its tracks and their indexes reopen, and so does every other tab on screen waiting on a file — a tab restored and never looked at stays unopened until clicked. Firefox and Safari keep no grants, and show no button.

  • Anonymous telemetry on the website, with a switch in About (§31). The site sends a visit, a format opened, a task run and how long it took, an error code met, an install and an allow access answered — each a word from a fixed list, checked against it before it is sent, and never a file name, a URL, a locus, a message or the page’s own address. Posted with no cookie and no referrer to Umami (https://cloud.umami.is/api/send, free up to its Hobby plan’s limit, or a self-hosted Umami), named by the repository’s TELEMETRY_ENDPOINT and TELEMETRY_WEBSITE variables (docs/deploy.md), when the page is idle, at most two hundred a page. On by default with no banner; About → telemetry turns it off at once, forget everything keeps it off, and a browser sending Global Privacy Control sends nothing; Do Not Track, which Safari and Firefox have retired, is not read. Only the site’s own build at the site’s own address sends any: the archived pins, the previews, the package, the desktop app, the single file, embeds and automated browsers never do.

  • The app held to its own command line, in a notebook (§18.10). notebooks/parity.qmd runs ten tasks in the app, in headless Chromium — insulation, compartments, P(s), coverage, virtual 4C, loop calling, a motif search and APA on GEO’s yeast Micro-C at 1.6 kb, framed on chrIV, and peak calling and the metaplot on GEO’s yeast ATAC-seq — saves every file the app offers for each run through the buttons a reader presses, runs the line the console wrote for it with chromosomix, and compares the two sets file by file: identical (a bigWig byte for byte, a text file line for line, its # provenance set aside), or the data apart. Rendered with the validation and the timings on every release and published at https://chromosomix.app/parity/, with every file and its verdict in summary.tsv beside it. The app and the command line must be built from one commit or nothing runs, and a session the app refuses to open stops at the app’s own words rather than after a ten-minute wait.

  • The validation notebook holds four more analyses to their references, and does not time them (§18.10). notebooks/validation.qmd adds cis and trans per sequence against cooltools’ expected_cis and expected_trans, on a chicken map whose microchromosomes meet one another at nearly twice what indifference predicts; the compartment call’s saddle against cooltools.saddle over chromosomix’s own E1, cell for cell; the fraction of signal in features against pyBigWig’s exact sums; and signal by group against pyBigWig’s exact stretch means, rank for rank in each group, and NumPy’s and SciPy’s summaries and rank tests. Their commands are written to commands.json with the others and left out of notebooks/timing.qmd’s runs.

  • Time and memory on your own machine (§18.10). notebooks/laptop.qmd runs 23 analyses genome-wide through chromosomix, once each — on the validation’s 69-million-contact mouse map and yeast Micro-C, on 4DN’s 1.08-billion-contact IMR-90 map (Rao et al. 2014), and on two ATAC-seq replicates and the mm10 sequence — and tabulates the wall-clock and CPU time and the peak memory of each, with the machine it ran on, a figure, and every command at the end. It needs Quarto with pandas, matplotlib and pyfaidx, and a built command line; the data, about 13.7 GB, are downloaded once, each held to its MD5. Render it on your own laptop, or read it at https://chromosomix.app/laptop/: a render made by hand, on a laptop, and committed as notebooks/laptop.html, the one rendered page .gitignore lets through. The page is one file that carries its table and its rendered.json, and the deploy unpacks the two beside it, so publishing a render is committing the file. The Notebooks workflow renders it on a GitHub runner with every release too, and keeps that as the run’s artifact and in its summary only: never on the site, nor on a release, and whether it rendered no longer holds back the other pages.

  • The .hic reader held to hic-straw on a file Juicer Tools wrote (§3.6). test/unit/hic-straw-oracle.test.ts read only fixtures this repository’s own writer produced, so straw and this reader agreed on our reading of the format and never met a block Juicer laid out. It now reads the demo map too, demo/yeast/Yeast_hic.hic (v8, Juicer Tools 1.9.9), with both readers, every cell of four windows — a whole chromosome with its short last bin, a window inside another, a megabase off the diagonal, and between two chromosomes — at all five resolutions, raw counts exactly and KR-balanced ones to float32 precision, through MetaCooler as a view reads it. The map is committed, so this runs on every push with nothing to build.

  • P(s)’s four orientations moved to Pairs QC (§18.8). The toggle and its .pairs box left the P(s) form; a stored run, a link or a chromosomix expected --stranded line still draws the same curves, with a note saying where they live now. A composite’s required task can now take the answers its own form asked (upstreamParams), which is how Pairs QC hands the reference and the enzyme to the digest.

  • The command line reads an address by range, as the app does, rather than downloading it whole (§23.1). A map — a cooler, an .mcool rung, a .hic, an .scool, a .pairs and its .px2 — an --against, a bigWig or .2bit in --tracks, and a .2bit or an indexed FASTA in --sequence are read through the app’s own RemoteRangeSource, with its cache, coalescing and read-ahead, and nothing of them is written to disk: one rung of the demo’s 46 MB E. coli .mcool costs 2.6 MB in three requests, and a metaplot of one chromosome’s peaks over a 15 MB bigWig 5.2 MB, where the command used to fetch both whole first. Node has no synchronous request, so a helper thread fetches while the task’s thread waits on Atomics.wait (cli/syncFetch.ts), and a range source is mounted under h5wasm’s Node filesystem as a path no file lies behind (h5/lazyFile.ts). Every file a command writes over an address is the file it writes over the same bytes on disk (test/unit/cli-range.test.ts). What is read whole from a copy is unchanged — a feature set, a HiC-Pro matrix, a workspace — and so is any address whose host will not serve a range, now said on stderr. A 404 names its status, a request that got no answer its cause, and neither its query. The console’s note that a remote line downloads the whole file is gone with the download.

  • A second file whose chromosomes run on past the first’s opens in the lower triangle (§5.7). A Juicer .hic keeps the mitochondrion after Y, where a 4DN cooler of the same assembly stops at chrY, and the two were refused for counting 25 sequences against 24. While the second list opens with the whole of the first, every bin the picture reads is the same locus in both, so the pair is drawn; the other way round is still refused, and the refusal now says which way round to open it. §5.7 also says what balanced means beside a .hic: Juicer’s KR is in the library’s depth where a cooler’s balancing sums to one, so the two are compared as O/E.

  • The paper’s bacterial panels are Fig. S3a–c (§24.2). The three tabs that were Fig. 2a–c — C. necator along both arms, the B. subtilis ParB time course and P. aeruginosa’s condensin mutants — are named for the supplementary figure they now make, behind Fig. 2’s new tab.

  • About is three tabs, and the licences are one of them (§22.4). Overview — what the viewer is, the tutorial and the source, with the build stamp and the version picker in a footer under it that never scrolls away — then Notebooks and Licences, each about that alone. The stamp used to sit under the licences, which had pushed it out of sight on a laptop. The row of tabs is walked with ← and →, Home and End.

  • C1 is “no server-side component of our own” (§1). It was “of any kind”; the site’s telemetry (§31) goes to a third-party endpoint the app never reads from or waits on. The landing page’s privacy note says no server ever sees a file, and adds a clause on the telemetry where the site sends it; the README says the same.

  • pastis-NB’s fit runs on every core, and gives the same model on any number of them (§25.6, §23.1). Each evaluation of the likelihood, and of pastis’ MDS start before it, is now summed in bands of rows laid out by the bead count alone, and the bands are shared among helper workers — web workers in the app, one fewer than the cores and seven at most, as the correlation’s are, and Node worker threads on the command line — while the thread that asked computes a stretch of its own. The same helpers serve the MDS and then the likelihood, and a helper still starting is not waited for: the thread that asked computes its bands until it says it is up. The bands’ values and gradients are added in band order whatever the threads, so the model is the same bits on one thread and on eight: the command line’s --threads 1 and --threads 4 write the same bed3d, and the app’s model, made with three helpers, is the command line’s on one thread. On the validation’s yeast Micro-C at 12.8 kb (905 beads) a model takes 4.6 s with --threads 4 where it takes 12.5 s on one, at 6.4 kb (1,807 beads) 13 s where it takes 41, and in Chromium a model of chromosome IV at 1 kb (1,400 beads) 9 s where it took 20.7 — on a four-core machine that draws the page on the same cores. A page shares no memory with its workers, so each helper is sent its rows of a triangle once per objective, the copies held to 512 MB; the command line allocates the counts on a SharedArrayBuffer and its helpers read the one matrix. A helper that cannot start, or fails, has its bands computed by the thread that asked, with the same numbers. The command line gains --threads N, one by default, so that the times notebooks/timing.qmd sets beside the references’ are one core’s; the estimate prices a hundred steps on the threads the run will have and counts the helpers’ copies in its memory. The walk underneath (structure/lbfgs.ts) now asks for each evaluation rather than making it, so the threads can be awaited.

  • pastis-NB is now pastis-NB as pastis runs it, and from the same seed gives pastis’ model (§25.6). Model from the map departed from pastis on purpose in three places, and no longer does in any of them. The dispersion was a moment estimate per separation, held fixed; it is now pastis’ curve against the mean — the bias-corrected counts’ mean and variance at each separation (compute_mean_variance), and r = exp(c₀ + c₁ log λ) fitted through them (ExponentialDispersion(degree=1)) — read off at each pair’s own mean as the beads move, its derivative in the gradient. On the validation’s yeast map it is pastis’ curve to seven figures, r = 3.22 · λ^0.151, where the old estimate ran the other way (11 near the diagonal, 123 far). The likelihood is the whole negative binomial, gamma functions and all, with pastis’ β = 1 and the biases scaled to a mean of 1: at pastis’ own final model its value is pastis’ to ten figures. The start was ShRec3D’s classical scaling and sixty SMACOF steps; it is now pastis’ MDS — the relative stress over the counted pairs — from beads placed at random by numpy’s own Mersenne Twister, so a new seed control, shown under pastis-NB, places every bead where pastis’ random_state does. Both are solved to pastis’ tolerances by an L-BFGS that now uses SciPy’s L-BFGS-B constants (a thousandth for Armijo, twenty probes, ten pairs of history, a restart on a failed search). The fit table shows the curve, the seed, the MDS’s steps and the −log L pastis would report; the scale β row is gone, β being 1.

    What that buys, on the validation’s yeast Micro-C at 12.8 kb (905 beads), against pastis from the same seed: distances within chromosomes agree at Spearman ρ = 0.988, 0.993 and 0.978 for seeds 0, 1 and 2, between chromosomes at 0.982, 0.997 and 0.901, and the per-chromosome Procrustes disparity is 0.016, 0.002 and 0.036 — where pastis agrees with itself across seeds at 0.94–0.95, 0.50–0.57 and 0.085–0.094, and the old port agreed with pastis at 0.87–0.88, 0.40–0.47 and 0.22–0.24, outside pastis’ own spread. The old port ranked the contacts within chromosomes better (ρ 0.945 against pastis’ 0.868–0.873); the new one ranks them as pastis does (0.872).

    And the time. The default 150 steps take about 12 seconds from the command line on that map (7 before, 65–80 for pastis): a pastis evaluation is a logarithm and an exponential per pair for the dispersion’s power of the mean, a logarithm for the spread and one for the gamma functions, about twenty passes’ worth of a SMACOF stress where the old one was eight. Its MDS start costs a third of a second where pastis’ costs nine, and the counts are the only matrix it holds — one n² matrix where both methods held two, so twelve thousand beads is half a gigabyte, not one. 150 steps is where pastis’ own runs end on that map (125–148); raised to pastis’ gradient tolerance a run goes on to a likelihood about a thousand units better than pastis’, in 900 steps. One small difference is new, and deliberate: a separation whose variance is within a Poisson’s own scatter of its mean is left out of the curve, and with fewer than three left the counts are read as Poisson — on a real map every separation is in, and on Poisson counts pastis would fit a curve that loses the model.

  • The notebooks render a job each, and the run’s summary has a section each (§18.10). .github/workflows/notebooks.yml renders the parity first, then the validation with the timings (which run the validation’s commands on its inputs) beside the laptop, each job on a runner of its own installing what it needs through .github/actions/notebooks-setup; a last job publishes the four pages only if all rendered. The summary gives each notebook its address, version and render time, the one line of findings the notebook now writes with its stamp (rendered.json), and its table, folded. Where each page is published and which files go beside it is one table, notebooks/scripts/pages.py, which the workflow and rendered.py both read.

  • The validation gives a reference a process a core, at most four, and every render logs the memory (§18.10). On the two-core, 8 GB runner a private repository is given, six renders of the notebooks in nine were shut down mid-step (“The runner has received a shutdown signal”), four in the cell where cooltools computes the expected over the mouse genome at 10 kb with four processes, the largest alone at 3.6 GB. notebooks/validation.qmd‘s THREADS is now the machine’s cores up to four, so the timings’ threads column reads 2 on that runner; and each render runs through notebooks/scripts/memory.sh, which writes the memory in use and available, and the three programs holding the most, into the job’s log every ten seconds: a runner shut down for want of memory leaves no error and runs no step after it, and that log is what is left.

  • The timings’ peak memory is every process of a run together (§18.10). notebooks/timing.qmd recorded the largest resident set any one process of a run reached, so a reference that runs a pool of workers was credited with its largest worker alone: cooltools’ expected over the mouse genome at 10 kb read 3.8 GB when its four processes held 9.5 to 12 GB between them. Each run is now weighed four times a second while it runs, its process and every process under it, by their proportional set sizes — a page forked workers share counted once between them, not again in each — and the most they held together is the peak_mb column of timings.tsv, in place of max_rss_mb, never less than the largest resident set any one reached. A command of one process, as every chromosomix command is, reads as before. A table on disk with the old column is timed again. Where the system does not list a process’s children, as macOS does not, the column is the largest process alone, and the page says which.

  • The command line names a track as the app does (§23.1). A file opened under --tracks is called what the app calls its row — signal for signal.bw — in a metaplot’s columns, a correlation’s axes, FRiP’s and Signal by group’s rows and every label a run writes, where it was the file’s base name. The shell’s tables and the drawer’s now share their headers.

  • Peak calling over a genome is nine times faster and holds less than a quarter of the memory, every number the same (§18.8). With IDR over a mouse ATAC-seq pair, genome-wide, a call took 29 minutes and 1.27 GB on a 4-core 2.1 GHz Xeon and takes 3 minutes and 290 MB, on the one thread it always ran on. The 456-tap convolution, which was nine-tenths of the time, is summed piece by piece rather than base by base, each base’s terms added in the order they were; a replicate’s own second derivative is summed only where the pooled one is concave, the only bases it scores; of the 6.5 million candidates, only those that can still be among the 100,000 strongest are held, where every one was held to the end; IDR bisects the pseudo-values both replicates share once a round rather than once each; and a window’s whole scan — pieces, pooling, both convolutions, the concave mask and the regions — runs in a WebAssembly kernel with SIMD (npm run wasm:yapc rebuilds it), with the TypeScript where WebAssembly has no SIMD. Each is tested bit for bit against what it replaced (§18.10), and the validation’s comparison with yapc 0.1 on chr19 is unchanged to the last digit.

  • An embed no longer recalls another frame’s tracks (§21.2). The memory of each file — its tracks, marks and filters — was kept in sessionStorage, which every frame a page holds on one origin shares; with no tab strip, every embed started with the same session id. Two embeds of one map on a page, or two notebook widgets, showed the first one’s tracks in the second, and a frame reloaded in place showed its own twice. An embed now keeps that memory in the frame alone.

  • A map read through a proxy that empties a HEAD opens (§11). jupyter-server-proxy, the way into a notebook kernel’s files behind JupyterHub, answers every HEAD with Content-Length: 0 and passes the server’s Accept-Ranges through, and the reader took both at their word: a 19 MB .cool was mounted as a file of no bytes and refused as “not an HDF5 file”, while its bigWigs, read by range, drew. A HEAD stating no bytes is now followed by the one-byte probe, whose Content-Range gives the size.

  • A map in a deck keeps its size in a short window (§16.5). Each deck cell’s map took its height from the window less the tracks stacked under it, with no floor, so a deck of two in a 620-pixel-tall window — a notebook’s frame, a laptop with its tools open — drew a map with three tracks under it as a 42-pixel thumbnail. It now has the single view’s 20rem floor, and the page scrolls, as the single view’s does.

  • A band below zero is drawn about its line (§18.13). A curve’s band is two stacked edges, and the chart library stacks only values of one sign unless told otherwise: a band whose lower edge was negative had its width stacked on zero, a strip from 0 to the band’s width far from its line. Every metaplot of an insulation track, a log ratio, drew its bands so; they are stacked whatever the signs.

  • A workspace’s analysis recipes come back (§18.2). What each task was last set to, and how its figures were styled, are saved in a workspace so that reproducing a figure is opening it and pressing Run — and the drawer kept them under the map’s address with its resolution, where a workspace neither put them back nor looked for them: every task opened on its defaults, a saved workspace carried none of what was run, and switching resolution forgot the form. They are kept under the tab’s key for the file, which is the workspace’s own, so the paper’s Fig. 2 tab opens each task of its chain on the figure’s recipe.

  • A tracks cell keeps its tracks (§16.11). A track placed on a deck’s tracks cell was kept against the file of the map it was on, and a tracks cell has none: a reload, a saved workspace opened again or a link brought the cell back empty and the track drawn nowhere. A tracks cell’s tracks are kept by its seat — tracks:1 for the first from the left, as its name and its height already were — and come back with the cell.

  • A model’s pinned bead is kept with it (§25.5). Spatial distance to pinned bin and contacts with pinned bin colour a model from the bead the reader pinned, and the pin went with the page: a model saved coloured by distance came back coloured from nothing. The pin is kept on the model’s row, by the sequence and base pair at its middle, and comes back with a session, a workspace or a link.

  • A metaplot’s columns are deepTools’ (§18.8). Three things put a profile off computeMatrix‘s by up to a percent. An odd-length feature’s centre was its midpoint rounded up, one base right of deepTools’; a window a sequence end clipped spread what was left over whole columns, so every column of that row stood a little off its place; and a track was read at the column’s width, which takes a zoom level up to twice as wide, whose records straddle the columns’ edges — 0.7 % on the insulation about CTCF peaks in 10-kb columns. The centre is now deepTools’, a clipped window keeps step-wide columns, and a track is read at its own resolution a megabase at a time: over GM12878’s CTCF peaks the profiles of CTCF, ATAC-seq and the insulation agree with deepTools within 5·10⁻⁶ (§ 24 of the validation notebook), at 1.7 s rather than 1.1 s for the insulation, in 210 MB rather than 300; mouse ATAC-seq about all 35,034 RefSeq TSSs (§ 14) stays identical and takes the 5.5 s it took, in 350 MB rather than 410.

  • Signal by group’s stretches are their exact means (§18.8). A stretch was read at a twentieth of a typical stretch’s width, from a zoom level wherever the file had one that fine, and a zoom record is a mean over its own span: on 4DN’s mouse ATAC-seq on chr19, the 896 bp records carried each neighbour’s signal into a stretch’s ends, the 10 kb stretches came out a median 0.2 % from their own means and one in thirteen more than 1 %, and the Mann–Whitney U between the two compartments as much as 0.2 % from SciPy’s on the exact means. Every track is now read as track correlation reads a window — at its own resolution, a megabase at a time, each record weighted by how much of the stretch it covers, pyBigWig’s stats(…, exact=True) — and the same run agrees with pyBigWig to within 5 × 10⁻⁶ in every stretch and with SciPy to every digit written in every test, in 1.0 s where it took 0.9 s, and half the memory. A track the groups are cut from, and the per-feature table, are read the same way.

Documentation of chromosomix v0.20.0 · built from 40ec1ec · source