Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/spec/builtins/file-io.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,13 @@ Writes an `Image` to a raster image file, or a `Graphics` object to an image fil
plot). Coordinates may be exact (`1/2`, `Pi/4`, `Sqrt[2]`): they are converted the same
way the on-screen renderer converts them. Text uses the PDF base-14 Helvetica, so no font
is embedded. This is the recommended format for print and for the book.
- In the PDF, `Text[s, pos, {ox, oy}]` aligns as Mathematica does (`{-1, 0}` puts the left
end of `s` at `pos`, `{0, 0}` centres it, using the Helvetica advance widths), and
`Text[Style[s, n | FontSize -> n | colour, ...], ...]` sets that string's size and
colour. `Arrowheads[s]` fixes the arrowhead length at `s` times the plot width, and an
arrow's shaft stops inside its head rather than poking past the point.
`AspectRatio -> Automatic` maps x and y with one scale (the page height follows the
data unless `ImageSize -> {w, h}` fixes both, in which case the picture is centred).
- **PNG** and **JPEG** render through the graphics backend into an offscreen buffer, so the
file is pixel-identical to the on-screen plot (the same axes, ticks, labels and text).
They therefore need graphics support compiled in (`USE_GRAPHICS`) **and** a usable GUI
Expand Down
109 changes: 88 additions & 21 deletions docs/spec/builtins/graphs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1133,34 +1133,101 @@ Out[5]= TopologicalSort[Graph[<3 vertices, 3 edges>]]

## GraphPlot

- `GraphPlot[g]`: a `Graphics[...]` object drawing `g`.

**Features**:
- `Protected`. Vertices are laid out on a circle, each drawn as a `Disk`; edges
are `Line`s. The specification calls for one `Text` label per vertex as well,
but the current binary emits no `Text` primitives (see the example below).
- Renders through the standard graphics path (a window when `USE_GRAPHICS=1`,
the text placeholder otherwise).
- MVP limitations: directed edges are drawn as plain lines (no arrowheads yet);
a force-directed layout is a future hook. Mathematica's `GraphPlot` uses a
spring-electrical layout.
- Unevaluated on a non-graph.

```mathematica
In[1]:= Head[GraphPlot[CycleGraph[8]]]
- `GraphPlot[g]`: a `Graphics[...]` object drawing the graph `g`.
- `GraphPlot[{u -> v, ...}]`: draws the graph of a list of rules.
- `GraphPlot[g, opts]`: with the options below; any other option (`ImageSize`,
`PlotLabel`, `Background`, ...) is passed through to `Graphics`.

**Features**:
- `Protected`. Implemented in `src/graph/graphplot.c` over the layout engine
`src/graph/glayout.c`. **Deterministic**: no random numbers anywhere, so the
same graph always gives an identical `Graphics` expression.
- **Default layout** (`GraphLayout -> Automatic`): a forest with a branching
vertex is drawn as tidy layered trees (hanging from the tree centre, or from
the source of an arborescence); an all-directed acyclic graph as a layered
drawing; everything else (paths and cycles included) by **stress
majorization** (SMACOF on BFS graph distances, weights `d^-2`), started from
Pivot MDS. Components of up to 60 vertices also try circle starts and Tutte
(barycentric) starts from shortest cycles, and keep, among the drawings within
10% of the least stress, the one with the fewest edge crossings; a
crossing-free drawing may cost up to 2.5x the stress when it removes at least
four crossings (so the dodecahedron comes out as its Schlegel diagram while
the cube stays the textbook Necker cube). Each drawing is rotated to a
canonical orientation (principal axis horizontal; snapped to the axes when the
edges are four-fold, then straightened so grids come out exactly as grids;
vertex 1 on top when there is no preferred axis). Every connected component is
laid out on its own and the components are shelf-packed, largest first, with
isolated vertices gathered into a square block.
- **Cost**: full stress majorization up to 1000 vertices per component, Pivot
MDS (50 pivots, `O(k (n + m))`) above that. A 500-vertex random graph takes
about 0.15 s, a 25x20 grid 0.03 s.
- `GraphLayout` values: `"StressEmbedding"`, `"SpringElectricalEmbedding"` (Hu's
spring-electrical model, exact repulsion up to 1000 vertices, grid
cut-off above), `"CircularEmbedding"` (`VertexList` order, vertex 1 on top),
`"LayeredEmbedding"` / `"LayeredDigraphEmbedding"` (longest-path layers for a
DAG, BFS layers from the centre otherwise, dummy vertices on long edges,
barycentre crossing reduction, isotonic-regression x placement; wide shallow
drawings get taller layer spacing), `"BipartiteEmbedding"` (the two parts in
two columns, barycentre-ordered; falls back to stress for a non-bipartite
graph), `"GridEmbedding"` (`VertexList` order on a square grid).
- `VertexCoordinates -> {{x1, y1}, ...}` (one pair per vertex, `VertexList`
order) fixes the drawing; `VertexCoordinates -> {v -> {x, y}, ...}` fixes the
given vertices and lays out the rest.
- `VertexLabels -> None` (default) | `"Name"` | `Automatic` | `True` | `All`
labels each vertex with its name; `{v -> lbl, ...}` labels only those. A label
sits beside its vertex on the side with the widest angular gap between the
incident edges (upper right when free), in 10 pt Helvetica, and the frame
grows so no label is clipped.
- **Directed edges** are `Arrow`s with an `Arrowheads` directive sized to the
vertex disks; each arrow starts outside its source disk and its tip stops just
short of the target disk. A mutual pair `u -> v`, `v -> u` is drawn as two
arrows offset to either side.
- `GraphHighlight -> {v, ..., e, ...}`: highlighted vertices and edges are drawn
red (`RGBColor[1, 0, 0]`), vertices 15% larger and edges 2.5x thicker, on top
of the others. Edges may be written `u <-> v`, `UndirectedEdge[u, v]`,
`u -> v` or `DirectedEdge[u, v]`.
- `VertexStyle -> style` or `{v -> style, ...}`; `EdgeStyle -> style` or
`{e -> style, ...}` (a colour, or a list of directives), e.g. a vertex
colouring from `FindVertexColoring`.
- `EdgeLabels -> "EdgeWeight"` writes each weight at its edge midpoint (offset
towards the inside of the drawing); `EdgeLabels -> {e -> lbl, ...}` labels
chosen edges. `VertexSize -> d` sets the disk diameter to `d` edge lengths.
- **Look**: vertices are disks in `RGBColor[0.368417, 0.506779, 0.709798]`
(Mathematica's `ColorData[97]` blue) with a thin darker rim, edges 1.1 pt in
grey-blue `RGBColor[0.571589, 0.586483, 0.699215]`, the disk radius scaled to
the median edge length and the extent of the drawing. The result carries
`PlotRange`, `AspectRatio -> Automatic`, `Axes -> False` and an explicit
`ImageSize -> {w, h}` (300 pt on the long side, growing gently with the vertex
count), so `Export["g.pdf", GraphPlot[g]]` gives a tight, equal-aspect picture.
- Unevaluated on a non-graph, or when an argument after the graph is not a rule.

```mathematica
In[1]:= Head[GraphPlot[PetersenGraph[]]]
Out[1]= Graphics

