Skip to content

Reduce any set of cells: the cell reduction generalized, with counting - #534

Open
ulfarsson wants to merge 21 commits into
developfrom
reduction
Open

ulfarsson wants to merge 21 commits into
developfrom
reduction

Conversation

@ulfarsson

@ulfarsson ulfarsson commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Generalizes the cell reduction (CellReductionFactory, #428) from one cell to any set of cells, and gives it a counting function, object generation and random sampling, which it did not have: its constructor raised NotImplementedError. The strategy, its constructor and its factory, ReductionStrategy, ReductionConstructor and ReductionFactory, are in tilings/strategies/reduction.py; the condition, the plan, the two children and the words the maps are built from are in tilings/algorithms/reduction.py, whose docstring has the condition and why the reduction holds, as fusion has its algorithm in tilings/algorithms. CellReductionFactory and CellReductionStrategy stay, as the reduction of single cells, with their names and encodings.

The rule. A set X of active cells of a tiling is reducible, in a direction (increasing or decreasing), if every obstruction with points both in X and outside it has exactly one point in X, in a cell c say, and

  • it does not have points both in another cell of the row of c and in another cell of the column of c;
  • it has a point in another cell of the column (row) of c only if the cells of X in that column (row) are already in the order of the direction: c is the only one, or X's own obstructions put the points of the lower (left) of any two of them all to the left of (below) those of the other, the other way round when decreasing;

and every requirement list lies in X or has, in each of its gridded permutations, no point in X or one point as above; every cell of X lies in an obstruction that lies in X, which keeps it active in the tiling of X (a requirement that lies in X would too, but the condition asks for an obstruction); and no assumption on X is a component assumption or tracks a gridded permutation of two or more points (the sorting below keeps the number of points in each cell, but not the occurrences of a pattern). For a single cell this is the cell reduction's condition.

Why it holds. A gridded permutation is the same as three things chosen independently: for each column, the word of the cells of its points from left to right; for each row, the word of the cells of its points from bottom to top; and the permutation in each cell. The share of X in these is the order of the points of X among themselves in each column and in each row, and the permutations in the cells of X; the rest is the places of X's points among the other points of each column and row, and the other cells' permutations. An obstruction with one point in X sees that point only through its cell and its place among the other points of its column or of its row, never both. Which of the places of X's points in that row are the cell's is fixed by the rest when c is X's only cell there, and also when X's own obstructions fix the order of X's cells there, as the sorting leaves that order as it is. So sorting the share of X into the canonical one, every cell of X increasing (or decreasing) and the points of the cells of X in each row and each column in that order, is a bijection

Grid(T) → ⋃_v Grid(T_reduced)_v × Grid(T_X)_v,

where v is the number of points in each cell of X, T_X is the tiling of the obstructions and requirements of X (every other cell empty) and T_reduced is the tiling with those replaced by the monotone obstructions. Going back, T_X has the obstructions that fix the order, so its share agrees with the canonical one there.

The children, and what the reduced tiling keeps. The children are, in this order as before, the reduced tiling and the tiling of X. The reduced tiling keeps only what the rest of the tiling sees of X:

  • a cell of X whose row and column have no cell outside X, and in which no obstruction or requirement with points outside X has a point, is seen only through its number of points, which T_X counts: the reduced tiling leaves it out;
  • cells of X in one row whose columns, and the columns between them, have no cell outside X, with no other cell of X that the rest sees between them, and that the obstructions and requirements with points outside X treat alike (each with its point in one of them is one with its point in the other), are seen as one: the reduced tiling keeps the first of them, with all their points (likewise in a column).

Each group seen as one is tracked once, by a tracking assumption in each child (in T_X unless there is one group and nothing left out, when its size is the group's points). The count is the Hadamard product of the two in the variables counting the points in the groups, at 1, and the product in the size of the cells left out:

T(n; p) = Σ_{w, m} T_reduced(n − m; p, w) · T_X(|w| + m; w),

printed with ⊙ (a ↣ b ⊙ c). The parent's assumptions go to the reduced tiling, and a tracked cell of the parent is never left out or grouped. The object maps are the sorting and its inverse, built from the words above (reduce_gridded_perm and combine_gridded_perms, with the own tiling's own RowColMap for its cells): the backward map takes the rest from the reduced tiling's gridded permutation and the share of X, the cells left out and the split of each group included, from T_X's. get_sub_objects pairs the children's objects with the same w and sizes that add up, and random sampling picks w and m by their weight first. There is no sympy equation (get_equation raises NotImplementedError, as for interleaving factors): a Hadamard product keeps D-finite series D-finite, but not algebraic ones.

The factory. ReductionFactory(tracked=True, region="cell", max_cells=None), exported from tilings.strategies beside CellReductionFactory. The region says which sets it tries:

  • "cell": every single cell, the current setup (CellReductionFactory is this);
  • "row": the active cells of each row;
  • "column": the active cells of each column;
  • "line": every set of cells in one row or in one column, so the single cells and the whole rows and columns too;
  • "cut": the active cells on one side of a line between two adjacent columns or two adjacent rows (left or right of it, below or above it), at most twice the number of columns and rows. The cells left of a placed point are such a set, and these are the sets a threshold reduction reduces: in the specification of Av(2143) built by hand with the reduction, every reduction is of the cells left of a column, five or seven of them, which "any" reaches only with max_cells at least 7 and very many more sets;
  • "any": every set of cells (the number of sets grows exponentially with the number of active cells).

With max_cells, the sets of "cut" and "any" have at most that many cells.

A set is offered, in each direction it is reducible in, if it is not all of the active cells, is not rigid (every cell monotone and the points of any two of its cells in a row or a column in a fixed order, where reducing would change nothing or reverse the cells), is not a single finite cell, and the rest of the tiling sees one of its cells (otherwise it is a factor).

The cell reduction. CellReductionFactory(tracked) is ReductionFactory(tracked, "cell"), yielding CellReductionStrategy. On 400 random tilings it gives the reductions develop's factory gives, except those of cells that are factors on their own (30 of 1052), which the factor strategy separates. Three kinds of cells are no longer reduced: a cell with no obstruction of its own, whose own tiling, without a requirement of its own either, would be Tiling(), with only the empty permutation, so that the count would be wrong; a cell in a component assumption, where develop's constructor raised NotImplementedError; and a cell in a tracked gridded permutation of two or more points. The kitchen sink pack uses ReductionFactory(tracked). What else changes in the public API, as the CHANGELOG says:

  • CellReductionStrategy.formal_step names the direction: "reducing cell (0, 0) to increasing", where it was "reducing cell (0, 0)".
  • The reduced tiling is built with Tiling's defaults (simplified, empty rows and columns removed), where develop built it with simplify=False, derive_empty=False and remove_empty_rows_and_cols=False. A specification saved with develop's CellReductionStrategy may then not rebuild its children identically: on 300 random tilings, 16 of 538 cell reductions have a different reduced tiling, only in develop's unsimplified obstructions, and the same own tiling. None of those specifications could have been counted, as develop's constructor raised NotImplementedError.
  • CellReductionConstructor is ReductionConstructor, which takes the parent, the children, the plan and the map of the parent's parameters to the reduced tiling's; develop's CellReductionConstructor(parameter) counted nothing.
  • CellReductionFactory.reducible_cells, can_reduce_cell_with_requirements_and_assumptions and gp_in_row_and_col are removed: the condition is tilings.algorithms.reduction.is_reducible.

Examples.

  • Av(2143) with its topmost point placed, a 21 inserted into the cell L left of it and the 2 of the 21 placed bottommost: the placed point is the lowest top of an inversion of L, and L becomes five cells, the low ones left and right of the point (every point of the left one below those of the right one), the point, and the high ones. The cell R right of the topmost point sees them only through the two 2143s with one point in a low cell, along the bottom row. The five cells were not reducible before, as the two low cells share that row; now they are, the point and the high cells are left out, the two low cells are one group, and each child tracks one variable, the number of points below the lowest top of an inversion of L. This is the threshold reduction of the cell L: what R sees of L is its values and that lowest top.
  • Av(1234, 1342, 2413): with its minimum and the first point to the right of it placed, the class's cell and a decreasing cell D are above, and Av(123, 2413) and Av(123, 231) below, tied to D by 123s with two points in a lower cell. Reducing the two lower cells and D moves those 123s into the tiling of the three cells; the two lower cells are left out of the reduced tiling, which is D beside the class's cell.
  • Av(1234, 2431): two cells sharing no row or column, Av(123, 2431) and an increasing cell to its upper right, tied by a 123 with two points in the first. Neither cell is reducible alone, and the pair is.

With the first version, TileScope with point placements, or row and column placements, with fusion and the factory, on the 56 symmetry classes of two patterns of length 4 and the 317 of three, found specifications that reduce sets of two to five cells for 119 classes, every one counting correctly to length 10; with row and column placements, Av(1234, 2431) and Av(1243, 2431) got specifications with the reduction and none without it in the same time.

Tests (tests/strategies/test_reduction.py, and the encoding tests):

  • the condition on tilings from these classes, sets that are not reducible, and a set reducible in one direction only, because of a row its own obstructions put in order; the empty set, and sets with an empty cell or a cell off the tiling, which are not reducible; a cell of the set alone in its column, which a crossing there touches, reducible in both directions;
  • rigid sets: two cells of a row, and two of a column, in a fixed order either way or in none, and two of a row in a fixed order one of which is not monotone;
  • the sets each region offers, the region "cut"'s being the sides of the lines between columns and between rows, each among the sets "any" offers, with and without max_cells; and the sets each region tries on an L of three cells; the factories' names;
  • the children, and that the rule is never an equivalence;
  • every rule's sanity check (counts, objects and random samples) to size 5, in both directions, for a single cell, a whole row, a whole column, a block of two rows and two cells sharing no row or column, one of them with tracked cells in the parent, and two rows of cells one of which the rest sees only through a 21 of a requirement list; two cells of a row that the rest sees alike one way only, and two cells of a row the first of which is in a column with a cell outside the set; and the five cells of Av(2143) above, and of its inverse, whose group is in a column, to size 6;
  • the forward map is a bijection onto the pairs with as many points in each group and sizes that add up, and the backward map is its inverse, for these and the fixtures below, and for the five cells of Av(2143);
  • the exact monotone obstructions of a set with two cells in a row and two in a column, in each direction;
  • two cells of a row that the rest sees alike (one group), two it does not (two groups, also when decreasing), the same two as a column (two groups, both kept, increasing and decreasing), and two in a row with a cell outside the set, the second of them, or the first, in a column with one too (two groups); two cells of a row that the rest sees alike one way only, a point of a requirement list in one of them and nothing in the other (two groups); a cell kept only because an obstruction touches it, and one kept only because a requirement list does, each beside a cell left out; a cell kept only because a gridded permutation of two points of a requirement list touches it, alone and beside a cell left out; a requirement list with a point in the set and one outside it, which sees two cells of a row apart, or alike when it has a point in each; a requirement list with points outside the set and a gridded permutation with two points in it, a 12 in a list with a point outside the set or a lone 231 with a point outside it, which makes the set not reducible;
  • a set with a cell of a tracked gridded permutation of two points is not reduced, and a strategy applied in a direction its set is not reducible in raises StrategyDoesNotApply;
  • is_reducible with no direction: a set reducible increasing only, and one reducible decreasing only, are reducible; its cells are read once, so that an iterator serves for both directions;
  • constructors that pair their children the same way are equivalent (equiv), and constructors that differ only in leaving cells out, or only in the place of the groups' or of the parent's parameters, are not;
  • the factory does not offer a single finite cell, one whose basis has an increasing and a decreasing pattern, though it offers a cell whose basis has only one of them; nor does it offer a set the rest of the tiling sees nothing of; its defaults are tracked, the region "cell" and no bound;
  • every reduction the factory offers on 30 random tilings, seeded, passes its sanity check to size 4 (7 seconds here; its timeout is 300 seconds, for slower runners);
  • an untracked reduction has no constructor, and a factory's strategies are tracked as the factory is;
  • extra_parameters, given no children, computes them;
  • CellReductionFactory is the region "cell", with its old encoding;
  • the JSON and repr encodings of the factory, in every region, and of the strategies, in each direction, tracked or not; a strategy's cells are sorted and without repeats;
  • the compositions the random sampling goes through;
  • a search: Av(1243, 2314, 2413) with row and column placements, tracked fusion and the factory with the region "any" (at most three cells), level by level. After the fourth level, the first with a specification, every specification of the class reduces a set of three cells, so the one extracted does, and it has the right counts (about 7 seconds). Level by level, so that the result does not depend on auto_search's timing or on the random choice of a specification it extracts.

Beside these: on 400 random tilings of up to 3 × 3 cells, every one of the 2838 sets of up to four cells reducible before is reducible now in both directions, 13 more sets are reducible now, and all 3817 reductions the factory offers pass their sanity check to size 5 (1012 of them leave cells out, 308 have a group of two or more cells); the cell reduction gives develop's 1052 single-cell reductions but 30, all of cells that are factors on their own; and on tilings grown from 14 classes by point placements, row and column placements and requirement placements, every one of the 791 reductions offered passes its sanity check to size 6. Those 791 tracked 2486 cells in the first version and track 1343 groups now; 661 leave cells out, 5 have a group of two or more cells, and 28 were not reducible before.

CI's mypy, pylint and test_positive_reverse_fusion fail on develop too, at the same lines; #531 fixes them. This pull request's own tests pass in every test job. It changes the CHANGELOG's Unreleased section, as #530 does.

Reviewed before submission, over six rounds, by an automated reviewer. It ran:

  • flake8, mypy, pylint, black and the tests on CPython 3.11 to 3.14 and PyPy 3.11, against the develop branches of all three libraries. The one failure is develop's test_positive_reverse_fusion, which Fix CI: the newest mypy and pylint, and test_positive_reverse_fusion #531 fixes.
  • The sanity check of every reduction the factory offers on 400 random tilings, in every region, to size 5 (4,518), with point requirements (2,388) and with two-point gridded permutations in requirement lists (2,806).
  • The forward and backward maps of every reduction offered on 120 tilings (1,124), and of the 1,770 reductions on 400 tilings that keep two cells of a row or a column.
  • develop's encodings decoded, and its cell reductions compared (810, identical).
  • The kitchen sink pack on develop and on this branch: the same classes found, in about the same time.
  • Mutation testing of both modules, with every surviving mutant classified. Every fault that changes a count, a map, the objects or samples generated, or what a saved specification decodes to is now caught by the tests.

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 21 commits October 22, 2026 10:41
ReductionStrategy reduces a set of cells whose crossing obstructions each
have one point in it, seen only along its row or its column: the cells become
increasing (or decreasing), their points in each row and column in that
order, and the tiling of the set's own obstructions and requirements goes
beside the reduced tiling. ReductionConstructor counts the rule as the
Hadamard product of the two children in the variables counting the points in
the cells, at 1, and generates and samples objects through the sorting.
ReductionFactory tries the sets of a region: single cells (the default, the
cell reduction), rows, columns, every set in one row or column, or any set,
of at most max_cells cells. CellReductionFactory and CellReductionStrategy
are the single-cell case, with their names and encodings; the kitchen sink
pack uses ReductionFactory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its own tiling would be Tiling(), with only the empty permutation; develop's
cell reduction factory offered such a cell.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… not see left out, and cells it sees as one tracked once

A crossing obstruction may now see its point of the set along a row or column the set has other
cells in, when the set's own obstructions already put those cells in the order of the reduction:
the sorting then leaves that row's order as it is, and the own tiling has the same obstructions.
This makes the threshold reduction of Av(2143) (the cell left of the maximum, after its lowest
inversion top is placed) a reduction of five cells.

The reduced tiling now keeps only what the rest of the tiling sees of the set. It leaves out the
cells whose row and column have no cell outside the set and that no crossing obstruction or
requirement touches, which the own tiling counts, and keeps one cell for each group of cells in a
row (or column) the rest cannot tell apart, tracked once in both children. The count is the
Hadamard product in the groups' sizes and the product in the size of the cells left out. The maps
are built from the column words, row words and cell permutations.

minimal=False keeps every cell, each tracked, as before; a strategy encoded without the key decodes
so, and keeps the children it had.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he rest of the tiling sees

Henning: "I don't think we really need to be able to read the old specs. They were mostly made for
testing purposes. We should rather care about the cleanlyness of the code". So minimal goes from
ReductionStrategy, ReductionFactory, the cell reduction's classes, reduction_plan and
reduction_children, and the constructor takes the plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The region "cut" offers, for each line between two adjacent columns or two
adjacent rows, the active cells on each side of it: left and right of it,
below and above it. These are the sets a threshold reduction reduces, the
cells left of a placed point, as in Av(2143)'s tree, where every reduction is
of the cells left of a column (five or seven of them), which "any" reaches
only with a bound of seven cells and very many more sets. At most twice the
number of columns and rows; max_cells bounds them as it bounds "any".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nts touches

A tracking assumption on a gridded permutation of two or more points counts
the occurrences of a pattern, and the sorting of the set's share keeps the
number of points in each cell but not those occurrences: reducing a cell of
such a pattern gave wrong counts or raised ValueError (the review of the pull
request, round 1, finding 1). The condition now refuses such a set, as it
refuses one with a component assumption, and the tracked cells the plan keeps
apart are every cell of the assumptions' gridded permutations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t cells

Mutation testing of reduction.py (the review of the pull request, round 1,
finding 2) left mutants of monotone_obstructions, _seen_alike, _moved,
_left_out and _runs alive under the tests. New tests: the exact monotone
obstructions of a set with two cells in a row and two in a column, in each
direction; two cells of a row the rest sees alike (one group) and two it
does not (two groups); a cell kept only because an obstruction touches it,
beside a cell left out; a requirement list with a point in the set and one
outside it; a direction a set is not reducible in; the maps for the new
fixtures and the threshold; and a seeded sanity check of every reduction
the factory offers on 30 random tilings, to size 4 (7 seconds). Each of the
eleven mutants the review named now fails a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… tilings/algorithms/reduction.py

The module had 1203 lines, over Tilings' max-module-lines of 1000, with a new
too-many-lines disable (the review of the pull request, round 1, finding 3).
As fusion has its algorithm in tilings/algorithms, the reduction's pure
functions move there: the condition (is_reducible, is_rigid,
monotone_obstructions), the plan (reduction_plan, with ReductionPlan, a named
tuple of the groups and the cells left out), the children
(reduction_children, own_is_tracked) and the words the maps are built from,
with the maps themselves as reduce_gridded_perm and combine_gridded_perms;
it takes Tiling for type checking only, as the other algorithm modules do,
and builds tilings with the tiling's class. tilings/strategies/reduction.py
keeps the argument, the constructor, the strategy and the factory, 682 lines,
and drops the disable.

The strategy's new public methods reduced_gp and parent_gp are now
reduce_gridded_perm(comb_class, gridded_perm) and combine_gridded_perms(
comb_class, reduced_gridded_perm, own_gridded_perm), with no abbreviation
(finding 4). _runs and _canonical_words take their direction as a keyword,
and _layout's cache says why it is there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Following the review of the pull request (round 1, its suggestions): full
names in the new code (gridded_perm, obstruction, requirement, pattern, and
strategy in the tests); is_reducible takes the cells once, so an iterator
serves both directions; the own tiling's own maps (its RowColMap) in place of
compression, which goes; the strategy's extra parameters passed to the
constructor; one generator of the matching pairs for get_terms and
get_sub_objects; the sampling choices as a named tuple, enumerated as the
group sizes of sum at most the size (with the sizes the parent fixes) rather
than all of (n + 1)^groups; equiv comparing where the children's parameters
are, not only how many; the constructor's symbols from the strategy's; the
factory's region sets in helpers, its docstring on tracked=False and on what
"any" costs, without the claim that the reduced tiling is never larger (a
requirement inside the set goes to the own tiling, and then the reduced
tiling can have the empty permutation); from_dict popping and checking its
keys; ranks by Perm.to_standard. The eleven mutants the review named still
fail a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The review of the pull request (round 1, finding 5): the changes to
CellReductionConstructor, CellReductionStrategy's formal step and its reduced
tiling (now simplified, so a specification saved with the old strategy may
not rebuild its children identically: on 300 random tilings, 16 of 538 cell
reductions differ, all in the reduced tiling's unsimplified obstructions; none
could have counted, as the old constructor raised NotImplementedError), and the
removed helpers of CellReductionFactory, are now in the CHANGELOG, whose
Added bullet is shorter. test_specification asserts a reduction of three
cells, as the description says.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… row a column sees apart

The review's round 2 found two mutants that broke counts and passed every test,
round 1's mutations at the other copy of a line that occurs twice:

- in _left_out, the cells a requirement list with points outside the set
  touches (round 1's tests covered the obstructions' copy). In the top row of
  row_above_a_cell with a point required in B or below it, B is kept though its
  row and column have no cell outside the set, and A is left out;
- in _runs, the place of a cell along a row (the copy in _canonical_words was
  covered). In the same row without the requirement, B and C are two groups,
  as C's column has a cell outside the set.

The same mutation of _canonical_words, along a row when decreasing, broke only
the forward map: unlike_row's complement, reduced decreasing with its two cells
kept, is added to test_maps for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7dafe8f's message says is_reducible takes the cells once, so that an iterator
serves both directions, but the code was not changed: with no direction, it
passed the cells to the increasing call and then to the decreasing one, so an
iterator was used up by the first, and a set reducible decreasing only was not
reducible (the review's round 2). The cells are now made a set first.
test_reducible_from_an_iterator passes an iterator of the cells of alike_row's
complement, which is reducible decreasing only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…2 suggestions

- The condition, and the argument why the reduction holds, are in the
  docstring of tilings.algorithms.reduction, where the code is, which defines
  the own tiling once; the strategy module's says what its constructor counts.
- One selection of what the rest of the tiling sees the set through:
  _seen_gridded_perms reads the view, for is_reducible and _left_out alike.
- ReductionConstructor(parent, children, plan, parent_to_reduced): the cells,
  never read, and the fallback for a missing map, which the strategy always
  passes, are gone, as the CHANGELOG says.
- ReductionFactory._lines uses Tiling.cells_in_row and cells_in_col, from_dict
  pops its keys without defaults, and is_finite_cell, strategy and the
  constructor's symbol methods have docstrings; _own_tiling says why it is not
  Tiling.sub_tiling.
- _monotone_ranks uses Perm.identity and Perm.monotone_decreasing; the
  constructor's helpers take parent_size, and the tests a random_generator.
- test_equiv; test_tracked_pattern's docstring gives the reason.
- The CHANGELOG's code span is on one line.

On 10,400 random tilings, every set of up to four cells has the same
reducibility in each direction and the same plan as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tation of a requirement list

The review's round 3: every requirement in the tests was a single point, so the
branch of _seen_gridded_perms that selects a requirement's gridded permutation
with a point in the set and a point outside it was never taken (any( and all(
agree on a single point), and any( changed to all( passed every test.

- seen_through_a_21 is the review's tiling: A = (0, 1), in a column and a row
  with no cell outside the set, is seen only through the 21 from A down to
  C = (1, 0) of the list [a point in D = (2, 0), that 21]. Its plan, the "row"
  region offering the set, both directions' sanity checks to size 5, and
  test_maps.
- left_out_beside_a_21 is row_above_a_cell with [a 21 from A down to D, a 12 in
  D] required: A is kept through the 21 and B left out, test_left_out_beside_kept
  with a requirement for the obstruction. Both lists have a second gridded
  permutation, as Tiling splits a lone 21 between two cells that share no row or
  column into a point in each.
- test_requirement_with_two_points_in_the_set: a 12 from A to B in a list with a
  point in C makes alike_row's two cells not reducible increasing.

With any( as all(, 8 of these tests fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…me again

2082857 made is_reducible build the whole view (the lines with cells outside
the set, the tracked cells and every crossing obstruction) before its first
check, where fd37016 stopped at the first gridded permutation not allowed. On
every set of at most three cells of 12 tilings of 10 and 11 active cells, grown
by point placements, it was 1.4 to 1.7 times slower (the review's round 3,
question 7(b)).

Now _crossing_obstructions and _crossing_requirements are generators, and
_seen_gridded_perms(tiling, cell_set) chains them for is_reducible. _view builds
its tuples, and the cells of the set that a seen gridded permutation touches,
from the same three, so the selection is still made in one place. _View has the
set and the touched cells, and _left_out takes the view alone (the review's
suggestion 2).

On those sets: the same answers as fd37016 and 2082857, and as fast as fd37016
after warm-up. On 10,400 random tilings (61,570 sets of up to four cells), the
same reducibility in each direction, the same answer for an iterator and the
same plans as 2082857.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ocstrings

- test_equiv: pairs that differ only in leaving a cell out, only in the place
  of the parent's parameter, or only in the place of the group's parameter in
  the reduced tiling, none equivalent; equivalent pairs of one group and of two;
  and one group each, with a cell left out in one. equiv no longer compares the
  number of groups: reduced_group_indices has an entry for each. The
  constructor's groups are a local name, as nothing else read them.
- test_candidate_sets: the sets each region tries on an L of three cells, the
  "line" region's single cells among them.
- The strategies module's docstring says when the own tiling tracks the groups,
  and _bounded_compositions' docstring is reworded.
- The condition that each cell of the set lie in an obstruction of the set's own
  is stricter than its reason, since a requirement of the set's own would keep
  the cell active in the own tiling too. The docstrings say so; the condition is
  unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…turn

None of these changes a count, so no test that compares counts saw them:

- test_degenerate_sets: the empty set, a set with an empty cell and a set
  with a cell off the tiling are not reducible; the cell of a one-cell
  tiling is.
- test_reducible_with_no_direction, which test_reducible_from_an_iterator
  becomes: a set reducible increasing only, and one reducible decreasing
  only, are reducible with no direction given, from a list and from an
  iterator.
- test_encoding: ReductionStrategy and CellReductionStrategy decode to
  themselves in each direction, tracked or not. One combination of each
  was round-tripped, so a from_dict that dropped or swapped increasing and
  tracked went unseen.
- test_rigid: two cells of a column, in no fixed order and in each fixed
  order, and two cells of a row in a fixed order, the second of which is
  not monotone. Only cells of a row were tested, so the column branch,
  and the skip of reversed pairs taken as a break, went unseen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ecification there reduces three cells

auto_search looks for a specification as its timing allows and extracts one
at random, the smallest of the random proof trees it builds within a time
limit. Av(1234, 1342, 2413) also has specifications without any reduction,
one of 20 rules at the fifth level. So the specification found there reduced
no set of three cells in one of 20 runs of the test, and in one mutation run
whose mutant could not change a search, and the test failed.

On Av(1243, 2314, 2413), with the same pack, the fourth level is the first
with a specification. Without the rules of reductions of three cells, no
specification is left there: pruning the rules without them leaves no root,
under three hash seeds. So the specification extracted reduces three cells in
every run, and it counts the class to length 8. The test passed under 16 hash
seeds, in about 7 seconds each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h, the strategy's cells and the sampling's compositions

- test_requirement_with_two_points_in_the_set also takes a lone 231 across A,
  B and C: two points in the set and one outside it, which only
  is_reducible's selection of a requirement's gridded permutations decides.
- test_factory_skips: a single finite cell, though reducible and not rigid,
  and a set the rest of the tiling sees nothing of are not offered.
- test_requirement_list asserts the plans. A point required in A or in C
  keeps A and B apart; a point required in A, in B or in C sees them alike.
- test_strategy_cells: a strategy's cells are sorted and without repeats.
- test_bounded_compositions: the compositions the random sampling goes
  through, against their number.
- equiv says why it compares the own tiling's tracking and the places of its
  groups' parameters, which follow from the rest; test_equiv's fifth pair
  differs in two ways, as its comment now says.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the factory's tracking, defaults and finite cells

Each of these went unseen by the tests:

- test_maps: unlike_column and unlike_column_decreasing, the inverses of
  unlike_row and unlike_row_decreasing, in which the reduction keeps the two
  cells of a column; test_groups_along_a_column asserts their plans. No case
  kept two cells of one column, so column words put in the wrong order went
  unseen.
- Two cells of a row that are two groups: seen_through_requirement's A and B,
  which the rest sees alike one way only, and split_by_first_column, the
  reverse of split_by_column, whose first cell of the row joins no group, as
  its column has a cell outside the set. Their plans are asserted, and
  test_sanity_check runs both in each direction: checking one way only, or
  starting a group at a cell that does not join, made each one group.
- test_crossing_in_a_column: a cell of the set alone in its column, which a
  crossing there touches, is reducible. Its column, 0, differs from its row,
  1, as in no other test of a crossing in a column, so reading the set's rows
  as its columns went unseen.
- test_not_tracked: the strategies of ReductionFactory and
  CellReductionFactory are tracked as the factory is.
- test_factory_defaults: a factory is tracked, of the region "cell" and
  without a bound unless told otherwise.
- test_factory_skips: a cell whose basis has an increasing pattern only, or a
  decreasing one only, is not finite, and is offered alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t the children, the factories' names and can_be_equivalent

- test_rigid's column is in rows 1 and 2 of column 0. In rows 0 and 1, the
  first cell's row equals the second cell's column, so is_rigid comparing
  those two passed test_rigid, and only test_specification failed.
- test_parent_assumptions calls extra_parameters without the children,
  which it then computes; comb_spec_searcher always passes them.
- test_regions checks the names of the factories of the regions "row",
  "column", "line" and "any", with a bound and without.
- test_children checks that the rule is never an equivalence.

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