Provenny

Lay out area-proportional euler/venn diagrams for any number of sets.

class provenny.Bounds(min_x: float, min_y: float, max_x: float, max_y: float)

Bases: NamedTuple

An axis-aligned bounding box; a (min_x, min_y, max_x, max_y) tuple with names.

A real tuple, so it unpacks, indexes, and compares equal to the plain 4-tuple – the added names and helpers are pure convenience. ax.set_xlim(box.min_x, box.max_x).

property center: tuple[float, float]

The box center (x, y).

property height: float

The extent along y, max_y - min_y.

max_x: float

Alias for field number 2

max_y: float

Alias for field number 3

min_x: float

Alias for field number 0

min_y: float

Alias for field number 1

union(other: Bounds) Bounds

Return the smallest box covering both this box and other.

property width: float

The extent along x, max_x - min_x.

class provenny.Diagram(names: tuple[_Name, ...], shapes: ndarray[tuple[int, ...], dtype[float64]])

Bases: Generic[_Name]

A solved layout: the set names and the (n, 5) shapes placed for them.

Returned by proportional_venn(). diagram[name] (or ellipse()) gives a set’s shape as an Ellipse, and the diagram acts as a mapping over the names – iteration, len, and in – in order of first appearance, generic over the (hashable) label type.

property bounds: Bounds

The axis-aligned box (min_x, min_y, max_x, max_y) enclosing every set’s ellipse.

ellipse(name: _Name) Ellipse

Return the shape placed for a set name, as an Ellipse.

The spelled-out form of diagram[name]; raises KeyError for an unknown name, as indexing does.

names: tuple[_Name, ...]
shapes: ndarray[tuple[int, ...], dtype[float64]]
zone(names: Collection[Hashable], *, tol: float = 1e-09) Zone | None

Return the zone inside exactly names (its boundary, area, and label point).

names are the sets the zone is inside; every other set is outside it, so {"A", "B"} is the A & B zone and {"A"} the “A only” zone. None means that zone is empty in this layout. Its label point is zone(...).center. tol is the boundary-test tolerance (as for zone()). Raises KeyError for an unknown name and ValueError for an empty names (the exterior is not a zone).

class provenny.Ellipse(array: ndarray[tuple[int, ...], dtype[float64]])

Bases: object

A placed shape wrapping its (5,) layout row [*center, major, minor, angle].

Indexing a Diagram returns one of these.

property angle: float

The major axis’s orientation from the x-axis, in radians, in [0, pi).

anomaly(x: float, y: float) float

Return the eccentric anomaly of a boundary point (x, y), in [0, tau).

property area: float

The enclosed area, pi * major * minor.

array: ndarray[tuple[int, ...], dtype[float64]]
property bounds: Bounds

The axis-aligned bounding box (min_x, min_y, max_x, max_y).

property center: tuple[float, float]

The (x, y) center.

contains(x: float, y: float, tol: float = 0.0) bool

Whether (x, y) lies within the closed ellipse.

frame(x: float, y: float) tuple[float, float]

(x, y) in the ellipse’s axis frame, scaled by the semi-axes (1 on boundary).

property major: float

The larger semi-axis.

matplotlib_path() tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[uint8]]]

Return the outline as (vertices, codes) for matplotlib.path.Path.

property minor: float

The smaller semi-axis.

point_at(anomaly: float) tuple[float, float]

Return the (x, y) boundary point at eccentric anomaly.

sample(num: int = 100) ndarray[tuple[int, ...], dtype[float64]]

Sample about num (x, y) points evenly around the outline.

svg_path() str

Return the outline as an svg <path> d-string.

class provenny.Zone(loops: list[list[Arc]], _ellipses: ndarray[tuple[int, ...], dtype[float64]], _inside: ndarray[tuple[int, ...], dtype[bool]])

Bases: object

A laid-out zone: its boundary loops and the interior point to label it.

loops are the closed arc loops (outer lobes, nested holes). area is exact and center is the chebyshev (max-clearance) interior point, always inside even a crescent.

property area: float

The exact enclosed area (holes subtracted), via green’s theorem on the arcs.

property bounds: Bounds

The axis-aligned bounding box (min_x, min_y, max_x, max_y) of the boundary.

property center: tuple[float, float]

The chebyshev (max-clearance) label point, computed on first access.

loops: list[list[Arc]]
matplotlib_path() tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[uint8]]]

Return the boundary as (vertices, codes) for matplotlib.path.Path.

