Skip to content

Test the fusion equations against the objects, and say why some fusions have none - #533

Open
ulfarsson wants to merge 7 commits into
developfrom
fusion-derivative-equation
Open

ulfarsson wants to merge 7 commits into
developfrom
fusion-derivative-equation

Conversation

@ulfarsson

@ulfarsson ulfarsson commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

FusionConstructor.get_equation raises NotImplementedError for a fusion whose two sides are given the same value: there are no one-sided parameters, and the parent's fusion parameters, if any, cover the whole region. The simplest such fusion has an untracked parent and a tracked child. This pull request keeps that, and makes the message say which fusions have no equation and how to get one: adding to the parent an assumption that tracks one side of the region first makes the fusion one-sided, with the divided difference as its equation. A program that solves a specification's equations can graft that in itself, at the cost of a rule: the parent T gets the assumption (T -> T_L, whose equation is T = T_L at 1), and T_L is fused as T was, to the same child.

An earlier version of this pull request gave these fusions an equation with a derivative, F(q) + q F'(q). We decided against it: the graft above gives an equation without a derivative where one is needed, and leaves the searches as they are.

What is left are tests that compare the equations develop gives with the objects: expanded with the child's objects, each equation must give the parent's objects of each size up to 4, monomial by monomial in the parent's parameters.

  • test_fusion_equation_counts: each one-sided tracking of easy_fusable (the left cell, the right one, both apart) with each of its positive sides (none, the left, the right, both, and a point in either cell, which a TODO said had no equation): 15 fusions.
  • test_fusion_one_sided_beyond_the_region: one parameter tracking a side of the region and the cell beside it together. It is one-sided, so the equation is the divided difference, though both sides of the region are given the value 1.
  • test_fusion_both_sides_tracked_beside_a_tracked_cell: the two sides tracked apart, and a cell beside the region. Substituting the child's parameters one at a time would give a wrong equation, so this checks that they are substituted simultaneously; ..._with_a_positive_side does the same with each positive side, where the equation also subtracts the child with one side empty.
  • test_fusion_with_both_sides_the_same_value and test_fusion_of_fixtures_with_both_sides_the_same_value: the fusions with no equation (an untracked parent, or one tracking the whole region, with each positive side; and the fixtures' fusions) raise the error, with its message.
  • On a tiling of one row and two columns, the tests check that FusionFactory makes only the one fusion, as test_fusion_gfs did for the cases it no longer has. eq_equality compares the left sides too, and t14's stale comment and the TODO are gone.

Nine faults put into get_equation by hand each fail a test. Substituting the child's parameters one at a time fails only the beside-a-tracked-cell tests.

Checked locally with tox: flake8, mypy, pylint and black pass, and on CPython 3.14 and PyPy 3.11 all 3,754 tests pass.

The CHANGELOG entry goes under [Unreleased] as ### Changed.

Reviewed before submission by an automated reviewer, in four rounds; the first two reviewed the derivative. This version ran:

  • flake8, mypy, pylint, black, and Tilings' tests on CPython 3.14 and PyPy 3.11, with Permuta's and comb_spec_searcher's develop.
  • The equations of 500 tracked fusion rules, on develop and on this branch: the same 400 raise the error, and the other 100 equations are the same, each giving the parent's objects to size 4.
  • This branch's tests on develop's code: only those that check the new message fail.
  • Mutation testing of get_equation, and faults made by hand: no survivor is on a line this pull request changes, except in the error's message.

AI assistance: written with Claude Code (Opus 5.5) as the TileScope agent, and reviewed before submission by Claude Code (Opus 5.5) as an automated reviewer; Henning Ulfarsson checked and opened it.

ulfarsson and others added 6 commits October 21, 2026 08:50
FusionConstructor.get_equation raised NotImplementedError when both sides of
the fused region are given the same value: no one-sided parameters, and the
parent's fusion parameters, if any, covering the whole region, as for an
untracked parent with a tracked child. Its divided difference
(q F(q) - p F(p)) / (q - p) then has q = p, and its limit is the derivative
F(q) + q F'(q): each child with k points in the fused region gives k + 1
parents. A side that must hold a point loses the split that leaves it empty,
F(q) for each, as in the other cases.

test_fusion_gfs gets the six equations it marked as not implemented, and a
new test checks the derivative against the counting of three such fusions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
As a reviewer asked: parent_function, child_function, equation, gridded_perm,
size and expanded in place of F0, F1, eq, gp, n and rhs, in the test this pull
request adds. The cases filled in test_fusion_gfs keep that test's own names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When both sides of the fused region are given the same value, the equation
differentiated the child's function in the fuse parameter after substituting
the parent's parameters into it. The child's fuse parameter can have the name
of a parent's parameter that another of the child's parameters is given: with
the parent tracking a cell beside the region and not the region, the child's
fuse parameter is k_0, and its parameter for that cell, k_1, is given the
parent's k_0. sympy kept the substitution in a Subs of the derivative, whose
bound k_0 took in the parent's k_0, so the derivative went through both, and
the equation was wrong.

The child's function is now written in a sympy Dummy for the fuse parameter,
the parent's parameters substituted simultaneously, and differentiated in the
Dummy. A comment says why the line keeps its pylint disable: pylint takes
sympy's subs for a function that returns None.

The new tests compare each equation with the objects, monomial by monomial in
the parent's parameters, to size 4:
- test_fusion_derivative_beside_a_tracked_cell: a cell (2, 0) beside the
  region tracked, and the region too or not; the first was wrong before.
- test_fusion_one_sided_beyond_the_region: one parameter tracking a side of
  the region and (2, 0) together. It is one-sided, so the equation stays the
  divided difference, though both sides are given the value 1; a guard
  reduced to p == q passed every test before.
- test_fusion_derivative_beside_eleven_tracked_cells: the child's k_1 to k_11
  given the parent's k_0 to k_10. sympy substitutes a dictionary's names in
  sorted order, k_10 before k_9, so one at a time would give k_10's place the
  parent's k_8; with fewer parameters the order never matters here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ases each a case of its own

- eq_equality compares the left sides too, so that an equation of the child's
  function in place of the parent's is seen.
- test_fusion_derivative_equation: the six equations of fusions whose sides
  are given the same value, moved from test_fusion_gfs, each a case of its
  own, so that one failing does not hide the others.
- test_fusion_equation_counts: every combination of easy_fusable's trackings
  and positive sides, among them a point required in either cell, whose
  equation the derivative now gives: the TODO saying there was none, and t14's
  stale comment, go.
- test_fusion_equation_counts_of_fixtures: the fusions of small_tiling, two
  cells a side, and the component fusions.
test_fusion_derivative_equation_counts, of three untracked fusions, is
superseded by test_fusion_equation_counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The derivative equation is taken out again: a fusion whose two sides are
given the same value has no equation, as on develop. The error now says
which fusions these are, and that adding to the parent an assumption that
tracks one side of the region first makes the fusion one-sided. A program
that needs the equation can graft that in itself, at the cost of a rule.

The tests compare the equations develop gives with the objects, and check
that the fusions with no equation raise the error:
- test_fusion_equation_counts: the one-sided trackings of easy_fusable with
  each of its positive sides, a point in either cell among them.
- test_fusion_one_sided_beyond_the_region, as before.
- test_fusion_both_sides_tracked_beside_a_tracked_cell, in place of the test
  beside eleven tracked cells: the child's k_1 is given the parent's k_2 and
  its k_0 the sides' values, so the child's parameters must be substituted
  simultaneously here too; one at a time fails this test alone.
- test_fusion_with_both_sides_the_same_value and
  test_fusion_of_fixtures_with_both_sides_the_same_value, in place of the
  derivative's tests: an untracked parent, or one tracking the whole region,
  with each positive side, and the fixtures' fusions.

The CHANGELOG entry is a ### Changed, for the message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ulfarsson ulfarsson changed the title Give the equation of a fusion whose sides take the same value Test the fusion equations against the objects, and say why some fusions have none Oct 25, 2026
…t once

- column_fusion asserts, on a tiling of one row and two columns, that
  FusionFactory makes only the one fusion, as test_fusion_gfs did for the
  cases it no longer has. A tiling with a third column has another fusion,
  so there the rule is picked by its name.
- test_fusion_both_sides_tracked_beside_a_tracked_cell_with_a_positive_side:
  the same tiling with a positive side, so that the equation also subtracts
  the child with one side empty, whose parameters must be substituted
  simultaneously too. Substituting them one at a time passed every test
  before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant