Qu

Changelog

Generated from CHANGELOG.md, so this page and the file cannot disagree about what changed.

Notable changes to Qu. Dates are when the work landed, not when it was released.

The format follows Keep a Changelog, and versions follow semantic versioning once 1.0 is reached; before that, minor versions may break things, and breaking changes are called out.

[Unreleased]

[0.4.8] - 2026-10-06

From Ahmed's feedback file and two internal audits: list element assignment, extend, for over a table, r"""...""" raw strings, error locations in Qu Studio and the kernel (e.file, e.trace), qu run --dry-run and --watch, a security fix making --sandbox deny file writes, and hardening of the file-format readers.

[0.4.7] - 2026-10-06

New: numerical integration and ODE solvers (trapz, cumtrapz, simpson, quad, ode45, ode23, ode_stiff, rk4), import sparse, NumPy and HDF5 readers, qu test and qu fmt, runtime errors with file:line and call chain, pdf.images/extract_image/to_html, and the second Office wave (xlsx sheet operations, pptx charts and shapes, docx editing).

[0.4.6] - 2026-10-01

Changed: rgb() channels are 0..255 unless the call says otherwise. 0.4.5 guessed 0..1 fractions when every channel was in 0..1 and one was not whole; that guess is gone. rgb(0.5, 0.75, 1.0) is bytes again (#010101); write rgb(0.5, 0.75, 1.0, scale=1) -- or scale="normalized" -- for fractions. A bare vector color = [r, g, b] is 0..255 too.

Installers: both Windows setups (command line and Qu Studio) have a Components page -- Jupyter kernel (on: qu-jupyter.exe, registered as the "Qu" kernel for JupyterLab/Notebook/VS Code, unregistered on uninstall), offline documentation (off: the site in docs\, which help(name) then points at), editor plugins (VS Code, Sublime Text, Notepad++, each preselected only when that editor is installed; now removable on uninstall), and shortcuts: Qu CLI (REPL), Start Jupyter (Qu) (says how to get Jupyter if it is missing) and Qu Documentation in the Start menu, optionally on the desktop. Silent switches /WITHDOCS /NOJUPYTER /NOPLUGINS /NOSHORTCUTS /DESKTOP. Adding qu to the PATH is a component too (on; untick it, or /NOPATH). The release archives now carry qu-jupyter, docs/ and editors/ too.

Fixed: the Qu Studio installer never actually put qu on the PATH (its in-process registry edit silently did nothing, 0.4.4 and 0.4.5 included). It now uses the same path-helper.ps1 as the command-line installer.

Fixed in Qu Studio: the Interactive tab opened to a black window (0.4.5): react-plotly.js is CommonJS, and under Vite 8 its default import was the module object, which crashed React. The CI smoke test now opens every top-level tab. The editor coloured everything after a transpose (X_b' * errors) as a string, to the end of the file: a ' right after a name, number or closing bracket is now highlighted as the operator it is.

Qu Studio's terminal has a qu> prompt: after Run, keep going in the same session -- the script's variables are there (Enter runs, Shift+Enter adds a line, Up/Down recall). A REPL setting in the terminal header chooses one shared session for the whole window (as before) or one session per file; in per-file mode the Variables and Figures panels follow the active file, and closing a file ends its session. The Variables panel hides the built-in constants (pi, e, inf, QuCr, QuTab, ...) behind a toggle unless reassigned (--emit-vars and the kernel mark them "system": true); plotting a variable from it no longer blanks the window (the same react-plotly.js import as the Interactive tab). Opening a figure zooms it out of its thumbnail. qu-jupyter install --system and qu-jupyter uninstall are new.

[0.4.5] - 2026-10-01

Fixed: Qu Studio 0.4.4 opened to a blank dark window. Two copies of React ended up in the bundle (the shared UI components resolved their own React after the Vite 8 upgrade), which crashes React as the app mounts. Studio's build now dedupes React, and CI and the release workflow render the built frontend in a headless browser and fail unless the app mounts. The status bar shows the real version instead of a fixed "v0.1.0".

The command line: qu script.qu [args] runs the script (as qu run); options may come before the command (qu --live repl x.qu, qu --sandbox x.qu); qu repl x.qu streams the script's output -- and every line typed at the prompt -- as it is printed instead of after it finishes. help(name) links to the published reference and the catalog on GitHub instead of repository paths an installed qu does not have.

Colours: rgb(0.5, 0.75, 1.0) reads MATLAB-style fractions (it rounded to near-black #010101), scale=1/scale=255 says which outright, and a bare vector color = [0.5, 0.75, 1.0] is a colour -- it reached the SVG verbatim and drew black. A non-colour value for color= is an error that says what is accepted.

Changed: remove_file/remove_dir send to the Recycle Bin/Trash by default. A deleted file can be recovered unless the call says permanent=true (the old recycle_bin=false still means permanent; contradicting the two is an error). Where no trash is available, the call fails naming permanent=true instead of deleting for good.

File history: file_versioning(true) (or QU_FILE_HISTORY=1) keeps the current content of every file a builtin is about to overwrite -- write_text, write_csv, save, savefig, fopen("w"/"a"), Office save_as, overwriting copies/moves, permanent deletes and more -- in a .qu-versions/ folder beside it, the same store Qu Studio's editor already keeps, so either sees the other's versions. file_history(path) lists them, read_version(path, v) reads one, restore_version(path, v) puts one back (undoably). 50 versions per file; files over 100 MB are not copied.

text(x, y, s, rotate=, align=): a label tilted (degrees, anticlockwise) and anchored exactly at its point by the given edge -- gene names at 45 degrees over their arrows, labels at computed positions. d[key] reads a dict (a missing key is an error listing the keys; get stays the forgiving form). Named y ticks (yticklabels) widen the left margin instead of running off the canvas. New catalog example qu_genome_synteny.qu: a gggenomes-style synteny map (gene arrows, homology ribbons with inversions, a transposon with its GC content) in about 100 lines of plain Qu.

import pptx edits inside slides: speaker notes can now be written (set_notes, creating the notes page -- and a notes master if the deck has none -- for a slide that has no notes), shapes can be listed with their kind, position and text (shapes) and edited (set_shape_text, set_slide_title, move_shape, resize_shape, delete_shape, bring_to_front, send_to_back), text runs can carry hyperlinks (set_link, links), and the theme's colour and font schemes can be read and set (theme_colors, theme_fonts, set_theme_colors, set_theme_fonts). Each edit rewrites only the parts it touches.

[0.4.4] - 2026-09-29

Every family on the distribution-relationships chart, under MATLAB's names: continuous and discrete uniform, Bernoulli, binomial, geometric, negative binomial, Poisson, hypergeometric, normal, log-normal, exponential, Weibull, gamma, beta, chi-square, Student t and F, each with *pdf (mass for the discrete ones), *cdf, *inv, *rnd and *stat (.mean/.var), and maximum-likelihood *fit for twelve of them (.params, .loglik; normfit, lognfit, expfit, gamfit, wblfit, betafit, unifit, poissfit, binofit, geofit, nbinfit, bernfit) -- 76 new builtins. Discrete masses use Loader's saddle-point algorithm (as R/SciPy), full precision where a difference of lgammas loses 12 digits; discrete samplers invert the CDF exactly (table-driven for wide distributions: a million binomial(1e6, 0.3) draws in 33 ms). Checked against SciPy and mpmath: worst error a few ulp.

Parameter uncertainty on every least-squares fit (curve_fit, least_squares, circuit_fit/sysid): stderr, cov, correlation, t, p_values (Student t on dof = n - p), ci at level=, rmse, sigma, and a note for zero degrees of freedom or unidentifiable parameters. bootstrap=N (with seed=) adds residual-resampling bootstrap_stderr/bootstrap_ci. method= selects "lm" (default), "gauss_newton" or "gradient_descent". summary(fit) prints the parameter table. least_squares flags parameters pinned on a bound in at_bound.

Accuracy and performance audit (docs/perf-audit-2026-09-28.md): sum, mean, dot, cumsum and tensor sum/mean use compensated (Neumaier) summation; mean no longer overflows on large finite values; norm no longer overflows/underflows; var/std use a corrected two-pass on the compensated mean. median/quantile use selection instead of a full sort (5-11x faster, identical results), which also speeds up table_describe. s = s + "..." string building appends in place when the terms provably run no user code (linear instead of quadratic). Leak probes (streamed printing, Office handles, repeated fits, general values) show flat memory.

sleep takes a duration in any time unit -- sleep(2 s), sleep(250 ms), sleep(10 us) -- and a bare number stays milliseconds, as it always was in the engine (the reference had wrongly documented seconds; the one book example that followed it, sleep(0.3), now reads sleep(300 ms)). A non-time unit is an error, and sub-millisecond durations are no longer truncated to whole milliseconds.

Release notes on GitHub now carry the changelog since the previous release instead of the whole file.

Boolean masks read as 0/1 in numeric reductions and arithmetic, as in MATLAB and NumPy: sum(x <= c) counts, mean(x <= c) is the fraction (an empirical CDF), cumsum/prod accept masks, (x > 0) * 2 is numeric, and any(mask)/all(mask) take a bare mask. Indexing with a mask is unchanged.

Dependencies: ureq 3, zip 8, calamine 0.36, rust_xlsxwriter 0.99, wgpu 30, nalgebra 0.35, thiserror 2, libloading 0.9, hmac 0.13 / sha2 0.11, tokenizers 0.22, hdf5-metno 0.15 (http_get, qu-gpu and Qu Studio's hosted-LLM client ported). hf-hub is gone: 1.0 is async-only, so llm_load now downloads model files itself (streamed through a .part file, so an interrupted download is never mistaken for a cached one), reads models an earlier version already cached, and honours HF_HOME/HF_HUB_CACHE/HF_TOKEN; the musl OpenSSL workaround it needed is gone with it. Frontends: vite 8 (the npm audit is clean in both packages), fast-uri 3.1.8.

[0.4.3] - 2026-09-28

Office toolkit (docs/design/toolkit-office.md): import docx, import pptx, and in-place workbook editing in import xlsx. Documents are handles (open/new ... save_as), and editing is surgical -- only the parts an operation touches are rewritten, everything else in the package (themes, fonts, embedded objects, macros, custom XML) is kept byte for byte. Word: text, paragraphs, headings, find/replace across runs and every story part, headings/paragraphs/tables/images/page breaks, cells, comments, footnotes, tracked-change accept/reject, to_markdown, to_latex (figures extracted). PowerPoint: slide text/titles/notes, replace, add slides from layouts, delete/move/duplicate/hide, text boxes, images and tables at mm positions. Excel: cells, formulas with fill-down reference shifting, ranges, sheets, rows/columns, formats, merge, freeze panes, defined names. to_pdf for all three through LibreOffice when installed. New crates qu-ooxml (shared package layer), qu-docx, qu-pptx; qu-xlsx gains umya-spreadsheet. Function names avoid every builtin, so these imports never make a builtin ambiguous.

pdf.render(src, page, dpi=): a PDF page to an Image through PDFium, loaded at run time (no native build step); QU_PDFIUM or a library file beside qu.

Statistics and models: normcdf/norminv, tcdf/tinv/tpdf, chi2inv, fcdf/finv/fpdf, gamcdf/gaminv/gampdf, betacdf/betainv/betapdf, expcdf/expinv/exppdf (MATLAB names and parameterisations, broadcasting); lda_model, qda_model, pls_model, ica_model/ica on the predict/transform protocol.

load_library/native_call: call C functions in a .dll/.so/.dylib with a stated signature (scalars of one type, or double-array kernels); refused under --sandbox.

Output: qu run and qu eval now stream output as it is printed (line-flushed on a terminal, buffered into files and pipes) instead of printing everything when the script ends -- a watcher loop shows each round, and output before an error is no longer lost; flush() and sleep/read_input push pending output out; --report keeps the old behaviour and --live still forces per-line flushing into pipes. replace/regex_replace gain keep= (leave matches inside the kept text alone -- the stand-in for lookahead). qu eval joins all its arguments and strips the single quotes cmd.exe leaves in place.

regionprops/blob_stats, image_regions and image.regions now share one measurement implementation; names and result shapes are unchanged.

Behaviour changes: image_regions(..., unit="px", pixel_size=...) is now refused (it labelled scaled numbers as pixels), and unit="px" without pixel_size= is accepted. qu-image is always linked; the image feature still gates the image.* module.

Known open: sleep takes milliseconds while its documentation says seconds -- unchanged pending a decision on which is right.

[0.4.0] - 2026-09-24

ML toolkit: function-level autodiff transforms (jacobian, hessian, value_and_grad, vmap, check_grads) on top of the existing reverse-mode tape; circuit_fit/sysid wiring the existing EIS circuit element set into the fit/predict/score protocol, with AIC-based topology selection over a 7-candidate ladder; four new estimators on the ModelHandle protocol (isolation_forest, gaussian_process, nmf, arima). hdbscan/umap deliberately deferred -- both are hard to get right from scratch and a shape-correct-but-subtly-wrong version is worse than not shipping. A real nmf.predict convergence bug (re-encoding a fitted row returned different coefficients than the fit itself assigned it) was caught by known-answer tests and fixed to run to convergence.

Shell/process toolkit: setenv/unsetenv, env= on exec/shell, kill(pid) and timeout= on exec (both tree-killing via taskkill /T on Windows so a cmd /C-wrapped child's own children don't survive past the deadline), and a VB.NET-Process-style async handle (process_spawn/process_poll/process_read/process_wait/ process_kill/etc.) for spawning without blocking and polling incrementally.

Six more toolkit branches landed: PID/sliding-mode/fuzzy-PID controllers; advanced time-frequency (signal-timefreq-advanced); JPEG/ TIFF image codecs; SVG basic I/O (save_svg/load_svg, svg.rect/svg.circle/svg.line/svg.path/svg.text); a PDF toolkit (import pdf -- page_count/info/merge/extract_pages/ extract_text, on lopdf).

Fixed a pre-existing, previously-unexplained test-infrastructure bug: several qu-interp tests (the NN/autodiff-heavy ones) overflow the default debug-build stack on Windows and abort the whole test binary, silently truncating every test scheduled after them. engine/.cargo/ config.toml already set RUST_MIN_STACK for this, but Cargo only discovers .cargo/config.toml walking up from the working directory, never down into subdirectories -- invisible to cargo test --manifest- path engine/Cargo.toml run from the repo root. A matching root-level .cargo/config.toml closes that gap.

Auto-laid-out diagram rendering, the box/arrow "smart art" layout layer noted as missing in BACKLOG.md (2026-09-10): diagram_pipeline(fn) draws a |> pipe chain as a left-to-right chain of boxes; algorigram(name) draws a function's control flow (if/else, loops, select case, try/catch) as a top-to-bottom flowchart. Both return SVG and accept file= to write .svg/.html directly (.png is refused, same whole-figure-rasterizer scoping as savefig).

The rest of the signal toolkit deferred out of v0.3.0 (§8-§11 of docs/design/toolkit-signal.md), now shipped: UART/SPI/I2C/CAN decoders (decode_uart/decode_spi/decode_i2c/decode_can); a streaming block/state processor (block_process, blocks, processor/process); WAV file I/O (codec.write_wav/encode_wav, joining the existing codec.decode_wav); transfer functions and impedance (transfer_function, impedance(voltage, current, ...), bode); calibration, units, markers and metadata (calibrate/apply_gain/apply_offset/convert_unit, metadata()/set_metadata, add_marker/add_region) -- Value::Signal now carries this as a third field, empty and free for every signal that never sets one; an LMS adaptive FIR filter (lms_init); multirate resampling (upsample/downsample/resample_int), plus a behavior change: resample_to() now anti-alias filters before decimating when lowering the rate, instead of aliasing (it was pure linear interpolation between grids before); digital-comms coding (qam_modulate/ qam_demodulate, hamming74_encode/hamming74_decode, crc/ crc_check); delay-and-sum beamforming (beamform, steer_delays). Also 3 P0 correctness fixes from a defect review: delay() no longer overflows on an extreme shift, gain() rejects a NaN db instead of silently producing NaN samples, and block_process() tags its output Signal-vs-Vec by genuine per-block agreement rather than a coincidental aggregate-length match.

[0.3.0] - 2026-09-18

Signal toolkit expansion, scoped to what actually shipped (see docs/design/toolkit-signal.md's "v0.3.0 status" note for the full shipped-vs-deferred breakdown): diagnostics and integrity checks (is_clipped/find_clipping/detect_saturation/verify_signal), rolling statistics, pulse/edge metrics, missing-sample and outlier handling, wrap()/remove_noise(), and a mem_usage() builtin plus a profile_start()/profile_end() region profiler for comparing methods on memory as well as speed. qu repl <file.qu> (run a script, keep its session alive) and a real Jupyter kernel (qu-jupyter, notebooks in VS Code/JupyterLab with persistent state across cells) round out the run/REPL story. Qu Studio's Run button is now backed by a persistent kernel session instead of a one-shot subprocess per click. Protocol decoders, audio file I/O, calibration, and transfer-function/impedance measurement (§8/§9/§10/§11 of the signal-toolkit spec) remain unstarted and are deferred to the next version.

[0.2.4] - 2026-09-18

Dependabot security fixes (26 of 32 alerts, all criticals), the linux-x86_64-static (musl) release build fixed for real (vendored OpenSSL, then a second musl-only C++ toolchain gap in tokenizers), Node.js 20->22 in CI (20 is past end-of-life), a Windows-runner npm install fix, and the macos-x86_64 leg dropped from the release matrix (the hosted runner never got scheduled, not a build problem). Also covers everything that had accumulated under [Unreleased] through v0.2.2/v0.2.3, retitled here since those tags shipped without a changelog update.

Breaking

Added

Changed

Fixed

Documentation

Verification

Ports checked against their reference implementations, on the same inputs:

PortResult
Per-tone EIS extractionsix reported figures reproduce the Python exactly
Kramers-Kronig fit with automatic parametersevery reported number exact; 0.014 s against numpy's 0.060 s
Known-support recoverythe reference's operational facts reproduced
Half-rule conditioningexact
nnls, least_squaresmatch SciPy to the digit
Complex linear algebramatches NumPy; L·U and Q·R reproduce to 1e-12