fix install nccl setup.py

This commit is contained in:
lucasdelimanogueira 2024-05-24 19:48:37 -03:00
parent b637c3e0f1
commit ef272b65ee
44 changed files with 7650 additions and 3 deletions

View file

@ -0,0 +1,9 @@
from norch.tensor import *
from .nn import *
from .optim import *
from .utils import *
from .norchvision import *
__version__ = "0.0.4"
__author__ = 'Lucas de Lima Nogueira'
__credits__ = 'Lucas de Lima Nogueira'

View file

@ -0,0 +1 @@
from .functions import *

View file

@ -0,0 +1,304 @@
import math
import norch
class AddBackward:
def __init__(self, x, y):
self.input = [x, y]
def backward(self, gradient):
return [gradient, gradient]
class AddBroadcastedBackward:
def __init__(self, x, y):
self.input = [x, y]
def backward(self, gradient):
x, y = self.input
grad_x = self._reshape_gradient(gradient, x.shape)
grad_y = self._reshape_gradient(gradient, y.shape)
return [grad_x, grad_y]
def _reshape_gradient(self, gradient, shape):
# Reduce gradient dimensions to match the target shape dimensions
while len(gradient.shape) > len(shape):
gradient = gradient.sum(axis=0)
# Sum along axes where the target shape dimension is 1
for i in range(len(shape)):
if shape[i] == 1:
gradient = gradient.sum(axis=i, keepdim=True)
return gradient
class SubBackward:
def __init__(self, x, y):
self.input = [x, y]
def backward(self, gradient):
return [gradient, -gradient]
class SubBroadcastedBackward:
def __init__(self, x, y):
self.input = [x, y]
def backward(self, gradient):
x, y = self.input
grad_x = self._reshape_gradient(gradient, x.shape)
grad_y = self._reshape_gradient(gradient, y.shape)
return [grad_x, -grad_y]
def _reshape_gradient(self, gradient, shape):
# Reduce gradient dimensions to match the target shape dimensions
while len(gradient.shape) > len(shape):
gradient = gradient.sum(axis=0)
# Sum along axes where the target shape dimension is 1
for i in range(len(shape)):
if shape[i] == 1:
gradient = gradient.sum(axis=i)
return gradient
class ScalarMulBackward:
def __init__(self, x, scalar):
self.input = [x]
self.scalar = scalar
def backward(self, gradient):
return [gradient * self.scalar]
class ElementwiseMulBackward:
def __init__(self, x, y):
self.input = [x, y]
def backward(self, gradient):
x = self.input[0]
y = self.input[1]
return [y * gradient, x * gradient]
class MatmulBackward:
def __init__(self, x, y):
self.input = [x, y]
def backward(self, gradient):
x, y = self.input
if x.ndim != y.ndim: # broadcasted case
aux = (gradient @ y.transpose(-1,-2))
aux_sum = aux.sum(axis=0)
return [aux_sum, x.transpose(-1,-2) @ gradient]
else:
return [gradient @ y.transpose(-1,-2), x.transpose(-1,-2) @ gradient]
"""class PowBackward:
def __init__(self, x, power):
self.input = [x]
self.power = power
def backward(self, gradient):
return [(gradient * self.power) * (self.input[0]) ** (self.power - 1)]"""
class PowBackward:
def __init__(self, base, exponent):
self.input = [base, exponent]
def backward(self, gradient):
base, exponent = self.input[0], self.input[1]
if isinstance(base, (int, float)):
grad_base = gradient * (base ** (exponent - 1))
grad_exponent = (gradient * base ** exponent) * math.log(base)
else:
grad_base = gradient * exponent * (base ** (exponent - 1))
grad_exponent = (gradient * base ** exponent) * (base.log())
return [grad_base, grad_exponent]
class LogBackward:
def __init__(self, x):
self.input = [x]
def backward(self, gradient):
grad_input = gradient / self.input[0]
return [grad_input]
class SumBackward:
def __init__(self, x, axis=None, keepdim=False):
self.input = [x]
self.axis = axis
self.keepdim = keepdim
def backward(self, gradient):
input_shape = self.input[0].shape.copy()
if self.axis == -1:
# If axis is None, sum reduces the tensor to a scalar.
grad_output = float(gradient[[0] * len(gradient.shape)]) * self.input[0].ones_like()
else:
if self.keepdim:
input_shape = input_shape[:self.axis] + [1] + input_shape[self.axis+1:]
else:
input_shape = input_shape[:self.axis] + input_shape[self.axis+1:]
# Broadcast the gradient to the input shape along the specified axis.
grad_output_shape = list(input_shape)
grad_output = gradient.reshape(grad_output_shape)
grad_output = grad_output + self.input[0].zeros_like()
return [grad_output]
class ReshapeBackward:
def __init__(self, x):
self.input = [x]
def backward(self, gradient):
return [gradient.reshape(self.input[0].shape)]
class TransposeBackward:
def __init__(self, x, axis1, axis2):
self.input = [x]
self.axis1 = axis1
self.axis2 = axis2
def backward(self, gradient):
return [gradient.transpose(self.axis2, self.axis1)]
class TBackward:
def __init__(self, x):
self.input = [x]
def backward(self, gradient):
return [gradient.T]
class DivisionBackward:
def __init__(self, x, y):
self.input = [x, y]
def backward(self, gradient):
x, y = self.input
grad_x = gradient / y
grad_y = -1 * gradient * (x / (y * y))
return [grad_x, grad_y]
class SinBackward:
def __init__(self, x):
self.input = [x]
def backward(self, gradient):
x = self.input[0]
return [gradient * x.cos()]
class CosBackward:
def __init__(self, x):
self.input = [x]
def backward(self, gradient):
x = self.input[0]
return [-gradient * x.sin()]
class MaxBackward:
def __init__(self, x, axis=None, keepdim=False):
self.input = [x]
self.axis = axis
self.keepdim = keepdim
def backward(self, gradient):
input_shape = self.input[0].shape.copy()
if self.axis == -1:
max_value = self.input[0].max()
mask = self.input[0].equal(max_value)
grad_output = float(gradient[[0] * len(gradient.shape)]) * self.input[0].ones_like()
grad_output = (grad_output * mask) / mask.sum()[0]
else:
if self.keepdim:
input_shape = input_shape[:self.axis] + [1] + input_shape[self.axis+1:]
else:
input_shape = input_shape[:self.axis] + input_shape[self.axis+1:]
# Broadcast the gradient to the input shape along the specified axis.
grad_output_shape = list(input_shape)
grad_output = gradient.reshape(grad_output_shape)
grad_output = grad_output + self.input[0].zeros_like()
max_values = self.input[0].max(axis=self.axis, keepdim=True)
mask = self.input[0].equal(max_values)
grad_output = (grad_output * mask)
return [grad_output]
class MinBackward:
def __init__(self, x, axis=None, keepdim=False):
self.input = [x]
self.axis = axis
self.keepdim = keepdim
def backward(self, gradient):
input_shape = self.input[0].shape.copy()
if self.axis == -1:
min_value = self.input[0].min()
mask = self.input[0].equal(min_value)
grad_output = float(gradient[[0] * len(gradient.shape)]) * self.input[0].ones_like()
grad_output = (grad_output * mask) / mask.sum()[0]
else:
if self.keepdim:
input_shape = input_shape[:self.axis] + [1] + input_shape[self.axis+1:]
else:
input_shape = input_shape[:self.axis] + input_shape[self.axis+1:]
# Broadcast the gradient to the input shape along the specified axis.
grad_output_shape = list(input_shape)
grad_output = gradient.reshape(grad_output_shape)
grad_output = grad_output + self.input[0].zeros_like()
max_values = self.input[0].min(axis=self.axis, keepdim=True)
mask = self.input[0].equal(max_values)
grad_output = (grad_output * mask)
return [grad_output]
class CrossEntropyLossBackward:
def __init__(self, logits, targets):
self.input = [logits, targets]
def backward(self, gradient):
logits, targets = self.input
if logits.ndim == 1:
softmax = norch.softmax(logits, dim=0)
grad_logits = (softmax - targets)
elif logits.ndim == 2:
# batched
batch_size = logits.shape[0]
softmax = norch.softmax(logits, dim=1)
grad_logits = (softmax - targets) / batch_size
return [grad_logits, None] # targets do not have a gradient
class SigmoidBackward:
def __init__(self, input):
self.input = [input]
def backward(self, gradient):
sigmoid_x = self.input[0].sigmoid()
grad_input = gradient * sigmoid_x * (1 - sigmoid_x)
return [grad_input]

View file

@ -0,0 +1,443 @@
#include "tensor.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
void add_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data) {
for (int i = 0; i < tensor1->size; i++) {
result_data[i] = tensor1->data[i] + tensor2->data[i];
}
}
void add_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size) {
int max_ndim = tensor1->ndim > tensor2->ndim ? tensor1->ndim : tensor2->ndim;
// Calculate strides for broadcasting
int* strides1 = (int*)malloc(max_ndim * sizeof(int));
int* strides2 = (int*)malloc(max_ndim * sizeof(int));
if (strides1 == NULL || strides2 == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(1);
}
int stride1 = 1, stride2 = 1;
for (int i = max_ndim - 1; i >= 0; i--) {
int dim1 = i < tensor1->ndim ? tensor1->shape[tensor1->ndim - max_ndim + i] : 1;
int dim2 = i < tensor2->ndim ? tensor2->shape[tensor2->ndim - max_ndim + i] : 1;
strides1[i] = dim1 == broadcasted_shape[i] ? stride1 : 0;
strides2[i] = dim2 == broadcasted_shape[i] ? stride2 : 0;
stride1 *= (dim1 == broadcasted_shape[i]) ? dim1 : 1;
stride2 *= (dim2 == broadcasted_shape[i]) ? dim2 : 1;
}
// Perform element-wise addition with broadcasting
for (int i = 0; i < broadcasted_size; i++) {
int index1 = 0, index2 = 0;
int linear_index = i;
for (int j = max_ndim - 1; j >= 0; j--) {
int pos = linear_index % broadcasted_shape[j];
linear_index /= broadcasted_shape[j];
if (strides1[j] != 0) index1 += pos * strides1[j];
if (strides2[j] != 0) index2 += pos * strides2[j];
}
result_data[i] = tensor1->data[index1] + tensor2->data[index2];
}
// Free strides
free(strides1);
free(strides2);
}
void sub_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data) {
for (int i = 0; i < tensor1->size; i++) {
result_data[i] = tensor1->data[i] - tensor2->data[i];
}
}
void sub_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size) {
int max_ndim = tensor1->ndim > tensor2->ndim ? tensor1->ndim : tensor2->ndim;
// Calculate strides for broadcasting
int* strides1 = (int*)malloc(max_ndim * sizeof(int));
int* strides2 = (int*)malloc(max_ndim * sizeof(int));
if (strides1 == NULL || strides2 == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(1);
}
int stride1 = 1, stride2 = 1;
for (int i = max_ndim - 1; i >= 0; i--) {
int dim1 = i < tensor1->ndim ? tensor1->shape[tensor1->ndim - max_ndim + i] : 1;
int dim2 = i < tensor2->ndim ? tensor2->shape[tensor2->ndim - max_ndim + i] : 1;
strides1[i] = dim1 == broadcasted_shape[i] ? stride1 : 0;
strides2[i] = dim2 == broadcasted_shape[i] ? stride2 : 0;
stride1 *= (dim1 == broadcasted_shape[i]) ? dim1 : 1;
stride2 *= (dim2 == broadcasted_shape[i]) ? dim2 : 1;
}
// Perform element-wise addition with broadcasting
for (int i = 0; i < broadcasted_size; i++) {
int index1 = 0, index2 = 0;
int linear_index = i;
for (int j = max_ndim - 1; j >= 0; j--) {
int pos = linear_index % broadcasted_shape[j];
linear_index /= broadcasted_shape[j];
if (strides1[j] != 0) index1 += pos * strides1[j];
if (strides2[j] != 0) index2 += pos * strides2[j];
}
result_data[i] = tensor1->data[index1] - tensor2->data[index2];
}
// Free strides
free(strides1);
free(strides2);
}
void elementwise_mul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data) {
for (int i = 0; i < tensor1->size; i++) {
result_data[i] = tensor1->data[i] * tensor2->data[i];
}
}
void scalar_mul_tensor_cpu(Tensor* tensor, float scalar, float* result_data) {
for (int i = 0; i < tensor->size; i++) {
result_data[i] = scalar * tensor->data[i];
}
}
void scalar_div_tensor_cpu(float scalar, Tensor* tensor, float* result_data) {
for (int i = 0; i < tensor->size; i++) {
result_data[i] = scalar / tensor->data[i];
}
}
void tensor_div_scalar_cpu(Tensor* tensor, float scalar, float* result_data) {
for (int i = 0; i < tensor->size; i++) {
result_data[i] = tensor->data[i] / scalar;
}
}
void tensor_div_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data) {
for (int i = 0; i < tensor1->size; i++) {
result_data[i] = tensor1->data[i] / tensor2->data[i];
}
}
void matmul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data) {
for (int i = 0; i < tensor1->shape[0]; i++) {
for (int j = 0; j < tensor2->shape[1]; j++) {
float sum = 0.0;
for (int k = 0; k < tensor1->shape[1]; k++) {
sum += tensor1->data[i * tensor1->shape[1] + k] * tensor2->data[k * tensor2->shape[1] + j];
}
result_data[i * tensor2->shape[1] + j] = sum;
}
}
}
void broadcasted_batched_matmul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data) {
int result_data_stride = tensor1->shape[0] * tensor2->shape[2];
for (int batch = 0; batch < tensor2->shape[0]; batch++) {
for (int i = 0; i < tensor1->shape[0]; i++) {
for (int j = 0; j < tensor2->shape[2]; j++) {
float sum = 0.0;
for (int k = 0; k < tensor1->shape[1]; k++) {
sum += tensor1->data[i * tensor1->shape[1] + k] * tensor2->data[batch*tensor2->strides[0] + (k * tensor2->shape[2] + j)];
}
result_data[(batch * result_data_stride) + (i * tensor2->shape[2] + j)] = sum;
}
}
}
}
void batched_matmul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data) {
int result_data_stride = tensor1->shape[1] * tensor2->shape[2];
for (int batch = 0; batch < tensor2->shape[0]; batch++) {
for (int i = 0; i < tensor1->shape[1]; i++) {
for (int j = 0; j < tensor2->shape[2]; j++) {
float sum = 0.0;
for (int k = 0; k < tensor1->shape[2]; k++) {
sum += tensor1->data[(batch * tensor1->strides[0]) + i * tensor1->shape[2] + k] * tensor2->data[batch*tensor2->strides[0] + (k * tensor2->shape[2] + j)];
}
result_data[(batch * result_data_stride) + (i * tensor2->shape[2] + j)] = sum;
}
}
}
}
void scalar_pow_tensor_cpu(float base, Tensor* tensor, float* result_data) {
for (int i = 0; i < tensor->size; i++) {
result_data[i] = powf(base, tensor->data[i]);
}
}
void tensor_pow_scalar_cpu(Tensor* tensor, float exponent, float* result_data) {
for (int i = 0; i < tensor->size; i++) {
result_data[i] = powf(tensor->data[i], exponent);
}
}
void log_tensor_cpu(Tensor* tensor, float* result_data) {
for (int i = 0; i < tensor->size; i++) {
result_data[i] = logf(tensor->data[i]);
}
}
void sum_tensor_cpu(Tensor* tensor, float* result_data, int size, int* result_shape, int axis) {
if (axis == -1) {
// Sum over all elements
float sum = 0.0;
for (int i = 0; i < tensor->size; i++) {
sum += tensor->data[i];
}
*result_data = sum;
} else {
if (axis < 0 || axis >= tensor->ndim) {
printf("Invalid axis");
return;
}
int axis_stride = tensor->strides[axis];
for (int i = 0; i < tensor->shape[axis]; i++) {
for (int j = 0; j < size; j++) {
int index = 0;
int remainder = j;
for (int k = tensor->ndim - 2; k >= 0; k--) {
index += (remainder % result_shape[k]) * tensor->strides[k < axis ? k : k + 1];
remainder /= result_shape[k];
}
result_data[j] += tensor->data[index + i * axis_stride];
}
}
}
}
void max_tensor_cpu(Tensor* tensor, float* result_data, int size, int* result_shape, int axis) {
if (axis == -1) {
float max_value = -INFINITY;
for (int i = 0; i < tensor->size; i++) {
max_value = fmax(max_value, tensor->data[i]);
}
*result_data = max_value;
} else {
for (int i = 0; i < size; i++) {
result_data[i] = -INFINITY;
}
if (axis < 0 || axis >= tensor->ndim) {
printf("Invalid axis");
return;
}
int axis_stride = tensor->strides[axis];
for (int i = 0; i < tensor->shape[axis]; i++) {
for (int j = 0; j < size; j++) {
int index = 0;
int remainder = j;
for (int k = tensor->ndim - 2; k >= 0; k--) {
index += (remainder % result_shape[k]) * tensor->strides[k < axis ? k : k + 1];
remainder /= result_shape[k];
}
result_data[j] = fmax(result_data[j], tensor->data[index + i * axis_stride]);
}
}
}
}
void min_tensor_cpu(Tensor* tensor, float* result_data, int size, int* result_shape, int axis) {
if (axis == -1) {
float min_value = INFINITY;
for (int i = 0; i < tensor->size; i++) {
min_value = fmin(min_value, tensor->data[i]);
}
*result_data = min_value;
} else {
for (int i = 0; i < size; i++) {
result_data[i] = INFINITY;
}
if (axis < 0 || axis >= tensor->ndim) {
printf("Invalid axis");
return;
}
int axis_stride = tensor->strides[axis];
for (int i = 0; i < tensor->shape[axis]; i++) {
for (int j = 0; j < size; j++) {
int index = 0;
int remainder = j;
for (int k = tensor->ndim - 2; k >= 0; k--) {
index += (remainder % result_shape[k]) * tensor->strides[k < axis ? k : k + 1];
remainder /= result_shape[k];
}
result_data[j] = fmin(result_data[j], tensor->data[index + i * axis_stride]);
}
}
}
}
void equal_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data) {
for (int i = 0; i < tensor1->size; i++) {
result_data[i] = (tensor1->data[i] == tensor2->data[i]) ? 1.0f : 0.0f;
}
}
void equal_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size) {
int max_ndim = tensor1->ndim > tensor2->ndim ? tensor1->ndim : tensor2->ndim;
// Calculate strides for broadcasting
int* strides1 = (int*)malloc(max_ndim * sizeof(int));
int* strides2 = (int*)malloc(max_ndim * sizeof(int));
if (strides1 == NULL || strides2 == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(1);
}
int stride1 = 1, stride2 = 1;
for (int i = max_ndim - 1; i >= 0; i--) {
int dim1 = i < tensor1->ndim ? tensor1->shape[tensor1->ndim - max_ndim + i] : 1;
int dim2 = i < tensor2->ndim ? tensor2->shape[tensor2->ndim - max_ndim + i] : 1;
strides1[i] = dim1 == broadcasted_shape[i] ? stride1 : 0;
strides2[i] = dim2 == broadcasted_shape[i] ? stride2 : 0;
stride1 *= (dim1 == broadcasted_shape[i]) ? dim1 : 1;
stride2 *= (dim2 == broadcasted_shape[i]) ? dim2 : 1;
}
// Perform element-wise equal with broadcasting
for (int i = 0; i < broadcasted_size; i++) {
int index1 = 0, index2 = 0;
int linear_index = i;
for (int j = max_ndim - 1; j >= 0; j--) {
int pos = linear_index % broadcasted_shape[j];
linear_index /= broadcasted_shape[j];
if (strides1[j] != 0) index1 += pos * strides1[j];
if (strides2[j] != 0) index2 += pos * strides2[j];
}
result_data[i] = (tensor1->data[index1] == tensor2->data[index2]) ? 1.0f : 0.0f;
}
// Free strides
free(strides1);
free(strides2);
}
void ones_like_tensor_cpu(Tensor* tensor, float* result_data) {
for (int i = 0; i < tensor->size; i++) {
result_data[i] = 1.0;
}
}
void zeros_like_tensor_cpu(Tensor* tensor, float* result_data) {
for (int i = 0; i < tensor->size; i++) {
result_data[i] = 0.0;
}
}
void transpose_1D_tensor_cpu(Tensor* tensor, float* result_data) {
for (int i = 0; i < tensor->shape[0]; i++) {
result_data[i] = tensor->data[i];
}
}
void transpose_2D_tensor_cpu(Tensor* tensor, float* result_data) {
int rows = tensor->shape[0];
int cols = tensor->shape[1];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
result_data[j * rows + i] = tensor->data[i * cols + j];
}
}
}
void transpose_3D_tensor_cpu(Tensor* tensor, float* result_data) {
int batch = tensor->shape[0];
int rows = tensor->shape[1];
int cols = tensor->shape[2];
for (int i = 0; i < batch; i++) {
for (int j = 0; j < rows; j++) {
for (int k = 0; k < cols; k++) {
result_data[k * rows * batch + j * batch + i] = tensor->data[i * rows * cols + j * cols + k];
}
}
}
}
void assign_tensor_cpu(Tensor* tensor, float* result_data) {
for (int i = 0; i < tensor->size; i++) {
result_data[i] = tensor->data[i];
}
}
void make_contiguous_tensor_cpu(Tensor* tensor, float* result_data, int* new_strides) {
for (int i = 0; i < tensor->size; i++) {
int index = 0;
int offset = i;
for (int j = 0; j < tensor->ndim; j++) {
index += (offset / new_strides[j]) * tensor->strides[j];
offset %= new_strides[j];
}
result_data[i] = tensor->data[index];
}
// Free old data and update tensor properties
free(tensor->data);
free(tensor->strides);
tensor->data = result_data;
tensor->strides = new_strides;
}
void sin_tensor_cpu(Tensor* tensor, float* result_data) {
for (int i = 0; i < tensor->size; i++) {
result_data[i] = sinf(tensor->data[i]);
}
}
void sigmoid_tensor_cpu(Tensor* tensor, float* result_data) {
for (int i = 0; i < tensor->size; i++) {
// avoid overflow
if (tensor->data[i] >= 0) {
float z = expf(-tensor->data[i]);
result_data[i] = 1 / (1 + z);
} else {
float z = expf(tensor->data[i]);
result_data[i] = z / (1 + z);
}
}
}
void cos_tensor_cpu(Tensor* tensor, float* result_data) {
for (int i = 0; i < tensor->size; i++) {
result_data[i] = cosf(tensor->data[i]);
}
}

