replace and improve integration tests - #477
davidkoski wants to merge 4 commits into
Conversation
- previously we used a python script to generate swift code and check output against known values from mlx (python) - this is replaced with a new and more powerful script (claude rewrite) - much more integration test coverage and tools to examine that - fixes per problems found by new integration tests - linspace output type, add dtype override - linspace default output type is float32 (matches python) - convolve .same padding side matches python (padRight) - nanToNum default for +/- infinity honors optionals (mlx defaults) - tensorDot axes default 1 -> 2 (matches python) - MLXNN Pooling padWidths matches python - MLXNN Alibi slop function matches python - MLXNN GRU apply bias to match python - MLXNN Lion optimizer default beta matches python - MLXNN Adafactor moving average matches python - MLXNN Muon zero power newton shulz matches python
| /// - ``linspace(_:_:count:stream:)-92x6l`` | ||
| /// - ``linspace(_:_:count:dtype:stream:)-92x6l`` | ||
| static public func linspace<T: HasDType>( | ||
| _ start: T, _ stop: T, count: Int = 50, stream: StreamOrDevice = .default | ||
| _ start: T, _ stop: T, count: Int = 50, dtype: DType? = nil, | ||
| stream: StreamOrDevice = .default | ||
| ) -> MLXArray where T: BinaryInteger { |
There was a problem hiding this comment.
linspace over integers would produce integer result in swift, float32 in python
| // `input.size` window of the full convolution | ||
| let padLeft = weightSize / 2 | ||
| let padRight = max(0, padLeft / 2 - 1) | ||
| let padRight = max(0, padLeft - 1) |
There was a problem hiding this comment.
convolve .same padding side matches python
| public func nanToNum( | ||
| _ array: MLXArray, | ||
| nan: Float = 0, posInf: Float? = 0, negInf: Float? = 0, | ||
| nan: Float = 0, posInf: Float? = nil, negInf: Float? = nil, |
There was a problem hiding this comment.
use mlx default values for nanToNum +/- infinity
| /// - ``tensordot(_:_:axes:stream:)-(MLXArray,MLXArray,Int,StreamOrDevice)`` | ||
| public func tensordot( | ||
| _ a: MLXArray, _ b: MLXArray, axes: Int = 1, stream: StreamOrDevice = .default | ||
| _ a: MLXArray, _ b: MLXArray, axes: Int = 2, stream: StreamOrDevice = .default |
There was a problem hiding this comment.
tensorDot axes default 1 -> 2 (matches python)
| // one width per dimension: batch and channel get no padding, each | ||
| // spatial dimension is padded symmetrically (`IntOrPair` is already | ||
| // the (before, after) pair for a single dimension) | ||
| let padWidths: [IntOrPair] = [0] + padding.map { IntOrPair($0) } + [0] |
There was a problem hiding this comment.
MLXNN Pooling padWidths matches python
| /// `1...n`. Otherwise python uses the slopes of the next power of two *below* | ||
| /// `n` and pads them with every other slope of the next power of two *above*, | ||
| /// which is not the same as extending the geometric series. | ||
| static func alibiSlope(numHeads: Int) -> MLXArray { |
There was a problem hiding this comment.
MLXNN Alibi slope function matches python
| } else if let bhn { | ||
| // matches python: without an incoming hidden state the hidden | ||
| // bias is still applied, gated by r | ||
| n = n + r * bhn |
There was a problem hiding this comment.
MLXNN GRU apply bias to match python
| /// The coefficients used for computing running averages of the gradient and its square | ||
| public var betas: (Float, Float) = (0.9, 0.999) | ||
| /// The coefficients used for computing the gradient momentum and update direction | ||
| public var betas: (Float, Float) = (0.9, 0.99) |
There was a problem hiding this comment.
MLXNN Lion optimizer default beta matches python
| return matmul(rFactor.expandedDimensions(axis: -1), cFactor.expandedDimensions(axis: 0)) | ||
| // broadcast rather than matmul so this also works for parameters with more | ||
| // than two dimensions | ||
| return rFactor.expandedDimensions(axis: -1) * cFactor.expandedDimensions(axis: -2) |
There was a problem hiding this comment.
MLXNN Adafactor moving average matches python
|
|
||
| /// Orthogonalize a 2D matrix via a quintic Newton-Schulz iteration. | ||
| private func zeropowerViaNewtonSchulz5(_ input: MLXArray, steps: Int) -> MLXArray { | ||
| func zeropowerViaNewtonSchulz5(_ input: MLXArray, steps: Int) -> MLXArray { |
There was a problem hiding this comment.
MLXNN Muon zero power newton shulz matches python
| @@ -0,0 +1,823 @@ | |||
| // Copyright © 2026 Apple Inc. | |||
| // | |||
| // GENERATED by tools/integration_tests -- DO NOT EDIT. | |||
There was a problem hiding this comment.
These are the new generated integration tests, replacing IntegrationTests.py (which was also generated)
| inputs_case("normal/2d", {"a": Normal(S)}) | ||
| inputs_case("normal/2d/seed2", {"a": Normal(S)}) | ||
| inputs_case("normal/1d", {"a": Normal((17,))}) |
There was a problem hiding this comment.
This replaces the generate_integration_tests.py
…them with TF32 disabled so we can guarantee consistent results on NAX vs non NAX hardware
nwh
left a comment
There was a problem hiding this comment.
LGTM -- my Claude reviewer noted an inconsistency in the README for the generator. Probably ok to remove the specific counts.
Co-authored-by: Nick Henderson <525247+nwh@users.noreply.github.com>
Proposed changes
previously we used a python script to generate swift code and check output against known values from mlx (python)
this is replaced with a new and more powerful script (claude rewrite)
much more integration test coverage and tools to examine that
fixes per problems found by new integration tests
linspace output type, add dtype override
linspace default output type is float32 (matches python)
convolve .same padding side matches python (padRight)
nanToNum default for +/- infinity honors optionals (mlx defaults)
tensorDot axes default 1 -> 2 (matches python)
MLXNN Pooling padWidths matches python
MLXNN Alibi slope function matches python
MLXNN GRU apply bias to match python
MLXNN Lion optimizer default beta matches python
MLXNN Adafactor moving average matches python
MLXNN Muon zero power newton shulz matches python
Overall the number of tests increased like this (roughly):
Checklist
Put an
xin the boxes that apply.pre-commit run --all-filesto format my code / installed pre-commit prior to committing changesAI disclosure: claude was used to write the new integration test generator (which in turn wrote the new integration tests), updated existing unit tests per issues found in integration tests, and in some cases make the code conform to the python reference.