Fix bugs from a worked-example sweep: core, images, optimization, graphics, graphs/ML, notebook ; v0.231 - #81
Merged
Merged
Conversation
…sages in cells, .mathilda files Five front-end bugs, plus the two cell limits found alongside them. Cell > Convert to: the three menu items called retypeActiveCell, which only relabels the toolbar's record of the active cell. Menu and toolbar now share convertCell(store, id, type) in cellCommands.ts, which retypes the cell in the store (source and output kept) and refuses reference-page cells. Stacking: the properties panel (40) and find bar (45) sat under the focused view (50). All window-level z-indexes now come from one --z-* scale in app.css; overlays sit above the notebook view and status dock, menus above everything. Find bar: a jump selected the match and focused the cell, so the next Enter replaced the match with a newline. The current match is now painted (a CodeMirror mark decoration in code cells, the CSS Custom Highlight API in prose) and scrolled into view while the find field keeps focus; the first Enter lands on the shown match and Escape places the caret on it. Print and messages: pipe mode sent Print text raw to stdout (dropped by the Rust side) and messages to stderr (only logged). A request may now carry "cell": true; the kernel then captures each statement's Print output and messages and sends them as "stream" and "message" lines before its result. Requests without the flag are unchanged (raw Print, stderr, "Null" payloads), which site/generate.py and the audit tools depend on. The Rust side also forwards stray non-JSON stdout and stderr to the cell for older kernels. .mathilda files: the Open dialog offered them but parsed every file as library JSON. They now open through the Rust stanza parser (moved to notebook_format.rs, stricter marker matching, preamble kept, CRLF accepted) as a new notebook, and Save As can export the current notebook in that format. Cells: statements on separate lines are each evaluated, as in Mathematica (parse_next_expression, whole cell syntax-checked first; ';' and Null suppress a result). Requests are read at any length instead of into a 10 KB buffer, and all JSON escapes including \uXXXX are decoded. Also fixes the two existing svelte-check errors (untyped plotly import, rename field typed as an input). Tests: make check-pipe-protocol (new, both request modes), npm run check:notebook (new), extended check:search, 16 Rust unit tests including a round trip through the real kernel.
…logy
ImageRotate turned clockwise on both the quarter-turn permutation path
(rot90_run had its odd turns swapped) and the free-angle resampler
(rot_free_run used the y-up inverse matrix on y-down rows). Both now
turn counterclockwise for a positive angle, as the docstring and
Mathematica say. Adds Mathematica's side forms, ImageRotate[img, side]
and ImageRotate[img, side1 -> side2]. Also frees the N[angle] wrapper,
which leaked on every call with an angle argument.
Image[data, type] now coerces the data to the stated type by rounding
and clipping, as Mathematica documents: Image[{{0, 300}}, "Byte"]
stores {{0, 255}}. Adds the "Bit16" type, accepts "Real32" and "Real64"
as synonyms for "Real", and adds Image[image, type] / Image3D[vol, type]
conversion. The old path had two root causes. A declined call stayed
unevaluated in exactly the canonical shape, and image_info checked only
the shape, so ImageQ gave True and ImageData divided 300 by 255. And an
auto-packed int64 buffer passed the fixed-point early return without its
values being checked. image_info now checks every element of nested
data, and the fixed point checks the range of integer buffers. Data
that cannot be represented (non-numeric, complex, ragged) is still left
unevaluated.
Dilation/Erosion/Opening/Closing keep a "Bit" image "Bit" (planar and
volumetric). ImageCorrelate accepts a single-channel Image as the
kernel or template.
Tests: new regression rows in test_image.c for rotation direction (both
paths, the side forms, colour images, packed images), type coercion
and conversion (planar, volumetric, packed, and with packing disabled),
Bit morphology, and image templates. Fixes
test_ncc_finds_the_patch_it_was_given: its test image contains a
second copy of the template shifted by a constant brightness, which NCC
scores as an exact tie. That test failed on main and aborted the suite.
Refpage docs/spec/builtins/image-processing.md updated.
…edComponents; robust mixture floor
FindSpanningTree ignored EdgeWeight. It returned a BFS tree, total weight 13 where the
minimum is 11. It now follows Mathematica case by case, checked output for output against
Mathematica 15:
- weighted undirected: Kruskal with union-find, giving a minimum spanning forest; ties
are broken by vertex position, which matches Mathematica's choice;
- weighted directed: Chu-Liu/Edmonds, O(E log V), with leftist heaps and a rollback
union-find. It gives the minimum arborescence, or the lightest branching with the
fewest roots;
- unweighted: a BFS forest, or a forward BFS branching rooted in decreasing DFS
finishing time.
Weights are compared exactly as GMP rationals, and each tree edge keeps its own weight
and capacity. {g, v} gives the tree grown from v, an invalid v emits
FindSpanningTree::inv, and options are accepted. Mixed graphs stay unevaluated, as in
Mathematica.
Graph[edges, opts] now takes options: EdgeWeight, and a new stored EdgeCapacity, in
either constructor form and in any order. The canonical graph lists them after the edges
in a fixed order. All consumers read them through graph_edge_weight_list and
graph_edge_capacity_list instead of assuming argument 3. FindMaximumFlow falls back to
the graph's own EdgeCapacity when no option is given.
ConnectedComponents on a graph with directed edges now gives the strongly connected
components, in Tarjan completion order, which is exactly Mathematica's order. On an
undirected graph the largest component comes first. The {v...} form is new.
WeaklyConnectedComponents stays weak and is ordered largest first. ConnectedGraphQ
requires strong connectivity on directed graphs. The refpage deviation note is removed.
LearnDistribution with Method -> "GaussianMixture" measured its variance floor as the
median nearest-neighbour distance over all points. On rounded data most points have an
exact duplicate, so the median was 0 and the floor fell to 1e-300. The spacing is now
measured between distinct points, with a floor of at least 1e-4 of the average variance,
and k is capped by the number of distinct points. On 45 iris petal lengths the fit goes
from 9 components, some of a single point, to 2, the model Mathematica learns.
Regression tests in graph_tests, hypergraph_tests and ml_dist_tests. Refpages updated in
graphs.md, hypergraphs.md and machine-learning.md.
…ch, held starts, MPFR bracket, Powell kinks, silent non-convergence
NMaximize[{f, c1, c2, ...}] negated the whole list (Times[-1, {...}] threads
over the constraints) and returned {-1e+300, garbage). NMaximize and
FindMaximum now negate only the objective; FindMinimum/FindMaximum also accept
the {f, c1, c2, ...} form.
FindMinimum[f, {x, x0, x1}] treated x0, x1 as bounds and returned x0. They are
now the two starting values: the interval is the initial bracket only when an
interior golden-section point lies below both ends, otherwise the bracket is
expanded downhill (new fm_bracket2 and a two-start branch in fm_bracket_mpfr).
HoldAll kept starting values unevaluated: {x, s} with s a Table iterator was
read as two variables. A trailing symbol that carries a value (or a numeric
constant such as Pi) is now a starting value (fm_symbol_is_free); a symbol
bound to a spec list is resolved; scalar option values (Method, MaxIterations,
WorkingPrecision, AccuracyGoal, PrecisionGoal) are evaluated.
The 1-D bracket expansion (machine and MPFR) backs off toward the last good
point when a step leaves the objective's real domain, fixing "MPFR
bracket-finding failed" on x^2 - 2 Log[x] from x = 3.
Powell restarts a stalled cycle from a deterministic rotated orthonormal basis
and accepts convergence only when that also fails, so it leaves Abs/Max kinks
where every axis direction is non-descent.
Every local method emits FindMinimum::cvmit when MaxIterations is reached
(silent inside the penalty wrapper and NMinimize's polish). ConjugateGradient
uses a strong-Wolfe line search with Powell restarts on its unconstrained
path. COBYLA separates the monotone resolution rho from a ratio-controlled
trust radius and takes the Euclidean trust-region step instead of a box corner.
The projected BFGS, CG and Newton solvers hold binding box bounds fixed
(fm_box_binding_mask), so the default method reaches 17.0140 on HS071 (was
19.84) and the NMinimize fixed-charge-flow polish returns a feasible flow; that
test asserts a feasible optimum again. SLSQP abandons feasibility only after 30
non-improving iterations (was any 8 infeasible ones), so a 10-link hanging
chain converges. The NelderMead restart polish no longer keeps a fixed-penalty
vertex 1e-6 outside an active constraint; the NM_FEAS_RETURN comment now states
its units and measurements.
Docs: numerical-calculus.md describes NMinimize as non-HoldAll throughout,
drops Or from its unsupported list, and documents the two-start form, argument
evaluation, cvmit, and the Powell/CG/COBYLA changes; docstrings updated.
Tests: new regressions in test_findmin.c, test_findmin_powell.c,
test_findmin_cobyla.c, test_findmin_slsqp.c and test_nminimize.c (each fails on
the parent commit); all 23 optimization-related suites pass.
…s, exact coordinates
Show[g1, g2, ...] and Show[{g1, g2, ...}] used to stay unevaluated. They now
overlay their inputs, as in Mathematica. Each input's primitives become one
List directive scope, and the input's own PlotStyle (the style a single-curve
Plot/ListPlot is drawn in) is baked into that scope. Options come from g1 unless
trailing options override them. PlotRange is the union of the inputs' ranges
(an input without one contributes its primitive extent) and stays automatic
when no input fixes one. Legends are concatenated, $PlotResample is dropped,
and 2D and 3D inputs do not mix.
Style directives: add Dashing, AbsoluteDashing, AbsoluteThickness,
AbsolutePointSize, Directive, the named sizes Tiny/Small/Medium/Large, and
the OwnValues Dashed, Dotted, DotDashed, Thick and Thin, which evaluate to
their long forms. The Raylib renderer (screen, PNG, JPEG) and the PDF writer
resolve all of them through shared helpers in plot_common.c: Thickness,
PointSize (a diameter) and Dashing are fractions of the plot width; the
Absolute forms are in points. A List is now a directive scope in both back
ends.
PlotStyle -> {s1, s2, ...} was ignored by multi-curve Plot, ListPlot and
ParametricPlot. Each style is now baked into its curve and cycles. A
non-colour style gets its own List scope, and legend swatches use the styled
colours.
PDF export: honour PlotStyle, Epilog/Prolog, PlotLabel, AxesLabel,
PlotLegends (a swatch legend), Dashing and the absolute sizes. Read every
coordinate through gfx_coerce_double, so primitives with exact coordinates
(Line[{{0,0},{1/2,1}}], Disk[{Pi/4,1/4},1/10], Point, Polygon, Circle,
Arrow, Text) are no longer dropped or misplaced. Circle[] and Disk[] draw the
unit circle.
PNG export: the rasteriser read PointSize[d] as a radius in plot coordinates,
so over a large coordinate range the dot became sub-pixel and vanished.
PointSize, and likewise Thickness, now use the PDF's fraction-of-width
meaning. Prolog/Epilog start from black, and a thick Circle outline is drawn
at its thickness. Mesh dots use PointSize[0.005].
Fix a leak in Plot/ParametricPlot option handling:
evaluate(expr_copy(rhs)) leaked the copy of every held option value.
Tests (graphics_tests): Show combination, option precedence, PlotRange union,
per-input styles and legends; directive symbols and size resolution;
per-curve PlotStyle; the PDF content stream for exact coordinates, PlotStyle,
dashes, Epilog/Prolog order, labels, legend and list scoping. There are also
opt-in raster checks (MATHILDA_TEST_RASTER=1) for PointSize and Dashing in
PNG.
Output of make docs for the pages the graphics change touched: Show's multi-graphic forms and examples, Export's PDF/PNG option and directive support, and ParametricPlot's test link. The other regenerated pages differed only in run-dependent noise and are left out.
…ator bounds; add Log10/Log2
Variadic comparison chains. The parser nested a run of === as
SameQ[SameQ[a, b], c], comparing True with c, so 1 === 1 === 1 was False;
=!= and != had the same defect (1 != 2 != 1 evaluated True != 1). A bare
run of SameQ / UnsameQ / Unequal now collects into one variadic call, as in
Mathematica; a parenthesised operand still nests. Unequal mixed with an
ordering operator joins the chain's Inequality (a < b != c), which
Inequality now evaluates and prints. Inequality decides its == and != pairs
through the Equal / Unequal builtins, so list tolerance and the exact zero
test apply inside chains.
Equal on Lists. Lists that were not structurally identical never reached a
numeric comparison, so {0.1 + 0.2} == {0.3} stayed unevaluated. Two Lists
are now compared elementwise with Equal, recursively: True when every
position is, False for different lengths or one decidably unequal position,
otherwise unevaluated. Unequal uses the same verdict.
Normal[SparseArray[...]]. Mathilda has no sparse storage and Normal handed
the SparseArray back unchanged. New src/sparsearray.c builds the dense
nested List for explicit positions (with or without dims and default),
{p1, p2} -> {v1, v2}, pattern rules, Band[...], dense lists and the
InputForm of Mathematica's internal SparseArray[Automatic, ...] form, at any
depth of Normal's argument.
Numeric symbolic iterator bounds. Table[x, {x, 0., 2 Pi, 1.}] and
Range[0, 2 Pi, 1.] stayed unevaluated, Do over {x, 0, 2 Pi, Pi/2} never
terminated, and Sum[i, {i, 1, Pi}] took its closed form over the raw bound
(Pi (1 + Pi)/2 instead of 6). iter_spec_parse_lattice (Table, Do, Sum,
Product) and Range now replace a real NumericQ, non-explicit upper bound by
the exact last lattice point imin + Floor[(imax - imin)/di] di; the
plotters keep the plain parser, whose ranges are continuous. Loops refresh
their termination value from a symbolic running value. Range with BigInt
bounds is counted exactly in GMP.
Log10 and Log2. Neither existed. Both are now Listable NumericFunction
builtins defined as Log[10, z] / Log[2, z] (Log10[x] -> Log[x]/Log[10]),
with libm log10/log2 for positive machine reals so exact powers stay exact,
NDArray kernels for the packed and visible-array surfaces, and Compile[]
lowering at scalar and rank-1 shape. Log[b, z] now returns the exact
rational whenever b and z are powers of one common rational
(Log[4, 8] = 3/2, Log[10, 1/100] = -2, Log[10, 10^30] = 30).
Tests: new sparsearray_tests suite; regressions added to parse_tests,
comparisons_tests, iter_tests and logexp_tests. Refpages updated in
docs/spec/builtins (comparisons, elementary-functions, lists-and-iteration,
control-flow, calculus, power-series, data-structures).
Record the batch of fixes on this branch in the weekly changelog and bump $VersionNumber to 0.231. It covers comparison chains, list Equal, Normal[SparseArray], symbolic iterator bounds and Log10/Log2; image rotation direction and stated-type coercion; the FindMinimum/NMinimize family; Show and PDF/PNG export styling; weighted spanning trees, Graph[edges, opts] and strong components; the LearnDistribution mixture floor; and five notebook front-end bugs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR fixes a batch of bugs that turned up while running several hundred worked examples through the binary, covering graphs, optimization, machine learning, image processing, graphics export and the notebook front end. Each fix has a regression test, and the full ctest run has no new failures against
main(v0.230). Bumps$VersionNumberto 0.231.Changes
Core language
a === b === c,=!=chains and!=chains now parse to one variadic call (SameQ[a, b, c]), so1 === 1 === 1isTrue.!=also joins mixedInequalitychains.EqualandUnequalon Lists compare element by element with the inexact tolerance, recursively:{0.1 + 0.2} == {0.3}isTrue.Normal[SparseArray[...]]produces the dense array. Newsrc/sparsearray.chandles position rules, pattern rules,Band, defaults and Mathematica's internal CSR form.Table,Range,Do,SumandProductaccept symbolic numeric bounds with a step (Table[x, {x, 0, 2 Pi, 1.}]). This also fixes aDoloop over a symbolic lattice that never ended, and Sum's closed form using the raw bound.Log10andLog2, with NDArray/packed kernels andCompilelowering (scalar and rank-1 both reportCompiled -> True).Log[b, z]gives the exact rational whenbandzare powers of one rational.Image processing
ImageRotatenow turns counterclockwise on both the quarter-turn and free-angle paths, as documented. Adds thesideandside1 -> side2forms, and fixes a leak of theN[angle]wrapper.Image[{{0, 300}}, "Byte"]stores{0, 255}. Adds"Bit16".Image[img, type]converts between types. Invalid data leaves the call unevaluated andImageQfalse."Bit"image"Bit".ImageCorrelateaccepts a single-channel Image template.test_ncc_finds_the_patch_it_was_given, which fails onmainand abortedimage_tests.Numerical optimization
NMaximizeandFindMaximumwith{f, c1, c2, ...}negate only the objective. They used to return garbage.FindMinimum[f, {x, x0, x1}]treats the two values as starting points and searches. It used to returnx0.Table.WorkingPrecision -> 30onx^2 - 2 Log[x]now works.FindMinimum::cvmitwarning is emitted by all local solvers atMaxIterations.Orconstraints are supported).Graphics
Showcombines several graphics, nested lists and trailing options. PlotRange is merged and each input keeps its ownPlotStyle.PlotStyle,Epilog/Prolog,AxesLabel,PlotLabeland a simplePlotLegends.Dashing,AbsoluteDashing,AbsoluteThickness,AbsolutePointSize,Directive, named sizes, andDashed/Dotted/DotDashed/Thick/Thin. Multi-curve plots apply per-curvePlotStyle.N) are drawn in every primitive. They used to be dropped.PointSizeandThicknessmean the same thing in the PNG and PDF renderers (a fraction of plot width). This fixes points that disappeared over large coordinate ranges.Graphs and machine learning
FindSpanningTreeon weighted graphs returns a minimum spanning forest (Kruskal) or, for directed graphs, a minimum arborescence (Chu–Liu/Edmonds). Weights are compared exactly and carried over to the result. The{g, v}form is supported.Graph[edges, EdgeWeight -> w]and a storedEdgeCapacitywork in both constructor forms, with one canonical form.FindMaximumFlowuses the graph's own capacities.ConnectedComponentson directed graphs returns strongly connected components in Mathematica's order.ConnectedGraphQrequires strong connectivity. Undirected components are listed largest first.LearnDistributionGaussian-mixture variance floor is measured between distinct points, so on the rounded iris data it finds 2 components (as Mathematica does) instead of 9 degenerate ones.Notebook front end
convertCell.Printoutput and messages reach the cell. There is an opt-in"cell": truepipe request, withstream/messagelines; requests without the flag behave as before..mathildafiles open and Save As through the existing Rust stanza parser.Testing
mainbuild: no new failures.image_testsnow passes; it fails onmain. The remaining failures are the same as onmain: corpus suites that assumetests/build, plus theiterandseriessuites that were already failing.test_parse,test_comparisons,test_logexp,test_iter,test_sparsearray(new),test_image,test_findmin,test_nminimize,test_findmin_powell,test_findmin_cobyla,test_findmin_slsqp,test_graphics,test_graph,test_hypergraph,test_ml_dist. Each new regression test fails on the parent commit.make check-c99,check-messages,check-packed-aware,check-array-exactnessandcheck-nd-surfacespass.check-compile-coveragereports only heads not touched here (the image heads andPackedArrayQ).leaks --atExiton a script that exercises every fix: 0 leaks.npm run checkhas 0 errors. Newcheck:notebookpasses 26/26;check:searchpasses 24/24.cargo testpasses 16/16. Newmake check-pipe-protocolpasses 22/22. The desktop app was not launched to check the UI changes by eye.Imagewithout a type is stored as"Byte", as onmain. PNG export crashes inside GLFW when no display is available; this happens onmaintoo, so the two raster tests are opt-in (MATHILDA_TEST_RASTER=1).JIRA Ticket
N/A