View file

@ -0,0 +1,38 @@
#ifndef CPU_H
#define CPU_H
#include "tensor.h"
void add_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data);
void add_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size);
void sum_tensor_cpu(Tensor* tensor, float* result_data, int size, int* shape, int axis);
void max_tensor_cpu(Tensor* tensor, float* result_data, int size, int* result_shape, int axis);
void min_tensor_cpu(Tensor* tensor, float* result_data, int size, int* result_shape, int axis);
void sub_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data);
void sub_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size);
void elementwise_mul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data);
void scalar_div_tensor_cpu(float scalar, Tensor* tensor, float* result_data);
void tensor_div_scalar_cpu(Tensor* tensor, float scalar, float* result_data);
void tensor_div_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data);
void matmul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data);
void broadcasted_batched_matmul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data);
void batched_matmul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data);
void scalar_pow_tensor_cpu(float base, Tensor* tensor, float* result_data);
void tensor_pow_scalar_cpu(Tensor* tensor, float exponent, float* result_data);
void log_tensor_cpu(Tensor* tensor, float* result_data);
void scalar_mul_tensor_cpu(Tensor* tensor, float scalar, float* result_data);
void equal_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data);
void equal_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size);
void ones_like_tensor_cpu(Tensor* tensor, float* result_data);
void zeros_like_tensor_cpu(Tensor* tensor, float* result_data);
void transpose_1D_tensor_cpu(Tensor* tensor, float* result_data);
void transpose_2D_tensor_cpu(Tensor* tensor, float* result_data);
void transpose_3D_tensor_cpu(Tensor* tensor, float* result_data);
void transpose_axes_cpu(Tensor* tensor, float* result_data, int axis1, int axis2, int* new_shape);
void assign_tensor_cpu(Tensor* tensor, float* result_data);
void make_contiguous_tensor_cpu(Tensor* tensor, float* result_data, int* new_strides);
void sin_tensor_cpu(Tensor* tensor, float* result_data);
void cos_tensor_cpu(Tensor* tensor, float* result_data);
void sigmoid_tensor_cpu(Tensor* tensor, float* result_data);
#endif /* CPU_H */

1248
build/lib/norch/csrc/cuda.cu Normal file

File diff suppressed because it is too large Load diff

104
build/lib/norch/csrc/cuda.h Normal file
View file

@ -0,0 +1,104 @@
#ifndef CUDA_KERNEL_H_
#define CUDA_KERNEL_H_
__host__ void cpu_to_cuda(Tensor* tensor, int device_id);
__host__ void cuda_to_cpu(Tensor* tensor);
__global__ void add_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int size);
__host__ void add_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data);
__global__ void add_broadcasted_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int* broadcasted_shape, int* strides1, int*strides2, int max_ndim, int size);
__host__ void add_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size);
__global__ void sub_broadcasted_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int* broadcasted_shape, int* strides1, int*strides2, int max_ndim, int size);
__host__ void sub_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size);
__global__ void sum_tensor_cuda_kernel(float* data, float* result_data);
__global__ void sum_tensor_cuda_kernel_axis(float* data, float* result_data, int* strides, int* shape, int axis, int ndim, int axis_stride, int size, int result_size);
__host__ void sum_tensor_cuda(Tensor* tensor, float* result_data, int axis);
__global__ void max_tensor_cuda_kernel(float* data, float* result_data);
__global__ void max_tensor_cuda_kernel_axis(float* data, float* result_data, int* strides, int* shape, int axis, int ndim, int axis_stride, int size, int result_size);
__host__ void max_tensor_cuda(Tensor* tensor, float* result_data, int axis);
__global__ void min_tensor_cuda_kernel(float* data, float* result_data);
__global__ void min_tensor_cuda_kernel_axis(float* data, float* result_data, int* strides, int* shape, int axis, int ndim, int axis_stride, int size, int result_size);
__host__ void min_tensor_cuda(Tensor* tensor, float* result_data, int axis);
__global__ void sub_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int size);
__host__ void sub_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data);
__global__ void elementwise_mul_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int size);
__host__ void elementwise_mul_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data);
__global__ void scalar_mul_tensor_cuda_kernel(float* data, float scalar, float* result_data, int size);
__host__ void scalar_mul_tensor_cuda(Tensor* tensor, float scalar, float* result_data);
__global__ void scalar_div_tensor_cuda_kernel(float scalar, float* data, float* result_data, int size);
__host__ void scalar_div_tensor_cuda(float scalar, Tensor* tensor, float* result_data);
__global__ void tensor_div_scalar_cuda_kernel(float* data, float scalar, float* result_data, int size);
__host__ void tensor_div_scalar_cuda(Tensor* tensor, float scalar, float* result_data);
__global__ void tensor_div_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int size);
__host__ void tensor_div_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data);
__global__ void matmul_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int rows1, int cols1, int cols2);
__host__ void matmul_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data);
__global__ void batched_matmul_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int batch_size, int rows1, int cols1, int cols2);
__host__ void batched_matmul_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data);
__global__ void broadcasted_batched_matmul_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int batch_size, int rows1, int cols1, int cols2);
__host__ void broadcasted_batched_matmul_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data);
__global__ void tensor_pow_scalar_cuda_kernel(float* data, float exponent, float* result_data, int size);
__host__ void tensor_pow_scalar_cuda(Tensor* tensor, float exponent, float* result_data);
__global__ void scalar_pow_tensor_cuda_kernel(float base, float* data, float* result_data, int size);
__host__ void scalar_pow_tensor_cuda(float base, Tensor* tensor, float* result_data);
__global__ void log_tensor_cuda_kernel(float* data, float* result_data, int size);
__host__ void log_tensor_cuda(Tensor* tensor, float* result_data);
__global__ void equal_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int size);
__host__ void equal_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data);
__global__ void equal_broadcasted_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int* broadcasted_shape, int* strides1, int*strides2, int max_ndim, int size);
__host__ void equal_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size);
__global__ void ones_like_tensor_cuda_kernel(float* data, float* result_data, int size);
__host__ void ones_like_tensor_cuda(Tensor* tensor, float* result_data);
__global__ void zeros_like_tensor_cuda_kernel(float* data, float* result_data, int size);
__host__ void zeros_like_tensor_cuda(Tensor* tensor, float* result_data);
__global__ void transpose_1D_tensor_cuda_kernel(float* data, float* result_data, int rows, int cols);
__host__ void transpose_1D_tensor_cuda(Tensor* tensor, float* result_data);
__global__ void transpose_2D_tensor_cuda_kernel(float* data, float* result_data, int rows, int cols);
__host__ void transpose_2D_tensor_cuda(Tensor* tensor, float* result_data);
__global__ void transpose_3D_tensor_cuda_kernel(float* data, float* result_data, int batch, int rows, int cols);
__host__ void transpose_3D_tensor_cuda(Tensor* tensor, float* result_data);
__global__ void assign_tensor_cuda_kernel(float* data, float* result_data, int size);
__host__ void assign_tensor_cuda(Tensor* tensor, float* result_data);
__global__ void make_contiguous_tensor_cuda_kernel(float* data, float* result_data, int ndim, int size, int* strides, int* new_strides);
__host__ void make_contiguous_tensor_cuda(Tensor* tensor, float* result_data, int* new_strides);
__global__ void sin_tensor_cuda_kernel(float* data, float* result_data, int size);
__host__ void sin_tensor_cuda(Tensor* tensor, float* result_data);
__global__ void cos_tensor_cuda_kernel(float* data, float* result_data, int size);
__host__ void cos_tensor_cuda(Tensor* tensor, float* result_data);
__global__ void sigmoid_tensor_cuda_kernel(float* data, float* result_data, int size);
__host__ void sigmoid_tensor_cuda(Tensor* tensor, float* result_data);
#endif /* CUDA_KERNEL_H_ */

View file

@ -0,0 +1,65 @@
#include <nccl.h>
#include <stdio.h>
#include <stdlib.h>
#include "distributed.h"
#include "tensor.h"
#include "cuda.h"
#include <mpi.h>
#include <nccl.h>
int rank;
int world_size;
ncclComm_t nccl_comm;
extern "C" {
void init_process_group(int env_rank, int env_world_size) {
rank = env_rank;
world_size = env_world_size;
// init MPI
MPI_CHECK(MPI_Init(NULL, NULL));
ncclUniqueId nccl_id;
if (rank == 0) {
ncclGetUniqueId(&nccl_id);
}
// send nccl unique id to all processes
MPI_CHECK(MPI_Bcast((void *)&nccl_id, sizeof(nccl_id), MPI_BYTE, 0, MPI_COMM_WORLD));
// init NCCL communication group
NCCL_CHECK(ncclCommInitRank(&nccl_comm, world_size, nccl_id, rank));
}
void broadcast_tensor(Tensor* tensor) {
cudaStream_t stream;
cudaStreamCreate(&stream);
NCCL_CHECK(ncclBroadcast(tensor->data, tensor->data, tensor->size * sizeof(float), ncclFloat, 0, nccl_comm, stream));
cudaStreamSynchronize(stream);
cudaStreamDestroy(stream);
}
void allreduce_sum_tensor(Tensor* tensor) {
cudaStream_t stream;
cudaStreamCreate(&stream);
// Perform NCCL AllReduce operation to calculate the sum of all tensors across all processes
NCCL_CHECK(ncclAllReduce(tensor->data, tensor->data, tensor->size, ncclFloat, ncclSum, nccl_comm, stream));
cudaStreamSynchronize(stream);
cudaStreamDestroy(stream);
}
void end_process_group() {
MPI_CHECK(MPI_Finalize());
NCCL_CHECK(ncclCommDestroy(nccl_comm));
}
}

View file

@ -0,0 +1,28 @@
#ifndef DISTRIBUTED_H
#define DISTRIBUTED_H
#define MPI_CHECK(cmd) do { \
int e = cmd; \
if( e != MPI_SUCCESS ) { \
printf("Failed: MPI error %s:%d '%d'\n", \
__FILE__,__LINE__, e); \
exit(EXIT_FAILURE); \
} \
} while(0)
#define NCCL_CHECK(cmd) do { \
ncclResult_t r = cmd; \
if (r!= ncclSuccess) { \
printf("Failed, NCCL error %s:%d '%s'\n", \
__FILE__,__LINE__,ncclGetErrorString(r)); \
exit(EXIT_FAILURE); \
} \
} while(0)
extern "C" {
void init_process_group(int rank, int world_size);
void broadcast_tensor(Tensor* tensor);
void allreduce_sum_tensor(Tensor* tensor);
}
#endif /* DISTRIBUTED_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,45 @@
#ifndef TENSOR_H
#define TENSOR_H
typedef struct {
float* data;
int* strides;
int* shape;
int* strides_cuda;
int* shape_cuda;
int ndim;
int size;
char* device;
} Tensor;
extern "C" {
Tensor* create_tensor(float* data, int* shape, int ndim, char* device);
float get_item(Tensor* tensor, int* indices);
Tensor* add_tensor(Tensor* tensor1, Tensor* tensor2);
Tensor* sum_tensor(Tensor* tensor, int axis, bool keepdims);
Tensor* max_tensor(Tensor* tensor, int axis, bool keepdim);
Tensor* min_tensor(Tensor* tensor, int axis, bool keepdim);
Tensor* sub_tensor(Tensor* tensor1, Tensor* tensor2);
Tensor* elementwise_mul_tensor(Tensor* tensor1, Tensor* tensor2);
Tensor* scalar_mul_tensor(Tensor* tensor, float scalar);
Tensor* scalar_div_tensor(float scalar, Tensor* tensor);
Tensor* tensor_div_scalar(Tensor* tensor, float scalar);
Tensor* tensor_div_tensor(Tensor* tensor1, Tensor* tensor2);
Tensor* reshape_tensor(Tensor* tensor, int* new_shape, int new_ndim);
Tensor* matmul_tensor(Tensor* tensor1, Tensor* tensor2);
Tensor* tensor_pow_scalar(Tensor* tensor, float exponent);
Tensor* scalar_pow_tensor(float base, Tensor* tensor);
Tensor* log_tensor(Tensor* tensor);
Tensor* equal_tensor(Tensor* tensor1, Tensor* tensor2);
Tensor* equal_broadcasted_tensor(Tensor* tensor1, Tensor* tensor2);
void to_device(Tensor* tensor, char* device);
Tensor* ones_like_tensor(Tensor* tensor);
Tensor* zeros_like_tensor(Tensor* tensor);
Tensor* sin_tensor(Tensor* tensor);
Tensor* cos_tensor(Tensor* tensor);
Tensor* transpose_tensor(Tensor* tensor);
Tensor* transpose_axes_tensor(Tensor* tensor, int axis1, int axis2);
void make_contiguous(Tensor* tensor);
}
#endif /* TENSOR_H */