In[2]:= Count[GraphPlot[CompleteGraph[6]], _Line, Infinity]
Out[2]= 15

In[3]:= Count[GraphPlot[CycleGraph[5]], _Disk, Infinity]
Out[3]= 5
In[3]:= Count[GraphPlot[Graph[{1 -> 2, 2 -> 3, 3 -> 1}]], _Arrow, Infinity]
Out[3]= 3

In[4]:= Count[GraphPlot[CycleGraph[5]], _Text, Infinity]
Out[4]= 0
In[4]:= Cases[GraphPlot[PathGraph[{1, 2, 3}], VertexCoordinates -> {{0, 0}, {1, 0}, {2, 1}}], Disk[p_, _] :> p, Infinity]
Out[4]= {{0.0, 0.0}, {1.0, 0.0}, {2.0, 1.0}}

In[5]:= Count[GraphPlot[CycleGraph[5], VertexLabels -> "Name"], _Text, Infinity]
Out[5]= 5

In[6]:= MemberQ[GraphPlot[CycleGraph[3], GraphHighlight -> {1}], RGBColor[1., 0., 0.], Infinity]
Out[6]= True

In[7]:= {Axes, AspectRatio} /. Rest[List @@ GraphPlot[CycleGraph[4]]]
Out[7]= {False, Automatic}

In[8]:= Length[Union[Cases[GraphPlot[GridGraph[{4, 4}]], Disk[{x_, _}, _] :> Round[x, 0.001], Infinity]]]
Out[8]= 4

In[5]:= GraphPlot[5]
Out[5]= GraphPlot[5]
In[9]:= GraphPlot[5]
Out[9]= GraphPlot[5]
```

## FindVertexColoring
Expand Down
47 changes: 47 additions & 0 deletions docs/spec/builtins/hypergraphs.md
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,53 @@ In[3]:= InputForm[HypergraphStarExpansion[Hypergraph[{Hyperedge[1], 2},{{Hypered
Out[3]= HypergraphStarExpansion[Hypergraph[{Hyperedge[1], 2}, {{Hyperedge[1], 2}}]]
```

## HypergraphPlot

- `HypergraphPlot[h]`: a `Graphics[...]` object drawing the hypergraph `h`.
- `HypergraphPlot[{e1, e2, ...}]`: draws the hypergraph of a list of hyperedges.
- `HypergraphPlot[h, opts]`: with `VertexLabels`, `VertexCoordinates`,
`VertexStyle` and `GraphLayout` as in `GraphPlot`; other options pass through
to `Graphics`.

