Tensor¶
The Tensor is Sorix's core data structure, analogous to NumPy arrays but with the added ability to record every operation within a computational graph. This operation tracking is what enables the automatic computation of gradients(Autograd).
# Uncomment the next line and run this cell to install sorix
#!pip install 'sorix @ git+https://github.com/Mitchell-Mirano/sorix.git@main'
import sorix
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
Create a Tensor¶
A tensor can be initialized from a NumPy array, a pandas DataFrame, or a Python list. Internally, Sorix converts any supported input into a NumPy array.
# from list
a = sorix.tensor([1,2,3])
a
tensor([1, 2, 3])
#from numpy
a = sorix.tensor(np.random.rand(5,5),dtype=sorix.float32)
a
tensor([[0.2071713 , 0.41947836, 0.07962975, 0.5244394 , 0.6548204 ],
[0.8944446 , 0.06502061, 0.10248217, 0.7747318 , 0.88705516],
[0.5643748 , 0.49590206, 0.1346305 , 0.70885235, 0.5168297 ],
[0.5906459 , 0.5085468 , 0.5742638 , 0.78206235, 0.9811358 ],
[0.8679026 , 0.40534765, 0.54185003, 0.2393661 , 0.78549093]])
# from pandas
data = pd.DataFrame({
'a': [0.464307, 0.182403, 0.664873, 0.906638, 0.725385],
'b': [0.278199, 0.187902, 0.887387, 0.473387, 0.904510],
'c': [0.793136, 0.957675, 0.035765, 0.639977, 0.622032],
'd': [0.618634, 0.784397, 0.841349, 0.352944, 0.783273],
'e': [0.729128, 0.467162, 0.687347, 0.432614, 0.980809]
}).to_numpy()
t = sorix.tensor(data)
t
tensor([[0.464307, 0.278199, 0.793136, 0.618634, 0.729128],
[0.182403, 0.187902, 0.957675, 0.784397, 0.467162],
[0.664873, 0.887387, 0.035765, 0.841349, 0.687347],
[0.906638, 0.473387, 0.639977, 0.352944, 0.432614],
[0.725385, 0.90451 , 0.622032, 0.783273, 0.980809]], dtype=sorix.float64)
To access the underlying NumPy array within a Sorix tensor, you can use the data attribute and apply any NumPy operation directly to it.
t.data
array([[0.464307, 0.278199, 0.793136, 0.618634, 0.729128],
[0.182403, 0.187902, 0.957675, 0.784397, 0.467162],
[0.664873, 0.887387, 0.035765, 0.841349, 0.687347],
[0.906638, 0.473387, 0.639977, 0.352944, 0.432614],
[0.725385, 0.90451 , 0.622032, 0.783273, 0.980809]])
type(t.data)
numpy.ndarray
Sorix utils to create tensors¶
t = sorix.as_tensor([1,2,3])
t
tensor([1, 2, 3])
t = sorix.randn(3,4)
t
tensor([[-0.51459839, -1.10703618, 1.06682148, 0.30795715],
[ 0.26241433, -0.17128717, -0.21749413, -2.74397071],
[-1.51422003, -1.19496629, 0.5895064 , 0.98470189]], dtype=sorix.float64)
t = sorix.zeros((3,4))
t
tensor([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]], dtype=sorix.float64)
t = sorix.ones((3,4))
t
tensor([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]], dtype=sorix.float64)
t = sorix.eye(3)
t
tensor([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]], dtype=sorix.float64)
t = sorix.diag(sorix.tensor([1,2,3]))
t
tensor([[1, 0, 0],
[0, 2, 0],
[0, 0, 3]])
t = sorix.randint(0,10,(3,4))
t
tensor([[0, 1, 1, 8],
[2, 2, 7, 8],
[8, 6, 7, 4]])
t = sorix.arange(0,10)
t
tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
t = sorix.linspace(0,10,5)
t
tensor([ 0. , 2.5, 5. , 7.5, 10. ], dtype=sorix.float64)
t = sorix.logspace(0,10,5)
t
tensor([1.00000000e+00, 3.16227766e+02, 1.00000000e+05, 3.16227766e+07,
1.00000000e+10], dtype=sorix.float64)
t = sorix.randperm(5)
t
tensor([1, 3, 0, 4, 2])
Basic Operations¶
a = sorix.tensor([1,2,3])
b = sorix.tensor([3,4,5])
print(a)
print(b)
tensor([1, 2, 3]) tensor([3, 4, 5])
c = a + b
c
tensor([4, 6, 8])
c = a - b
c
tensor([-2, -2, -2])
c = a * b
c
tensor([ 3, 8, 15])
c = a@b
c
tensor(26)
c = a**2
c
tensor([1, 4, 9])
Slicing¶
a = sorix.tensor(np.random.rand(5,5))
a
tensor([[0.80177217, 0.55241063, 0.95463729, 0.77167495, 0.86233854],
[0.31276827, 0.51473794, 0.19621999, 0.12381903, 0.48983477],
[0.03545405, 0.14491229, 0.76614029, 0.79323029, 0.0176238 ],
[0.9068692 , 0.32968585, 0.87899129, 0.2838805 , 0.55193655],
[0.81679509, 0.65089285, 0.70083308, 0.48012314, 0.85958796]], dtype=sorix.float64)
a[3,:]
tensor([0.9068692 , 0.32968585, 0.87899129, 0.2838805 , 0.55193655], dtype=sorix.float64)
a[3,3]
tensor(0.2838805)
a[:,3]
tensor([0.77167495, 0.12381903, 0.79323029, 0.2838805 , 0.48012314], dtype=sorix.float64)
Using GPU¶
When running on a GPU, Sorix uses CuPy arrays instead of NumPy. You can enable GPU execution by setting the device parameter to 'cuda' (the default is 'cpu'). When 'cuda' is specified, Sorix creates CuPy-based tensors and executes all operations on the GPU.
To check whether a GPU is available, you can call sorix.cuda.is_available(). Refer to the examples below.
device = 'cuda' if sorix.cuda.is_available() else 'cpu'
device
ā GPU basic operation passed ā GPU available: NVIDIA GeForce RTX 4070 Laptop GPU CUDA runtime version: 13000 CuPy version: 13.6.0
'cuda'
a = sorix.tensor(np.random.rand(5,5), device=device)
b = sorix.tensor(np.random.rand(5,5), device=device)
c = a + b
c
tensor([[0.72742743, 0.62952234, 0.26912146, 1.19496364, 1.65595822],
[0.90332437, 0.41796393, 0.76028652, 1.04050809, 0.68133805],
[0.40553032, 0.22315913, 0.87077003, 1.13289381, 0.8473109 ],
[0.34116413, 1.05820453, 1.68829801, 1.44227615, 0.49505028],
[0.46997088, 0.72281559, 1.03850478, 1.10188645, 1.87770445]], device='cuda:0', dtype=sorix.float64)