handle hadamard and addmm on empty inputs

This commit is contained in:
Awni Hannun 2025-05-12 09:25:20 -07:00
parent caaa3f1f8c
commit bc3357e6ad
5 changed files with 44 additions and 1 deletions

View File

@ -132,6 +132,10 @@ void AddMM::eval_cpu(const std::vector<array>& inputs, array& out) {
throw std::runtime_error( throw std::runtime_error(
"[AddMM::eval_cpu] Currently only supports float32."); "[AddMM::eval_cpu] Currently only supports float32.");
} }
if (out.size() == 0) {
out.set_data(allocator::malloc(out.nbytes()));
return;
}
// Fill output with C // Fill output with C
auto& c = inputs[2]; auto& c = inputs[2];
@ -139,7 +143,9 @@ void AddMM::eval_cpu(const std::vector<array>& inputs, array& out) {
? CopyType::Scalar ? CopyType::Scalar
: (c.flags().row_contiguous ? CopyType::Vector : CopyType::General); : (c.flags().row_contiguous ? CopyType::Vector : CopyType::General);
copy(c, out, ctype, stream()); copy(c, out, ctype, stream());
if (inputs[0].shape(-1) == 0) {
return;
}
matmul_general(inputs[0], inputs[1], out, stream(), alpha_, beta_); matmul_general(inputs[0], inputs[1], out, stream(), alpha_, beta_);
} }

View File

@ -716,6 +716,23 @@ void AddMM::eval_gpu(const std::vector<array>& inputs, array& out) {
throw std::runtime_error( throw std::runtime_error(
"[matmul] Does not yet support non-floating point types."); "[matmul] Does not yet support non-floating point types.");
} }
// Return 0s if either input is empty
if (out.size() == 0) {
out.set_data(allocator::malloc(out.nbytes()));
return;
}
// Copy c into out and return
if (inputs[0].shape(-1) == 0) {
copy_gpu(
inputs[2],
out,
inputs[2].flags().row_contiguous ? CopyType::Vector : CopyType::General,
stream());
return;
}
out.set_data(allocator::malloc(out.nbytes())); out.set_data(allocator::malloc(out.nbytes()));
auto& s = stream(); auto& s = stream();
auto& d = metal::device(s.device); auto& d = metal::device(s.device);

View File

@ -472,6 +472,10 @@ array hadamard_transform(
const array& a, const array& a,
std::optional<float> scale_ /* = std::nullopt */, std::optional<float> scale_ /* = std::nullopt */,
StreamOrDevice s /* = {} */) { StreamOrDevice s /* = {} */) {
if (a.size() == 0) {
throw std::invalid_argument(
"[hadamard_transform] Does not support empty arrays.");
}
// Default to an orthonormal Hadamard matrix scaled by 1/sqrt(N) // Default to an orthonormal Hadamard matrix scaled by 1/sqrt(N)
int n = a.ndim() > 0 ? a.shape(-1) : 1; int n = a.ndim() > 0 ? a.shape(-1) : 1;
float scale = scale_.has_value() ? *scale_ : 1.0f / std::sqrt(n); float scale = scale_.has_value() ? *scale_ : 1.0f / std::sqrt(n);

View File

@ -745,6 +745,19 @@ class TestBlas(mlx_tests.MLXTestCase):
mx.eval(c) mx.eval(c)
self.assertEqual(c.shape, (0, 0)) self.assertEqual(c.shape, (0, 0))
c = mx.array([], dtype=mx.float32)
a = mx.array([], dtype=mx.float32)
b = mx.array([], dtype=mx.float32)
out = mx.addmm(a, b, c)
mx.eval(out)
self.assertEqual(out.shape, ())
a = mx.zeros(shape=(5, 0))
b = mx.zeros(shape=(0, 5))
c = mx.random.uniform(shape=(5, 5))
out = mx.addmm(c, a, b)
self.assertTrue(mx.allclose(out, c))
def test_block_masked_matmul(self): def test_block_masked_matmul(self):
def ref_block_masked_mm( def ref_block_masked_mm(
a, b, block_size, out_mask=None, lhs_mask=None, rhs_mask=None a, b, block_size, out_mask=None, lhs_mask=None, rhs_mask=None

View File

@ -2830,6 +2830,9 @@ class TestOps(mlx_tests.MLXTestCase):
return H return H
def test_hadamard(self): def test_hadamard(self):
with self.assertRaises(ValueError):
mx.hadamard_transform(mx.array([]))
h28_str = """ h28_str = """
+------++----++-+--+-+--++-- +------++----++-+--+-+--++--
-+-----+++-----+-+--+-+--++- -+-----+++-----+-+--+-+--++-