Skip to content

D-TRACT-SHAPE-1 follow-up: int64 compares/Where, ConstantOfShape, Expand; optional-output producer test - #1854

Merged
AdaWorldAPI merged 7 commits into
mainfrom
ccr-0161f442-un1mrc
Oct 30, 2026
Merged

AdaWorldAPI merged 7 commits into
mainfrom
ccr-0161f442-un1mrc

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

Follow-up to #1853 and ndarray #400, applying the owner decisions recorded after both merged. This is lab probe work only (crates/tract-harvest-probe): no production ClassId is minted and no full Jina runtime is started. Plan: .claude/plans/d-tract-harvest-0-native-algebra-bridge-v1.md §0.3.

Optional empty output slots, producer identity, stale bindings

source_producers now enumerates each node's ONNX output positions first, then converts each position to Tract's outlet slot with dr3::tract_slot. That slot is the number of named outputs before the position, which is how Tract numbers outlets (onnx/src/model.rs:150 filters empty names before enumerating). Using the raw ONNX position as the slot would point at the wrong outlet whenever an omitted output comes first. Measured on LSTM ["", "Y_h"], that would reject Y_h's correct fact.

FactSources now records the source it was read from, and merged_for refuses facts read from another model.

Each case below has its own test:

  • ["", "Y"] and multiple outputs with a gap
  • producer collisions: two producers for one value, a value that is also a graph input or an initializer, duplicate node names, an unnamed node without a first output
  • a fact whose producer was renamed in the current source
  • facts from another model (gen2 applied to base)
  • the committed LSTM fixtures lstm_skip / lstm_full against Tract and ORT: the fact admitted for Y_h is Y_h's [1,1,2], not the omitted Y's [5,1,1,2]

