Skip to content

feat(api): an axis is written as plain data with to_dict, and solve_over takes that data in its place - #1981

Merged
FBumann merged 3 commits into
feat/carry-on-axisfrom
feat/axis-to-dict
Oct 12, 2026
Merged

FBumann merged 3 commits into
feat/carry-on-axisfrom
feat/axis-to-dict

Conversation

@FBumann

@FBumann FBumann commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Prompt: And about the axis from: not sure what's the best api here!! Maybe the dataclass could have a from and to json or yaml? Or dict?

Prompt: Stack it onto 1980

Note

The following content was generated by AI.

EachCoordinate.to_dict() and EachWindow.to_dict() write an axis as plain data, carry included. solve_over takes that mapping in the axis's place. An archive's axis.json is the same mapping, so the browser app can solve from it without importing specsolve.axes.

The shape, and why it matches the rest of the API
  • Write with a method, read where the value is taken. This follows the spec: mathspec's Spec.to_dict() writes it, and to_spec, check, build and solve take the mapping in its place. There is no public reader function. axis_from stays private, as solve_over's reader and the archive's.
  • A dict, not JSON or YAML. json.dumps(axis.to_dict()) is the JSON form. The spec has to_yaml because its file is YAML; an axis lives in axis.json.
  • The keys are the dataclass fields. axis_from reads the keys an axis takes and the ones it needs off dataclasses.fields, so a field added to an axis is accepted and named in refusals with no second list to update. each picks the class from one table, _EACH.
  • The carry is one nested key, CARRY. Its parameters and variables are keys inside it, and a dimension's name is always a value, so no name in a spec can collide with the format: a dimension called steps and a carry of {'steps': ..., 'dim': ...} read back equal.
  • Refusals name the keys. An each that names no axis, a key the axis does not take, and a key it needs and lacks each raise SpecsolveError before a slice is taken. For example: EachWindow given as a mapping takes 'each' and ['dim', 'steps', 'lookahead', 'into', 'carry'], of which ['dim', 'steps', 'lookahead', 'into'] are required: ['lookahead'] is missing.
  • No carry key where nothing is carried, so an archive's axis.json stays byte-identical and INPUTS_LAYOUT stays at 1. A test holds the literal dict.
  • axis_manifest is gone: the archive writes axis.to_dict().
Mutation table, full suite

I took these by hand, because each is a substitution: committed tree, git checkout -- restore, __pycache__ dropped on both sides, tree checked clean after every run. The first four were taken on ab3f613. The last was taken again on 8a558c0, after to_dict was rewritten.

mutation result tree
axis_from: no refusal of an each that names no axis caught: …refused_with_the_keys_it_takes[no-each], [an-each-that-names-no-axis] clean
axis_from: unknown keys not looked for caught: [a-key-the-axis-does-not-take] clean
axis_from: missing keys not looked for caught: [a-key-the-axis-needs] clean
solve_over: the mapping not read as an axis caught: test_solve_over_takes_an_axis_as_its_dict and the four refusal cases clean
to_dict: the carry key written even when empty (if self.carry → if True) caught: test_an_axis_that_carries_nothing_writes_no_carry_key clean

The last test was written for that mutation. The round-trip and archive tests would not catch it, because both sides go through to_dict.

Gates, and what was not done

🤖 Generated with Claude Code

https://claude.ai/code/session_01G1TmcQZZCctsVr54HE93qS

…ver takes that data in its place

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G1TmcQZZCctsVr54HE93qS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G1TmcQZZCctsVr54HE93qS
@read-the-docs-community

read-the-docs-community Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Documentation build overview

📚 specsolve | 🛠️ Build #35084625 | 📁 Comparing 8a558c0 against latest (990de2a)

  🔍 Preview build  

6 files changed · ± 6 modified

± Modified

@codspeed

codspeed Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 93 untouched benchmarks
⏩ 307 skipped benchmarks1


Comparing feat/axis-to-dict (8a558c0) with feat/carry-on-axis (918e5b8)

Open in CodSpeed

Footnotes

  1. 307 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

…ere it carries something

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G1TmcQZZCctsVr54HE93qS
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