View file

@ -0,0 +1 @@
from .distributed import *

View file

@ -0,0 +1,24 @@
import os
import ctypes
from norch import Tensor, CTensor
def init_process_group(rank, world_size, backend='nccl'):
Tensor._C.init_process_group.argtypes = [ctypes.c_int, ctypes.c_int]
Tensor._C.init_process_group.restype = None
Tensor._C.init_process_group(rank, world_size)
def broadcast_tensor(tensor):
Tensor._C.broadcast_tensor.argtypes = [ctypes.POINTER(CTensor)]
Tensor._C.broadcast_tensor.restype = None
Tensor._C.broadcast_tensor(tensor)
def allreduce_sum_tensor(tensor):
Tensor._C.allreduce_mean_tensor.argtypes = [ctypes.POINTER(CTensor)]
Tensor._C.allreduce_mean_tensor.restype = None
Tensor._C.allreduce_mean_tensor(tensor)

BIN
build/lib/norch/libtensor.so Executable file

Binary file not shown.

View file

@ -0,0 +1,4 @@
from .modules import *
from .activation import *
from .loss import *
from .functional import *

View file

@ -0,0 +1,30 @@
from .module import Module
from . import functional as F
import math
class Activation(Module):
"""
Abstract classes for activations
"""
def __init__(self):
super(Activation, self).__init__()
def forward(self, x):
raise NotImplementedError
class Sigmoid(Activation):
def __init__(self):
super().__init__()
def forward(self, x):
return F.sigmoid(x)
class Softmax(Activation):
def __init__(self, dim):
super(Softmax, self).__init__()
self.dim = dim
def forward(self, x):
return F.softmax(x, self.dim)

View file

@ -0,0 +1,32 @@
import math
import norch
import numpy as np
from norch.autograd.functions import *
def sigmoid(x):
z = x.sigmoid()
return z
def softmax(x, dim=None):
if dim is not None and dim < 0:
dim = x.ndim + dim
x_max = x.max(axis=dim, keepdim=True)
exp_x = math.e ** (x - x_max)
if dim is not None:
sum_exp_x = exp_x.sum(axis=dim, keepdim=True) + exp_x.zeros_like()
return exp_x / sum_exp_x
else:
sum_exp_x = exp_x.sum()
return exp_x / sum_exp_x
def one_hot_encode(x, num_classes):
one_hot = [[0] * num_classes for _ in range(x.numel)]
# Set the appropriate elements to 1
for i in range(x.numel):
target_idx = int(x[i])
one_hot[i][target_idx] = 1
return norch.Tensor(one_hot)

View file

@ -0,0 +1,93 @@
from .module import Module
from norch.autograd.functions import *
import norch
from abc import ABC
class Loss(Module, ABC):
"Abstract class for loss functions"
def __init__(self):
super().__init__()
def forward(self, predictions, labels):
raise NotImplementedError
def __call__(self, *inputs):
return self.forward(*inputs)
class MSELoss(Loss):
def __init__(self):
super().__init__()
def forward(self, predictions, target):
assert target.shape == predictions.shape, \
"Labels and predictions shape does not match: {} and {}".format(target.shape, predictions.shape)
cost = ((predictions - target) ** 2).sum() / predictions.numel
return cost
class CrossEntropyLoss(Loss):
def __init__(self):
super().__init__()
def forward(self, input, target):
assert isinstance(input, norch.Tensor), \
"Cross entropy argument 'input' must be Tensor, not {}".format(type(input))
assert isinstance(target, norch.Tensor), \
"Cross entropy argument 'target' must be Tensor, not {}".format(type(target))
if input.ndim > 2:
input = input.squeeze(-1)
if input.ndim == 1:
if target.numel == 1:
num_classes = input.shape[0]
target = norch.one_hot_encode(target, num_classes).to(target.device)
logits = norch.softmax(input, dim=0)
target = target.reshape(logits.shape)
cost = -(logits.log() * target).sum()
else:
# target -> class probabilities (one-hot encoded)
assert target.shape == input.shape, \
"Input and target shape does not match: {} and {}".format(input.shape, target.shape)
logits = norch.softmax(input, dim=0)
target = target.reshape(logits.shape)
cost = -(logits.log() * target).sum()
elif input.ndim == 2:
if target.ndim > 1:
target = target.squeeze(-1)
# batched
if target.ndim == 1:
# target -> Ground truth class indices:
num_classes = input.shape[1]
target = norch.one_hot_encode(target, num_classes).to(target.device)
batch_size = input.shape[0]
logits = norch.softmax(input, dim=1)
target = target.reshape(logits.shape)
cost = -(logits.log() * target).sum() / batch_size
else:
# target -> class probabilities (one-hot encoded)
assert target.shape == input.shape, \
"Input and target shape does not match: {} and {}".format(input.shape, target.shape)
batch_size = input.shape[0]
logits = norch.softmax(input, dim=1)
target = target.reshape(logits.shape)
cost = -(logits.log() * target).sum() / batch_size
if input.requires_grad:
cost.grad_fn = CrossEntropyLossBackward(input, target)
return cost

View file

@ -0,0 +1,107 @@
from .parameter import Parameter
from collections import OrderedDict
from abc import ABC
import pickle
import json
import inspect
import warnings
class Module(ABC):
"""
Abstract class for modules
"""
def __init__(self):
self._modules = OrderedDict()
self._params = OrderedDict()
self._grads = OrderedDict()
self.training = True
def forward(self, *inputs, **kwargs):
raise NotImplementedError
def __call__(self, *inputs, **kwargs):
return self.forward(*inputs, **kwargs)
def train(self):
self.training = True
for param in self.parameters():
param.requires_grad = True
def eval(self):
self.training = False
for param in self.parameters():
param.requires_grad = False
def parameters(self):
for name, value in inspect.getmembers(self):
if isinstance(value, Parameter):
yield self, name, value
elif isinstance(value, Module):
yield from value.parameters()
def modules(self):
yield from self._modules.values()
def gradients(self):
for module in self.modules():
yield module._grads
def zero_grad(self):
for _, _, parameter in self.parameters():
parameter.zero_grad()
def to(self, device):
for module, name, _ in self.parameters():
parameter = getattr(module, name)
parameter = parameter.to(device)
setattr(module, name, parameter)
return self
def state_dict(self):
state = OrderedDict()
for i, param in enumerate(self.parameters()):
state[f'param{i}'] = param.tolist()
return state
def load_state(self, state_dict):
for i, param in self.parameters():
data = state_dict[f'param{i}']
if param.shape != data.shape:
warnings.warn(f"The 'state_dict' shape does not match model's parameter shape. "
f"Got {data.shape}, expected {param.shape}.")
param.data = Parameter(data=data)
def save(self, filename='model.pickle'):
with open(filename, 'wb') as f:
pickle.dump(self, f)
def save_dict(self, filename='state_dict.json'):
state = self.state_dict()
with open(filename, 'w') as f:
json.dump(state, f)
def inner_repr(self):
return ""
def __repr__(self):
string = f"{self.get_name()}("
tab = " "
modules = self._modules
if modules == {}:
string += f'\n{tab}(parameters): {self.inner_repr()}'
else:
for key, module in modules.items():
string += f"\n{tab}({key}): {module.get_name()}({module.inner_repr()})"
return f'{string}\n)'
def get_name(self):
return self.__class__.__name__
def __setattr__(self, key, value):
self.__dict__[key] = value
if isinstance(value, Module):
self._modules[key] = value
elif isinstance(value, Parameter):
self._params[key] = value

View file

@ -0,0 +1 @@
from .linear import *

View file

@ -0,0 +1,26 @@
from ..module import Module
from ..parameter import Parameter
class Linear(Module):
def __init__(self, input_dim, output_dim, bias=True):
super().__init__()
self.input_dim = input_dim
self.output_dim = output_dim
self.weight = Parameter(shape=[self.output_dim, self.input_dim])
if bias:
self.bias = Parameter(shape=[self.output_dim, 1])
else:
self.bias = None
def forward(self, x):
if self.bias:
z = self.weight @ x + self.bias
else:
z = self.weight @ x
return z
def inner_repr(self):
return f"input_dim={self.input_dim}, output_dim={self.output_dim}, " \
f"bias={True if self.bias is not None else False}"

View file

@ -0,0 +1,11 @@
from norch.tensor import Tensor
from norch.utils import utils
import random
class Parameter(Tensor):
"""
A parameter is a trainable tensor.
"""
def __init__(self, shape):
data = utils.generate_random_list(shape=shape)
super().__init__(data, requires_grad=True)

View file

@ -0,0 +1 @@
from .datasets import *

View file

@ -0,0 +1 @@
from .mnist import *

View file

@ -0,0 +1,90 @@
import gzip
import os
import norch
from norch.utils.data import Dataset
import numpy as np
class MNIST(Dataset):
"""
Loads training, validation, and test partitions of the mnist dataset
(http://yann.lecun.com/exdb/mnist/). If the data is not already contained in data_dir, it will
try to download it.
This dataset contains 60000 training examples, and 10000 test examples of handwritten digits
in {0, ..., 9} and corresponding labels. Each handwritten image has an "original" dimension of
28x28x1, and is stored row-wise as a string of 784x1 bytes. Pixel values are in range 0 to 255
(inclusive).
Args:
data_dir: String. Relative or absolute path of the dataset.
devel_size: Integer. Size of the development (validation) dataset partition.
Returns:
X_train: float64 numpy array with shape [784, 60000-devel_size] with values in [0, 1].
Y_train: uint8 numpy array with shape [60000-devel_size]. Labels.
X_devel: float64 numpy array with shape [784, devel_size] with values in [0, 1].
Y_devel: uint8 numpy array with shape [devel_size]. Labels.
X_test: float64 numpy array with shape [784, 10000] with values in [0, 1].
Y_test: uint8 numpy array with shape [10000]. Labels.
"""
urls = ['https://ossci-datasets.s3.amazonaws.com/mnist/train-images-idx3-ubyte.gz',
'https://ossci-datasets.s3.amazonaws.com/mnist/train-labels-idx1-ubyte.gz',
'https://ossci-datasets.s3.amazonaws.com/mnist/t10k-images-idx3-ubyte.gz',
'https://ossci-datasets.s3.amazonaws.com/mnist/t10k-labels-idx1-ubyte.gz',]
name = 'mnist-data-py'
dirname = 'mnist'
def __init__(self, path_data, path_label, transform=None, target_transform=None):
self.data = self._load_mnist(path_data, header_size=16).reshape((-1, 28, 28))
self.labels = self._load_mnist(path_label, header_size=8)
self.transform = transform
self.target_transform = target_transform
def _load_mnist(self, path, header_size):
with gzip.open(path, 'rb') as f:
data = np.frombuffer(f.read(), np.uint8, offset=header_size)
return np.asarray(data, dtype=np.uint8)
@classmethod
def splits(cls, root='.data', train_data='train-images-idx3-ubyte.gz', train_label='train-labels-idx1-ubyte.gz',
test_data='t10k-images-idx3-ubyte.gz', test_label='t10k-labels-idx1-ubyte.gz', **kwargs):
r"""
Loads training and test partitions of the [mnist dataset](https://www.cs.toronto.edu/~kriz/cifar.html). If
the data is not already contained in the ``root`` folder, it will download it.
Args:
root (str): relative or absolute path of the dataset.
Returns:
tuple(Dataset): training and testing datasets
"""
path = os.path.join(root, cls.dirname, cls.name)
if not os.path.isdir(path):
path = cls.download(root)
train_data = os.path.join(path, train_data)
train_label = os.path.join(path, train_label)
test_data = os.path.join(path, test_data)
test_label = os.path.join(path, test_label)
return MNIST(train_data, train_label, **kwargs), MNIST(test_data, test_label, **kwargs)
def __getitem__(self, item):
data = self.data[item].tolist()
label = self.labels[item].tolist()
if self.transform is not None:
data = self.transform(data)
if self.target_transform is not None:
label = self.target_transform(label)
return data, label
def __setitem__(self, key, value):
self.data[key], self.labels[key] = value
def __len__(self):
return len(self.data)

View file

@ -0,0 +1,22 @@
import norch
class ToTensor:
def __call__(self, x):
return norch.Tensor(x)
class Reshape:
def __init__(self, shape):
self.shape = shape
def __call__(self, x):
return x.reshape(self.shape)
class Sequential:
def __init__(self, transforms):
self.transforms = transforms
def __call__(self, x):
for transform in self.transforms:
x = transform(x)
return x

View file

@ -0,0 +1 @@
from .optimizers import *

View file

@ -0,0 +1,22 @@
from abc import ABC
from norch.tensor import Tensor
class Optimizer(ABC):
"""
Abstract class for optimizers
"""
def __init__(self, parameters):
if isinstance(parameters, Tensor):
raise TypeError("parameters should be an iterable but got {}".format(type(parameters)))
elif isinstance(parameters, dict):
parameters = parameters.values()
self.parameters = list(parameters)
def step(self):
raise NotImplementedError
def zero_grad(self):
for module, name, parameter in self.parameters:
parameter.zero_grad()

View file

@ -0,0 +1 @@
from .sgd import *

View file

@ -0,0 +1,27 @@
from ..optimizer import Optimizer
from norch.tensor import Tensor
import time
class SGD(Optimizer):
def __init__(self, parameters, lr=1e-1, momentum=0):
super().__init__(parameters)
self.lr = lr
self.momentum = momentum
self._cache = {'velocity': [p.zeros_like() for (_, _, p) in self.parameters]}
def step(self):
for i, (module, name, _) in enumerate(self.parameters):
parameter = getattr(module, name)
velocity = self._cache['velocity'][i]
velocity = self.momentum * velocity - self.lr * parameter.grad
updated_parameter = parameter + velocity
setattr(module, name, updated_parameter)
self._cache['velocity'][i] = velocity
parameter.detach()
velocity.detach()

1033
build/lib/norch/tensor.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
from .utils import *
from .data import *

View file

@ -0,0 +1,4 @@
from .dataset import *
from .example import *
from .dataloader import *
from .batch import *

View file

@ -0,0 +1,13 @@
class Batch(object):
"""
A ``Batch``depends on a ``Dataset`` and is made of ``batch_size`` ``Example``.
"""
def __init__(self, example, batch_size):
self.example = example
self.batch_size = batch_size
def __getitem__(self, item):
return self.example[item]
def __len__(self):
return self.batch_size

View file

@ -0,0 +1,20 @@
import numpy as np
from .batch import Batch
class Dataloader(object):
def __init__(self, dataset, batch_size=32):
self.dataset = dataset
self.batch_size = batch_size
def __iter__(self):
starts = np.arange(0, len(self.dataset), self.batch_size)
for start in starts:
end = start + self.batch_size
batch_size = min(end, len(self.dataset)) - start
yield Batch(self.dataset[start:end], batch_size)
def __len__(self):
return len(self.dataset) // self.batch_size

View file

@ -0,0 +1,74 @@
from abc import ABC, abstractmethod
import os
from norch.utils import extract_to_dir, download_from_url
from .example import Example
import norch
class Dataset(ABC):
r"""
Abstract Dataset class. All dataset for machine learning purposes can inherits from this architecture,
for convenience.
"""
urls = []
name = ''
dirname = ''
def __init__(self, examples, fields):
self.examples = examples
self.fields = fields
@classmethod
def splits(cls, train=None, test=None, valid=None, root='.'):
raise NotImplementedError
@classmethod
def download(cls, root):
r"""Download and unzip a web archive (.zip, .gz, or .tgz).
Args:
root (str): Folder to download data to.
Returns:
string: Path to extracted dataset.
"""
path_dirname = os.path.join(root, cls.dirname)
path_name = os.path.join(path_dirname, cls.name)
if not os.path.isdir(path_dirname):
for url in cls.urls:
filename = os.path.basename(url)
zpath = os.path.join(path_dirname, filename)
if not os.path.isfile(zpath):
if not os.path.exists(os.path.dirname(zpath)):
os.makedirs(os.path.dirname(zpath))
print(f'Download {filename} from {url} to {zpath}')
download_from_url(url, zpath)
extract_to_dir(zpath, path_name)
return path_name
def __repr__(self):
name = self.__class__.__name__
string = f"Dataset {name}("
tab = " "
for (key, value) in self.__dict__.items():
if key[0] != "_":
if isinstance(value, Example):
fields = self.fields
for (name, field) in fields:
string += f"\n{tab}({name}): {field.__class__.__name__}" \
f"(transform={True if field.transform is not None else None}, dtype={field.dtype})"
elif isinstance(value, norch.Tensor):
string += f"\n{tab}({key}): {value.__class__.__name__}(shape={value.shape}, dtype={value.dtype})"
else:
string += f"\n{tab}({key}): {value.__class__.__name__}"
return f'{string}\n)'
def __getitem__(self, item):
return self.examples[item]
def __setitem__(self, key, value):
self.examples[key] = value
def __len__(self):
return len(self.examples)

