Skip to content

Part assignment: lists of positions, All, negative positions and position 0 - #2083

Merged
rocky merged 8 commits into
masterfrom
fix-part-assign-positions
Oct 30, 2026
Merged

rocky merged 8 commits into
masterfrom
fix-part-assign-positions

Conversation

@tombouctou

Copy link
Copy Markdown
Collaborator

Found while running the examples of Skiena and Pemmaraju's Computational Discrete Mathematics with the Combinatorica package and comparing every result with WMA 15.

Part assignment crashed or changed the wrong parts in several cases:

  • x[[{2, 5}]] = v crashed: SubExpression returned None for a ListExpression position.
  • x[[All, 1]] = v assigned to x[[1]] instead of the first element of every row.
  • x[[-1]] = v wrote the wrong element.
  • A part that does not exist raised IndexError, after some parts had already been changed.
  • l[[0]] = f on a List, m[[2, 0]] = g on a nested List, and k[[0]] = List raised TypeError.
  • x[[{2, f[y]}]] = v raised AttributeError.

Positions are now resolved and checked before anything is replaced. A list of positions, All and a Span select several parts, and negative positions count from the end. A missing part gives Part::partw and leaves the value unchanged, and a non-integer position gives pkspec1. Assigning to position 0 replaces the part by a new expression with the new head. The value is held while it changes, so the value itself can be replaced too.

All the fixes from this series together, merged on one branch: full pytest 4336 passed, 0 failed; doctests 4679 passed, 0 failed.

🤖 Generated with Claude Code

tombouctou and others added 4 commits October 29, 2026 19:04
x[[{2, 5}]] = v crashed (SubExpression returned None for a
ListExpression position), x[[All, 1]] = v assigned to x[[1]] instead of
the first element of every row, x[[-1]] = v wrote the wrong element, and
a part that does not exist raised IndexError after some parts had
already been changed.

Positions are now resolved and checked before anything is replaced:
a list of positions, All and a Span select several parts, negative
positions count from the end, and a missing part gives Part::partw and
leaves the value unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
l[[0]] = f with l a List, m[[2, 0]] = g on a nested List, and k[[0]] =
List crashed with a TypeError: a List cannot take another head in
place. The part is now replaced by a new expression with the new head.
The value assigned to is held while it changes, so that the value itself
can be replaced as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pa[[{2, f[x]}]] = 5 raised an AttributeError; it now gives the message
WMA gives and leaves pa unchanged.

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

rocky commented Oct 30, 2026

Copy link
Copy Markdown
Member

Found while running the examples of Skiena and Pemmaraju's Computational Discrete Mathematics with the Combinatorica package and comparing every result with WMA 15.

When I first tried using Mathics (as it was called back then) on these examples, I was very depressed by how badly it did. And I had very little in the way of even being able to easily isolate problems. And that's why I had to start with the older version of this, which was much simpler, albeit slower and more primitive.

This is just to say a world of thanks for undertaking getting Mathics3 at least to the state that Mathematica was circa 2003!

Any chance of getting these examples from the book into the Combinatorica package's test suite?

@rocky

rocky commented Oct 30, 2026

Copy link
Copy Markdown
Member

There may still be problems here with negative indices. I see

In[4]:= part = {1, 2, 3}; part[[-4]] = 0; part

part = {1, 2, 3}; part[[-4]] = 0; part
Out[4]= 0[1, 2, 3]  (* Should there appear a Part::pw message above? *)

In[5]:= 
In[5]:= part = {1, 2, 3}; part[[{ -4, 2}]] = {x, y}; part

part = {1, 2, 3}; part[[{ -4, 2}]] = {x, y}; part
Out[5]= x[1, y, 3] (* should there also be a Part:pw message above? *)

part[[-4]] = 0 on a list of three elements turned -4 into position 0
and replaced the head, giving 0[1, 2, 3]. A negative position now has
to be between -Length and -1, as in WMA; position 0 is reached only as
0.

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

Copy link
Copy Markdown
Collaborator Author

@rocky Thank you, that means a lot. The groundwork you did is what made this possible: having Combinatorica in Mathics3 at all, plus its tests, gave us a place to start.

Negative indices: you're right, thanks for catching it. With part = {1, 2, 3}, the check let -4 turn into position 0, the head. A negative position now has to be between -Length and -1, so the head is reached only by an explicit 0:

part = {1, 2, 3}; part[[-4]] = 0; part
  Part::partw: Part -4 of {1, 2, 3} does not exist.
  {1, 2, 3}

part = {1, 2, 3}; part[[{-4, 2}]] = {x, y}; part
  Part::partw: Part -4 of {1, 2, 3} does not exist.
  {1, 2, 3}

The values match WMA 15. One difference: WMA reports the message as Set::partw, while Mathics3 reports the Part assignment messages under Part. I can change the tag in a follow-up if you'd like. The fix is in da632bc, with tests. I merged it, together with your merge and black commits, into #2084 and #2085, which build on this one.

The book's examples in the package's test suite: yes, I'd like to do that. We run every input cell of the book's notebooks (Chapters 3–5, plus the "new features" and tutorial notebooks, about 1,300 cells) under Mathics3 and under WMA 15's Combinatorica, and compare the results. With the PRs in this series, the share of cells that give WMA's result is now:

Notebook Before Now
Chapter 3 52% 79%
Chapter 4 39% 71%
Chapter 5 49% 72%
New features 49% 84%
Tutorial 98% 100%

Most of what is left is speed: the book's timing experiments on large graphs. The plan is to take the cells that pass, are deterministic and run quickly, and add them with WMA's results as a test file in Mathics3-Package-Combinatorica. The cells that still fail will be listed with their reasons, so they turn into tests as they get fixed. I'll open that as a PR there.

@rocky

rocky commented Oct 30, 2026

Copy link
Copy Markdown
Member

A big thanks for this and for picking up the mantle where I have not been able to do so!

@rocky
rocky merged commit 19391b2 into master Oct 30, 2026
15 checks passed
@rocky
rocky deleted the fix-part-assign-positions branch October 30, 2026 13:55
rocky added a commit that referenced this pull request Oct 31, 2026
…2084)

Found while running the examples of Skiena and Pemmaraju's
*Computational Discrete Mathematics* with the Combinatorica package and
comparing every result with WMA 15.

**Builds on #2083 (Part assignment positions); please merge that one
first.**

- `b = a; b[[2, 1]] = 9` changed `a` too.
- `k[] := (w = {1, {2}}; w[[2, 1]] = 5; w)` changed the definition of
`k`.

The parts on the way to the assigned position were changed in place,
though they may be shared with other values and definitions. They are
now copied first. WMA leaves `a` and the definition of `k` alone.

Along the way:
- A `ListExpression` changed with `set_element` now works out again
whether it is literal; before, a stale literal flag could trip an
assertion.
- Span positions counted from the end, and spans with a negative step,
now select the parts WMA selects. `m[[-2 ;; -1, 2]] = 0` changed
nothing, and `x[[-1 ;; 1 ;; -2]] = 0` gave a span message.

All the fixes from this series together, merged on one branch: full
pytest 4336 passed, 0 failed; doctests 4679 passed, 0 failed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: rocky <rb@dustyfeet.com>
Co-authored-by: R. Bernstein <rocky@users.noreply.github.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.

2 participants