Indexing

Indexing in ArrayFire is a powerful but easy to abuse feature. This feature allows you to reference or copy subsections of a larger array and perform operations on only a subset of elements.

This chapter is split into the following sections:

Indexer structure is the key element used in Rust wrapper of ArrayFire for creating references to existing Arrays. The above sections illustrate how it can be used in conjunction with Seq and/or Array. Apart from that, each section also showcases a macro based equivalent code(if one exists) that is more terse in syntax but offers the same functionality.

Using Seq objects

Create a view of an existing Array

We will Sequences and the function index in this approach.

        let dims = Dim4::new(&[5, 5, 1, 1]);
        let a = randu::<f32>(dims);
        //af_print!("a", a);
        //a
        //[5 5 1 1]
        //    0.3990     0.5160     0.8831     0.9107     0.6688
        //    0.6720     0.3932     0.0621     0.9159     0.8434
        //    0.5339     0.2706     0.7089     0.0231     0.1328
        //    0.1386     0.9455     0.9434     0.2330     0.2657
        //    0.7353     0.1587     0.1227     0.2220     0.2299

        // Index array using sequences
        let seqs = &[Seq::new(1u32, 3, 1), Seq::default()];
        let _sub = index(&a, seqs);
        //af_print!("a(seq(1,3,1), span)", sub);
        // [3 5 1 1]
        //     0.6720     0.3932     0.0621     0.9159     0.8434
        //     0.5339     0.2706     0.7089     0.0231     0.1328
        //     0.1386     0.9455     0.9434     0.2330     0.2657

However, the same above code can be condensed into a much terse syntax with the help of view macro. Take a look at the following two approaches using view macro.

        let dims = dim4!(5, 5, 1, 1);
        let a = randu::<f32>(dims);
        let first3 = seq!(1:3:1);
        let allindim2 = seq!();
        let _sub = view!(a[first3, allindim2]);
OR
        let a = randu::<f32>(dim4!(5, 5));
        let _sub = view!(a[1:3:1, 1:1:0]); // 1:1:0 means all elements along axis

Modify a sub region of an existing Array

Let us take a look at an example where a portion of an existing Array will be set to with another Array. We will an constant value Array and the function assign_seq in the below example.

        let mut a = constant(2.0 as f32, dim4!(5, 3));
        //print(&a);
        // 2.0 2.0 2.0
        // 2.0 2.0 2.0
        // 2.0 2.0 2.0
        // 2.0 2.0 2.0
        // 2.0 2.0 2.0

        let b = constant(1.0 as f32, dim4!(3, 3));
        let seqs = [seq!(1:3:1), seq!()];
        assign_seq(&mut a, &seqs, &b);
        //print(&a);
        // 2.0 2.0 2.0
        // 1.0 1.0 1.0
        // 1.0 1.0 1.0
        // 1.0 1.0 1.0
        // 2.0 2.0 2.0

A much terser way of doing the same using macro is shown below

        let mut a = randu::<f32>(dim4!(5, 5));
        let b = randu::<f32>(dim4!(2, 2));
        eval!(a[1:2:1, 1:2:1] = b);

NOTE Normally you want to avoid accessing individual elements of the array like this for performance reasons.

Using Array and Seq combination

Create a view of an existing Array

To use a combination of Array and Seq objects to index an existing Array, we will need a more generalized function index_gen.

        let values: [f32; 3] = [1.0, 2.0, 3.0];
        let indices = Array::new(&values, Dim4::new(&[3, 1, 1, 1]));
        let seq4gen = Seq::new(0.0, 2.0, 1.0);
        let a = randu::<f32>(Dim4::new(&[5, 3, 1, 1]));
        // [5 3 1 1]
        //     0.0000     0.2190     0.3835
        //     0.1315     0.0470     0.5194
        //     0.7556     0.6789     0.8310
        //     0.4587     0.6793     0.0346
        //     0.5328     0.9347     0.0535

        let mut idxrs = Indexer::default();
        idxrs.set_index(&indices, 0, None); // 2nd arg is indexing dimension
        idxrs.set_index(&seq4gen, 1, Some(false)); // 3rd arg indicates batch operation

        let _sub2 = index_gen(&a, idxrs);
        //println!("a(indices, seq(0, 2, 1))"); print(&sub2);
        // [3 3 1 1]
        //     0.1315     0.0470     0.5194
        //     0.7556     0.6789     0.8310
        //     0.4587     0.6793     0.0346

Similar to how view macro helped with abreviating the syntax when indexing with just sequences, it can also help when using a combination of Seq and Array.

        let values: [f32; 3] = [1.0, 2.0, 3.0];
        let indices = Array::new(&values, Dim4::new(&[3, 1, 1, 1]));
        let seq4gen = seq!(0:2:1);
        let a = randu::<f32>(Dim4::new(&[5, 3, 1, 1]));
        let _sub2 = view!(a[indices, seq4gen]);

