Skip to content

Honor interleave in concat's value_inference - #2788

Open
LeSingh1 wants to merge 1 commit into
apple:mainfrom
LeSingh1:concat-interleave-value-inference
Open

Honor interleave in concat's value_inference#2788
LeSingh1 wants to merge 1 commit into
apple:mainfrom
LeSingh1:concat-interleave-value-inference

Conversation

@LeSingh1

@LeSingh1 LeSingh1 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Problem

concat.value_inference always calls np.concatenate(values, axis=self.axis.val) and never reads self.interleave. So a concat of const inputs with interleave=True const-folds to the plain, non-interleaved concatenation.

Using the example spelled out in the op's own docstring:

in1 = [[1, 2], [3, 4], [5, 6]]     # (3, 2)
in2 = [[7, 8], [9, 10], [11, 12]]  # (3, 2)

@mb.program(input_specs=[])
def prog():
    return mb.concat(values=[in1, in2], axis=0, interleave=True)
value
docstring / runtime [[1,2],[7,8],[3,4],[9,10],[5,6],[11,12]]
value_inference [[1,2],[3,4],[5,6],[7,8],[9,10],[11,12]]

type_inference computes the correct shape (6, 2), so nothing flags the mismatch, and common::const_elimination then replaces the concat with a const holding the wrong values — the compiled model computes the wrong answer at runtime:

@mb.program(input_specs=[mb.TensorSpec(shape=(6, 2))])
def prog(x):
    c = mb.concat(values=[in1, in2], axis=0, interleave=True)
    return mb.add(x=x, y=c)

apply_pass_and_basic_check(prog, "common::const_elimination")
# ['concat', 'add'] -> ['add'],  with the non-interleaved constant baked in

Fix

All inputs of an interleaved concat share the same shape (type_inference enforces exactly this), so interleaving along axis is a stack immediately after axis followed by folding that new dimension back into axis.

The all-scalar branch above is deliberately left alone: interleaving N scalars and stacking them produce the same result.

I checked the folded value against a slice-assignment reference (out[k::N] = values[k] along the axis) for ranks 1–3, every positive and negative axis, and 2 or 3 inputs — all match.

Tests

In TestConcat (ops/tests/iOS14/test_tensor_operation.py):

  • test_builder_eval_interleave[axis=0,1,-1,-2] — checks the folded value against the slice-assignment reference, including negative axes.
  • test_builder_eval_interleave_docstring_example — the exact example from the op docstring.

All five fail on main and pass with the fix.

The existing interleave coverage, TestConcat.test_builder_to_backend_stress_interleave in ops/tests/iOS14/test_tensor_transformation.py, feeds mb.placeholder inputs, so value_inference never runs there — which is why this went unnoticed.

I ran all of ops/tests/iOS14/test_tensor_operation.py and the concat-related pass tests before and after; the failure sets are identical (this environment cannot load CoreML.framework, so the run_compare_builder / assert_model_is_valid tests fail there either way).

concat.value_inference always calls np.concatenate and never reads
self.interleave, so a concat of const inputs with interleave=True folds to the
plain (non-interleaved) concatenation. type_inference gets the shape right, so
nothing catches the mismatch, and common::const_elimination replaces the op
with a const holding the wrong values.

Using the example from the op's own docstring:

    in1 = [[1, 2], [3, 4], [5, 6]]
    in2 = [[7, 8], [9, 10], [11, 12]]
    mb.concat(values=[in1, in2], axis=0, interleave=True)

    docstring / runtime: [[1,2],[7,8],[3,4],[9,10],[5,6],[11,12]]
    value_inference:     [[1,2],[3,4],[5,6],[7,8],[9,10],[11,12]]

All inputs of an interleaved concat share the same shape (type_inference
enforces this), so the interleave is a stack right after the concat axis
followed by folding that new dimension back into the axis. The all-scalar
branch above is left alone: interleaving scalars is the same as stacking them.

The existing interleave coverage
(TestConcat.test_builder_to_backend_stress_interleave) feeds placeholders, so
value_inference never runs there.
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