View file

@ -0,0 +1,65 @@
# File: example.py
# Creation: Wednesday August 19th 2020
# Author: Arthur Dujardin
# Contact: arthur.dujardin@ensg.eu
# arthurd@ifi.uio.no
# --------
# Copyright (c) 2020 Arthur Dujardin
class Field(object):
r"""
A ``Field`` defines the data to process from a raw dataset. It will convert the data into a tensor. The data can
be a string, integers, float etc. The data is meant to be preprocessed and the attribute ``transform`` handles
the way the user want to process the raw data.
"""
def __init__(self, transform=None, dtype=None):
self.transform = transform
self.dtype = dtype
def process(self, value):
"""Applies the transformation and changes the data's type if necessary.
Args:
value (Tensor):
Returns:
"""
if self.transform is not None:
value = self.transform(value)
if self.dtype is not None:
value = value.astype(self.dtype)
return value
class Example(object):
r"""
Store a single training / testing example, and store it as an attribute.
Highly inspired from PyTorch `Example <https://github.com/pytorch/text/blob/master/torchtext/data/example.py>`__.
"""
@classmethod
def fromlist(cls, values, fields):
"""
Add an example from a list of data with respect to the fields.
Args:
values: raw data
fields (tuple(string, Field)): fields to preprocess the data on
Returns:
None
"""
example = cls()
for (value, field) in zip(values, fields):
assert len(field) == 2, f"expected a field template similar to \
('name_field', Field()) but got {format(field)}"
assert isinstance(field[1], Field), f"expected a field template similar to \
('name_field', Field()) but got {format(field)}"
name = field[0]
value = field[1].process(value)
setattr(example, name, value)
return example

View file

