Source code for csdl_alpha.src.operations.minimum

from csdl_alpha.src.graph.operation import Operation, set_properties
from csdl_alpha.src.graph.variable import Variable
from csdl_alpha.src.operations.operation_subclasses import ComposedOperation
from csdl_alpha.utils.inputs import variablize, validate_and_variablize
import csdl_alpha.utils.testing_utils as csdl_tests
import csdl_alpha as csdl

import numpy as np

class Minimum(ComposedOperation):
    '''
    Minimum entries in the input tensor along the specified axes.
    Or elementwise minimum if multiple variables of the same shape
    are provided.
    '''
    def __init__(self, *args, axes=None, rho=20.):
        super().__init__(*args)
        self.name  = 'minimum'
        self.axes  = axes
        self.rho   = rho

    def evaluate_composed(self, *args):
        return evaluate_minimum(args, self.axes, self.rho)
    
def evaluate_minimum(args, axes, rho):
    neg_args = [-arg for arg in args]
    out = -csdl.maximum(*neg_args, axes=axes, rho=rho)
    return out

[docs]def minimum(*args, axes=None, rho=20.): ''' Computes the minimum entry in the input tensor if a single argument is provided. Computes the minimum entries along the specified axes if `axes` argument is given. Computes the elementwise minimum of multiple variables of the same shape, if multiple arguments are provided. Axes argument is not allowed in this case. Parameters ---------- *args : tuple of Variable or np.ndarray objects Input tensor/s whose minimum needs to be computed. axes : tuple of int, default=None Axes along which to compute the minimum of the input tensor, if there's only one input tensor. rho : float, default=20. Smoothing parameter for the minimum function. Returns ------- Variable Minimum entry in the input tensor if a single argument is provided. Minimum entries along the specified axes if `axes` argument is given. Elementwise minimum of multiple variables of the same shape, if multiple arguments are provided. Examples -------- >>> recorder = csdl.Recorder(inline = True) >>> recorder.start() >>> x_val = np.arange(6).reshape(2,3) >>> x = csdl.Variable(value = x_val) >>> y1 = csdl.minimum(x) >>> y1.value array([-1.03057685e-10]) Note that the value of `y1` is not exactly `0.0` due to the smoothing term. The value of `y1` can be made closer to `0.0` by increasing the value of the smoothing parameter rho as shown below. >>> y = csdl.minimum(x, rho=200) >>> y.value array([-0.]) Minimum of a single tensor variable along a specified axis >>> y2 = csdl.minimum(x, axes=(1,)) >>> y2.value array([-1.03057685e-10, 3.00000000e+00]) Elementwise minimum of multiple tensor variables >>> y3 = csdl.minimum(x, 2 * np.ones((2,3)), np.ones((2,3))) >>> y3.value array([[-1.03057685e-10, 9.65342641e-01, 1.00000000e+00], [ 1.00000000e+00, 1.00000000e+00, 1.00000000e+00]]) ''' # Multiple Variables to find minimum if axes is not None and len(args) > 1: raise ValueError('Cannot find minimum of multiple variables along specified axes. \ Use X = min(A,B,...) followed by out=min(X, axes=(...)) instead.') if any(args[i].shape != args[0].shape for i in range(1, len(args))): raise ValueError('All Variables must have the same shape.') # Single Variable to find minimum if axes is not None: if any(np.asarray(axes) > (len(args[0].shape)-1)): raise ValueError('Specified axes cannot be more than the rank of the Variable.') if any(np.asarray(axes) < 0): raise ValueError('Axes cannot have negative entries.') if len(args) == 1: if axes is None: out_shape = (1,) else: out_shape = tuple([x for i, x in enumerate(args[0].shape) if i not in axes]) if len(out_shape) == 0: # raise ValueError('It is inefficient to find the minimum of a tensor Variable along all axes. \ # Use minimum(A) to find the minimum of all tensor entries.') out_shape = (1,) axes = None args = [validate_and_variablize(x) for x in args] op = Minimum(*args, axes=axes, rho=rho) return op.finalize_and_return_outputs()
class TestMinimum(csdl_tests.CSDLTest): def test_functionality(self,): self.prep() import csdl_alpha as csdl import numpy as np recorder = csdl.build_new_recorder(inline = True) recorder.start() x_val = 3.0*np.arange(6).reshape(2,3) y_val = 2.0*np.ones((2,3)) z_val = np.ones((2,3)) d_val = np.arange(12).reshape(2,3,2) x = csdl.Variable(name = 'x', value = x_val) y = csdl.Variable(name = 'y', value = y_val) z = csdl.Variable(name = 'z', value = z_val) d = csdl.Variable(name = 'd', value = d_val) compare_values = [] # minimum of a single tensor variable s1 = csdl.minimum(x) t1 = np.array([0.0]) compare_values += [csdl_tests.TestingPair(s1, t1, tag = 's1')] # sum of a single tensor variable s1 = csdl.minimum(x, axes=(0,1)) compare_values += [csdl_tests.TestingPair(s1, t1, tag = 's1')] # minimum of a single tensor constant s2 = csdl.minimum(x_val) compare_values += [csdl_tests.TestingPair(s2, t1, tag = 's2')] # minimum of a single tensor variable along a specified axes s3 = csdl.minimum(x, axes=(1,)) t3 = np.min(x_val, axis=1) compare_values += [csdl_tests.TestingPair(s3, t3, tag = 's3')] # minimum of a single tensor variable along 2 specified axes s4 = csdl.minimum(d, axes=(0,2)) t4 = np.min(d_val, axis=(0,2)) compare_values += [csdl_tests.TestingPair(s4, t4, tag = 's4', decimal=8)] # elementwise minimum of multiple tensor variables s5 = csdl.minimum(x, y, z) t5 = np.minimum(x_val, y_val) t5 = np.minimum(t5, z_val) compare_values += [csdl_tests.TestingPair(s5, t5, tag = 's5', decimal=8)] # elementwise minimum of multiple tensor constants s6 = csdl.minimum(x_val, y_val, z_val) compare_values += [csdl_tests.TestingPair(s6, t5, tag = 's6', decimal=8)] self.run_tests(compare_values = compare_values, verify_derivatives=True) def test_example(self,): self.docstest(minimum) if __name__ == '__main__': test = TestMinimum() test.test_functionality() test.test_example()