Skip to content

Array Creation

rizukirr edited this page Mar 30, 2026 · 3 revisions

Array Creation

NumcArray is the core tensor type. It supports N-dimensional data with any of the 10 numeric types, contiguous and non-contiguous memory layouts, and zero-copy views.

Creating Arrays

Uninitialized: numc_array_create

Creates an array with uninitialized memory. Fastest option -- use when you plan to fill it immediately.

size_t shape[] = {2, 3};
NumcArray *a = numc_array_create(ctx, shape, 2, NUMC_DTYPE_FLOAT32);
// a contains garbage data -- write before reading!

The second argument (shape) is an array of dimension sizes, and the third (2) is the number of dimensions.

Zero-Filled: numc_array_zeros

Creates an array with all elements set to zero:

NumcArray *z = numc_array_zeros(ctx, (size_t[]){3, 3}, 2, NUMC_DTYPE_INT32);
// All elements are 0

Constant-Filled: numc_array_fill

Creates an array where every element has the same value. The value pointer must match the dtype:

// Float32 array filled with 3.14
float val = 3.14f;
NumcArray *f = numc_array_fill(ctx, (size_t[]){2, 4}, 2,
                                NUMC_DTYPE_FLOAT32, &val);

// Int32 array filled with 42
int32_t ival = 42;
NumcArray *g = numc_array_fill(ctx, (size_t[]){5}, 1,
                                NUMC_DTYPE_INT32, &ival);

Tip: for inline float values, use a compound literal:

NumcArray *f = numc_array_fill(ctx, shape, 2, NUMC_DTYPE_FLOAT32,
                                &(float){2.0f});

Scalar (0-D): Pass NULL Shape

To create a scalar (0-dimensional) array:

float val = 10.0f;
NumcArray *scalar = numc_array_fill(ctx, NULL, 0, NUMC_DTYPE_FLOAT32, &val);

Deep Copy: numc_array_copy

Creates a new array with the same shape, dtype, and data as the original:

NumcArray *original = numc_array_rand(ctx, (size_t[]){4, 4}, 2,
                                       NUMC_DTYPE_FLOAT64);
NumcArray *copy = numc_array_copy(original);
// Modifying copy does not affect original

The copy is allocated in the same context as the original.

Writing Data: numc_array_write

Copies raw bytes from a buffer into the array's data. The buffer must contain exactly numc_array_size(arr) * numc_array_elem_size(arr) bytes in row-major order:

// 1D array
size_t shape1[] = {4};
NumcArray *a = numc_array_create(ctx, shape1, 1, NUMC_DTYPE_FLOAT32);
float data1[] = {1.0f, 2.0f, 3.0f, 4.0f};
numc_array_write(a, data1);

// 2D array (row-major: rows are contiguous)
size_t shape2[] = {2, 3};
NumcArray *b = numc_array_create(ctx, shape2, 2, NUMC_DTYPE_INT32);
int32_t data2[] = {1, 2, 3,    // row 0
                   4, 5, 6};   // row 1
numc_array_write(b, data2);

// 3D array
size_t shape3[] = {2, 2, 4};
NumcArray *c = numc_array_create(ctx, shape3, 3, NUMC_DTYPE_INT32);
int32_t data3[][2][4] = {
    {{1, 2, 3, 4}, {5, 6, 7, 8}},
    {{9, 10, 11, 12}, {13, 14, 15, 16}},
};
numc_array_write(c, data3);

One-Hot Encoding: numc_one_hot

Creates a 2-D one-hot encoded array from a 1-D integer label array. Essential for classification tasks in neural networks (e.g., encoding MNIST digit labels for cross-entropy loss).

// Labels: [0, 2, 1, 3] with 4 classes
int32_t data[] = {0, 2, 1, 3};
NumcArray *labels = numc_array_create(ctx, (size_t[]){4}, 1, NUMC_DTYPE_INT32);
numc_array_write(labels, data);

NumcArray *oh = numc_one_hot(ctx, labels, 4, NUMC_DTYPE_FLOAT32);
// Shape: (4, 4)
// [[1, 0, 0, 0],
//  [0, 0, 1, 0],
//  [0, 1, 0, 0],
//  [0, 0, 0, 1]]

Parameters:

  • labels -- 1-D array with any integer dtype (int8 through uint64)
  • num_classes -- number of columns in the output
  • dtype -- output dtype (must be NUMC_DTYPE_FLOAT32 or NUMC_DTYPE_FLOAT64)

Behavior:

  • Out-of-bounds or negative label values produce an all-zero row (no error)
  • OpenMP parallelized for batches larger than 100K rows
  • Works with non-contiguous label arrays (e.g., sliced views)

Random Arrays

See Random Initialization for numc_array_rand, numc_array_randn, and weight initializers.

// Quick preview
NumcArray *r = numc_array_rand(ctx, (size_t[]){3, 3}, 2, NUMC_DTYPE_FLOAT32);
// Each element is uniform random in [0, 1)

Reading Array Properties

NumcArray *a = numc_array_fill(ctx, (size_t[]){2, 3, 4}, 3,
                                NUMC_DTYPE_FLOAT32, &(float){1.0f});

// Number of dimensions
size_t ndim = numc_array_ndim(a);       // 3

// Total number of elements
size_t size = numc_array_size(a);       // 24 (2*3*4)

// Allocated capacity in elements
size_t cap = numc_array_capacity(a);    // >= 24

// Size of one element in bytes
size_t es = numc_array_elem_size(a);    // 4 (sizeof(float))

// Data type
NumcDType dt = numc_array_dtype(a);     // NUMC_DTYPE_FLOAT32

// Raw data pointer
void *ptr = numc_array_data(a);

// Is the memory layout contiguous?
bool contig = numc_array_is_contiguous(a);  // true (freshly created)

Shape and Strides

Shape and strides are copied into a buffer you provide:

size_t ndim = numc_array_ndim(a);
size_t shape[ndim], strides[ndim];

numc_array_shape(a, shape);
// shape = {2, 3, 4}

numc_array_strides(a, strides);
// strides = {48, 16, 4} (bytes: 3*4*4, 4*4, 4)

Strides are in bytes, not elements. For a contiguous float32 array with shape {2, 3, 4}:

  • strides[2] = 4 (one float32)
  • strides[1] = 16 (4 elements * 4 bytes)
  • strides[0] = 48 (3 * 4 elements * 4 bytes)

Printing

numc_array_print(a);

Prints the array contents to stdout in a nested bracket format similar to NumPy.

Accessing Raw Data

Use numc_array_data() to get a pointer to the raw data buffer, then cast to the appropriate type:

NumcArray *a = numc_array_create(ctx, (size_t[]){4}, 1, NUMC_DTYPE_FLOAT32);
float data[] = {1.0f, 2.0f, 3.0f, 4.0f};
numc_array_write(a, data);

float *raw = (float *)numc_array_data(a);
printf("First element: %f\n", raw[0]);  // 1.0
printf("Last element: %f\n", raw[3]);   // 4.0

Warning: Only index raw data directly if the array is contiguous (numc_array_is_contiguous(a) == true). Non-contiguous arrays (from transpose or slice) have non-trivial strides, so flat indexing will access wrong elements.

Clone this wiki locally