**Features**:
- `Protected`. Implemented in `src/graph/hyp_plot.c`; Mathematica has no
built-in hypergraph drawing (the Function Repository's `HypergraphPlot` is the
model). Deterministic, like `GraphPlot`.
- **Layout**: the vertices are placed by the stress layout of the star
expansion (one extra node per hyperedge of two or more vertices, joined to its
members), so the members of a hyperedge sit around a common centre and
hyperedges sharing vertices are drawn side by side. `GraphLayout` picks another
embedding of the star expansion; `VertexCoordinates` overrides any subset.
- **Hyperedges**: each is the convex hull of its (distinct) members, inflated
by a margin with rounded corners (sampled every 15 degrees), drawn as a
translucent (`Opacity[0.22]`) filled `Polygon` with a darker outline, in its
own colour of the `ColorData[97]` palette (cycled by hyperedge index). A
hyperedge of size 2 is therefore a stadium (a thick translucent line with round
caps) and one of size 1 a circle around its vertex; an empty one is not drawn.
Larger shapes are drawn first so smaller ones stay visible; a hyperedge sharing
a vertex with smaller ones gets a wider margin, so nested and repeated
hyperedges show as concentric outlines.
- **Vertices** are dark disks drawn on top; labels avoid the directions of the
vertex's hyperedges.
- Unevaluated on a non-hypergraph.

```mathematica
In[1]:= Head[HypergraphPlot[Hypergraph[{{1,2,3},{3,4},{4,5,6},{7}}]]]
Out[1]= Graphics

In[2]:= Count[HypergraphPlot[Hypergraph[{{1,2,3},{3,4},{4,5,6},{7}}]], _Polygon, Infinity]
Out[2]= 4

In[3]:= Count[HypergraphPlot[{{1, 2, 3}, {3, 4}}, VertexLabels -> "Name"], _Text, Infinity]
Out[3]= 4

In[4]:= HypergraphPlot[Hypergraph[{{1,2,3}}]] === HypergraphPlot[Hypergraph[{{1,2,3}}]]
Out[4]= True

In[5]:= HypergraphPlot[5]
Out[5]= HypergraphPlot[5]
```

## HypergraphToGraph

- `HypergraphToGraph[h]`: the directed `Graph` obtained by reading `h` as an
Expand Down
35 changes: 35 additions & 0 deletions docs/spec/changelog/2026-09-28.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
# Changelog: week of 2026-09-28 (Mon) – 2026-10-04 (Sun)

## `GraphPlot` rewritten for publication-quality drawings; new `HypergraphPlot` (v0.237)

`GraphPlot` was an MVP (every vertex on a circle, directed edges as plain lines, axes on).
It is now a deterministic layout engine (`src/graph/glayout.c`) plus a drawing layer
(`src/graph/graphplot.c`) shared with the new `HypergraphPlot` (`src/graph/hyp_plot.c`).

- **Layouts.** Default: tidy layered trees for branching forests, layered drawings for DAGs
(longest-path layers, dummy vertices on long edges, barycentre crossing reduction,
isotonic-regression x placement), stress majorization (SMACOF on BFS distances, Pivot MDS
start) for everything else. Small components try circle and Tutte starts and keep the
drawing with the fewest crossings among those within 10% of the least stress (a
crossing-free drawing may cost 2.5x when it removes four or more crossings), then get a
canonical orientation: principal axis horizontal, axis snap and exact straightening for
grids, vertex 1 on top for symmetric graphs. Components are shelf-packed; isolated vertices
form a square block. `GraphLayout -> "StressEmbedding" | "SpringElectricalEmbedding" |
"CircularEmbedding" | "LayeredEmbedding" | "BipartiteEmbedding" | "GridEmbedding"`.
Full stress up to 1000 vertices per component, Pivot MDS above; 500 vertices in ~0.15 s.
- **Options.** `VertexCoordinates` (list or rules), `VertexLabels` (`"Name"`, `Automatic`,
rules; placed in the widest gap between incident edges, never clipped), `GraphHighlight`
(red, larger vertices, 2.5x thicker edges), `VertexStyle`, `EdgeStyle`,
`EdgeLabels -> "EdgeWeight"` or rules, `VertexSize`; other options pass through to
`Graphics`. `GraphPlot[{u -> v, ...}]` draws a rule list.
- **Look.** Mathematica's vertex blue with a darker rim, grey-blue edges, `Arrow`s whose tips
stop at the target disk (mutual pairs offset apart), no axes, equal aspect ratio, a tight
explicit `PlotRange` and `ImageSize`.
- **`HypergraphPlot[h]`** (new, `Protected`): stress layout of the star expansion; each
hyperedge a translucent rounded convex hull in its own `ColorData[97]` colour (a stadium
for size 2, a circle for size 1), largest first, nested hyperedges with widening margins;
vertex disks and labels on top.
- **PDF export** (`src/graphics/graphics_export.c`): `Text` offsets and
`Style[s, n | FontSize -> n | colour]`, the `Arrowheads[s]` directive, arrow shafts that
stop inside the head, and `AspectRatio -> Automatic` (equal x/y scale).
- Tests: `tests/test_graphplot.c` (determinism, options, degenerate and 500-vertex graphs,
hypergraphs, PDF export). `leaks --atExit`: 0 leaks.

## Merge: `worked-example-fixes` lands on main (v0.233)

The `worked-example-fixes` branch (8 commits, Michael Sollami) and main's `flint_field_gcd`
Expand Down
Loading
Loading