@ -0,0 +1,120 @@
import random
import requests
import tarfile
import zipfile
import shutil
import os
def generate_random_list(shape):
"""
Generate a list with random numbers and shape 'shape'
"""
if len(shape) == 0:
return []
else:
inner_shape = shape[1:]
if len(inner_shape) == 0:
return [random.uniform(-1, 1) for _ in range(shape[0])]
else:
return [generate_random_list(inner_shape) for _ in range(shape[0])]
def download_from_url(url, save_path, chunk_size=128):
"""Download a file from an URL.
Original answer from https://stackoverflow.com/questions/9419162/download-returned-zip-file-from-url
Args:
url (str): path to the URL.
save_path (str): path to the saving directory.
chunk_size (int): download chunk.
Returns:
None
"""
response = requests.get(url, stream=True)
total = response.headers.get('content-length')
with open(save_path, 'wb') as f:
if total is None:
f.write(response.content)
else:
downloaded = 0
total = int(total)
for data in response.iter_content(chunk_size=max(int(total / 1000), 1024 * 1024)):
downloaded += len(data)
f.write(data)
progress_bar(downloaded, total, "Downloading...")
def extract_to_dir(filename, dirpath='.'):
# Does not create folder twice with the same name
name, ext = os.path.splitext(filename)
# if os.path.basename(name) == os.path.basename(dirpath):
# dirpath = '.'
# Extract
print(dirpath)
print("Extracting...", end="")
if tarfile.is_tarfile(filename):
tarfile.open(filename, 'r').extractall(dirpath)
elif zipfile.is_zipfile(filename):
zipfile.ZipFile(filename, 'r').extractall(dirpath)
elif ext == '.gz':
if not os.path.exists(dirpath):
os.mkdir(dirpath)
shutil.move(filename, os.path.join(dirpath, os.path.basename(filename)))
print(f" | NOTE: gzip files are not extracted, and moved to {dirpath}", end="")
# Return the path where the file was extracted
print(" | Done !")
return os.path.abspath(dirpath)
def progress_bar(current_index, max_index, prefix=None, suffix=None, start_time=None):
"""Display a progress bar and duration.
Args:
current_index (int): current state index (or epoch number).
max_index (int): maximal numbers of state.
prefix (str, optional): prefix of the progress bar. The default is None.
suffix (str, optional): suffix of the progress bar. The default is None.
start_time (float, optional): starting time of the progress bar. If not None, it will display the time
spent from the beginning to the current state. The default is None.
Returns:
None. Display the progress bar in the console.
"""
# Add a prefix to the progress bar
prefix = "" if prefix is None else str(prefix) + " "
# Get the percentage
percentage = current_index * 100 // max_index
loading = "[" + "=" * (percentage // 2) + " " * (50 - percentage // 2) + "]"
progress_display = "\r{0}{1:3d}% | {2}".format(prefix, percentage, loading)
# Add a suffix to the progress bar
progress_display += "" if suffix is None else " | " + str(suffix)
# Add a timer
if start_time is not None:
time_min, time_sec = get_time(start_time, time.time())
time_display = " | Time: {0}m {1}s".format(time_min, time_sec)
progress_display += time_display
# Print the progress bar
# TODO: return a string instead
print(progress_display, end="{}".format("" if current_index < max_index else " | Done !\n"))
def get_time(start_time, end_time):
"""Get ellapsed time in minutes and seconds.
Args:
start_time (float): strarting time
end_time (float): ending time
Returns:
elapsed_mins (float): elapsed time in minutes
elapsed_secs (float): elapsed time in seconds.
"""
elapsed_time = end_time - start_time
elapsed_mins = int(elapsed_time / 60)
elapsed_secs = int(elapsed_time - (elapsed_mins * 60))
return elapsed_mins, elapsed_secs

View file

@ -0,0 +1,24 @@
import torch
def to_torch(custom_tensor):
shape = custom_tensor.shape
pytorch_tensor = torch.zeros(shape)
def _iterate_indices(shape):
if len(shape) == 0:
yield ()
else:
for index in range(shape[0]):
for sub_indices in _iterate_indices(shape[1:]):
yield (index,) + sub_indices
# Iterate over all elements using the custom tensor's __getitem__ method
for indices in _iterate_indices(shape):
value = custom_tensor[indices]
pytorch_tensor[tuple(indices)] = value
return pytorch_tensor
def compare_torch(tensor1, tensor2, epsilon=1e-5):
diff = torch.abs(tensor1 - tensor2)
return torch.all(diff < epsilon)

View file

@ -0,0 +1,3 @@
from .test_operations import *
from .test_autograd import *
from .test_nn import *

View file

@ -0,0 +1,993 @@
import unittest
import norch
from norch.utils import utils_unittests as utils
import torch
import os
class TestTensorAutograd(unittest.TestCase):
def setUp(self):
self.device = os.environ.get('device')
if self.device is None or self.device != 'cuda':
self.device = 'cpu'
print(f"Running tests on: {self.device}")
def test_addition(self):
"""
Test autograd from addition two tensors: tensor1 + tensor2
"""
norch_tensor1 = norch.Tensor([[[1, 2.5], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True).to(self.device)
norch_tensor2 = norch.Tensor([[[1, 1.], [1, 1.9]], [[1, 1], [1, 1]]], requires_grad=True).to(self.device)
norch_result = (norch_tensor1 + norch_tensor2).sum()
norch_result.backward()
norch_tensor1_grad = utils.to_torch(norch_tensor1.grad).to(self.device)
norch_tensor2_grad = utils.to_torch(norch_tensor2.grad).to(self.device)
torch_tensor1 = torch.tensor([[[1, 2.5], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True, device=self.device)
torch_tensor2 = torch.tensor([[[1, 1.], [1, 1.9]], [[1, 1], [1, 1]]], requires_grad=True, device=self.device)
torch_result = (torch_tensor1 + torch_tensor2).sum()
torch_result.backward()
torch_tensor1_grad = torch_tensor1.grad
torch_tensor2_grad = torch_tensor2.grad
self.assertTrue(utils.compare_torch(norch_tensor1_grad, torch_tensor1_grad))
self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad))
def test_sum_axis(self):
"""
Test autograd from sum specifying axis
"""
norch_tensor1 = norch.Tensor([[[1, 2.5], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True).to(self.device)
norch_tensor2 = norch.Tensor([[[1, 1.], [1, 1.9]], [[1, 1], [1, 1]]], requires_grad=True).to(self.device)
norch_result = (norch_tensor1 + norch_tensor2).sum(axis=0).sum(axis=0).sum()
norch_result.backward()
norch_tensor1_grad = utils.to_torch(norch_tensor1.grad).to(self.device)
norch_tensor2_grad = utils.to_torch(norch_tensor2.grad).to(self.device)
torch_tensor1 = torch.tensor([[[1, 2.5], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True, device=self.device)
torch_tensor2 = torch.tensor([[[1, 1.], [1, 1.9]], [[1, 1], [1, 1]]], requires_grad=True, device=self.device)
torch_result = (torch_tensor1 + torch_tensor2).sum(axis=0).sum(axis=0).sum()
torch_result.backward()
torch_tensor1_grad = torch_tensor1.grad
torch_tensor2_grad = torch_tensor2.grad
self.assertTrue(utils.compare_torch(norch_tensor1_grad, torch_tensor1_grad))
self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad))
norch_tensor1 = norch.Tensor([[[1, 2.5], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True).to(self.device)
norch_tensor2 = norch.Tensor([[[1, 1.], [1, 1.9]], [[1, 1], [1, 1]]], requires_grad=True).to(self.device)
norch_result = (norch_tensor1 + norch_tensor2).sum(axis=1).sum()
norch_result.backward()
norch_tensor1_grad = utils.to_torch(norch_tensor1.grad).to(self.device)
norch_tensor2_grad = utils.to_torch(norch_tensor2.grad).to(self.device)
torch_tensor1 = torch.tensor([[[1, 2.5], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True, device=self.device)
torch_tensor2 = torch.tensor([[[1, 1.], [1, 1.9]], [[1, 1], [1, 1]]], requires_grad=True, device=self.device)
torch_result = (torch_tensor1 + torch_tensor2).sum(axis=1).sum()
torch_result.backward()
torch_tensor1_grad = torch_tensor1.grad
torch_tensor2_grad = torch_tensor2.grad
self.assertTrue(utils.compare_torch(norch_tensor1_grad, torch_tensor1_grad))
self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad))
def test_max(self):
"""
Test autograd from max
"""
norch_tensor = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device)
norch_result = norch_tensor.max()
norch_result.backward()
norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device)
torch_tensor = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True, device=self.device)
torch_result = torch_tensor.max()
torch_result.backward()
torch_tensor_grad = torch_tensor.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad))
def test_max_axis(self):
"""
Test autograd from max specifying axis
"""
norch_tensor = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device)
norch_max_axis = norch_tensor.max(axis=1)
norch_result = norch_max_axis.sum()
norch_result.backward()
norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device)
torch_tensor = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True, device=self.device)
torch_max_axis, _ = torch_tensor.max(axis=1)
torch_result = torch_max_axis.sum()
torch_result.backward()
torch_tensor_grad = torch_tensor.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad))
norch_tensor = norch.Tensor([[[10, 1], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True).to(self.device)
norch_max_axis = norch_tensor.max(axis=2)
norch_result = norch_max_axis.sum()
norch_result.backward()
norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device)
torch_tensor = torch.tensor([[[10, 1], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True, device=self.device)
torch_max_axis, _ = torch_tensor.max(axis=2)
torch_result = torch_max_axis.sum()
torch_result.backward()
torch_tensor_grad = torch_tensor.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad))
## evaluate case with some repeated values and axis 2
#def test_max_axis(self):
# """
# Test autograd from max specifying axis
# """
#
# norch_tensor = norch.Tensor([[[10, 10], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True).to(self.device)
# norch_max_axis = norch_tensor.max(axis=2)
# norch_result = norch_max_axis.sum()
#
# norch_result.backward()
# norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device)
#
# torch_tensor = torch.tensor([[[10, 10], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True).to(self.device)
#
# torch_max_axis, _ = torch_tensor.max(axis=2)
# torch_result = torch_max_axis.sum()
# torch_result.backward()
# torch_tensor_grad = torch_tensor.grad
# self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad))
#def test_min_axis(self):
# """
# Test autograd from max specifying axis
# """
#
# norch_tensor = norch.Tensor([[[10, 10], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True).to(self.device)
# norch_min_axis = norch_tensor.min(axis=2)
# norch_result = norch_min_axis.sum()
#
# norch_result.backward()
# norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device)
#
# torch_tensor = torch.tensor([[[10, 10], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True).to(self.device)
#
# torch_min_axis, _ = torch_tensor.min(axis=2)
# torch_result = torch_min_axis.sum()
# torch_result.backward()
# torch_tensor_grad = torch_tensor.grad
# self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad))
def test_min(self):
"""
Test autograd from min
"""
norch_tensor = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device)
norch_result = norch_tensor.min()
norch_result.backward()
norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device)
torch_tensor = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True, device=self.device)
torch_result = torch_tensor.min()
torch_result.backward()
torch_tensor_grad = torch_tensor.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad))
def test_min_axis(self):
"""
Test autograd from min specifying axis
"""
norch_tensor = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device)
norch_min = norch_tensor.min(axis=1)
norch_result = norch_min.sum()
norch_result.backward()
norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device)
torch_tensor = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True, device=self.device)
torch_min, _ = torch_tensor.min(axis=1)
torch_result = torch_min.sum()
torch_result.backward()
torch_tensor_grad = torch_tensor.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad))
norch_tensor = norch.Tensor([[[10, 1], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True).to(self.device)
norch_min = norch_tensor.min(axis=2)
norch_result = norch_min.sum()
norch_result.backward()
norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device)
torch_tensor = torch.tensor([[[10, 1], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True, device=self.device)
torch_min, _ = torch_tensor.min(axis=2)
torch_result = torch_min.sum()
torch_result.backward()
torch_tensor_grad = torch_tensor.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad))
def test_broadcasted_addition_autograd(self):
"""
Test autograd for broadcasting addition: tensor1 + tensor2
"""
norch_tensor1 = norch.Tensor([[[1., 2, 3], [4, 5, 6]]], requires_grad=True).to(self.device) # Shape (1, 2, 3)
norch_tensor2 = norch.Tensor([1.5, -1, 0], requires_grad=True).to(self.device) # Shape (3)
norch_result = (norch_tensor1 + norch_tensor2).sum()
norch_result.backward()
norch_tensor1_grad = utils.to_torch(norch_tensor1.grad).to(self.device)
norch_tensor2_grad = utils.to_torch(norch_tensor2.grad).to(self.device)
torch_tensor1 = torch.tensor([[[1., 2, 3], [4, 5, 6]]], requires_grad=True, device=self.device) # Shape (1, 2, 3)
torch_tensor2 = torch.tensor([1.5, -1, 0], requires_grad=True, device=self.device) # Shape (3)
torch_result = (torch_tensor1 + torch_tensor2).sum()
torch_result.backward()
torch_tensor1_grad = torch_tensor1.grad
torch_tensor2_grad = torch_tensor2.grad
self.assertTrue(utils.compare_torch(norch_tensor1_grad, torch_tensor1_grad))
self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad))
## reversed order broadcasting
norch_tensor1 = norch.Tensor([[[1., 2, 3], [4, 5, 6]]], requires_grad=True).to(self.device) # Shape (1, 2, 3)
norch_tensor2 = norch.Tensor([1.5, -1, 0], requires_grad=True).to(self.device) # Shape (3)
norch_result = (norch_tensor2 + norch_tensor1).sum()
norch_result.backward()
norch_tensor1_grad = utils.to_torch(norch_tensor1.grad).to(self.device)
norch_tensor2_grad = utils.to_torch(norch_tensor2.grad).to(self.device)
torch_tensor1 = torch.tensor([[[1., 2, 3], [4, 5, 6]]], requires_grad=True, device=self.device) # Shape (1, 2, 3)
torch_tensor2 = torch.tensor([1.5, -1, 0], requires_grad=True, device=self.device) # Shape (3)
torch_result = (torch_tensor2 + torch_tensor1).sum()
torch_result.backward()
torch_tensor1_grad = torch_tensor1.grad
torch_tensor2_grad = torch_tensor2.grad
self.assertTrue(utils.compare_torch(norch_tensor1_grad, torch_tensor1_grad))
self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad))
def test_subtraction(self):
"""
Test autograd from subtraction two tensors: tensor1 - tensor2
"""
norch_tensor1_sub = norch.Tensor([[[1, 2.5], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True).to(self.device)
norch_tensor2_sub = norch.Tensor([[[1, 1.], [1, 1.9]], [[1, 1], [1, 1]]], requires_grad=True).to(self.device)
norch_result_sub = (norch_tensor1_sub - norch_tensor2_sub).sum()
norch_result_sub.backward()
norch_tensor1_grad_sub = utils.to_torch(norch_tensor1_sub.grad).to(self.device)
norch_tensor2_grad_sub = utils.to_torch(norch_tensor2_sub.grad).to(self.device)
torch_tensor1_sub = torch.tensor([[[1, 2.5], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True, device=self.device)
torch_tensor2_sub = torch.tensor([[[1, 1.], [1, 1.9]], [[1, 1], [1, 1]]], requires_grad=True, device=self.device)
torch_result_sub = (torch_tensor1_sub - torch_tensor2_sub).sum()
torch_result_sub.backward()
torch_tensor1_grad_sub = torch_tensor1_sub.grad
torch_tensor2_grad_sub = torch_tensor2_sub.grad
self.assertTrue(utils.compare_torch(norch_tensor1_grad_sub, torch_tensor1_grad_sub))
self.assertTrue(utils.compare_torch(norch_tensor2_grad_sub, torch_tensor2_grad_sub))
def test_broadcasted_subtraction_autograd(self):
"""
Test autograd for broadcasting subtraction: tensor1 - tensor2
"""
norch_tensor1 = norch.Tensor([[[1., 2, 3], [4, 5, 6]]], requires_grad=True).to(self.device) # Shape (1, 2, 3)
norch_tensor2 = norch.Tensor([1.5, -1, 0], requires_grad=True).to(self.device) # Shape (3)
norch_result = (norch_tensor1 - norch_tensor2).sum()
norch_result.backward()
norch_tensor1_grad = utils.to_torch(norch_tensor1.grad).to(self.device)
norch_tensor2_grad = utils.to_torch(norch_tensor2.grad).to(self.device)
torch_tensor1 = torch.tensor([[[1., 2, 3], [4, 5, 6]]], requires_grad=True, device=self.device) # Shape (1, 2, 3)
torch_tensor2 = torch.tensor([1.5, -1, 0], requires_grad=True, device=self.device) # Shape (3)
torch_result = (torch_tensor1 - torch_tensor2).sum()
torch_result.backward()
torch_tensor1_grad = torch_tensor1.grad
torch_tensor2_grad = torch_tensor2.grad
self.assertTrue(utils.compare_torch(norch_tensor1_grad, torch_tensor1_grad))
self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad))
# reversed order broadcasting
norch_tensor1 = norch.Tensor([[[1., 2, 3], [4, 5, 6]]], requires_grad=True).to(self.device) # Shape (1, 2, 3)
norch_tensor2 = norch.Tensor([1.5, -1, 0], requires_grad=True).to(self.device) # Shape (3)
norch_result = (norch_tensor2 - norch_tensor1).sum()
norch_result.backward()
norch_tensor1_grad = utils.to_torch(norch_tensor1.grad).to(self.device)
norch_tensor2_grad = utils.to_torch(norch_tensor2.grad).to(self.device)
torch_tensor1 = torch.tensor([[[1., 2, 3], [4, 5, 6]]], requires_grad=True, device=self.device) # Shape (1, 2, 3)
torch_tensor2 = torch.tensor([1.5, -1, 0], requires_grad=True, device=self.device) # Shape (3)
torch_result = (torch_tensor2 - torch_tensor1).sum()
torch_result.backward()
torch_tensor1_grad = torch_tensor1.grad
torch_tensor2_grad = torch_tensor2.grad
self.assertTrue(utils.compare_torch(norch_tensor1_grad, torch_tensor1_grad))
self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad))
def test_division(self):
"""
Test autograd from dividing two tensors: tensor1 / tensor2
"""
norch_tensor1_div = norch.Tensor([[[2, 5.1], [6, -8]], [[10, 12], [14, 16]]], requires_grad=True).to(self.device)
norch_tensor2_div = norch.Tensor([[[1, 1], [2, 2.2]], [[3, 3], [4, 4]]], requires_grad=True).to(self.device)
norch_result_div = (norch_tensor1_div / norch_tensor2_div).sum()
norch_result_div.backward()
norch_tensor1_grad_div = utils.to_torch(norch_tensor1_div.grad).to(self.device)
norch_tensor2_grad_div = utils.to_torch(norch_tensor2_div.grad).to(self.device)
torch_tensor1_div = torch.tensor([[[2, 5.1], [6, -8]], [[10, 12], [14, 16]]], requires_grad=True, device=self.device)
torch_tensor2_div = torch.tensor([[[1, 1], [2, 2.2]], [[3, 3], [4, 4]]], requires_grad=True, device=self.device)
torch_result_div = (torch_tensor1_div / torch_tensor2_div).sum()
torch_result_div.backward()
torch_tensor1_grad_div = torch_tensor1_div.grad
torch_tensor2_grad_div = torch_tensor2_div.grad
self.assertTrue(utils.compare_torch(norch_tensor1_grad_div, torch_tensor1_grad_div))
self.assertTrue(utils.compare_torch(norch_tensor2_grad_div, torch_tensor2_grad_div))
def test_tensor_division_scalar(self):
"""
Test autograd from dividing tensor by scalar: tensor / scalar
"""
norch_tensor_div_scalar = norch.Tensor([[[2, 4.7], [6, 8]], [[10, 12], [14, 16]]], requires_grad=True).to(self.device)
scalar = 2
norch_result_div_scalar = (norch_tensor_div_scalar / scalar).sum()
norch_result_div_scalar.backward()
norch_tensor_grad_div_scalar = utils.to_torch(norch_tensor_div_scalar.grad).to(self.device)
torch_tensor_div_scalar = torch.tensor([[[2, 4.7], [6, 8]], [[10, 12], [14, 16]]], requires_grad=True, device=self.device)
torch_result_div_scalar = (torch_tensor_div_scalar / scalar).sum()
torch_result_div_scalar.backward()
torch_tensor_grad_div_scalar = torch_tensor_div_scalar.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_div_scalar, torch_tensor_grad_div_scalar))
def test_scalar_division_tensor(self):
"""
Test autograd from dividing scalar by tensor: scalar / tensor
"""
scalar = 2
norch_tensor_scalar_div = norch.Tensor([[[1, 2.23], [3, 4]], [[5, 6], [7, 8]]], requires_grad=True).to(self.device)
norch_result_scalar_div = (scalar / norch_tensor_scalar_div).sum()
norch_result_scalar_div.backward()
norch_tensor_grad_scalar_div = utils.to_torch(norch_tensor_scalar_div.grad).to(self.device)
torch_tensor_scalar_div = torch.tensor([[[1, 2.23], [3, 4]], [[5, 6], [7, 8]]], requires_grad=True, device=self.device)
torch_result_scalar_div = (scalar / torch_tensor_scalar_div).sum()
torch_result_scalar_div.backward()
torch_tensor_grad_scalar_div = torch_tensor_scalar_div.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_scalar_div, torch_tensor_grad_scalar_div))
def test_power_scalar_tensor(self):
"""
Test autograd from scalar raised to tensor: scalar ** tensor
"""
scalar = 2
norch_tensor_power_st = norch.Tensor([[[2, 3.21], [4, 2.1]], [[6, 7], [8, 9]]], requires_grad=True).to(self.device)
norch_result_power_st = (scalar ** norch_tensor_power_st).sum()
norch_result_power_st.backward()
norch_tensor_grad_power_st = utils.to_torch(norch_tensor_power_st.grad).to(self.device)
torch_tensor_power_st = torch.tensor([[[2, 3.21], [4, 2.1]], [[6, 7], [8, 9]]], requires_grad=True, device=self.device)
torch_result_power_st = (scalar ** torch_tensor_power_st).sum()
torch_result_power_st.backward()
torch_tensor_grad_power_st = torch_tensor_power_st.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_power_st, torch_tensor_grad_power_st))
def test_power_tensor_scalar(self):
"""
Test autograd from tensor raised to scalar: tensor ** scalar
"""
scalar = 2
norch_tensor_power_ts = norch.Tensor([[[2, 3], [4, 2.1]], [[6, 7], [8, 9]]], requires_grad=True).to(self.device)
norch_result_power_ts = (norch_tensor_power_ts ** scalar).sum()
norch_result_power_ts.backward()
norch_tensor_grad_power_ts = utils.to_torch(norch_tensor_power_ts.grad).to(self.device)
torch_tensor_power_ts = torch.tensor([[[2, 3], [4, 2.1]], [[6, 7], [8, 9]]], requires_grad=True, device=self.device)
torch_result_power_ts = (torch_tensor_power_ts ** scalar).sum()
torch_result_power_ts.backward()
torch_tensor_grad_power_ts = torch_tensor_power_ts.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_power_ts, torch_tensor_grad_power_ts))
def test_matmul(self):
"""
Test autograd from matrix multiplication: matmul(tensor1, tensor2)
"""
norch_tensor1_matmul = norch.Tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True).to(self.device)
norch_tensor2_matmul = norch.Tensor([[[1.1, 3], [4, 5]], [[6, 7], [8, 9]]], requires_grad=True).to(self.device)
norch_result_matmul = (norch_tensor1_matmul @ norch_tensor2_matmul).sum()
norch_result_matmul.backward()
norch_tensor1_grad_matmul = utils.to_torch(norch_tensor1_matmul.grad).to(self.device)
norch_tensor2_grad_matmul = utils.to_torch(norch_tensor2_matmul.grad).to(self.device)
torch_tensor1_matmul = torch.tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True, device=self.device)
torch_tensor2_matmul = torch.tensor([[[1.1, 3], [4, 5]], [[6, 7], [8, 9]]], requires_grad=True, device=self.device)
torch_result_matmul = (torch_tensor1_matmul @ torch_tensor2_matmul).sum()
torch_result_matmul.backward()
torch_tensor1_grad_matmul = torch_tensor1_matmul.grad
torch_tensor2_grad_matmul = torch_tensor2_matmul.grad
self.assertTrue(utils.compare_torch(norch_tensor1_grad_matmul, torch_tensor1_grad_matmul))
self.assertTrue(utils.compare_torch(norch_tensor2_grad_matmul, torch_tensor2_grad_matmul))
def test_batched_matmul(self):
"""
Test autograd from batched matrix multiplication: BxMxP = BxNxM @ BxMxP
"""
B = 3 # Batch size
norch_tensor1_matmul = norch.Tensor([[[1., 2], [3, -4], [5, 6], [7, 8]] for _ in range(B)], requires_grad=True).to(self.device)
norch_tensor2_matmul = norch.Tensor([[[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)], requires_grad=True).to(self.device)
norch_result_matmul = norch_tensor1_matmul @ norch_tensor2_matmul
norch_result_matmul_sum = norch_result_matmul.sum() # Sum over all elements
norch_result_matmul_sum.backward()
# Convert gradients to torch tensors
norch_tensor1_grad_matmul = utils.to_torch(norch_tensor1_matmul.grad).to(self.device)
norch_tensor2_grad_matmul = utils.to_torch(norch_tensor2_matmul.grad).to(self.device)
# Repeat the same process with torch tensors
torch_tensor1_matmul = torch.tensor([[[1., 2], [3, -4], [5, 6], [7, 8]] for _ in range(B)], requires_grad=True, device=self.device)
torch_tensor2_matmul = torch.tensor([[[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)], requires_grad=True, device=self.device)
torch_result_matmul = torch.matmul(torch_tensor1_matmul, torch_tensor2_matmul)
torch_result_matmul_sum = torch_result_matmul.sum()
torch_result_matmul_sum.backward()
# Extract gradients from torch tensors
torch_tensor1_grad_matmul = torch_tensor1_matmul.grad
torch_tensor2_grad_matmul = torch_tensor2_matmul.grad
# Assertions to compare the gradients
self.assertTrue(utils.compare_torch(norch_tensor1_grad_matmul, torch_tensor1_grad_matmul))
self.assertTrue(utils.compare_torch(norch_tensor2_grad_matmul, torch_tensor2_grad_matmul))
def test_broadcasted_batched_matmul(self):
"""
Test autograd from broadcasted batched matrix multiplication: BxMxP = NxM @ BxMxP
"""
B = 3 # Batch size
norch_tensor1_matmul = norch.Tensor([[1., 2], [3, -4], [5, 6], [7, 8]], requires_grad=True).to(self.device)
norch_tensor2_matmul = norch.Tensor([[[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)], requires_grad=True).to(self.device)
norch_result_matmul = norch_tensor1_matmul @ norch_tensor2_matmul
norch_result_matmul_sum = norch_result_matmul.sum() # Sum over all elements
norch_result_matmul_sum.backward()
# Convert gradients to torch tensors
norch_tensor1_grad_matmul = utils.to_torch(norch_tensor1_matmul.grad).to(self.device)
norch_tensor2_grad_matmul = utils.to_torch(norch_tensor2_matmul.grad).to(self.device)
# Repeat the same process with torch tensors
torch_tensor1_matmul = torch.tensor([[1., 2], [3, -4], [5, 6], [7, 8]], requires_grad=True, device=self.device)
torch_tensor2_matmul = torch.tensor([[[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)], requires_grad=True, device=self.device)
torch_result_matmul = torch.matmul(torch_tensor1_matmul, torch_tensor2_matmul)
torch_result_matmul_sum = torch_result_matmul.sum()
torch_result_matmul_sum.backward()
# Extract gradients from torch tensors
torch_tensor1_grad_matmul = torch_tensor1_matmul.grad
torch_tensor2_grad_matmul = torch_tensor2_matmul.grad
# Assertions to compare the gradients
self.assertTrue(utils.compare_torch(norch_tensor1_grad_matmul, torch_tensor1_grad_matmul))
self.assertTrue(utils.compare_torch(norch_tensor2_grad_matmul, torch_tensor2_grad_matmul))
def test_elementwise_mul_scalar(self):
"""
Test autograd from elementwise multiplication with scalar: scalar * tensor
"""
scalar = 2
norch_tensor_elemwise_mul_scalar = norch.Tensor([[[1.1, 2], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True).to(self.device)
norch_result_elemwise_mul_scalar = (scalar * norch_tensor_elemwise_mul_scalar).sum()
norch_result_elemwise_mul_scalar.backward()
norch_tensor_grad_elemwise_mul_scalar = utils.to_torch(norch_tensor_elemwise_mul_scalar.grad).to(self.device)
torch_tensor_elemwise_mul_scalar = torch.tensor([[[1.1, 2], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True, device=self.device)
torch_result_elemwise_mul_scalar = (scalar * torch_tensor_elemwise_mul_scalar).sum()
torch_result_elemwise_mul_scalar.backward()
torch_tensor_grad_elemwise_mul_scalar = torch_tensor_elemwise_mul_scalar.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_elemwise_mul_scalar, torch_tensor_grad_elemwise_mul_scalar))
def test_elementwise_mul_tensor(self):
"""
Test autograd from elementwise multiplication between two tensors: tensor1 * tensor2
"""
norch_tensor1_elemwise_mul = norch.Tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True).to(self.device)
norch_tensor2_elemwise_mul = norch.Tensor([[[1.1, 3], [4, 5]], [[6, 7], [8, 9]]], requires_grad=True).to(self.device)
norch_result_elemwise_mul = (norch_tensor1_elemwise_mul * norch_tensor2_elemwise_mul).sum()
norch_result_elemwise_mul.backward()
norch_tensor1_grad_elemwise_mul = utils.to_torch(norch_tensor1_elemwise_mul.grad).to(self.device)
norch_tensor2_grad_elemwise_mul = utils.to_torch(norch_tensor2_elemwise_mul.grad).to(self.device)
torch_tensor1_elemwise_mul = torch.tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True, device=self.device)
torch_tensor2_elemwise_mul = torch.tensor([[[1.1, 3], [4, 5]], [[6, 7], [8, 9]]], requires_grad=True, device=self.device)
torch_result_elemwise_mul = (torch_tensor1_elemwise_mul * torch_tensor2_elemwise_mul).sum()
torch_result_elemwise_mul.backward()
torch_tensor1_grad_elemwise_mul = torch_tensor1_elemwise_mul.grad
torch_tensor2_grad_elemwise_mul = torch_tensor2_elemwise_mul.grad
self.assertTrue(utils.compare_torch(norch_tensor1_grad_elemwise_mul, torch_tensor1_grad_elemwise_mul))
self.assertTrue(utils.compare_torch(norch_tensor2_grad_elemwise_mul, torch_tensor2_grad_elemwise_mul))
def test_sin_tensor(self):
"""
Test autograd from sin operation: sin(tensor)
"""
norch_sin_tensor = norch.Tensor([[[2, 3.21], [4, 2.1]], [[6, 7], [8, 9]]], requires_grad=True).to(self.device)
norch_result_sin_tensor = (norch_sin_tensor.sin()).sum()
norch_result_sin_tensor.backward()
torch_result_sin_tensor_grad = utils.to_torch(norch_sin_tensor.grad).to(self.device)
torch_sin_tensor = torch.tensor([[[2, 3.21], [4, 2.1]], [[6, 7], [8, 9]]], requires_grad=True, device=self.device)
torch_expected_sin_tensor = (torch.sin(torch_sin_tensor)).sum()
torch_expected_sin_tensor.backward()
torch_expected_sin_tensor_grad = torch_sin_tensor.grad
self.assertTrue(utils.compare_torch(torch_result_sin_tensor_grad, torch_expected_sin_tensor_grad))
def test_cos_tensor(self):
"""
Test autograd from cosine operation: cos(tensor)
"""
norch_cos_tensor = norch.Tensor([[[2, 3.21], [4, 2.1]], [[6, 7], [8, 9]]], requires_grad=True).to(self.device)
norch_result_cos_tensor = (norch_cos_tensor.sin()).sum()
norch_result_cos_tensor.backward()
torch_result_cos_tensor_grad = utils.to_torch(norch_cos_tensor.grad).to(self.device)
torch_cos_tensor = torch.tensor([[[2, 3.21], [4, 2.1]], [[6, 7], [8, 9]]], requires_grad=True, device=self.device)
torch_expected_cos_tensor = (torch.sin(torch_cos_tensor)).sum()
torch_expected_cos_tensor.backward()
torch_expected_cos_tensor_grad = torch_cos_tensor.grad
self.assertTrue(utils.compare_torch(torch_result_cos_tensor_grad, torch_expected_cos_tensor_grad))
def test_sigmoid(self):
"""
Test autograd from sigmoid
"""
norch_tensor = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device)
norch_sigmoid = norch.sigmoid(norch_tensor)
norch_result = norch_sigmoid.sum()
norch_result.backward()
norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device)
torch_tensor = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True, device=self.device)
torch_sigmoid = torch.sigmoid(torch_tensor)
torch_result = torch_sigmoid.sum()
torch_result.backward()
torch_tensor_grad = torch_tensor.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad))
def test_mse_loss_autograd(self):
"""
Test the MSELoss with autograd functionality
"""
loss_fn_norch = norch.nn.MSELoss()
loss_fn_torch = torch.nn.MSELoss()
predictions_norch = norch.Tensor([1.1, 2, 3, 4], requires_grad=True).to(self.device)
labels_norch = norch.Tensor([4, 3, 2.1, 1]).to(self.device)
loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch)
loss_norch.backward() # Backpropagate the loss
grad_norch = predictions_norch.grad
predictions_torch = torch.tensor([1.1, 2, 3, 4], requires_grad=True, device=self.device)
labels_torch = torch.tensor([4, 3, 2.1, 1], device=self.device)
loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch)
loss_torch_expected.backward() # Backpropagate the loss
grad_torch_expected = predictions_torch.grad
# Convert norch gradient to torch tensor for comparison
grad_norch_torch = utils.to_torch(grad_norch).to(self.device)
self.assertTrue(utils.compare_torch(grad_norch_torch, grad_torch_expected))
def test_cross_entropy_loss_autograd(self):
"""
Test the CrossEntropyLoss with autograd functionality
"""
loss_fn_norch = norch.nn.CrossEntropyLoss()
loss_fn_torch = torch.nn.CrossEntropyLoss()
# Test case 1: Single class, single sample
predictions_norch = norch.Tensor([2.0, 1.0, 0.1], requires_grad=True).to(self.device)
labels_norch = norch.Tensor([0]).to(self.device)
loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch)
loss_norch.backward() # Backpropagate the loss
grad_norch = predictions_norch.grad
predictions_torch = torch.tensor([2.0, 1.0, 0.1], requires_grad=True, device=self.device)
labels_torch = torch.tensor(0, device=self.device)
loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch)
loss_torch_expected.backward() # Backpropagate the loss
grad_torch_expected = predictions_torch.grad
# Convert norch gradient to torch tensor for comparison
grad_norch_torch = utils.to_torch(grad_norch).to(self.device)
self.assertTrue(utils.compare_torch(grad_norch_torch, grad_torch_expected))
# Test case 2: Multiple classes, multiple samples
predictions_norch = norch.Tensor([[0.5, 1.5, 2.5], [1.0, 2.0, 3.0]], requires_grad=True).to(self.device)
labels_norch = norch.Tensor([2, 1]).to(self.device)
loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch)
loss_norch.backward() # Backpropagate the loss
grad_norch = predictions_norch.grad
predictions_torch = torch.tensor([[0.5, 1.5, 2.5], [1.0, 2.0, 3.0]], requires_grad=True, device=self.device)
labels_torch = torch.tensor([2, 1], device=self.device)
loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch)
loss_torch_expected.backward() # Backpropagate the loss
grad_torch_expected = predictions_torch.grad
# Convert norch gradient to torch tensor for comparison
grad_norch_torch = utils.to_torch(grad_norch).to(self.device)
self.assertTrue(utils.compare_torch(grad_norch_torch, grad_torch_expected))
# Test case 3: Edge case - all predictions are zero
predictions_norch = norch.Tensor([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], requires_grad=True).to(self.device)
labels_norch = norch.Tensor([1, 2]).to(self.device)
loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch)
loss_norch.backward() # Backpropagate the loss
grad_norch = predictions_norch.grad
predictions_torch = torch.tensor([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], requires_grad=True, device=self.device)
labels_torch = torch.tensor([1, 2], device=self.device)
loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch)
loss_torch_expected.backward() # Backpropagate the loss
grad_torch_expected = predictions_torch.grad
# Convert norch gradient to torch tensor for comparison
grad_norch_torch = utils.to_torch(grad_norch).to(self.device)
self.assertTrue(utils.compare_torch(grad_norch_torch, grad_torch_expected))
# Test case 4: Batched class probabilities instead of class index
predictions_norch = norch.Tensor([[0.5, 0.2, 0.1], [0.1, 0.5, 0.7]], requires_grad=True).to(self.device)
labels_norch = norch.Tensor([[1., 0, 0], [0, 1, 0]]).to(self.device)
loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch)
loss_norch.backward() # Backpropagate the loss
grad_norch = predictions_norch.grad
predictions_torch = torch.tensor([[0.5, 0.2, 0.1], [0.1, 0.5, 0.7]], requires_grad=True, device=self.device)
labels_torch = torch.tensor([[1., 0, 0], [0, 1, 0]], device=self.device)
loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch)
loss_torch_expected.backward() # Backpropagate the loss
grad_torch_expected = predictions_torch.grad
# Convert norch gradient to torch tensor for comparison
grad_norch_torch = utils.to_torch(grad_norch).to(self.device)
self.assertTrue(utils.compare_torch(grad_norch_torch, grad_torch_expected))
# implement grad pure softmax --> 0
# def test_softmax(self):
# """
# Test autograd from softmax
# """
# norch_tensor = norch.Tensor([[[-5, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device)
# norch_softmax = norch.softmax(norch_tensor, dim=1)
# norch_result = norch_softmax.sum()
# norch_result.backward()
# norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device)
# torch_tensor = torch.tensor([[[-5, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device)
# torch_softmax = torch.softmax(torch_tensor, dim=1)
# torch_result = torch_softmax.sum()
# torch_result.backward()
# torch_tensor_grad = torch_tensor.grad
# self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad))
# norch_tensor = norch.Tensor([[[10, 1], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True).to(self.device)
# norch_softmax = norch.softmax(norch_tensor, dim=2)
# norch_result = norch_softmax.sum()
# norch_result.backward()
# norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device)
# torch_tensor = torch.tensor([[[10, 1], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True).to(self.device)
# torch_softmax = torch.softmax(torch_tensor, dim=2)
# torch_result = torch_softmax.sum()
# torch_result.backward()
# torch_tensor_grad = torch_tensor.grad
# self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad))
def test_reshape(self):
"""
Test autograd from reshaping a tensor: tensor.reshape(shape)
"""
norch_tensor_reshape = norch.Tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True).to(self.device)
new_shape = [2, 4]
norch_result_reshape = norch_tensor_reshape.reshape(new_shape).sum()
norch_result_reshape.backward()
norch_tensor_grad_reshape = utils.to_torch(norch_tensor_reshape.grad).to(self.device)
torch_tensor_reshape = torch.tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True, device=self.device)
torch_result_reshape = torch_tensor_reshape.reshape(new_shape).sum()
torch_result_reshape.backward()
torch_tensor_grad_reshape = torch_tensor_reshape.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_reshape, torch_tensor_grad_reshape))
def test_transpose_axes(self):
"""
Test autograd from transposing a tensor with specific axes: tensor.transpose(axis1, axis2)
"""
norch_tensor_transpose = norch.Tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True).to(self.device)
axis1, axis2 = 0, 2
norch_result_transpose = norch_tensor_transpose.transpose(axis1, axis2).sum()
norch_result_transpose.backward()
norch_tensor_grad_transpose = utils.to_torch(norch_tensor_transpose.grad).to(self.device)
torch_tensor_transpose = torch.tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True, device=self.device)
torch_result_transpose = torch_tensor_transpose.transpose(axis1, axis2).sum()
torch_result_transpose.backward()
torch_tensor_grad_transpose = torch_tensor_transpose.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_transpose, torch_tensor_grad_transpose))
def test_T(self):
"""
Test autograd from transposing a tensor using .T attribute
"""
norch_tensor_T = norch.Tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True).to(self.device)
norch_result_T = norch_tensor_T.T.sum()
norch_result_T.backward()
norch_tensor_grad_T = utils.to_torch(norch_tensor_T.grad).to(self.device)
torch_tensor_T = torch.tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True, device=self.device)
torch_result_T = torch_tensor_T.mT.sum()
torch_result_T.backward()
torch_tensor_grad_T = torch_tensor_T.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_T, torch_tensor_grad_T))
def test_reshape_then_matmul(self):
"""
Test autograd from reshaping a tensor then performing matrix multiplication: matmul(tensor1.reshape(shape), tensor2)
"""
norch_tensor1 = norch.Tensor([[1, 2.1], [3, -4], [5, 6], [7, 8]], requires_grad=True).to(self.device)
norch_tensor2 = norch.Tensor([[1, 5.1], [0.1, -4], [0, 6], [7, 8]], requires_grad=True).to(self.device)
new_shape = [2, 4]
norch_result_reshape_matmul = (norch_tensor1.reshape(new_shape) @ norch_tensor2).sum()
norch_result_reshape_matmul.backward()
norch_tensor_grad_reshape_matmul1 = utils.to_torch(norch_tensor1.grad).to(self.device)
norch_tensor_grad_reshape_matmul2 = utils.to_torch(norch_tensor2.grad).to(self.device)
torch_tensor1 = torch.tensor([[1, 2.1], [3, -4], [5, 6], [7, 8]], dtype=torch.float32, requires_grad=True, device=self.device)
torch_tensor2 = torch.tensor([[1, 5.1], [0.1, -4], [0, 6], [7, 8]], dtype=torch.float32, requires_grad=True, device=self.device)
torch_result_reshape_matmul = (torch_tensor1.reshape(new_shape) @ torch_tensor2).sum()
torch_result_reshape_matmul.backward()
torch_tensor_grad_reshape_matmul1 = torch_tensor1.grad
torch_tensor_grad_reshape_matmul2 = torch_tensor2.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_reshape_matmul1, torch_tensor_grad_reshape_matmul1))
self.assertTrue(utils.compare_torch(norch_tensor_grad_reshape_matmul2, torch_tensor_grad_reshape_matmul2))
def test_unsqueeze(self):
"""
Test autograd from unsqueezing a tensor: tensor.unsqueeze(dim)
"""
# Unsqueeze at dim=0
norch_tensor_unsqueeze = norch.Tensor([[1., 2], [3, 4]], requires_grad=True).to(self.device)
norch_result_unsqueeze_0 = norch_tensor_unsqueeze.unsqueeze(0).sum()
norch_result_unsqueeze_0.backward()
norch_tensor_grad_unsqueeze_0 = utils.to_torch(norch_tensor_unsqueeze.grad).to(self.device)
torch_tensor_unsqueeze = torch.tensor([[1., 2], [3, 4]], requires_grad=True, device=self.device)
torch_result_unsqueeze_0 = torch_tensor_unsqueeze.unsqueeze(0).sum()
torch_result_unsqueeze_0.backward()
torch_tensor_grad_unsqueeze_0 = torch_tensor_unsqueeze.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_unsqueeze_0, torch_tensor_grad_unsqueeze_0))
# Unsqueeze at dim=1
norch_tensor_unsqueeze = norch.Tensor([[1., 2.], [3, 4]], requires_grad=True).to(self.device)
norch_result_unsqueeze_1 = norch_tensor_unsqueeze.unsqueeze(1).sum()
norch_result_unsqueeze_1.backward()
norch_tensor_grad_unsqueeze_1 = utils.to_torch(norch_tensor_unsqueeze.grad).to(self.device)
torch_tensor_unsqueeze = torch.tensor([[1., 2.], [3, 4]], requires_grad=True, device=self.device)
torch_result_unsqueeze_1 = torch_tensor_unsqueeze.unsqueeze(1).sum()
torch_result_unsqueeze_1.backward()
torch_tensor_grad_unsqueeze_1 = torch_tensor_unsqueeze.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_unsqueeze_1, torch_tensor_grad_unsqueeze_1))
# Unsqueeze at dim=2
norch_tensor_unsqueeze = norch.Tensor([[1., 2], [3, 4]], requires_grad=True).to(self.device)
norch_result_unsqueeze_2 = norch_tensor_unsqueeze.unsqueeze(2).sum()
norch_result_unsqueeze_2.backward()
norch_tensor_grad_unsqueeze_2 = utils.to_torch(norch_tensor_unsqueeze.grad).to(self.device)
torch_tensor_unsqueeze = torch.tensor([[1., 2], [3, 4]], requires_grad=True, device=self.device)
torch_result_unsqueeze_2 = torch_tensor_unsqueeze.unsqueeze(2).sum()
torch_result_unsqueeze_2.backward()
torch_tensor_grad_unsqueeze_2 = torch_tensor_unsqueeze.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_unsqueeze_2, torch_tensor_grad_unsqueeze_2))
def test_unsqueeze_then_matmul(self):
"""
Test autograd from unsqueezing a tensor then performing matrix multiplication: matmul(tensor1.unsqueeze(dim), tensor2)
"""
norch_tensor1 = norch.Tensor([[1, 2], [3, 4]], requires_grad=True).to(self.device)
norch_tensor2 = norch.Tensor([[1, 2], [3, 4]], requires_grad=True).to(self.device)
# Unsqueeze at dim=0 then matmul
norch_result_unsqueeze_matmul = (norch_tensor1 @ norch_tensor2.unsqueeze(0)).sum()
norch_result_unsqueeze_matmul.backward()
norch_tensor_grad_unsqueeze_matmul1 = utils.to_torch(norch_tensor1.grad).to(self.device)
norch_tensor_grad_unsqueeze_matmul2 = utils.to_torch(norch_tensor2.grad).to(self.device)
torch_tensor1 = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32, requires_grad=True, device=self.device)
torch_tensor2 = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32, requires_grad=True, device=self.device)
torch_result_unsqueeze_matmul = (torch_tensor1 @ torch_tensor2.unsqueeze(0)).sum()
torch_result_unsqueeze_matmul.backward()
torch_tensor_grad_unsqueeze_matmul1 = torch_tensor1.grad
torch_tensor_grad_unsqueeze_matmul2 = torch_tensor2.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_unsqueeze_matmul1, torch_tensor_grad_unsqueeze_matmul1))
self.assertTrue(utils.compare_torch(norch_tensor_grad_unsqueeze_matmul2, torch_tensor_grad_unsqueeze_matmul2))
def test_squeeze(self):
"""
Test autograd from squeezing a tensor: tensor.squeeze(dim)
"""
# Squeeze at dim=0
norch_tensor_squeeze = norch.Tensor([[[1., 2], [3, 4]]], requires_grad=True).to(self.device)
norch_result_squeeze_0 = norch_tensor_squeeze.squeeze(0).sum()
norch_result_squeeze_0.backward()
norch_tensor_grad_squeeze_0 = utils.to_torch(norch_tensor_squeeze.grad).to(self.device)
torch_tensor_squeeze = torch.tensor([[[1., 2], [3, 4]]], requires_grad=True, device=self.device)
torch_result_squeeze_0 = torch_tensor_squeeze.squeeze(0).sum()
torch_result_squeeze_0.backward()
torch_tensor_grad_squeeze_0 = torch_tensor_squeeze.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_squeeze_0, torch_tensor_grad_squeeze_0))
def test_squeeze_then_matmul(self):
"""
Test autograd from squeezing a tensor then performing matrix multiplication: matmul(tensor1.squeeze(dim), tensor2)
"""
norch_tensor1 = norch.Tensor([[[1., 2], [3, 4]]], requires_grad=True).to(self.device)
norch_tensor2 = norch.Tensor([[[1., 2], [3, 4]]], requires_grad=True).to(self.device)
# Squeeze at dim=0 then matmul
norch_result_squeeze_matmul = (norch_tensor1.squeeze(0) @ norch_tensor2).sum()
norch_result_squeeze_matmul.backward()
norch_tensor_grad_squeeze_matmul1 = utils.to_torch(norch_tensor1.grad,).to(self.device)
norch_tensor_grad_squeeze_matmul2 = utils.to_torch(norch_tensor2.grad).to(self.device)
torch_tensor1 = torch.tensor([[[1., 2], [3, 4]]], dtype=torch.float32, requires_grad=True, device=self.device)
torch_tensor2 = torch.tensor([[[1., 2], [3, 4]]], dtype=torch.float32, requires_grad=True, device=self.device)
torch_result_squeeze_matmul = (torch_tensor1.squeeze(0) @ torch_tensor2).sum()
torch_result_squeeze_matmul.backward()
torch_tensor_grad_squeeze_matmul1 = torch_tensor1.grad
torch_tensor_grad_squeeze_matmul2 = torch_tensor2.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_squeeze_matmul1, torch_tensor_grad_squeeze_matmul1))
self.assertTrue(utils.compare_torch(norch_tensor_grad_squeeze_matmul2, torch_tensor_grad_squeeze_matmul2))
def test_T_then_matmul(self):
"""
Test autograd from transposing a tensor then performing matrix multiplication: matmul(tensor.T, tensor)
"""
norch_tensor1 = norch.Tensor([[1, 2.1], [3, -4], [5, 6], [7, 8]], requires_grad=True)
norch_tensor2 = norch.Tensor([[1, 5.1], [0.1, -4], [0, 6], [7, 8]], requires_grad=True)
norch_result_T_matmul = (norch_tensor1.T @ norch_tensor2).sum()
norch_result_T_matmul.backward()
norch_tensor_grad_T_matmul1 = utils.to_torch(norch_tensor1.grad).to(self.device)
norch_tensor_grad_T_matmult2 = utils.to_torch(norch_tensor2.grad).to(self.device)
torch_tensor1 = torch.tensor([[1, 2.1], [3, -4], [5, 6], [7, 8]], dtype=torch.float32, requires_grad=True, device=self.device)
torch_tensor2 = torch.tensor([[1, 5.1], [0.1, -4], [0, 6], [7, 8]], dtype=torch.float32, requires_grad=True, device=self.device)
torch_result_T_matmul = (torch_tensor1.T @ torch_tensor2).sum()
torch_result_T_matmul.backward()
torch_tensor_grad_T_matmul1 = torch_tensor1.grad
torch_tensor_grad_T_matmul2 = torch_tensor2.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_T_matmul1, torch_tensor_grad_T_matmul1))
self.assertTrue(utils.compare_torch(norch_tensor_grad_T_matmult2, torch_tensor_grad_T_matmul2))
def todo(self):
"""
The code has a problem on the following operation
tensor1.reshape(..) @ tensor1
print(tensor1.grad)
(also transpsoe and .T)
"""
pass
def test_transpose_axes_then_matmul(self):
"""
Test autograd from transposing a tensor with specific axes then performing matrix multiplication: matmul(tensor.transpose(axis1, axis2), tensor)
"""
norch_tensor1 = norch.Tensor([[1, 2.1], [3, -4], [5, 6], [7, 8]], requires_grad=True).to(self.device)
norch_tensor2 = norch.Tensor([[1, 5.1], [0.1, -4], [0, 6], [7, 8]], requires_grad=True).to(self.device)
norch_result_transpose_matmul = (norch_tensor1.transpose(0, 1) @ norch_tensor2).sum()
norch_result_transpose_matmul.backward()
norch_tensor_grad_transpose_matmul1 = utils.to_torch(norch_tensor1.grad).to(self.device)
norch_tensor_grad_transpose_matmult2 = utils.to_torch(norch_tensor2.grad).to(self.device)
torch_tensor1 = torch.tensor([[1, 2.1], [3, -4], [5, 6], [7, 8]], dtype=torch.float32, requires_grad=True, device=self.device)
torch_tensor2 = torch.tensor([[1, 5.1], [0.1, -4], [0, 6], [7, 8]], dtype=torch.float32, requires_grad=True, device=self.device)
torch_result_transpose_matmul = (torch_tensor1.T @ torch_tensor2).sum()
torch_result_transpose_matmul.backward()
torch_tensor_grad_transpose_matmul1 = torch_tensor1.grad
torch_tensor_grad_transpose_matmul2 = torch_tensor2.grad
self.assertTrue(utils.compare_torch(norch_tensor_grad_transpose_matmul1, torch_tensor_grad_transpose_matmul1))
self.assertTrue(utils.compare_torch(norch_tensor_grad_transpose_matmult2, torch_tensor_grad_transpose_matmul2))
if __name__ == '__main__':
unittest.main()

