import torch


def calculate_linear_lrp_fast(in_layer_activations: torch.Tensor, weights: torch.Tensor, out_layer_relevance: torch.Tensor, epsilon: torch.Tensor = torch.Tensor([0.0])) -> torch.Tensor:
    """
    @in_layer_activations: the activations from each neuron from the "previous" layer
    @weights: the weights between the previous and the latter layer. torch.Tensor of shape [out_layer_relevance.shape, in_layer_activations.shape]
    @out_layer_relevance: The relevance values for each neuron from the latter layer.
    @epsilon: The epsilon value for the LRP_epsilon algorithm
    @return: The relevance values for each neuron from the @in_layer_activations
    """

    R = torch.nn.ReLU()(torch.mul(weights, in_layer_activations))
    R = torch.mul(R.T, out_layer_relevance)
    denominator = torch.add(torch.sum(R,dim=0),epsilon)
    R = torch.divide(R,denominator)
    R = torch.sum(R, dim=1)
    return R