C.W.K.
Stream
Lesson 02 of 07 · published

Arrays, dtypes, and the Unified-Memory Difference

~14 min · arrays, dtypes, unified-memory

Level 0Curious
0 XP0/51 lessons0/15 achievements
0/100 XP to next level100 XP to go0% complete

The mx.array constructor

An MLX array is a typed N-dimensional buffer in unified memory. You make one from a Python list (or a NumPy array, or another MLX array), it has a shape, a dtype, and an ndim. If you've used NumPy or PyTorch tensors, the next 30 seconds of typing will feel exactly the same.

What you'll notice as different is small but architectural — there's no device argument anywhere, and there will not be one in the next lesson, or the lesson after that.

The dtype set is ML-shaped, not science-wide

NumPy supports a wide set of numeric types because it's a general-purpose scientific library — complex128, datetime64, structured records, the works. MLX's dtype set is shaped for ML workloads. As of mlx 0.31.x, the supported dtypes are float32 (the default for most things), float16, bfloat16, int8 / int16 / int32 / int64, uint8 / uint16 / uint32 / uint64, bool, and a couple of complex types. No datetime, no structured records, no extended precision.

That's a feature, not a limitation. The smaller surface means simpler kernel code in MLX, which means fewer paths to optimize and fewer corner cases to maintain. If you need complex128, you're probably reaching for the wrong tool.

Default device is unified memory, always

Every mx.array you create lives in unified memory by default. mx.default_device() returns Device(gpu, 0) on Apple Silicon — but as you learned in foundations.lesson2, that's a label about which compute unit will run the next op, not where the bytes live. The bytes live in one shared pool the whole time.

Type promotion is the one place to be careful

If you mix dtypes in an op (e.g. int32_array + float32_array), MLX promotes to the wider type using rules that match NumPy in most cases but not all. The safe habit, especially when you're mixing learned weights (always float) with integer indices, is to call .astype(mx.float32) explicitly when you mean to. Implicit promotion is convenient until it loses you a debugging hour.

Code

Building arrays — the same shapes you know from NumPy·python
import mlx.core as mx

a = mx.array([1, 2, 3, 4])
b = mx.array([1.5, 2.5, 3.5, 4.5])
c = mx.zeros((3, 4))
d = mx.ones((2, 2), dtype=mx.float16)

print('a:', a, 'dtype:', a.dtype, 'shape:', a.shape, 'size:', a.size, 'ndim:', a.ndim)
print('b:', b, 'dtype:', b.dtype)
print('c shape:', c.shape, 'dtype:', c.dtype)
print('d:', d, 'dtype:', d.dtype)

# Verified output (2026-05-03):
#   a: array([1, 2, 3, 4], dtype=int32) dtype: mlx.core.int32 shape: (4,) size: 4 ndim: 1
#   b: array([1.5, 2.5, 3.5, 4.5], dtype=float32) dtype: mlx.core.float32
#   c shape: (3, 4) dtype: mlx.core.float32
#   d: array([[1, 1], [1, 1]], dtype=float16) dtype: mlx.core.float16
Explicit dtype conversion with .astype·python
import mlx.core as mx

a = mx.array([1, 2, 3, 4])               # inferred → int32
print('a       :', a, a.dtype)

a_f = a.astype(mx.float32)
print('a.astype:', a_f, a_f.dtype)        # → float32

# Verified:
#   a       : array([1, 2, 3, 4], dtype=int32) mlx.core.int32
#   a.astype: array([1, 2, 3, 4], dtype=float32) mlx.core.float32

# Be explicit when you mean float — implicit promotion to float32
# happens in arithmetic anyway, but .astype documents your intent.
Default device confirms unified-memory placement·python
import mlx.core as mx

# The default 'device' is just a label about where compute runs next.
# All bytes live in the unified-memory pool either way.
print('default device:', mx.default_device())   # → Device(gpu, 0) on Apple Silicon

x = mx.array([1.0, 2.0, 3.0])
print('x lives in :', mx.default_device(), '— but the bytes are in unified memory.')

External links

Exercise

Build five arrays of your own choosing — one int32, one float32, one float16, one bool, and one shape (3, 5) of zeros. For each, print the dtype, shape, ndim, and size. Then take any two of them and do an arithmetic op (+ or *) — what dtype does the result have? Try one combination that promotes to a higher-precision dtype and one that doesn't. Two sentences on what you noticed.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.