Array NumPy adalah objek Python yang paling sering melintasi batas pada program numerik. Topik ini membahas pertukaran array dua arah, yaitu meneruskan array NumPy masuk sebagai tampilan Span (view), menghitung pada tampilan tersebut, lalu mengembalikan hasil Mojo ke sisi NumPy sebagai array baru. Contoh yang digunakan adalah pembacaan sensor suhu, dan setelah membacanya, data semacam itu dapat dihitung Mojo tanpa salinan pada arah masuk, sedangkan hasilnya langsung dapat diproses kembali oleh NumPy.
Meneruskan array NumPy sebagai tampilan Span#
Fungsi from_numpy_array dari modul std.python.numpy membentuk tampilan Span atas buffer array NumPy tanpa menyalin datanya, dan sifat ini disebut zero-copy, yakni kedua sisi berbagi memori yang sama. Parameter dtype pada kurung siku wajib ditulis eksplisit dan wajib cocok dengan dtype array NumPy, sehingga from_numpy_array[DType.float64] hanya menerima array bertipe float64. Tampilan yang dihasilkan berupa Span biasa, sehingga dapat diteruskan ke fungsi Mojo mana pun yang menerima Span tanpa penyesuaian tambahan.
Program berikut meneruskan lima pembacaan sensor suhu dari NumPy ke Mojo, menghitung jumlahnya dengan perulangan biasa, lalu mengulang penghitungan setelah array NumPy berubah dari sisi Python. Simpan kode berikut sebagai terima-tampilan.mojo.
from std.python import Python
from std.python.numpy import from_numpy_array
from std.collections.span import Span
def jumlah(data: Span[Float64, _]) -> Float64:
var total: Float64 = 0.0
for i in range(len(data)):
total += data[i]
return total
def main() raises:
var np = Python.import_module("numpy")
var suhu = np.array([22.0, 21.5, 22.5, 21.0, 23.0])
var tampilan = from_numpy_array[DType.float64](suhu)
print(jumlah(tampilan))
# array NumPy berubah dari sisi Python, tampilan langsung mengikutinya
suhu[0] = 25.0
print(jumlah(tampilan))# jalankan program menerima tampilan
mojo run terima-tampilan.mojo110.0
113.0Jumlah pertama sebesar 110.0 dihitung dari lima pembacaan awal. Setelah itu, penugasan suhu[0] = 25.0 mengubah elemen pertama dari sisi Python, dan penghitungan ulang langsung menunjukkan 113.0 tanpa pembentukan tampilan baru. Perilaku ini membuktikan bahwa tampilan dan array NumPy membaca buffer yang sama, sehingga tidak ada biaya salinan pada arah masuk, dan data sebesar apa pun dapat diteruskan dengan biaya tetap.
Setiap elemen tampilan dibaca sebagai Float64 melalui indeks kurung siku, sedangkan len(tampilan) menghitung banyaknya elemen, sehingga isi fungsi jumlah tidak berbeda dengan perulangan atas Array Mojo. Penyesuaian tidak dilakukan di dalam kernel, karena perbedaan asal data diselesaikan sepenuhnya oleh fungsi jembatan di atas. Inilah alasan tipe masukan berupa Span cocok untuk API kernel, yaitu satu tipe yang menerima data dari kedua bahasa tanpa salinan di sisi mana pun.
Mengembalikan hasil ke NumPy#
Arah keluar menggunakan fungsi copy_to_numpy_array, yang menerima koleksi Mojo dan mengembalikan ndarray NumPy berisi salinan datanya. Salinan dilakukan satu kali di batas, sehingga hasil sekecil apa pun maupun sebesar apa pun dikirim dengan satu panggilan. Koleksi sumbernya fleksibel, yaitu Span atas Array Mojo maupun List langsung diterima, dan List dipilih apabila panjang hasil tidak diketahui sebelumnya, sebab Array Mojo berukuran tetap pada waktu kompilasi.
Program berikut menghitung simpangan tiap pembacaan terhadap rata-ratanya di sisi Mojo, menyusun hasilnya dalam List, lalu mengirimnya balik ke NumPy. Nilai kembalian langsung diuji dengan metode mean milik NumPy untuk membuktikan bahwa objek yang kembali adalah ndarray sungguhan. Simpan kode berikut sebagai kembalikan-hasil.mojo.
from std.python import Python
from std.python.numpy import from_numpy_array, copy_to_numpy_array
from std.collections.span import Span
from std.collections.list import List
def rata_rata(data: Span[Float64, _]) -> Float64:
var total: Float64 = 0.0
for i in range(len(data)):
total += data[i]
return total / Float64(len(data))
def main() raises:
var np = Python.import_module("numpy")
var suhu = np.array([22.0, 21.5, 22.5, 21.0, 23.0])
var tampilan = from_numpy_array[DType.float64](suhu)
var rata = rata_rata(tampilan)
print(rata)
# simpangan terhadap rata-rata dihitung Mojo, lalu dikirim balik ke NumPy
var simpangan = List[Float64]()
for i in range(len(tampilan)):
simpangan.append(tampilan[i] - rata)
var kembali = copy_to_numpy_array(simpangan)
print(kembali)
print(kembali.mean())# jalankan program mengembalikan hasil
mojo run kembalikan-hasil.mojo22.0
[ 0. -0.5 0.5 -1. 1. ]
0.0Baris pertama adalah rata-rata lima pembacaan sebesar 22.0 yang dihitung dari tampilan Span. Baris kedua mencetak ndarray yang dikembalikan copy_to_numpy_array, yaitu simpangan masing-masing pembacaan terhadap rata-rata, dan format cetaknya mengikuti konvensi NumPy. Baris ketiga sebesar 0.0 berasal dari kembali.mean(), yang hanya dapat berjalan karena kembali benar-benar ndarray milik NumPy dan bukan koleksi Mojo yang disamarkan.
Struktur program ini mengikuti pola persilangan batas yang hemat, yaitu data masuk diterima sekali di awal dan hasil dikirim sekali di akhir. Seluruh perulangan penyusunan hasil berjalan murni di Mojo tanpa menyentuh Python, sehingga biaya bongkar-bungkus tidak bertambah seiring banyaknya elemen. Apabila perhitungannya bertambah kompleks, pola ini tidak perlu berubah, karena kernel yang lebih besar tetap diapit dua panggilan jembatan yang sama.
Kesalahan umum#
Kesalahan yang paling mudah ditemui adalah dtype yang tidak cocok. Program berikut membentuk tampilan DType.float64 atas array yang elemennya bilangan bulat, dan pembentukan itu berakhir dengan error ketika program berjalan. Simpan kode berikut sebagai dtype-salah.mojo.
from std.python import Python
from std.python.numpy import from_numpy_array
def main() raises:
var np = Python.import_module("numpy")
var hitung = np.arange(10)
var tampilan = from_numpy_array[DType.float64](hitung)
print(tampilan[0])# jalankan program dtype yang tidak cocok
mojo run dtype-salah.mojostack trace was not collected. Enable stack trace collection with environment variable `MODULAR_DEBUG=stack-trace-on-error`
Unhandled exception caught during execution: from_numpy_array: dtype mismatch: array is 'int64' but 'float64' was requested
/home/kal/.pixi/envs/mojo/bin/mojo: error: execution exited with a non-zero result: 1Pesan dtype mismatch: array is 'int64' but 'float64' was requested menyatakan ketidakcocokan tipe secara eksplisit, yaitu array bertipe int64 sedangkan tampilan menuntut float64. Perbaikannya adalah menyamakan parameter dtype dengan dtype array, misalnya from_numpy_array[DType.int64] untuk array int64, atau memastikan array dibuat dengan elemen pecahan terlebih dahulu. Periksa dtype array melalui atribut dtype sebelum membentuk tampilan, karena kesalahan jenis ini tidak terlihat pada waktu kompilasi.
Kesalahan kedua menyangkut umur tampilan, yaitu menyimpan tampilan dalam variabel yang berumur lebih panjang daripada array NumPy di baliknya. Apabila variabel NumPy diisi dengan array yang lain, tampilan lama tetap menunjuk buffer lama dan tidak mengikuti isi array tersebut. Kebiasaan yang aman adalah membentuk tampilan, menghitung, lalu menyelesaikan pekerjaan dalam lingkup yang sama, sehingga umur tampilan tidak pernah melebihi umur datanya.
Ringkasan#
from_numpy_array[DType.float64](arr)membentuk tampilan Span atas buffer NumPy tanpa salinan, dengan parameter dtype eksplisit yang wajib cocok.- Tampilan dan array NumPy berbagi memori yang sama, sehingga perubahan dari sisi mana pun langsung terlihat oleh sisi lainnya.
copy_to_numpy_arraymengirim koleksi Mojo ke sisi NumPy sebagai ndarray sungguhan, dengan satu salinan di batas.- Dtype yang tidak cocok berakhir dengan error
dtype mismatchpada saat program berjalan, bukan pada waktu kompilasi.
Referensi#
- Calling Python from Mojo — Mojo Manual — Panduan resmi pertukaran nilai antara Mojo dan Python, termasuk jembatan array NumPy.
- Mojo standard library — Mojo
— Referensi modul
std.python.numpyyang memuatfrom_numpy_arraydancopy_to_numpy_array.