Registry v3 (ndarray #400 and existing ndarray functionality)

op classid DO
int64 Equal 0x0F70 eq_i64_pair_to_mask / eq_i64_to_mask
int64 LessOrEqual 0x0F71 le_i64_pair_to_mask / le_i64_to_mask
int64 Where 0x0F72 blend_i64 / blend_i64_scalar
ConstantOfShape 0x0F73 ArrayD::from_elem
Expand (int64, f32) 0x0F74/75 ndarray broadcast

int64 compares and Where. The right side must have the same shape or one element; any other broadcast is WHEN FALSE, and the same case is refused at run time too. Tested on 70 elements, across a mask word: the pair and scalar forms set the right bits and leave every bit past the last element clear. Where is tested with a scalar and with a tensor Y.

ConstantOfShape, ONNX semantics:

  • the value is the attribute's one element, default float 0
  • an empty shape gives a scalar; zero dimensions give an empty tensor
  • a bool value fills the mask without setting tail bits
  • the shape input must be 1-D, with non-negative constant dimensions; otherwise FALSE, and UNKNOWN if not constant

Expand uses the multidirectional broadcast: a target dimension of 1 keeps the input's dimension, and a shorter target is padded on the left.

On fixture shape_i64 (ORT ORT_DISABLE_ALL), every int64 node over Shape(F) folds, Expand(F) runs at run time, and every output equals ORT exactly.

Jina census (inputs pinned to [1,10])

Coverage, as [WHEN-true, WHEN-not-true, unregistered]:

registry coverage folded nodes
v1 4,146 / 0 / 3,474 —
v2 (#1853) 6,404 / 56 / 1,160 4,440
v3 (this PR) 6,581 / 114 / 925 4,499 (+59 ConstantOfShape)

The 56 Reshapes that were blocked do not resolve yet. For every one of them, the first unregistered op their target depends on is int64 Mul.

The 114 not-true nodes:

  • Reshape 56 and Expand 57 are UNKNOWN: their target is computed through int64 Mul/Div.
  • LessOrEqual 1 compares [S,1] against [1,S], a broadcast that is not admitted.

Nodes that are not folded, 3,121 in total:

  • runtime (they read an input value): 2,461. Mul 450, Add 253, MatMul 252, Cast 228, Reshape 170, Slice 140, Transpose 140, then Div, Pow, ReduceMean and Sqrt at 113 each, and smaller groups.
  • shape-only but blocked: 660. Unsqueeze 181, Cast 119, Mul 117, Equal 58, Where 58, Concat 57, Div 56, Range 4, and a few singletons.

int64 arithmetic: the ndarray ask

int64 Add/Sub/Mul/Div/Range stay refused and are not implemented locally. Plan §0.3 lists:

  • the exact checked signatures (Div truncates toward zero; overflow, a zero divisor or a zero step returns an error)
  • the accuracy requirement (exact)
  • the negative cases

The failing fixture is fixtures/tiny/i64_arith.onnx, with its ORT outputs frozen; int64_arithmetic_is_refused_until_checked_words_exist pins the current refusal. The int64 compares, blends, Gather and MatMul from #400 are not re-requested.

Integration acceptance — a whole shape-arithmetic chain that folds or executes through admitted methods and matches ORT exactly — waits on those words, with i64_arith as its fixture.

Tests and disable runs

55 tests pass with --include-ignored; clippy (-D warnings) and fmt are clean. Each disable run turned its named test red:

  • the Equal kernel replaced by le
  • the slot counted over empty outputs (the ONNX position used as the slot)
  • a general broadcast admitted
  • a broadcast mismatch accepted
  • ConstantOfShape ignoring its value
  • no source-binding check
  • collisions keeping a producer
  • bool fill setting tail bits
  • ConstantOfShape accepting a shape input of any rank

🤖 Generated with Claude Code

https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v

…and; optional-output producer test

Registry v3 registers int64 Equal and LessOrEqual through ndarray #400's
eq/le_i64 pair and scalar mask kernels, int64 Where through blend_i64 /
blend_i64_scalar, ConstantOfShape through ArrayD::from_elem and Expand
(int64, f32) through ndarray broadcast. A comparison admits a same-shape
or one-element right side; other broadcasts are not admitted.

New fixtures (ORT ORT_DISABLE_ALL): shape_i64 (every int64 node folds,
Expand(F) runs, all outputs match ORT exactly) and lstm_skip/lstm_full,
which pin source_producers on a node with an omitted optional output:
Tract numbers outlets after dropping empty names, and the fact admitted
for Y_h is Y_h's (ORT [1,1,2]), not the omitted Y's.

Jina [1,10] coverage 6,404/56/1,160 -> 6,581/114/925; the 58 newly
not-true nodes are UNKNOWN (an Expand target computed by unregistered
int64 arithmetic) or not admitted (one general-broadcast LessOrEqual).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
int64 Div (on negatives), Mul, Add, Sub and Range over Shape(F), with
ORT's outputs frozen; the test pins that they stay refused (unregistered)
until ndarray supplies checked words.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0d8e1057-a6db-4c15-8b55-d272d19f12b0
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…oducer, binding and mask-tail tests

source_producers enumerates each node's ONNX output positions first and
maps a position to Tract's outlet slot with tract_slot (the number of
named outputs before it, as onnx/src/model.rs:150 numbers outlets).
FactSources records the source it was read from; merged_for refuses facts
of another model.

Tests: ['', 'Y'] and multi-output slot mapping; producer collisions (two
producers, a value that is also an input or initializer, duplicate node
names, an unnamed node without a first output); a fact from a renamed
producer; facts of another model; int64 compares and Where over a
70-element mask (tail bits clear, scalar and pair); ConstantOfShape
(default value, scalar output for an empty shape, zero dims, bool fill,
K3 refusals; the shape input must be 1-D) and Expand (multidirectional
broadcast) semantics.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
…e vs shape-only nodes; plan, board

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review October 30, 2026 11:26
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-30T11:28:10.374061Z 7637427 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@AdaWorldAPI
AdaWorldAPI merged commit d97b872 into main Oct 30, 2026
13 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 76374275b2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1090 to +1091
if let (1, Some(&yv)) = (y.len(), y.iter().next()) {
ndarray::simd::blend_i64_scalar(words, &xs, yv, &mut out);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject scalar Y tensors that increase the Where output rank

When C and X have shape [3] and the one-element Y has shape [1, 1], the reused guard_where returns TRUE, but ONNX multidirectional broadcasting requires an output of shape [1, 3]. This scalar branch instead returns a tensor with X's shape [3]. Both cold folding and runtime execution can therefore silently produce the wrong output shape for an admitted int64 Where. Require Y's rank to be no greater than X's in both the guard and runtime scalar path, or construct the full broadcast output shape.

Useful? React with 👍 / 👎.

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