Melange
API ReferenceAndroid

Tensor

Complete API reference for the Tensor class on Android.

This page reflects ZeticMLange Android 1.9.0.

The Tensor class is the unified data container passed to and returned from ZeticMLangeModel.run(). It wraps a direct ByteBuffer together with its DataType and shape, and provides typed accessors for reading and writing the underlying data.

Package

com.zeticai.mlange.core.tensor

Import

import com.zeticai.mlange.core.tensor.Tensor
import com.zeticai.mlange.core.tensor.DataType

Companion Factories

The Tensor.of(...) factories are the recommended way to create tensors from typed arrays or NIO buffers. Each overload infers a sensible default DataType matching the source type.

Tensor.of(
    data: FloatArray,
    dataType: DataType = DataType.Float32,
    shape: IntArray = intArrayOf(data.size),
    immediateRelease: Boolean = false,
): Tensor

Overloads are provided for the following source types:

SourceDefault DataType
ByteBufferDataType.Int8
ByteArrayDataType.Int8
FloatArray, FloatBufferDataType.Float32
DoubleArray, DoubleBufferDataType.Float64
IntArray, IntBufferDataType.Int32
LongArray, LongBufferDataType.Int64
ShortArray, ShortBufferDataType.Int16
CharArray, CharBufferDataType.Int16

Each overload also has a variant that accepts shape: Array<Int> in addition to shape: IntArray.

val input = Tensor.of(
    data = floatArrayOf(/* ... */),
    dataType = DataType.Float32,
    shape = intArrayOf(1, 3, 640, 640),
)

Tensor.of(data, dataType, shape) (generic array)

inline fun <reified T> of(
    data: Array<T>,
    dataType: DataType,
    shape: Array<Int>,
    immediateRelease: Boolean = false,
): Tensor

Supported element types: Int, Float, Long, Double, Char, Short.

Tensor.random(dataType, shape, immediateRelease)

Creates a tensor filled with random bytes. Handy for smoke-testing a model graph.

fun random(
    dataType: DataType,
    shape: IntArray,
    immediateRelease: Boolean = false,
): Tensor
val fakeInput = Tensor.random(DataType.Float32, intArrayOf(1, 3, 640, 640))

Constructor

Tensor(data, dataType, shape, immediateRelease)

Creates a tensor directly from a direct ByteBuffer. Prefer the Tensor.of(...) factories above; use this constructor only when you already hold a direct ByteBuffer (for example, wrapping a native allocation).

Tensor(
    data: ByteBuffer,
    dataType: DataType = DataType.Int8,
    shape: IntArray = intArrayOf(data.capacity()),
    immediateRelease: Boolean = false,
)
ParameterTypeDescription
dataByteBufferA direct ByteBuffer holding the raw tensor bytes. Non-direct buffers will throw.
dataTypeDataTypeElement type of the tensor. See DataType. Defaults to DataType.Int8.
shapeIntArrayTensor shape. The product of its dimensions times dataType.size must equal data.capacity().
immediateReleaseBooleanWhen true, registers the tensor with TensorCleaner so its native memory is released as soon as it becomes unreachable.

Throws: IllegalArgumentException if data is not a direct ByteBuffer, or if shape does not match data.capacity().

val buffer = ByteBuffer.allocateDirect(4 * 3 * 4).order(ByteOrder.nativeOrder())
val tensor = Tensor(buffer, DataType.Float32, intArrayOf(1, 3, 2, 2))

Methods

count()

Returns the number of elements in the tensor (total bytes divided by dataType.size).

fun count(): Int

size()

Returns the size of the tensor in bytes (equivalent to data.capacity()).

fun size(): Int

data<T>()

Reifies the underlying ByteBuffer as a typed view or array. Useful for reading inference outputs as floats, ints, etc.

inline fun <reified T> data(): T

Supported T:

TypeReturns
ByteBufferThe underlying buffer itself.
IntBuffer, LongBuffer, FloatBuffer, DoubleBuffer, CharBuffer, ShortBufferA typed view on the buffer.
ByteArray, IntArray, LongArray, FloatArray, DoubleArray, CharArray, ShortArrayA backing array copy.

