Summary
AIProgram.optimize() silently changes the semantics of broadcast arithmetic used by the standard expanded squared-distance expression:
D[i,j] = ||x_i||² - 2·x_i·y_j + ||y_j||²
For equal-length inputs, the two norm tensors have these shapes:
x norms: (1,N,1)
y norms: (1,1,N)
The second shape is commonly produced by either:
torch.sum(y ** 2, dim=-1).unsqueeze(-2)
or:
torch.sum(y ** 2, dim=-1, keepdim=True).transpose(-1, -2)
Although moving (1,N,1) to (1,1,N) does not reorder its element values, the shape change is essential to broadcasting. After optimize(), that axis move is absent. The final addition consumes the unrearranged (1,N,1) reduction and broadcasts ||y_i||² where ||y_j||² belongs.
The resulting expression is:
||x_i||² - 2·x_i·y_j + ||y_i||²
Because the operands have equal lengths, the invalid rewrite still produces an output with the expected (1,N,N) shape. No diagnostic is emitted.
Environment
- macOS 27.0 on Apple Silicon
- originally reproduced on build
26A5378j
- reproduced again on 2026-07-22 on build
26A5388g
coreai-torch 0.4.1
coreai-core 1.0.0b2
torch 2.11.0
- Python 3.12.13
These were the newest PyPI releases, including prereleases, when retested on 2026-07-22.
Steps to reproduce
Run the attached repro_coreai_optimize_axismove.py. It creates a fresh exported program for every optimized and unoptimized arm, saves each Core AI asset, executes it, and compares its output with eager PyTorch.
The minimal failing form uses z as a graph input, so a matmul is not required to trigger the problem:
s1 = torch.sum(x ** 2, dim=-1).unsqueeze(-1) # (1,N,1)
s2 = torch.sum(y ** 2, dim=-1).unsqueeze(-2) # (1,1,N)
out = (s1 - 2 * z + s2).clamp(min=0.0)
Expected result
AIProgram.optimize() preserves the program’s output. All arms should match eager PyTorch to normal float32 noise.
Actual result
Chain optimize=False: max|d| = 1.907e-06 OK
Chain optimize=True : max|d| = 1.022e+01 MISCOMPILED
ChainKeepdim optimize=False: max|d| = 1.907e-06 OK
ChainKeepdim optimize=True : max|d| = 1.022e+01 MISCOMPILED
ChainReordered optimize=False: max|d| = 3.815e-06 OK
ChainReordered optimize=True : max|d| = 3.815e-06 OK
The optimized result matches an independent NumPy implementation of the wrong-axis expression to 1.907e-06.
Isolation and controls
The following controls were tested independently:
| Control |
Result |
| Eager PyTorch versus NumPy |
Exact |
exported.module() versus eager |
Exact |
Conversion without optimize() |
Correct |
SpecializationOptions.cpu_only() |
Same miscompile |
| Expression without the final clamp |
Miscompiled |
Real x @ y.transpose(-1,-2), distinct inputs of equal length |
Miscompiled |
| Real self-distance expression using the same tensor |
Miscompiled |
Reordered (s1 + s2) - 2*z expression |
Correct |
s1 + s2 alone |
Correct |
Unequal input lengths, producing a 17×23 result |
Correct |
For the distinct-input case, the optimized graph retains separate reductions for x and y but removes the axis move from the y reduction. This is therefore not a valid common-subexpression merge.
The identical CPU-only result also indicates that this is not caused by GPU placement or reduced-precision execution.
IR evidence
Before optimization, the y reduction is moved to (1,1,32), and the final addition consumes that shape:
%y_norm = coreai.reduce_sum ... -> tensor<1x32x1xf32>
%y_norm_moved = coreai.expand_dims ... -> tensor<1x1x32xf32>
%tmp = ...broadcasting_sub ...
: (tensor<1x32x1xf32>, tensor<1x32x32xf32>)
-> tensor<1x32x32xf32>
%out = ...broadcasting_add %tmp, %y_norm_moved
: (tensor<1x32x32xf32>, tensor<1x1x32xf32>)
-> tensor<1x32x32xf32>
After optimization, both reductions remain, but the axis move is gone:
%x_norm = coreai.reduce_sum ... %arg0 ... -> tensor<1x32x1xf32>
%y_norm = coreai.reduce_sum ... %arg1 ... -> tensor<1x32x1xf32>
%tmp = ...broadcasting_sub ...
: (tensor<1x32x1xf32>, tensor<1x32x32xf32>)
-> tensor<1x32x32xf32>
%out = ...broadcasting_add %tmp, %y_norm
: (tensor<1x32x32xf32>, tensor<1x32x1xf32>)
-> tensor<1x32x32xf32>
In the keepdim=True variant, the pre-optimization graph contains one transpose operation and the optimized graph contains none.
Scope and impact
The verified failure requires shape compatibility that allows the unrearranged operand to broadcast successfully. Equal-length and square/self-distance matrices are affected; the tested unequal-length 17×23 case remained correct.
Models lowered through this expanded squared-distance formulation may include point-cloud registration and segmentation networks, geometric attention, and k-nearest-neighbor graph construction. Fused distance operators and differently ordered formulations have not been shown to be affected.
The failure is particularly hazardous because the output has the expected shape and contains plausible values. In a larger GeoTransformer conversion, this appeared as approximately 17 dB PSNR versus eager PyTorch and scrambled nearest-neighbor relationships. Disabling optimize() restored approximately 78–85 dB parity.
Workarounds
Both of these workarounds were verified:
-
Do not call AIProgram.optimize(). Conversion, save_asset, specialization, loading, and inference work correctly without it.
-
Reorder the expression so the two norm operands are added first:
(||x_i||² + ||y_j||²) - 2·x_i·y_j
The reordered graph remains correct after optimization.
Related report
Possibly related to #9, which also reports a silent semantics-changing simplification reached through prog.optimize(), but the affected operation and rewrite pattern are different.
This issue was also submitted through Feedback Assistant as FB23695952, with the original sysdiagnose attached there.
repro_coreai_optimize_axismove.py
Summary
AIProgram.optimize()silently changes the semantics of broadcast arithmetic used by the standard expanded squared-distance expression:For equal-length inputs, the two norm tensors have these shapes:
The second shape is commonly produced by either:
or:
Although moving
(1,N,1)to(1,1,N)does not reorder its element values, the shape change is essential to broadcasting. Afteroptimize(), that axis move is absent. The final addition consumes the unrearranged(1,N,1)reduction and broadcasts||y_i||²where||y_j||²belongs.The resulting expression is:
Because the operands have equal lengths, the invalid rewrite still produces an output with the expected
(1,N,N)shape. No diagnostic is emitted.Environment
26A5378j26A5388gcoreai-torch 0.4.1coreai-core 1.0.0b2torch 2.11.0These were the newest PyPI releases, including prereleases, when retested on 2026-07-22.
Steps to reproduce
Run the attached
repro_coreai_optimize_axismove.py. It creates a fresh exported program for every optimized and unoptimized arm, saves each Core AI asset, executes it, and compares its output with eager PyTorch.The minimal failing form uses
zas a graph input, so a matmul is not required to trigger the problem:Expected result
AIProgram.optimize()preserves the program’s output. All arms should match eager PyTorch to normal float32 noise.Actual result
The optimized result matches an independent NumPy implementation of the wrong-axis expression to
1.907e-06.Isolation and controls
The following controls were tested independently:
exported.module()versus eageroptimize()SpecializationOptions.cpu_only()x @ y.transpose(-1,-2), distinct inputs of equal length(s1 + s2) - 2*zexpressions1 + s2alone17×23resultFor the distinct-input case, the optimized graph retains separate reductions for
xandybut removes the axis move from theyreduction. This is therefore not a valid common-subexpression merge.The identical CPU-only result also indicates that this is not caused by GPU placement or reduced-precision execution.
IR evidence
Before optimization, the
yreduction is moved to(1,1,32), and the final addition consumes that shape:After optimization, both reductions remain, but the axis move is gone:
In the
keepdim=Truevariant, the pre-optimization graph contains onetransposeoperation and the optimized graph contains none.Scope and impact
The verified failure requires shape compatibility that allows the unrearranged operand to broadcast successfully. Equal-length and square/self-distance matrices are affected; the tested unequal-length
17×23case remained correct.Models lowered through this expanded squared-distance formulation may include point-cloud registration and segmentation networks, geometric attention, and k-nearest-neighbor graph construction. Fused distance operators and differently ordered formulations have not been shown to be affected.
The failure is particularly hazardous because the output has the expected shape and contains plausible values. In a larger GeoTransformer conversion, this appeared as approximately 17 dB PSNR versus eager PyTorch and scrambled nearest-neighbor relationships. Disabling
optimize()restored approximately 78–85 dB parity.Workarounds
Both of these workarounds were verified:
Do not call
AIProgram.optimize(). Conversion,save_asset, specialization, loading, and inference work correctly without it.Reorder the expression so the two norm operands are added first:
The reordered graph remains correct after optimization.
Related report
Possibly related to #9, which also reports a silent semantics-changing simplification reached through
prog.optimize(), but the affected operation and rewrite pattern are different.This issue was also submitted through Feedback Assistant as
FB23695952, with the original sysdiagnose attached there.repro_coreai_optimize_axismove.py