Source code for neograd.autograd.ops.basics

import numpy as np
from .operation import Operation


# <------------ADD------------>
[docs]class Add(Operation): '''Element wise addition between two Tensors or Tensor-like '''
[docs] def forward(self, tens1, tens2): '''Calculates element wise addition Args: tens1 (Tensor or int or float or list or np.ndarray): First operand tens2 (Tensor or int or float or list or np.ndarray): Second operand Returns: Tensor of the result ''' tens1, tens2 = self.get_tensors(tens1, tens2) return self.get_result_tensor(tens1.data+tens2.data, tens1, tens2)
[docs] def backward(self, tens1, tens2): '''Sets grad_fn of operands Local gradient is an identity matrix, that should be dotted with the upper gradient which results in upper gradient Args: tens1 (Tensor): First operand tens2 (Tensor): Second operand ''' tens1.set_grad_fn(lambda ug:ug) tens2.set_grad_fn(lambda ug:ug)
[docs]def add(tens1, tens2): '''Abstraction for Add.forward Args: tens1 (Tensor or int or float or list or np.ndarray): First operand tens2 (Tensor or int or float or list or np.ndarray): Second operand Returns: Tensor of the result ''' return Add().forward(tens1, tens2)
# <------------SUB------------>
[docs]class Sub(Operation): '''Element wise subtraction between two Tensors or Tensor-like '''
[docs] def forward(self, tens1, tens2): '''Calculates element wise subtraction Args: tens1 (Tensor or int or float or list or np.ndarray): First operand tens2 (Tensor or int or float or list or np.ndarray): Second operand Returns: Tensor of the result ''' tens1, tens2 = self.get_tensors(tens1, tens2) return self.get_result_tensor(tens1.data-tens2.data, tens1, tens2)
[docs] def backward(self, tens1, tens2): '''Sets grad_fn of operands Local gradient is an identity matrix, that should be dotted with the upper gradient which results in upper gradient, for the other one local gradient is a negative identity matrix which results in negative upper gradient Args: tens1 (Tensor): First operand tens2 (Tensor): Second operand ''' tens1.set_grad_fn(lambda ug:ug) tens2.set_grad_fn(lambda ug:-ug)
[docs]def sub(tens1, tens2): '''Abstraction for Sub.forward Args: tens1 (Tensor or int or float or list or np.ndarray): First operand tens2 (Tensor or int or float or list or np.ndarray): Second operand Returns: Tensor of the result ''' return Sub().forward(tens1, tens2)
# <------------MUL------------>
[docs]class Mul(Operation): '''Element wise multiplication between two Tensors or Tensor-like '''
[docs] def forward(self, tens1, tens2): '''Calculates element wise multiplication Args: tens1 (Tensor or int or float or list or np.ndarray): First operand tens2 (Tensor or int or float or list or np.ndarray): Second operand Returns: Tensor of the result ''' tens1, tens2 = self.get_tensors(tens1, tens2) return self.get_result_tensor(tens1.data*tens2.data, tens1, tens2)
[docs] def backward(self, tens1, tens2): '''Sets grad_fn of operands Local gradient for each Tensor is the other Tensor's data, which is element-wise multiplied with upper gradient Args: tens1 (Tensor): First operand tens2 (Tensor): Second operand ''' tens1.set_grad_fn(lambda ug:tens2.data*ug) tens2.set_grad_fn(lambda ug:tens1.data*ug)
[docs]def mul(tens1, tens2): '''Abstraction for Mul.forward Args: tens1 (Tensor or int or float or list or np.ndarray): First operand tens2 (Tensor or int or float or list or np.ndarray): Second operand Returns: Tensor of the result ''' return Mul().forward(tens1, tens2)
# <------------DIV------------>
[docs]class Div(Operation): '''Element wise division between two Tensors or Tensor-like '''
[docs] def forward(self, tens1, tens2): '''Calculates element wise division Args: tens1 (Tensor or int or float or list or np.ndarray): First operand tens2 (Tensor or int or float or list or np.ndarray): Second operand Returns: Tensor of the result ''' tens1, tens2 = self.get_tensors(tens1, tens2) return self.get_result_tensor(tens1.data/tens2.data, tens1, tens2)
[docs] def backward(self, tens1, tens2): '''Sets grad_fn of operands Local gradient of tens1 is 1/tens2.data, local gradient of tens2 is -1*tens1.data/tens2.data^2, which is element wise multiplied with upper gradient Args: tens1 (Tensor): First operand tens2 (Tensor): Second operand ''' tens1.set_grad_fn(lambda ug:(1/tens2.data)*ug) tens2.set_grad_fn(lambda ug:((-1*tens1.data)/np.power(tens2.data, 2))*ug)
[docs]def div(tens1, tens2): '''Abstraction for Div.forward Args: tens1 (Tensor or int or float or list or np.ndarray): First operand tens2 (Tensor or int or float or list or np.ndarray): Second operand Returns: Tensor of the result ''' return Div().forward(tens1, tens2)
# <------------DOT------------>
[docs]class Dot(Operation): '''Dot product between two Tensors or Tensor-like '''
[docs] def forward(self, tens1, tens2): '''Calculates dot product Args: tens1 (Tensor or int or float or list or np.ndarray): First operand tens2 (Tensor or int or float or list or np.ndarray): Second operand Returns: Tensor of the result ''' tens1, tens2 = self.get_tensors(tens1, tens2) return self.get_result_tensor(np.dot(tens1.data, tens2.data), tens1, tens2)
[docs] def backward(self, tens1, tens2): '''Sets grad_fn of operands Local gradient of tens1 is transpose of tens2.data, local gradient of tens2 is transpose of tens1.data, which is dotted with upper gradient Args: tens1 (Tensor): First operand tens2 (Tensor): Second operand ''' tens1.set_grad_fn(lambda ug:np.dot(ug, tens2.data.T)) tens2.set_grad_fn(lambda ug:np.dot(tens1.data.T, ug))
[docs]def dot(tens1, tens2): '''Abstraction for Dot.forward Args: tens1 (Tensor or int or float or list or np.ndarray): First operand tens2 (Tensor or int or float or list or np.ndarray): Second operand Returns: Tensor of the result ''' return Dot().forward(tens1, tens2)
# <------------EXP------------>
[docs]class Exp(Operation): '''Exponentiates the Tensor or Tensor-like '''
[docs] def forward(self, tens): '''Calculates exponentiation Args: tens (Tensor or int or float or list or np.ndarray): Operand Returns: Tensor of the result ''' tens = self.get_tensors(tens) return self.get_result_tensor(np.exp(tens.data), tens)
[docs] def backward(self, tens): '''Sets grad_fn of operand Local gradient is exponentiation of tens.data itself Args: tens (Tensor or int or float or list or np.ndarray): Operand ''' tens.set_grad_fn(lambda ug:np.exp(tens.data)*ug)
[docs]def exp(tens): '''Abstraction for Exp.forward Args: tens (Tensor): Operand Returns: Tensor of the result ''' return Exp().forward(tens)
# <------------LOG------------>
[docs]class Log(Operation): '''Natural Logarithm of the Tensor or Tensor-like '''
[docs] def forward(self, tens): '''Calculates natural logarithm Args: tens (Tensor or int or float or list or np.ndarray): Operand Returns: Tensor of the result ''' tens = self.get_tensors(tens) return self.get_result_tensor(np.log(tens.data), tens)
[docs] def backward(self, tens): '''Sets grad_fn of operand Local gradient is exponentiation of 1/tens.data itself Args: tens (Tensor or int or float or list or np.ndarray): Operand ''' tens.set_grad_fn(lambda ug:(1/tens.data)*ug)
[docs]def log(tens): '''Abstraction for Log.forward Args: tens (Tensor): Operand Returns: Tensor of the result ''' return Log().forward(tens)
# <------------POW------------>
[docs]class Pow(Operation): '''Raises one Tensor or Tensor-like to the power of another Tensor or Tensor-like '''
[docs] def forward(self, tens1, tens2): '''Calculates raising to a power Args: tens1 (Tensor or int or float or list or np.ndarray): First operand tens2 (Tensor or int or float or list or np.ndarray): Second operand Returns: Tensor of the result ''' tens1, tens2 = self.get_tensors(tens1, tens2) return self.get_result_tensor(np.power(tens1.data, tens2.data), tens1, tens2)
[docs] def backward(self, tens1, tens2): '''Sets grad_fn of operands Local gradient of tens1 is tens1.data^(tens2.data-1), local gradient of tens2 is log(tens1.data), which is element wise multiplied with upper gradient Args: tens1 (Tensor): First operand tens2 (Tensor): Second operand ''' result = np.power(tens1.data, tens2.data) tens1.set_grad_fn(lambda ug:(np.power(tens1.data, tens2.data-1) * tens2.data)*ug) tens2.set_grad_fn(lambda ug:(result*np.log(tens1.data))*ug)
[docs]def pow(tens1, tens2): '''Abstraction for Pow.forward Args: tens1 (Tensor or int or float or list or np.ndarray): First operand tens2 (Tensor or int or float or list or np.ndarray): Second operand Returns: Tensor of the result ''' return Pow().forward(tens1, tens2)
# <------------SUM------------>
[docs]class Sum(Operation): '''Performs sum along a specified axis If axis is None, then the sum of the entire Tensor is calculated Parameters: axis (None or int or tuple of int): Axis along which it should be summed ''' def __init__(self, axis=None): ''' Args: axis (None or int or tuple of int): Axis along which it should be summed Defaults to None ''' self.axis = axis
[docs] def forward(self, tens): '''Calculates sum along an axis Args: tens (Tensor or int or float or list or np.ndarray): Operand Returns: Tensor of the result ''' tens = self.get_tensors(tens) return self.get_result_tensor(np.sum(tens.data, axis=self.axis), tens)
[docs] def backward(self, tens): '''Sets grad_fn of operand Local gradient is all ones and the upper gradient must be added a new axis along the axis attribute if axis is not None, for broadcasting of upper_gradient as during forward pass the dimension will be reduced along the axis it is summed Args: tens (Tensor or int or float or list or np.ndarray): Operand ''' def sum_backward(ug): if self.axis is not None: ug = np.expand_dims(ug, axis=self.axis) grads = np.ones(tens.shape)*ug return grads tens.set_grad_fn(sum_backward)
[docs]def sum(tens, axis=None): '''Abstraction for Sum.forward Args: tens (Tensor): Operand axis (None or int or tuple of int): Axis along which it should be summed Defaults to None Returns: Tensor of the result ''' return Sum(axis).forward(tens)
# <------------TRANSPOSE------------>
[docs]class Transpose(Operation): '''Performs transpose of Tensor or Tensor-like '''
[docs] def forward(self, tens): '''Performs transpose Args: tens (Tensor or int or float or list or np.ndarray): Operand Returns: Tensor of the result ''' tens = self.get_tensors(tens) return self.get_result_tensor(tens.data.T, tens)
[docs] def backward(self, tens): '''Sets grad_fn of operand No local gradient, upper gradient is just transposed Args: tens (Tensor or int or float or list or np.ndarray): Operand ''' tens.set_grad_fn(lambda ug:ug.T)
[docs]def transpose(tens): '''Abstraction for Transpose.forward Args: tens (Tensor): Operand Returns: Tensor of the result ''' return Transpose().forward(tens)
# <------------FLATTEN------------>
[docs]class Flatten(Operation): '''Performs flattening of Tensor or Tensor-like '''
[docs] def forward(self, tens): '''Performs flattening from any dimension to 1D Args: tens (Tensor or int or float or list or np.ndarray): Operand Returns: Tensor of the result ''' tens = self.get_tensors(tens) flattened = tens.data.flatten() return self.get_result_tensor(flattened.reshape(flattened.shape[0],1), tens)
[docs] def backward(self, tens): '''Sets grad_fn of operand No local gradient, upper gradient is reshaped to original shape Args: tens (Tensor or int or float or list or np.ndarray): Operand ''' tens.set_grad_fn(lambda ug:ug.reshape(tens.shape))
[docs]def flatten(tens): '''Abstraction for Flatten.forward Args: tens (Tensor): Operand Returns: Tensor of the result ''' return Flatten().forward(tens)
# <------------RESHAPE------------>
[docs]class Reshape(Operation): '''Performs reshaping of Tensor or Tensor-like '''
[docs] def forward(self, tens, new_shape): '''Performs reshaping of Tensor to a new shape Args: tens (Tensor or int or float or list or np.ndarray): Operand new_shape (tuple): New shape to be reshaped into Returns: Tensor of the result ''' tens = self.get_tensors(tens) return self.get_result_tensor(tens.data.reshape(new_shape), tens)
[docs] def backward(self, tens): '''Sets grad_fn of operand No local gradient, upper gradient is reshaped to original shape Args: tens (Tensor or int or float or list or np.ndarray): Operand ''' tens.set_grad_fn(lambda ug:ug.reshape(tens.shape))
[docs]def reshape(tens, new_shape): '''Abstraction for Reshape.forward Args: tens (Tensor): Operand Returns: Tensor of the result ''' return Reshape().forward(tens, new_shape)