NuMojo v0.8.0 update is here!

NuMojo v0.8.0 is finally here!, a substantial update that moves the project forward on multiple fronts while keeping us firmly on the track toward v1.0.

Below are the highlights, focusing first on the most impactful changes. Check out our changelog for a more comprehensive list of all the updates and examples!


:star: Highlights

Python-like complex numbers, everywhere

NuMojo now supports Python-style imaginary literals via the new ImaginaryUnit and the 1j alias. This enables natural, readable complex-number expressions across:

  • Scalars
  • SIMD vectors
  • NDArray / ComplexNDArray

Complex arithmetic, broadcasting, and operator support have all been expanded and refined, making complex workflows significantly more ergonomic and consistent.

Major expansion of the complex ecosystem

Complex support has grown into a first-class citizen across the stack:

  • New ComplexDType variants for all real dtypes (e.g. f64 → cf64)
  • Rich APIs for ComplexSIMD and ComplexNDArray
  • Statistical, reduction, and manipulation methods (e.g. sum, mean, argmax, cumsum, flatten, transpose, etc.)
  • Magnitude-based comparison operators
  • Convenience constructors like zero(), one(), I(), and from_polar()

Matrix views (initial rollout)

Matrix views are finally here.

  • Matrix methods can now return views, laying the foundation for view-based semantics across Matrix and NDArray.
  • Due to a current Mojo compiler limitation, view-returning operations are temporarily exposed via get() / set(), while __getitem__ / __setitem__ continue to return copies.
  • This groundwork enables more efficient, NumPy-like behavior as Mojo support matures.

Explicit copy semantics

NuMojo now fully aligns with Mojo’s explicit copy rules:

  • Large containers like NDArray, ComplexNDArray and Matrix implement Copyable
  • Use .copy() for shared-origin duplication and .deep_copy() for fully independent memory
  • Reduced accidental copies and clearer ownership semantics throughout the codebase. More work upcoming to reduce copies.

Infrastructure & Tooling

  • Migrated entirely to the TestSuite-based testing framework, replacing the deprecated mojo test
  • Introduced a Pixi-based build backend, enabling installation directly from GitHub with:
    • Automatic path setup
    • No dependency on Conda or external package channels

:scissors: NDArray & slicing improvements

  • Getting closer to NumPy compatibility for slicing!
    • Full negative indexing and reverse slicing
    • Automatic bounds clamping
    • Improved performance and reduced memory overhead for slicing computations.
  • Better diagnostics and named errors for invalid indexing and shape mismatches etc.
  • Configurable per-array printing options (e.g. precision control)

:shovel: Internal changes & fixes

  • Reworked Matrix internals and indexing behavior
  • Standardized error handling via a unified NumojoError providing better diagnostics.
  • Temporary compatibility layer for pointer semantics while transitioning to the new UnsafePointer model
  • Numerous correctness fixes across slicing, cumulative ops, and memory handling
  • Removal of deprecated integer types (isize, intp)

:books: Documentation

This release also includes a major documentation push:

  • Expanded and clarified docs for complex numbers, SIMD, arrays, and copy semantics
  • Updated READMEs (including a new Korean version)
  • Improved test, error-handling, and roadmap documentation

v0.8.0 brings NuMojo more closer to a polished, stable v1.0 while unlocking powerful new numerical and complex-number workflows today.

As always, feedback is very welcome, please try it out, report issues, and let us know what you think! Happy computing!

9 Likes

Wow, this is a huge update, congratulations!!

1 Like

This looks great, but I do have one point of concern:

Use .copy() for shared-origin duplication and .deep_copy() for fully independent memory

.copy is going to be a mechanism used to disentangle origins in many cases, so I think it should be a deep copy. To me, a separate shallow copy makes more sense in Mojo.

1 Like

Thanks for catching it. That went unnoticed on my part. It is a leftover artefact from an earlier attempt to implement views in Matrix, when I was still working through the semantics of the origins system. At that time, I observed that using __copyinit__ produced a new Matrix instance that appeared to be a copy, but shared the same origin. It seemed that the copied instance influenced when the original instance went out of scope, which felt like behaviour appropriate for a view rather than for a true copy (I might have been doing something wrong with the origins too since It was pretty new to me).

To work around this, I introduced a deep_copy helper that explicitly created a new instance with a fresh origin and no references to the original. That issue appears to have since been fixed in Mojo (which is great), so this behaviour will need to be revisited and updated for future NuMojo versions.

Also, could you clarify what you mean by a shallow copy in this context?

A shallow copy would be creating a new alias for the matrix that might have a separate layout (in LayoutTensor terms), but have a pointer to the same data. This lets you do things like have two different views into the same tensor at the same time, which lets you do interesting things with iteration order. It is a borrow, so it means both tensors have to be immutable, but that doesn’t matter if they’re operation inputs.

As of now, NuMojo lets you create a mutable instance of a Matrix that does not own the underlying data. Is there any difference between this (like in numpy, pytorch) and a Shallow copy?

Are there locks on the matrices, or is the whole library going to be thread unsafe?

As of now, it is thread unsafe. I am still wondering about what would be the better choice going forward. Since numpy, pytorch both are thread unsafe afaik (all their views are mutable), I am trying to follow the same model. If you have any other suggestions, I’ll be happy to discuss!

You might be able to use the type system once the threading model is a bit more concrete. Until then, the only thread safe way to do it would be locks.