sample(num: int = 100) list[ndarray[tuple[int, ...], dtype[float64]]]

Sample about num points around each boundary loop.

svg_path() str

Return the boundary as an svg <path> d-string.

provenny.proportional_venn(areas: Mapping[Collection[_Name], float], *, mode: Literal['circle', 'ellipse', 'optimal'] = 'circle', restarts: int | None = None, tol: float = 1e-09, rng: Generator | None = None) Diagram[_Name]

Lay out a venn/euler diagram from named subset areas.

Parameters:
  • areas – A mapping from a subset of set names to that subset’s area (e.g. {"A": 1.0, "B": 1.0, "AB": 0.4}); omitted subsets are empty. A string key is a subset of single-character names, so "AB" is the pair {"A", "B"}.

  • mode"circle" places circles. "ellipse" places ellipses (annealed from pairwise-mds circle positions), which fit targets that circles cannot, while keeping every pairwise overlap a single connected lens – the mode to reach for when you want ellipses. "optimal" drops the connectedness penalty for the lowest area error it can reach, letting a pair overlap in two disconnected lobes. All modes return circles when circles are already optimal.

  • restarts – The number of random mds restarts. "ellipse"/"optimal" anneal each distinct basin the restarts uncover and keep the best fit; "circle" keeps the best circle. None picks the default: 2 * (n - 1) for the ellipse modes (about the basin count, with margin), a small constant for "circle".

  • tol – The numeric tolerance for area computations.

  • rng – Generator for the restart seeds; None uses a fresh numpy.random.default_rng(). Pass a seeded one for reproducibility.

Returns:

  • A Diagram pairing each set name with the canonical ellipse placed for it;

  • index or iterate it to read the shapes.

Raises:

ValueError – If mode is unknown, restarts is below 1, the mapping names no set, or a subset is malformed – repeated across keys, naming a set twice, or with a non-finite or negative area.

provenny.proportional_venn_array(areas: ndarray[tuple[int, ...], dtype[float64]], *, mode: Literal['circle', 'ellipse', 'optimal'] = 'circle', restarts: int | None = None, tol: float = 1e-09, rng: Generator | None = None) ndarray[tuple[int, ...], dtype[float64]]

Lay out a venn/euler diagram from a raw subset-area array.

Parameters:
  • areas – A 1-D array of the area of every non-empty subset of the n sets, length 2**n - 1, indexed by subset bitmask minus one (bit i marks set i). For two sets this is [|A|, |B|, |A & B|].

  • mode"circle" places circles. "ellipse" places ellipses (annealed from pairwise-mds circle positions), which fit targets that circles cannot, while keeping every pairwise overlap a single connected lens – the mode to reach for when you want ellipses. "optimal" drops the connectedness penalty for the lowest area error it can reach, letting a pair overlap in two disconnected lobes. All modes return circles when circles are already optimal.

  • restarts – The number of random mds restarts. "ellipse"/"optimal" anneal each distinct basin the restarts uncover and keep the best fit; "circle" keeps the best circle. None picks the default: 2 * (n - 1) for the ellipse modes (about the basin count, with margin), a small constant for "circle".

  • tol – The numeric tolerance for area computations.

  • rng – Generator for the restart seeds; None uses a fresh numpy.random.default_rng(). Pass a seeded one for reproducibility.

Returns:

  • The shapes, one canonical ellipse row per set (an (n, 5) array of)

  • (cx, cy, major, minor, angle) with major >= minor and angle in

  • [0, pi). Every mode returns this same (n, 5) layout, so a caller never

  • branches on the mode.

Raises:

ValueError – If mode is unknown, restarts is below 1, or the areas are malformed – the wrong length, non-finite, negative, or leaving a set with no area.

provenny.zone(ellipses: ndarray[tuple[int, ...], dtype[float64]], inside: ndarray[tuple[int, ...], dtype[bool]], *, tol: float = 1e-09) Zone | None

Build the zone inside the flagged sets and outside the rest, or None if empty.

Parameters:
  • ellipses – An (n, 5) layout of canonical (cx, cy, major, minor, angle) rows (major >= minor, angle in [0, pi)), as proportional_venn_array() returns.

  • inside – A boolean mask over the sets; inside[i] true keeps the zone inside set i.

  • tol – Numeric tolerance for the boundary tests.

Return type:

The zone, or None when it is empty in this layout.

Raises:

ValueError – If the rows are not canonical ellipses, or no set is flagged – the exterior is not a zone.

Indices and tables