Throws: IllegalArgumentException if T is not one of the supported types.

val floats: FloatArray = tensor.data()
val floatView: FloatBuffer = tensor.data()

get<T>(index)

Reads a single element at index as the requested numeric type.

inline fun <reified T : Number> get(index: Int): T

Supported T: Int, Long, Float, Double, Char, Short, Byte.

val first: Float = tensor.get<Float>(0)

set(index, value)

Writes value into the tensor at index. The write type is inferred from the runtime type of value.

fun <T> set(index: Int, value: T)

Supported value types: Int, Long, Float, Double, Char, Short.

tensor.set(0, 1.5f)

order(order)

Changes the byte order of the underlying buffer.

fun order(order: ByteOrder)
tensor.order(ByteOrder.LITTLE_ENDIAN)

from(tensor)

Copies another tensor's bytes into this tensor. The source tensor must have the same shape.

fun from(tensor: Tensor)

Throws: IllegalArgumentException if the source tensor's shape does not match.

output.from(reusedOutputTensor)

copy(data, shape, dataType, immediateRelease)

Copies bytes from data into this tensor in place. The total byte size implied by shape and dataType must equal this tensor's current size.

fun copy(
    data: ByteBuffer = this.data,
    shape: IntArray = this.shape,
    dataType: DataType = this.dataType,
    immediateRelease: Boolean = this.immediateRelease,
)

Throws: IllegalArgumentException if the source or destination buffer is smaller than the expected byte count.


DataType

DataType is a sealed interface describing the element type and byte size of a tensor.

import com.zeticai.mlange.core.tensor.DataType
VariantElement size (bytes)
DataType.Float324
DataType.Float648
DataType.Float16, DataType.BFloat162
DataType.UInt8, DataType.Int8, DataType.QInt8, DataType.QInt41
DataType.UInt16, DataType.Int16, DataType.QInt162
DataType.UInt32, DataType.Int32, DataType.QInt324
DataType.UInt64, DataType.Int648
DataType.Boolean1
DataType.Unknown(size)Caller-provided

Use DataType.from(name) to look up a type by its lowercase name (e.g. "float32", "int8", "bool"), and DataType.toName(type) for the reverse.


Properties

data

The underlying direct ByteBuffer backing the tensor.

val data: ByteBuffer

Prefer the typed data<T>() accessor when you need a typed view.


Full Working Example

Create tensors that match the model's expected shape and data type, then pass them to ZeticMLangeModel.run(...).

import com.zeticai.mlange.core.model.ZeticMLangeModel
import com.zeticai.mlange.core.tensor.Tensor

class MainActivity : AppCompatActivity() {
    private lateinit var model: ZeticMLangeModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        model = ZeticMLangeModel(this, PERSONAL_KEY, "Steve/YOLOv11_comparison")
    }

    private fun runFrame(input: Tensor) {
        // (1) Run inference with tensors matching the model input order
        val outputs: Array<Tensor> = model.run(arrayOf(input))

        // (2) Read outputs as typed arrays
        val logits: FloatArray = outputs[0].data()
    }
}

Memory Management Recommendation

Tensor wraps a direct ByteBuffer that lives in off-heap native memory. How you manage that memory depends on who owns the underlying buffer:

  1. Reuse tensors when practical. If your preprocessing output has the same shape every frame, keep the source arrays stable and update their contents before creating or reusing Tensor values for run(...).

  2. Only set immediateRelease = true on tensors you allocate yourself. When you build a tensor with Tensor.of(...) (for example, a per-frame camera input) and pass it to run(), the model copies your bytes into its own input buffer. Your tensor is useless after run() returns, so registering it with TensorCleaner via immediateRelease = true lets off-heap memory be reclaimed as soon as the object becomes unreachable — which prevents OutOfMemoryError: Direct buffer memory under tight loops.

See Also

On this page