3.3. Accessing elements¶
Next, let’s take a look on how we can access elements of a Tensor.
3.3.1. Get elements¶
On the Python side, we can simply use slice to get the elements, just as common with list/numpy.array/torch.tensor in Python. See This page for more details. In C++, Cytnx ports this approach from Python to the C++ API. You can simply use a slice string to access elements.
For example:
In Python:
1A = cytnx.arange(24).reshape(2,3,4)
2print(A)
3
4B = A[0,:,1:4:2]
5print(B)
6
7C = A[:,1]
8print(C)
In C++:
1auto A = cytnx::arange(24).reshape(2, 3, 4);
2cout << A << endl;
3
4auto B = A(0, ":", "1:4:2");
5cout << B << endl;
6
7auto C = A(":", 1);
8cout << C << endl;
Output >>
Total elem: 24
type : Double (Float64)
cytnx device: CPU
Shape : (2,3,4)
[[[0.00000e+00 1.00000e+00 2.00000e+00 3.00000e+00 ]
[4.00000e+00 5.00000e+00 6.00000e+00 7.00000e+00 ]
[8.00000e+00 9.00000e+00 1.00000e+01 1.10000e+01 ]]
[[1.20000e+01 1.30000e+01 1.40000e+01 1.50000e+01 ]
[1.60000e+01 1.70000e+01 1.80000e+01 1.90000e+01 ]
[2.00000e+01 2.10000e+01 2.20000e+01 2.30000e+01 ]]]
Total elem: 6
type : Double (Float64)
cytnx device: CPU
Shape : (3,2)
[[1.00000e+00 3.00000e+00 ]
[5.00000e+00 7.00000e+00 ]
[9.00000e+00 1.10000e+01 ]]
Total elem: 8
type : Double (Float64)
cytnx device: CPU
Shape : (2,4)
[[4.00000e+00 5.00000e+00 6.00000e+00 7.00000e+00 ]
[1.60000e+01 1.70000e+01 1.80000e+01 1.90000e+01 ]]
Note
To convert between Python and C++ APIs, notice that in C++ you need to use operator() instead of operator[] if you are using slice strings to access elements.
The return value will always be a Tensor object, even it only contains one element.
In the case where you have only one element in a Tensor, you can use item() to get the element as a standard Python/C++ type.
In Python:
1A = cytnx.arange(24).reshape(2,3,4)
2B = A[0,0,1]
3C = B.item()
4print(B)
5print(C)
In C++:
1auto A = cytnx::arange(24).reshape(2, 3, 4);
2auto B = A(0, 0, 1);
3Scalar C = B.item();
4double Ct = B.item<double>();
5
6cout << B << endl;
7cout << C << endl;
8cout << Ct << endl;
Output >>
Total elem: 1
type : Double (Float64)
cytnx device: CPU
Shape : (1)
[1.00000e+00 ]
1.0
Note
In C++, using item<>() to get the element requires to explicitly specify the type that matches the dtype of the Tensor. If the type specifier does not match, an error will be prompted.
Starting from v0.7+, users can use item() in C++ without explicitly specifying the type with a template.
3.3.2. Set elements¶
Setting elements is pretty much the same as in numpy.array/torch.tensor. You can assign a Tensor to a specific slice, or set all the elements in that slice to be the same value.
For example:
In Python:
1A = cytnx.arange(24).reshape(2,3,4)
2B = cytnx.zeros([3,2])
3print(A)
4print(B)
5
6A[1,:,::2] = B
7print(A)
8
9A[0,::2,2] = 4
10print(A)
In C++:
1auto A = cytnx::arange(24).reshape(2, 3, 4);
2auto B = cytnx::zeros({3, 2});
3cout << A << endl;
4cout << B << endl;
5
6A(1, ":", "::2") = B;
7cout << A << endl;
8
9A(0, "::2", 2) = 4;
10cout << A << endl;
Output >>
Total elem: 24
type : Double (Float64)
cytnx device: CPU
Shape : (2,3,4)
[[[0.00000e+00 1.00000e+00 2.00000e+00 3.00000e+00 ]
[4.00000e+00 5.00000e+00 6.00000e+00 7.00000e+00 ]
[8.00000e+00 9.00000e+00 1.00000e+01 1.10000e+01 ]]
[[1.20000e+01 1.30000e+01 1.40000e+01 1.50000e+01 ]
[1.60000e+01 1.70000e+01 1.80000e+01 1.90000e+01 ]
[2.00000e+01 2.10000e+01 2.20000e+01 2.30000e+01 ]]]
Total elem: 6
type : Double (Float64)
cytnx device: CPU
Shape : (3,2)
[[0.00000e+00 0.00000e+00 ]
[0.00000e+00 0.00000e+00 ]
[0.00000e+00 0.00000e+00 ]]
Total elem: 24
type : Double (Float64)
cytnx device: CPU
Shape : (2,3,4)
[[[0.00000e+00 1.00000e+00 2.00000e+00 3.00000e+00 ]
[4.00000e+00 5.00000e+00 6.00000e+00 7.00000e+00 ]
[8.00000e+00 9.00000e+00 1.00000e+01 1.10000e+01 ]]
[[0.00000e+00 1.30000e+01 0.00000e+00 1.50000e+01 ]
[0.00000e+00 1.70000e+01 0.00000e+00 1.90000e+01 ]
[0.00000e+00 2.10000e+01 0.00000e+00 2.30000e+01 ]]]
Total elem: 24
type : Double (Float64)
cytnx device: CPU
Shape : (2,3,4)
[[[0.00000e+00 1.00000e+00 4.00000e+00 3.00000e+00 ]
[4.00000e+00 5.00000e+00 6.00000e+00 7.00000e+00 ]
[8.00000e+00 9.00000e+00 4.00000e+00 1.10000e+01 ]]
[[0.00000e+00 1.30000e+01 0.00000e+00 1.50000e+01 ]
[0.00000e+00 1.70000e+01 0.00000e+00 1.90000e+01 ]
[0.00000e+00 2.10000e+01 0.00000e+00 2.30000e+01 ]]]
3.3.3. Low-level API (C++ only)¶
On the C++ side, Cytnx provides lower-level APIs with slightly smaller overhead for getting elements. These low-level APIs require using an Accessor object.
- Accessor:
Accessor object is equivalent to Python slice. It is sometimes convenient to use aliases to simplify the expression when using it.
1using ac = cytnx::Accessor;
2
3ac(4); // this is equal to index '4' in Python
4ac::all(); // this is equal to ':' in Python
5ac::range(0, 4, 2); // this is equal to '0:4:2' in Python
In the following, let’s see how it can be used to get/set the elements from/in a Tensor.
operator[] (middle level API) :
1using ac = cytnx::Accessor;
2auto A = cytnx::arange(24).reshape(2, 3, 4);
3auto B = cytnx::zeros({3, 2});
4
5// [get] this is equal to A[0,:,1:4:2] in Python:
6auto C = A[{ac(0), ac::all(), ac::range(1, 4, 2)}];
7
8// [set] this is equal to A[1,:,0:4:2] = B in Python:
9A[{ac(1), ac::all(), ac::range(0, 4, 2)}] = B;
Note
Remember to write a braket{} around the elements to be accessed. This is needed because the C++ operator[] can only accept one argument.
- get/set (low level API) :
get() and set() are part of the low-level API. Operator() and Operator[] are all built based on these.
1using ac = cytnx::Accessor;
2auto A = cytnx::arange(24).reshape(2, 3, 4);
3auto B = cytnx::zeros({3, 2});
4
5// [get] this is equal to A[0,:,1:4:2] in Python:
6auto C = A.get({ac(0), ac::all(), ac::range(1, 4, 2)});
7
8// [set] this is equal to A[1,:,0:4:2] = B in Python:
9A.set({ac(1), ac::all(), ac::range(0, 4, 2)}, B);
Hint
Similarly, you can also pass a C++ vector<cytnx_int64> as an argument.
Tip
If your code makes frequent use of get/set elements, using the low-level API can reduce the overhead.