204
build/lib/tests/test_nn.py Normal file
View file

@ -0,0 +1,204 @@
import unittest
import norch
from norch.utils import utils_unittests as utils
import torch
import os
class TestNNModuleLoss(unittest.TestCase):
def setUp(self):
self.device = os.environ.get('device')
if self.device is None or self.device != 'cuda':
self.device = 'cpu'
print(f"Running tests on: {self.device}")
def test_mse_loss(self):
"""
Test the MSELoss
"""
loss_fn_norch = norch.nn.MSELoss()
loss_fn_torch = torch.nn.MSELoss()
# Test case 1: Predictions and labels are equal
predictions_norch = norch.Tensor([[1.1, 2, 3, 4], [1.1, 2, 3, 4]]).to(self.device)
labels_norch = norch.Tensor([[1.1, 2, 3, 4], [1.1, 2, 3, 3]]).to(self.device)
loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch)
loss_torch_result = utils.to_torch(loss_norch).to(self.device)
predictions_torch = torch.tensor([[1.1, 2, 3, 4], [1.1, 2, 3, 4]], device=self.device)
labels_torch = torch.tensor([[1.1, 2, 3, 4], [1.1, 2, 3, 3]], device=self.device)
loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch)
self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected))
# Test case 2: Predictions and labels are different
predictions_norch = norch.Tensor([1.1, 2, 3, 4]).to(self.device)
labels_norch = norch.Tensor([4, 3, 2.1, 1]).to(self.device)
loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch)
loss_torch_result = utils.to_torch(loss_norch).to(self.device)
predictions_torch = torch.tensor([1.1, 2, 3, 4], device=self.device)
labels_torch = torch.tensor([4, 3, 2.1, 1], device=self.device)
loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch)
self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected))
def test_cross_entropy_loss(self):
"""
Test the CrossEntropyLoss
"""
loss_fn_norch = norch.nn.CrossEntropyLoss()
loss_fn_torch = torch.nn.CrossEntropyLoss()
# Test case 1: Single class, single sample
predictions_norch = norch.Tensor([2.0, 1.0, 0.1]).to(self.device)
labels_norch = norch.Tensor([0]).to(self.device)
loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch)
loss_torch_result = utils.to_torch(loss_norch).to(self.device)
predictions_torch = torch.tensor([2.0, 1.0, 0.1], device=self.device)
labels_torch = torch.tensor(0, device=self.device)
loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch)
self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected))
# Test case 2: Multiple classes, multiple samples
predictions_norch = norch.Tensor([[0.5, 1.5, 2.5], [1.0, 2.0, 3.0]]).to(self.device)
labels_norch = norch.Tensor([2, 1]).to(self.device)
loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch)
loss_torch_result = utils.to_torch(loss_norch).to(self.device)
predictions_torch = torch.tensor([[0.5, 1.5, 2.5], [1.0, 2.0, 3.0]], device=self.device)
labels_torch = torch.tensor([2, 1], device=self.device)
loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch)
self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected))
# Test case 3: Edge case - all predictions are zero
predictions_norch = norch.Tensor([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]).to(self.device)
labels_norch = norch.Tensor([1, 2]).to(self.device)
loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch)
loss_torch_result = utils.to_torch(loss_norch).to(self.device)
predictions_torch = torch.tensor([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], device=self.device)
labels_torch = torch.tensor([1, 2], device=self.device)
loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch)
self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected))
# Test case 4: Class probabilities instead of class index
predictions_norch = norch.Tensor([0.5, 0.2, 0.1]).to(self.device)
labels_norch = norch.Tensor([1., 0, 0]).to(self.device)
loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch)
loss_torch_result = utils.to_torch(loss_norch).to(self.device)
predictions_torch = torch.tensor([0.5, 0.2, 0.1])
labels_torch = torch.tensor([1., 0, 0])
predictions_torch.to(self.device)
labels_torch.to(self.device)
loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch)
self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected))
# Test case 4: Batched class probabilities instead of class index
predictions_norch = norch.Tensor([[0.5, 0.2, 0.1], [0.1, 0.5, 0.7]]).to(self.device)
labels_norch = norch.Tensor([[1., 0, 0], [0, 1, 0]]).to(self.device)
loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch)
loss_torch_result = utils.to_torch(loss_norch).to(self.device)
predictions_torch = torch.tensor([[0.5, 0.2, 0.1], [0.1, 0.5, 0.7]], device=self.device)
labels_torch = torch.tensor([[1., 0, 0], [0, 1, 0]], device=self.device)
loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch)
self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected))
class TestNNModuleActivationFn(unittest.TestCase):
def setUp(self):
self.device = os.environ.get('device')
if self.device is None or self.device != 'cuda':
self.device = 'cpu'
def test_sigmoid_activation(self):
"""
Test Sigmoid activation function
"""
sigmoid_fn_norch = norch.nn.Sigmoid()
sigmoid_fn_torch = torch.nn.Sigmoid()
# Test case 1: Positive input
x = norch.Tensor([[1, 2, 3]]).to(self.device)
sigmoid_norch = sigmoid_fn_norch.forward(x)
sigmoid_torch_result = utils.to_torch(sigmoid_norch).to(self.device)
x = torch.tensor([[1, 2, 3]], device=self.device)
sigmoid_torch_expected = sigmoid_fn_torch.forward(x)
self.assertTrue(utils.compare_torch(sigmoid_torch_result, sigmoid_torch_expected))
# Test case 1: Negative input
x = norch.Tensor([-1, 2, -3]).to(self.device)
sigmoid_norch = sigmoid_fn_norch.forward(x)
sigmoid_torch_result = utils.to_torch(sigmoid_norch).to(self.device)
x = torch.tensor([-1, 2, -3], device=self.device)
sigmoid_torch_expected = sigmoid_fn_torch.forward(x)
self.assertTrue(utils.compare_torch(sigmoid_torch_result, sigmoid_torch_expected))
# Test case 1: Zero input
x = norch.Tensor([0, 0, 0]).to(self.device)
sigmoid_norch = sigmoid_fn_norch.forward(x)
sigmoid_torch_result = utils.to_torch(sigmoid_norch).to(self.device)
x = torch.tensor([0, 0, 0], device=self.device)
sigmoid_torch_expected = sigmoid_fn_torch.forward(x)
self.assertTrue(utils.compare_torch(sigmoid_torch_result, sigmoid_torch_expected))
def test_softmax_activation(self):
"""
Test Softmax activation function
"""
# Test different axes
axes = [0, 1, 2, -1]
# Define the input tensors for different test cases
test_cases = [
(norch.Tensor([[[1., 2, 3], [4, 5, 6]]]).to(self.device), torch.tensor([[[1., 2, 3], [4, 5, 6]]], device=self.device)),
(norch.Tensor([[[1., -1, 0], [2, -2, 0]]]).to(self.device), torch.tensor([[[1., -1, 0], [2, -2, 0]]], device=self.device)),
(norch.Tensor([[[0., 0, 0], [0, 0, 0]]]).to(self.device), torch.tensor([[[0., 0, 0], [0, 0, 0]]], device=self.device))
]
for dim in axes:
softmax_fn_norch = norch.nn.Softmax(dim=dim)
softmax_fn_torch = torch.nn.Softmax(dim=dim)
for norch_input, torch_input in test_cases:
# Forward pass using norch
softmax_norch = softmax_fn_norch.forward(norch_input)
softmax_torch_result = utils.to_torch(softmax_norch).to(self.device)
# Forward pass using torch
softmax_torch_expected = softmax_fn_torch.forward(torch_input)
# Compare the results
self.assertTrue(utils.compare_torch(softmax_torch_result, softmax_torch_expected))