Modify a sub region of an existing Array

Set a portion of an existing Array with another Array using a combination of Seq and Array. We will use assign_gen function to do it.

       let values: [f32; 3] = [1.0, 2.0, 3.0];
       let indices = Array::new(&values, dim4!(3, 1, 1, 1));
       let seq4gen = seq!(0:2:1);
       let mut a = randu::<f32>(dim4!(5, 3, 1, 1));
       // [5 3 1 1]
       //     0.0000     0.2190     0.3835
       //     0.1315     0.0470     0.5194
       //     0.7556     0.6789     0.8310
       //     0.4587     0.6793     0.0346
       //     0.5328     0.9347     0.0535

       let b = constant(2.0 as f32, dim4!(3, 3, 1, 1));

       let mut idxrs = Indexer::default();
       idxrs.set_index(&indices, 0, None); // 2nd arg is indexing dimension
       idxrs.set_index(&seq4gen, 1, Some(false)); // 3rd arg indicates batch operation

       let _sub2 = assign_gen(&mut a, &idxrs, &b);
       //println!("a(indices, seq(0, 2, 1))"); print(&sub2);
       // [5 3 1 1]
       //     0.0000     0.2190     0.3835
       //     2.0000     2.0000     2.0000
       //     2.0000     2.0000     2.0000
       //     2.0000     2.0000     2.0000
       //     0.5328     0.9347     0.0535
OR
       let values: [f32; 3] = [1.0, 2.0, 3.0];
       let indices = Array::new(&values, dim4!(3));
       let seq4gen = seq!(0:2:1);
       let mut a = randu::<f32>(dim4!(5, 3));

       let b = constant(2.0 as f32, dim4!(3, 3));

       eval!(a[indices, seq4gen] = b);

Extract or Set rows/columns of an Array

Extract a specific set of rows/coloumns from an existing Array.

        let a = randu::<f32>(dim4!(5, 5, 1, 1));
        //print(&a);
        // [5 5 1 1]
        //     0.6010     0.5497     0.1583     0.3636     0.6755
        //     0.0278     0.2864     0.3712     0.4165     0.6105
        //     0.9806     0.3410     0.3543     0.5814     0.5232
        //     0.2126     0.7509     0.6450     0.8962     0.5567
        //     0.0655     0.4105     0.9675     0.3712     0.7896
        let _r = row(&a, 4);
        // [1 5 1 1]
        //     0.0655     0.4105     0.9675     0.3712     0.7896
        let _c = col(&a, 4);
        // [5 1 1 1]
        //     0.6755
        //     0.6105
        //     0.5232
        //     0.5567
        //     0.7896

You can also use rows & cols to retrieve a subset of rows or coloumns respectively.

Similarly, set_row & set_rows can be used to change the values in a particular set of rows using another Array. set_col & set_cols has same functionality, except that it is for coloumns.

Negative Indices

Negative indices can also be used to refer elements from the end of a given axis. Negative value for a row/column/slice will fetch corresponding row/column/slice in reverse order. Given below are some examples that showcase getting row(s)/col(s) from an existing Array.

        let a = randu::<f32>(dim4!(5, 5));
        // [5 5 1 1]
        //     0.6010     0.5497     0.1583     0.3636     0.6755
        //     0.0278     0.2864     0.3712     0.4165     0.6105
        //     0.9806     0.3410     0.3543     0.5814     0.5232
        //     0.2126     0.7509     0.6450     0.8962     0.5567
        //     0.0655     0.4105     0.9675     0.3712     0.7896
        let _r = row(&a, -1);
        // [1 5 1 1]
        //     0.0655     0.4105     0.9675     0.3712     0.7896
        let _c = col(&a, -1);
        // [5 1 1 1]
        //     0.6755
        //     0.6105
        //     0.5232
        //     0.5567
        //     0.7896
        let a = randu::<f32>(dim4!(5, 5));
        // [5 5 1 1]
        //     0.6010     0.5497     0.1583     0.3636     0.6755
        //     0.0278     0.2864     0.3712     0.4165     0.6105
        //     0.9806     0.3410     0.3543     0.5814     0.5232
        //     0.2126     0.7509     0.6450     0.8962     0.5567
        //     0.0655     0.4105     0.9675     0.3712     0.7896
        let _r = rows(&a, -1, -2);
        // [2 5 1 1]
        //     0.2126     0.7509     0.6450     0.8962     0.5567
        //     0.0655     0.4105     0.9675     0.3712     0.7896
        let _c = cols(&a, -1, -3);
        // [5 3 1 1]
        //     0.1583     0.3636     0.6755
        //     0.3712     0.4165     0.6105
        //     0.3543     0.5814     0.5232
        //     0.6450     0.8962     0.5567
        //     0.9675     0.3712     0.7896