View file

@ -0,0 +1,753 @@
import unittest
import norch
from norch.utils import utils_unittests as utils
import torch
import sys
import os
class TestTensorOperations(unittest.TestCase):
def setUp(self):
self.device = os.environ.get('device')
if self.device is None or self.device != 'cuda':
self.device = 'cpu'
print(f"Running tests on: {self.device}")
def test_creation_and_conversion(self):
"""
Test creation and convertion of norch tensor to pytorch
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_tensor = utils.to_torch(norch_tensor)
self.assertTrue(torch.is_tensor(torch_tensor))
def test_addition(self):
"""
Test addition two tensors: tensor1 + tensor2
"""
norch_tensor1 = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_tensor2 = norch.Tensor([[[1, 1], [1, 1]], [[1, 1], [1, 1]]]).to(self.device)
norch_result = norch_tensor1 + norch_tensor2
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor1 = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_tensor2 = torch.tensor([[[1, 1], [1, 1]], [[1, 1], [1, 1]]]).to(self.device)
torch_expected = torch_tensor1 + torch_tensor2
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_addition_broadcasted(self):
"""
Test addition of two tensors with broadcasting: tensor1 + tensor2
"""
norch_tensor1 = norch.Tensor([[[1, 2, 3], [4, 5, 6]]]).to(self.device) # Shape (1, 2, 3)
norch_tensor2 = norch.Tensor([1, 1, 1]).to(self.device) # Shape (3)
norch_result = norch_tensor1 + norch_tensor2
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor1 = torch.tensor([[[1, 2, 3], [4, 5, 6]]]).to(self.device) # Shape (1, 2, 3)
torch_tensor2 = torch.tensor([1, 1, 1]).to(self.device) # Shape (3)
torch_expected = torch_tensor1 + torch_tensor2
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
norch_tensor1 = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]]).to(self.device) # Shape (1, 2, 3)
norch_tensor2 = norch.Tensor([[10, 10], [5, 6]]).to(self.device) # Shape (3)
norch_result = norch_tensor1 + norch_tensor2
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor1 = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]]).to(self.device) # Shape (1, 2, 3)
torch_tensor2 = torch.tensor([[[10, 10], [5, 6]]]).to(self.device) # Shape (3)
torch_expected = torch_tensor1 + torch_tensor2
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
# reversed order broadcasting
norch_tensor1 = norch.Tensor([[0, 2]]).to(self.device)
norch_tensor2 = norch.Tensor([[3, 4], [5, -1]]).to(self.device)
norch_result = norch_tensor1 + norch_tensor2
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor1 = torch.tensor([[0, 2]]).to(self.device)
torch_tensor2 = torch.tensor([[3, 4], [5, -1]]).to(self.device)
torch_expected = torch_tensor1 + torch_tensor2
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
norch_result = norch_tensor2 + norch_tensor1
torch_expected = torch_tensor2 + torch_tensor1
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_subtraction(self):
"""
Test subtraction of two tensors: tensor1 - tensor2
"""
norch_tensor1 = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_tensor2 = norch.Tensor([[[1, 1], [1, 1]], [[1, 1], [1, 1]]]).to(self.device)
norch_result = norch_tensor1 - norch_tensor2
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor1 = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_tensor2 = torch.tensor([[[1, 1], [1, 1]], [[1, 1], [1, 1]]]).to(self.device)
torch_expected = torch_tensor1 - torch_tensor2
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_broadcasting_subtraction(self):
"""
Test subtraction of two tensors with broadcasting: tensor1 - tensor2
"""
norch_tensor1 = norch.Tensor([[[1, 2, 3], [4, 5, 6]]]).to(self.device) # Shape (1, 2, 3)
norch_tensor2 = norch.Tensor([1, 1, 1]).to(self.device) # Shape (3)
norch_result = norch_tensor1 - norch_tensor2
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor1 = torch.tensor([[[1, 2, 3], [4, 5, 6]]]).to(self.device) # Shape (1, 2, 3)
torch_tensor2 = torch.tensor([1, 1, 1]).to(self.device) # Shape (3)
torch_expected = torch_tensor1 - torch_tensor2
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
# reversed order broadcasting
norch_result = norch_tensor2 - norch_tensor1
torch_result = utils.to_torch(norch_result).to(self.device)
torch_expected = torch_tensor2 - torch_tensor1
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_division_by_scalar(self):
"""
Test division of a tensor by a scalar: tensor / scalar
"""
norch_tensor = norch.Tensor([[[2, 4], [6, -8]], [[10, 12], [14, 16]]]).to(self.device)
scalar = 2
norch_result = norch_tensor / scalar
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[2, 4], [6, -8]], [[10, 12], [14, 16]]]).to(self.device)
torch_expected = torch_tensor / scalar
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_scalar_division_by_tensor(self):
"""
Test scalar division by a tensor: scalar / tensor
"""
scalar = 10
norch_tensor = norch.Tensor([[[2, 4], [6, -8]], [[10, 12], [14, 16]]]).to(self.device)
norch_result = scalar / norch_tensor
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[2, 4], [6, -8]], [[10, 12], [14, 16]]]).to(self.device)
torch_expected = scalar / torch_tensor
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_matrix_multiplication(self):
"""
Test matrix multiplication: tensor1 @ tensor2
"""
norch_tensor1 = norch.Tensor([[[1., 2], [3, 4]], [[5, 6], [7, 8]]]).to(self.device)
norch_tensor2 = norch.Tensor([[[1., 0], [0, 1]], [[-1, 0], [0, -1]]]).to(self.device)
norch_result = norch_tensor1 @ norch_tensor2
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor1 = torch.tensor([[[1., 2], [3, 4]], [[5, 6], [7, 8]]]).to(self.device)
torch_tensor2 = torch.tensor([[[1., 0], [0, 1]], [[-1, 0], [0, -1]]]).to(self.device)
torch_expected = torch_tensor1 @ torch_tensor2
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_elementwise_multiplication_by_scalar(self):
"""
Test elementwise multiplication of a tensor by a scalar: tensor * scalar
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
scalar = 2
norch_result = norch_tensor * scalar
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = torch_tensor * scalar
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_elementwise_multiplication_by_tensor(self):
"""
Test elementwise multiplication of two tensors: tensor1 * tensor2
"""
norch_tensor1 = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_tensor2 = norch.Tensor([[[2, 2], [2, 2]], [[2, 2], [2, 2]]]).to(self.device)
norch_result = norch_tensor1 * norch_tensor2
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor1 = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_tensor2 = torch.tensor([[[2, 2], [2, 2]], [[2, 2], [2, 2]]]).to(self.device)
torch_expected = torch_tensor1 * torch_tensor2
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_reshape(self):
"""
Test reshaping of a tensor: tensor.reshape(shape)
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
new_shape = [2, 4]
norch_result = norch_tensor.reshape(new_shape)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = torch_tensor.reshape(new_shape)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_unsqueeze(self):
"""
Test unsqueeze operation on a tensor
"""
norch_tensor = norch.Tensor([[1, 2], [3, 4]]).to(self.device)
# Unsqueeze at dim=0
norch_unsqueeze_0 = norch_tensor.unsqueeze(0)
torch_unsqueeze_0 = utils.to_torch(norch_unsqueeze_0).to(self.device)
torch_tensor = torch.tensor([[1, 2], [3, 4]]).to(self.device)
torch_expected_0 = torch_tensor.unsqueeze(0)
self.assertTrue(utils.compare_torch(torch_unsqueeze_0, torch_expected_0))
# Unsqueeze at dim=1
norch_unsqueeze_1 = norch_tensor.unsqueeze(1)
torch_unsqueeze_1 = utils.to_torch(norch_unsqueeze_1).to(self.device)
torch_expected_1 = torch_tensor.unsqueeze(1)
self.assertTrue(utils.compare_torch(torch_unsqueeze_1, torch_expected_1))
# Unsqueeze at dim=2
norch_unsqueeze_2 = norch_tensor.unsqueeze(2)
torch_unsqueeze_2 = utils.to_torch(norch_unsqueeze_2).to(self.device)
torch_expected_2 = torch_tensor.unsqueeze(2)
self.assertTrue(utils.compare_torch(torch_unsqueeze_2, torch_expected_2))
# Unsqueeze at dim=-1
norch_unsqueeze_neg_1 = norch_tensor.unsqueeze(-1)
torch_unsqueeze_neg_1 = utils.to_torch(norch_unsqueeze_neg_1).to(self.device)
torch_expected_neg_1 = torch_tensor.unsqueeze(-1)
self.assertTrue(utils.compare_torch(torch_unsqueeze_neg_1, torch_expected_neg_1))
# Unsqueeze at dim=-2
norch_unsqueeze_neg_2 = norch_tensor.unsqueeze(-2)
torch_unsqueeze_neg_2 = utils.to_torch(norch_unsqueeze_neg_2).to(self.device)
torch_expected_neg_2 = torch_tensor.unsqueeze(-2)
self.assertTrue(utils.compare_torch(torch_unsqueeze_neg_2, torch_expected_neg_2))
def test_squeeze(self):
"""
Test squeeze operation on a tensor
"""
# Create a tensor with some dimensions of size 1
norch_tensor = norch.Tensor([[[1, 2], [3, 4]]]).to(self.device) # shape [1, 2, 2]
# Squeeze at dim=0
norch_squeeze_0 = norch_tensor.squeeze(0)
torch_squeeze_0 = utils.to_torch(norch_squeeze_0).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, 4]]]).to(self.device)
torch_expected_0 = torch_tensor.squeeze(0)
self.assertTrue(utils.compare_torch(torch_squeeze_0, torch_expected_0))
# Create a tensor with a dimension of size 1 in the middle
norch_tensor_middle_1 = norch.Tensor([[[1, 2]], [[3, 4]]]).to(self.device) # shape [2, 1, 2]
# Squeeze at dim=1
norch_squeeze_1 = norch_tensor_middle_1.squeeze(1)
torch_squeeze_1 = utils.to_torch(norch_squeeze_1).to(self.device)
torch_tensor_middle_1 = torch.tensor([[[1, 2]], [[3, 4]]]).to(self.device)
torch_expected_1 = torch_tensor_middle_1.squeeze(1)
self.assertTrue(utils.compare_torch(torch_squeeze_1, torch_expected_1))
# Squeeze at dim=-2 (same as dim=1 in this case)
norch_squeeze_neg_2 = norch_tensor_middle_1.squeeze(-2)
torch_squeeze_neg_2 = utils.to_torch(norch_squeeze_neg_2).to(self.device)
torch_expected_neg_2 = torch_tensor_middle_1.squeeze(-2)
self.assertTrue(utils.compare_torch(torch_squeeze_neg_2, torch_expected_neg_2))
# Squeeze all dimensions of size 1 (None)
norch_tensor_all_1 = norch.Tensor([[[[1, 2], [3, 4]]]]).to(self.device) # shape [1, 1, 2, 2]
norch_squeeze_all = norch_tensor_all_1.squeeze()
torch_squeeze_all = utils.to_torch(norch_squeeze_all).to(self.device)
torch_tensor_all_1 = torch.tensor([[[[1, 2], [3, 4]]]]).to(self.device)
torch_expected_all = torch_tensor_all_1.squeeze()
self.assertTrue(utils.compare_torch(torch_squeeze_all, torch_expected_all))
# Squeeze no dimensions (no dimensions of size 1)
norch_tensor_no_1 = norch.Tensor([[1, 2], [3, 4]]).to(self.device) # shape [2, 2]
norch_squeeze_none = norch_tensor_no_1.squeeze()
torch_squeeze_none = utils.to_torch(norch_squeeze_none).to(self.device)
torch_tensor_no_1 = torch.tensor([[1, 2], [3, 4]]).to(self.device)
torch_expected_none = torch_tensor_no_1.squeeze()
self.assertTrue(utils.compare_torch(torch_squeeze_none, torch_expected_none))
def test_transpose(self):
"""
Test transposition of a tensor: tensor.transpose(dim1, dim2)
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
dim1, dim2 = 0, 2
norch_result = norch_tensor.transpose(dim1, dim2)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = torch_tensor.transpose(dim1, dim2)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_logarithm(self):
"""
Test elementwise logarithm of a tensor: tensor.log()
"""
norch_tensor = norch.Tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.log()
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = torch.log(torch_tensor)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_sum(self):
"""
Test summation of a tensor: tensor.sum()
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.sum()
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = torch.sum(torch_tensor)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_sum_axis(self):
"""
Test summation of a tensor along a specific axis without keeping the dimensions
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.sum(axis=1)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = torch.sum(torch_tensor, dim=1)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
# negative axis
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.sum(axis=-2)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = torch.sum(torch_tensor, dim=-2)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_sum_axis_keepdim(self):
"""
Test summation of a tensor along a specific axis with keepdim=True
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.sum(axis=1, keepdim=True)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = torch.sum(torch_tensor, dim=1, keepdim=True)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_max(self):
"""
Test max of a tensor: tensor.max()
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.max()
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = torch.max(torch_tensor)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_max_axis(self):
"""
Test max of a tensor along a specific axis without keeping the dimensions
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.max(axis=1)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected, _ = torch.max(torch_tensor, dim=1)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
# negative axis
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.max(axis=-1)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected, _ = torch.max(torch_tensor, dim=-1)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_max_axis_keepdim(self):
"""
Test max of a tensor along a specific axis with keepdim=True
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.max(axis=1, keepdim=True)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected, _ = torch.max(torch_tensor, dim=1, keepdim=True)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_min(self):
"""
Test min of a tensor: tensor.min()
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.min()
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = torch.min(torch_tensor)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_min_axis(self):
"""
Test min of a tensor along a specific axis without keeping the dimensions
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.min(axis=1)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected, _ = torch.min(torch_tensor, dim=1)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
# negative axis
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.min(axis=-1)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected, _ = torch.min(torch_tensor, dim=-1)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_min_axis_keepdim(self):
"""
Test min of a tensor along a specific axis with keepdim=True
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.min(axis=1, keepdim=True)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected, _ = torch.min(torch_tensor, dim=1, keepdim=True)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_1D_T(self):
"""
Test transposition of a 1D tensor.
"""
norch_tensor = norch.Tensor([1, 2, 3, 4]).to(self.device)
norch_result = norch_tensor.T
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([1, 2, 3, 4]).to(self.device)
torch_expected = torch_tensor.T
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_2D_T(self):
"""
Test transposition of a 2D tensor.
"""
norch_tensor = norch.Tensor([[1, 2, 3], [4, 5, 6]]).to(self.device)
norch_result = norch_tensor.T
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[1, 2, 3], [4, 5, 6]]).to(self.device)
torch_expected = torch_tensor.T
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_3D_T(self):
"""
Test transposition of a tensor: tensor.T
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor.T
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = torch_tensor.T
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_matmul(self):
"""
Test matrix multiplication: MxP = NxM @ MxP
"""
# Creating batched tensors for Norch
norch_tensor1 = norch.Tensor([[1., 2], [3, -4], [5, 6], [7, 8]]).to(self.device)
norch_tensor2 = norch.Tensor([[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]]).to(self.device)
norch_result = norch_tensor1 @ norch_tensor2
torch_result = utils.to_torch(norch_result).to(self.device)
# Converting to PyTorch tensors for comparison
torch_tensor1 = torch.tensor([[1., 2], [3, -4], [5, 6], [7, 8]]).to(self.device)
torch_tensor2 = torch.tensor([[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]]).to(self.device)
torch_expected = torch.matmul(torch_tensor1, torch_tensor2)
# Comparing results
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_reshape_then_matmul(self):
"""
Test reshaping a tensor followed by matrix multiplication: (tensor.reshape(shape) @ other_tensor)
"""
norch_tensor = norch.Tensor([[1., 2], [3, -4], [5, 6], [7, 8]]).to(self.device)
new_shape = [2, 4]
norch_reshaped = norch_tensor.reshape(new_shape)
norch_result = norch_reshaped @ norch_tensor
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[1., 2], [3, -4], [5, 6], [7, 8]]).to(self.device)
torch_expected = torch_tensor.reshape(new_shape) @ torch_tensor
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_batched_matmul(self):
"""
Test batched matrix multiplication: BxMxP = BxNxM @ BxMxP
"""
B = 3 # Batch size
# Creating batched tensors for Norch
norch_tensor1 = norch.Tensor([[[1., 2], [3, -4], [5, 6], [7, 8]] for _ in range(B)]).to(self.device)
norch_tensor2 = norch.Tensor([[[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)]).to(self.device)
norch_result = norch_tensor1 @ norch_tensor2
torch_result = utils.to_torch(norch_result).to(self.device)
# Converting to PyTorch tensors for comparison
torch_tensor1 = torch.tensor([[[1., 2], [3, -4], [5, 6], [7, 8]] for _ in range(B)]).to(self.device)
torch_tensor2 = torch.tensor([[[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)]).to(self.device)
torch_expected = torch.matmul(torch_tensor1, torch_tensor2)
# Comparing results
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_broadcasted_batched_matmul(self):
"""
Test broadcasted batched matrix multiplication: BxMxP = NxM @ BxMxP
"""
B = 3 # Batch size
# Creating batched tensors for Norch
norch_tensor1 = norch.Tensor([[1., 2], [3, -4], [5, 6], [7, 8]]).to(self.device)
norch_tensor2 = norch.Tensor([[[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)]).to(self.device)
norch_result = norch_tensor1 @ norch_tensor2
torch_result = utils.to_torch(norch_result).to(self.device)
# Converting to PyTorch tensors for comparison
torch_tensor1 = torch.tensor([[1., 2], [3, -4], [5, 6], [7, 8]]).to(self.device)
torch_tensor2 = torch.tensor([[[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)]).to(self.device)
torch_expected = torch.matmul(torch_tensor1, torch_tensor2)
# Comparing results
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_transpose_then_matmul(self):
"""
Test transposing a tensor followed by matrix multiplication: (tensor.transpose(dim1, dim2) @ other_tensor)
"""
norch_tensor = norch.Tensor([[[1., 2], [3, 4]], [[5, 6], [7, 8]]]).to(self.device)
dim1, dim2 = 0, 2
norch_result = norch_tensor.transpose(dim1, dim2) @ norch_tensor
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1., 2], [3, 4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = torch_tensor.transpose(dim1, dim2) @ torch_tensor
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_add_div_matmul_then_reshape(self):
"""
Test a combination of operations: (tensor.sum() + other_tensor) / scalar @ another_tensor followed by reshape
"""
norch_tensor1 = norch.Tensor([[[1., 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_tensor2 = norch.Tensor([[[1., 1], [1, 1]], [[1, 1], [1, 1]]]).to(self.device)
scalar = 2
new_shape = [2, 4]
norch_result = ((norch_tensor1 + norch_tensor2) / scalar) @ norch_tensor1
norch_result = norch_result.reshape(new_shape)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor1 = torch.tensor([[[1., 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_tensor2 = torch.tensor([[[1., 1], [1, 1]], [[1, 1], [1, 1]]]).to(self.device)
torch_expected = ((torch_tensor1 + torch_tensor2) / scalar) @ torch_tensor1
torch_expected = torch_expected.reshape(new_shape)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_scalar_power_tensor(self):
"""
Test scalar power of a tensor: scalar ** tensor
"""
scalar = 3
norch_tensor = norch.Tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = scalar ** norch_tensor
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = scalar ** torch_tensor
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_tensor_power_scalar(self):
"""
Test tensor power of a scalar: tensor ** scalar
"""
scalar = 3
norch_tensor = norch.Tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_result = norch_tensor ** scalar
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[1, 2.1], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_expected = torch_tensor ** scalar
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_tensor_sin(self):
"""
Test sine function on tensor
"""
norch_tensor = norch.Tensor([[[0, 30], [45, 60]], [[90, 120], [135, 180]]]).to(self.device)
norch_result = norch_tensor.sin()
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[0, 30], [45, 60]], [[90, 120], [135, 180]]]).to(self.device)
torch_expected = torch.sin(torch_tensor)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_tensor_cos(self):
"""
Test cosine function on tensor
"""
norch_tensor = norch.Tensor([[[0, 30], [45, 60]], [[90, 120], [135, 180]]]).to(self.device)
norch_result = norch_tensor.cos()
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor = torch.tensor([[[0, 30], [45, 60]], [[90, 120], [135, 180]]]).to(self.device)
torch_expected = torch.cos(torch_tensor)
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_equal(self):
"""
Test equal two tensors: tensor1.equal(tensor2)
"""
norch_tensor1 = norch.Tensor([[[1, 2], [3, -4]], [[5, 1], [7, 8]]]).to(self.device)
norch_tensor2 = norch.Tensor([[[1, 1], [1, 1]], [[1, 1], [1, 1]]]).to(self.device)
norch_result = norch_tensor1.equal(norch_tensor2)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor1 = torch.tensor([[[1, 2], [3, -4]], [[5, 1], [7, 8]]]).to(self.device)
torch_tensor2 = torch.tensor([[[1, 1], [1, 1]], [[1, 1], [1, 1]]]).to(self.device)
torch_expected = (torch_tensor1 == torch_tensor2).float()
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_broadcasted_equal(self):
"""
Test broadcasted equal two tensors: tensor1.equal(tensor2)
"""
norch_tensor1 = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]]).to(self.device)
norch_tensor2 = norch.Tensor([[[10, 10]], [[5, 6]]]).to(self.device)
norch_result = norch_tensor1.equal(norch_tensor2)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor1 = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]]).to(self.device)
torch_tensor2 = torch.tensor([[[10, 10]], [[5, 6]]]).to(self.device)
torch_expected = (torch_tensor1 == torch_tensor2).float()
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
norch_tensor1 = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]]).to(self.device)
norch_tensor2 = norch.Tensor([[[10.0,], [-4.0,]],[[6.0,], [8.0,]]]).to(self.device)
norch_result = norch_tensor1.equal(norch_tensor2)
torch_result = utils.to_torch(norch_result).to(self.device)
torch_tensor1 = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]]).to(self.device)
torch_tensor2 = torch.tensor([[[10.0,], [-4.0,]],[[6.0,], [8.0,]]]).to(self.device)
torch_expected = (torch_tensor1 == torch_tensor2).float()
self.assertTrue(utils.compare_torch(torch_result, torch_expected))
def test_zeros_like(self):
"""
Test creating a tensor of zeros with the same shape as another tensor.
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_zeros = norch_tensor.zeros_like()
torch_zeros_result = utils.to_torch(norch_zeros).to(self.device)
torch_tensor_expected = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_zeros_expected = torch.zeros_like(torch_tensor_expected)
self.assertTrue(utils.compare_torch(torch_zeros_result, torch_zeros_expected))
def test_ones_like(self):
"""
Test creating a tensor of ones with the same shape as another tensor.
"""
norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
norch_ones = norch_tensor.ones_like()
torch_ones_result = utils.to_torch(norch_ones).to(self.device)
torch_tensor_expected = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device)
torch_ones_expected = torch.ones_like(torch_tensor_expected)
self.assertTrue(utils.compare_torch(torch_ones_result, torch_ones_expected))
if __name__ == '__main__':
unittest.main()