def test_exp_log():
    """Test exponents and logarithms"""
    x = np.linspace(-5, 5, 21)
    exp_x = np.exp(x)
    y = exp_x

    # Evaluate exp(x), log(y)
    _exp, _dexp = fl.exp(x)()
    _log, _dlog = fl.log(y)()

    # Known answers
    assert np.allclose(_exp, exp_x)
    assert np.allclose(_dexp, exp_x)
    assert np.allclose(_log, x)
    assert np.allclose(_dlog, 1.0 / y)

    # Log base 2 and 10; exp base 2
    _log2, _dlog2 = fl.log2(y)()
    _log10, _dlog10 = fl.log10(y)()
    _exp2, _dexp2 = fl.exp2(x)()

    # Known answers
    assert np.allclose(_log2, x / np.log(2.0))
    assert np.allclose(_dlog2, 1.0 / y / np.log(2.0))
    assert np.allclose(_log10, x / np.log(10.0))
    assert np.allclose(_dlog10, 1.0 / y / np.log(10.0))
    assert np.allclose(_exp2, 2.0**x)
    assert np.allclose(_dexp2, np.log(2.0) * (2.0**x))

    # exponential minus 1, log plus 1
    _expm1, _dexpm1 = fl.expm1(x)()
    _log1p, _dlog1p = fl.log1p(y)()

    # Known answers
    assert np.allclose(_expm1, exp_x - 1.0)
    assert np.allclose(_dexpm1, exp_x)
    assert np.allclose(_log1p, np.log(1.0 + y))
    assert np.allclose(_dlog1p, 1.0 / (1.0 + y))

    # exponential minus 1, log plus 1
    _logaddexp, _dlogaddexp = fl.logaddexp(x, x)()
    _logaddexp2, _dlogaddexp2 = fl.logaddexp2(x, x)()
    assert (str(fl.logaddexp(
        fl.Var('x'), fl.Var('y'))) == "logaddexp(Var(x, None),Var(y, None))")

    # Known answers
    assert np.allclose(_logaddexp, np.logaddexp(x, x))
    assert np.allclose(_dlogaddexp,
                       np.vstack([0 * y + 1 / 2, 0 * y + 1 / 2]).T)
    assert np.allclose(_logaddexp2, np.logaddexp2(x, x))
    assert np.allclose(_dlogaddexp2,
                       np.vstack([0 * y + 1 / 2, 0 * y + 1 / 2]).T)

    # forward mode
    f = fl.logaddexp(fl.Var('x'), fl.Var('y'))
    val, diff = f(0, 0)
    assert (np.isclose(val, np.array([[0.69314718]])))
    assert (diff.all() == np.array([[0.5, 0.5]]).all())

    report_success()
def test_compositions():
    """ TEST: composition of elementary functions:
             (i) compositions of multiple elementary functions
            (ii) compositions of elementary functions & other ops (Fluxions)
    """
    theta_vec = np.expand_dims(np.linspace(-5, 5, 21) * np.pi, axis=1)

    # composition of 2 elementary functions:
    # (a) immediate evaluation
    val, diff = fl.log(fl.exp(theta_vec))()
    assert (np.all(val == theta_vec))
    assert (np.all(np.isclose(diff, 1.0)))

    # (b) delayed evaluation
    logexp = fl.log(fl.exp(fl.Var('theta')))
    assert (np.all(logexp.val({'theta': theta_vec}) == theta_vec))
    assert (np.all(
        np.isclose(logexp.diff({'theta': theta_vec}),
                   np.ones_like(theta_vec))))

    # composition of elementary functions and basic ops (other Fluxions)
    # (a) immediate evaluation
    ans_1 = fl.cos(theta_vec).val()**2 - fl.sin(theta_vec).val()**2
    ans_2 = fl.cos(2 * theta_vec).val()
    assert (np.all(np.isclose(ans_1, ans_2)))

    # (b) delayed evaluation
    theta = fl.Var('theta', theta_vec)
    f = fl.cos(theta)**2 - fl.sin(theta)**2
    assert (np.all(
        np.isclose(f.val({'theta': theta_vec}),
                   fl.cos(2 * theta_vec).val())))

    report_success()
Example #3
0
def test_jacobian_dims():
    # test different possible combinations of function input and output dimensions
    global x, y, z

    J = jacobian([x * y, x**2, x + y], ['x', 'y'], {'x': 2, 'y': 3})
    assert (np.all(J == np.array([[3, 2], [4, 0], [1, 1]]).T))

    # Jacobian of a (1x1) function is the same as its derivative
    #F = fl.sin(fl.log(x**x))
    F = fl.sin(fl.log(x))
    J = jacobian(F, ['x'], {'x': 2})
    assert (np.all(J == F.diff({'x': 2})))

    # Jacobian of a scalar (3x1) function (= transpose of its gradient)
    J = jacobian([2 * x + x * y**3 + fl.log(z)], ['z', 'y', 'x'], {
        'x': 2,
        'y': 3,
        'z': 4
    })
    assert (np.all(J == np.array([29., 54., 0.25])))

    # partials with respect to only one variable -> Jacobian is still mxn
    #J = jacobian([2*x + z*y**3 + fl.log(z)], ['z'], {'x':2, 'y':3, 'z':4})
    #assert(np.all(J == np.array(27.25)))
    # NOTE: SHOULD THIS BE np.array([27.25])? --> Good call - code throws an error now!

    J = jacobian([x + y, y], ['x', 'y', 'z'], {'x': 2, 'y': 3, 'z': 1})
    assert (np.all(J == np.array([[1, 1, 0], [0, 1, 0]]).T))

    r = fl.Var('r')
    theta = fl.Var('theta')
    F = [r * fl.cos(theta), r * fl.sin(theta)]
def test_basics_singlevar():
    theta_vec = np.expand_dims(np.linspace(-5, 5, 21) * np.pi, axis=1)

    #### TEST: passing in vector of values: "immediate" evaluation
    _cos, _dcos = fl.cos(theta_vec)()
    _sin, _dsin = fl.sin(theta_vec)()
    _tan, _dtan = fl.tan(theta_vec)()

    assert (all(_dcos == -_sin))
    assert (all(np.isclose(_tan, _sin / _cos)))

    #### TEST: passing a fluxion Var: "delayed" evaluation

    theta = fl.Var('theta')
    _cos = fl.cos(theta)
    _sin = fl.sin(theta)

    assert (np.all(
        np.isclose(_sin.diff({'theta': theta_vec}), np.cos(theta_vec))))
    assert (np.all(
        _cos.diff({'theta': theta_vec}) == -1 *
        _sin.val({'theta': theta_vec})))

    #### TEST: basic functionality of other elementary functions
    # tan' = sec^2
    _dtan = (fl.tan(theta)).diff({'theta': theta_vec})
    _sec2 = ((1 / fl.cos(theta))**2).val({'theta': theta_vec})
    assert (np.all(np.isclose(_dtan, _sec2)))

    # test Fluxions returns NaN as numpy does
    x = np.linspace(-5, 5, 21)
    _varx = fl.Var('x')
    _log = fl.log(_varx)
    # This test is tricky.  two subtleties:
    # (1) do everything under this with statement to catch runtime warnings about bad log inputs
    # (2) can't just compare numbers with ==; also need to compare whether they're both nans separately
    # this is because nan == nan returns FALSE in numpy!
    with np.warnings.catch_warnings():
        np.warnings.filterwarnings('ignore')
        _log_fl = _log.val({'x': x})
        _log_np = np.expand_dims(np.log(x), axis=1)
        assert (np.all((_log_fl == _log_np)
                       | (np.isnan(_log_fl) == np.isnan(_log_np))))
        _log_der_1 = _log.diff({'x': x})
        _log_der_2 = 1 / _varx.val({'x': x})
        assert (np.all((_log_der_1 == _log_der_2)))

    _hypot, _hypot_der = fl.hypot(
        fl.sin(theta_vec).val(),
        fl.cos(theta_vec).val())()
    assert (np.all(_hypot == np.ones_like(theta_vec)))

    # test arccos vs. sec?

    report_success()
def test_basics_multivar():
    """ TEST: elementary functions of multiple variables """
    theta_vec = np.expand_dims(np.linspace(-5, 5, 21) * np.pi, axis=1)

    sin_z = fl.sin(fl.Var('x') * fl.Var('y'))
    assert (np.all(
        np.isclose(sin_z.val({
            'x': theta_vec,
            'y': theta_vec
        }), np.sin(theta_vec**2))))

    #WHAT TO DO WHEN TOO FEW VARS PASSED?
    # sin_z.val({'x':theta_vec})

    # HOW ARE PARTIALS EVALUATED?
    # sin_z.diff({'x': theta_vec})
    # sin_z.diff({'x': theta_vec, 'y': theta_vec*2})

    report_success()
def test_elementary_functions():
    # Create a variable theta with angles from 0 to 360 degrees, with values in radians
    theta_val = np.expand_dims(np.linspace(0, 2 * np.pi, 361), axis=1)
    theta = fl.Var('theta')

    # Scalar version
    f_theta = fl.sin(theta)
    assert f_theta.val({'theta': 2}) == np.sin(2)
    assert f_theta.diff({'theta': 2}) == np.cos(2)
    assert (str(f_theta) == "sin(Var(theta, None))")

    # Vector version
    f_theta = fl.sin(theta)
    assert np.all(f_theta.val({'theta': theta_val}) == np.sin(theta_val))
    assert np.all(f_theta.diff({'theta': theta_val}) == np.cos(theta_val))

    report_success()
import fluxions as fl


def newtons_method_scalar(f: fl.Fluxion, x: float, tol: float = 1e-8) -> float:
    """Solve the equation f(x) = 0 for a function from R->R using Newton's method"""
    max_iters: int = 100
    for i in range(max_iters):
        # Evaluate f(x) and f'(x)
        y, dy_dx = f(x)
        # Is y within the tolerance?
        if abs(y) < tol:
            break
        # Compute the newton step
        dx = -y / dy_dx
        # update x
        x += float(dx)
    # Return x and the number of iterations required
    return x, i


# Use this newton's method solver to find the root to equation
# e^x = 10x
x = fl.Var('x')
f = fl.exp(x) - 10 * x

root, iters = newtons_method_scalar(f, 0.0)
f_root = float(f.val(root))
print(f'Solution of exp(x) == 10x by Newton' 's Method:')
print(f'Solution converged after {iters} iterations.')
print(f'x={root:0.8f}, f(x) = {f_root:0.8f}')
Example #8
0
def test_basic_usage():
    """Test basic usage of Fluxions objects"""
    # Create a variable, x
    x = fl.Var('x', 1.0)

    #f0 = x - 1
    f0 = x - 1
    assert (f0.val({'x': 1}) == 0)
    assert (f0.diff({'x': 1}) == 1)
    var_tbl = {'x': 1}
    seed_tbl = {'x': 1}
    val, diff = f0(var_tbl, seed_tbl)
    assert val == 0
    assert diff == 1
    assert repr(f0) == "Subtraction(Var(x, 1.0), Const(1.0))"

    # f1(x) = 5x
    f1 = 5 * x
    assert (f1.shape() == (1, 1))
    # Evaluate f1(x) at the bound value of x
    assert (f1() == (5.0, 5.0))
    assert (f1(None) == (5.0, 5.0))
    assert (f1(1, 1) == (5.0, 5.0))
    assert (f1(np.array(1), np.array(1)) == (5.0, 5.0))
    # Evaluate f1(x) using function calling syntax
    assert (f1(2) == (10.0, 5.0))
    # Evaluate f1(x) using dictionary binding syntax
    assert (f1.val({'x': 2}) == 10)
    assert (f1.diff({'x': 2}) == 5)
    assert (f1({'x': 2}) == (10.0, np.array([5.])))
    assert repr(f1) == "Multiplication(Var(x, 1.0), Const(5.0))"

    # f2(x) = 1 + (x * x)
    f2 = 1 + x * x
    assert (f2(4.0) == (17.0, 8.0))
    assert (f2.val({'x': 2}) == 5)
    assert (f2.diff({'x': 3}) == 6)

    # f3(x) = (1 + x)/(x * x)
    f3 = (1 + x) / (x * x)
    assert (f3.val({'x': 2}) == 0.75)
    assert (f3.diff({'x': 2}) == -0.5)
    assert repr(
        f3
    ) == "Division(Addition(Var(x, 1.0), Const(1.0)), Multiplication(Var(x, 1.0), Var(x, 1.0)))"

    # f4(x) = (1 + 5x)/(x * x)
    f4 = (1 + 5 * x) / (x * x)
    assert (f4.val({'x': 2}) == 2.75)
    assert (f4.diff({'x': 2}) == -1.5)

    # Take a power
    f5 = fl.Power(x, 2)
    assert (f5.val(8) == 64)
    assert (f5.diff(8) == 16)
    assert (f5() == (1.0, 2.0))
    assert (f5(1) == (1.0, 2.0))
    assert (f5({'x': 1}) == (1.0, 2.0))
    assert repr(f5) == "Power(Var(x, 1.0), 2)"

    #check assignment
    a = fl.Fluxion()
    b = fl.Unop(a)
    c = fl.Var('x')
    assert (c.diff(0) == 1)
    assert (c.diff({'x': 1}) == 1)
    assert (c.diff({'x': 1}, {'x': 2}) == 2)
    assert (np.array_equal(c.diff({
        'x': 1,
        'y': 1
    }, {
        'x': 2,
        'y': 1
    }), np.array([[2., 0.]])))
    assert (c(1) == (1, np.array([1])))

    #check division
    f6 = 1 / x
    assert (f6.val({'x': 1, 'y': 1}) == 1)
    assert (np.array_equal(f6.diff({'x': 1, 'y': 1}), np.array([[-1., 0.]])))

    #check subtraction and division
    f7 = (1 - x + 1 - 1) / ((x * x) / 1)
    assert (f7.val({'x': 2}) == -0.25)
    assert (f7.diff({'x': 2}) == 0)

    # check negation
    f8 = -x
    assert (f8.val({'x': 2}) == -2)
    assert (f8.diff({'x': 2}) == -1)

    y = fl.Var('y')
    f9 = -(x * y)
    assert (f9.val({'x': -2, 'y': 3}) == 6)
    val, diff = f9(1, 1, 1, 1)
    assert (val == np.array([[-1.]]))
    assert (val == np.array([[-1., -1.]])).all()
Example #9
0
def test_basics_vectors():
    """Test using Fluxions objects with vector inputs"""
    # Create some vectors
    n = 10
    xs = np.expand_dims(np.linspace(0, 1, num=n), axis=1)
    ys = np.linspace(1, 2, num=n)
    ys_ex = np.expand_dims(np.linspace(1, 2, num=n), axis=1)

    # Create variables x and y bound to vector values
    x = fl.Var('x', xs)
    y = fl.Var('y', ys)

    # f1(x) = 5x
    f1 = 5 * x
    assert (f1.val(xs) == 5 * xs).all()
    assert (f1.diff({'x': xs}) == 5.0 * np.ones(np.shape(xs))).all()
    val, diff = f1(ys)
    assert (val == 5.0 * ys_ex).all()
    assert (diff == 5.0 * np.ones(np.shape(xs))).all()

    # f2(x) = 1 + (x * x)
    f2 = 1 + x * x
    assert (f2.val({'x': xs}) == 1 + np.power(xs, 2)).all()
    assert (f2.diff({'x': xs}) == 2.0 * xs).all()

    # f3(y) = (1 + y)/(y * y)
    f3 = (1 + y) / (y * y)
    assert (f3.val({'y': ys}) == np.divide(1 + ys_ex, np.power(ys_ex,
                                                               2))).all()
    assert np.isclose(
        f3.diff({'y': ys_ex}),
        np.divide(-2 - ys_ex, np.multiply(np.power(ys_ex, 2), ys_ex))).all()

    # f(x) = (1 + 5x)/(x * x)
    f4 = (1 + 5 * x) / (x * x)
    assert (f4.val({'x': ys}) == np.divide(1 + 5 * ys_ex, np.power(ys_ex,
                                                                   2))).all()
    assert np.isclose(
        f4.diff({'x': ys}),
        np.divide(-2 - 5 * ys_ex, np.multiply(np.power(ys_ex, 2),
                                              ys_ex))).all()

    # f5(x,y) = 5x+y
    f5 = 5 * x + y
    var_tbl_scalar = {'x': 2, 'y': 3}
    var_tbl_vector = {'x': xs, 'y': xs}
    assert (f5.val(var_tbl_scalar) == 13)
    assert (f5.diff(var_tbl_scalar) == np.array([5, 1])).all()
    assert (f5.val(var_tbl_vector) == 5 * xs + xs).all()
    assert (f5.diff(var_tbl_vector) == np.asarray([np.array([5, 1])] *
                                                  n)).all()

    # f(x,y) = 5xy
    f6 = 5 * x * y
    assert (f6.val(var_tbl_scalar) == 30)
    assert (f6.diff(var_tbl_scalar) == np.array([15, 10])).all()
    assert (f6.val(var_tbl_vector) == np.multiply(5 * xs, xs)).all()
    assert (f6.diff(var_tbl_vector) == np.transpose([5 * xs, 5 * xs])).all()

    # f(x,y,z) = 3x+2y+z
    z = fl.Var('z')
    f7 = 3 * x + 2 * y + z
    var_tbl_scalar = {'x': 1, 'y': 1, 'z': 1}
    assert (f7.val(var_tbl_scalar) == 6)
    assert (f7.diff(var_tbl_scalar) == np.array([3, 2, 1])).all()
    var_tbl_vector = {'x': xs, 'y': xs, 'z': xs}
    assert (f7.val(var_tbl_vector) == 3 * xs + 2 * xs + xs).all()
    assert (f7.diff(var_tbl_vector) == np.asarray([np.array([3, 2, 1])] *
                                                  10)).all()
    var_tbl_vector = {'z': xs}
    f7.val(var_tbl_vector)
    assert (f7.val(var_tbl_vector) == 3 * xs + 2 * xs + xs + 2).all()

    # f(x,y,z) = (3x+2y+z)/xyz
    f8 = (x * 3 + 2 * y + z) / (x * y * z)
    assert (f8.val(var_tbl_scalar) == 6)
    assert (f8.diff(var_tbl_scalar) == np.array([-3., -4., -5.])).all()
    # Rebind 'x', 'y', ans 'z' to the values in ys (slightly tricky!)
    var_tbl_vector = {'x': ys, 'y': ys, 'z': ys}
    assert (f8.val(var_tbl_vector) == (3 * ys_ex + 2 * ys_ex + ys_ex) /
            (ys_ex * ys_ex * ys_ex)).all()
    assert np.isclose(
        f8.diff(var_tbl_vector),
        np.transpose([
            -3 * ys / np.power(ys, 4), -4 * ys / np.power(ys, 4),
            -5 * ys / np.power(ys, 4)
        ])).all()

    #f(x,y) = xy
    f9 = y * x
    assert (f9.val({'x': 0, 'y': 0, 'z': 1}) == 0).all()
    assert (f9.diff({
        'x': 0,
        'y': 0,
        'z': 1
    }) == np.asarray([np.array([0, 0, 0])])).all()
Example #10
0
import os
# Handle import of module fluxions differently if module
# module is being loaded as __main__ or a module in a package.
if __name__ == '__main__':
    cwd = os.getcwd()
    os.chdir('../..')
    import fluxions as fl
    from fluxions import jacobian
    os.chdir(cwd)
else:
    import fluxions as fl
    from fluxions import jacobian

# *************************************************************************************************

x = fl.Var('x')
y = fl.Var('y')
z = fl.Var('z')


def test_vectorization():
    global x, y, z

    # Jacobian of a function from R^2 -> R^3, T=1 is squeezed to remove T dimension
    J = jacobian([x * y, x**2, x + y], ['x', 'y'], {'x': 1, 'y': 2})
    assert (J.shape == (2, 3))
    assert (J == np.array([[2., 2., 1.], [1., 0., 1.]])).all()

    # Jacobian of a function from R^2 -> R^3, T=4
    v_mapping = {'x': list(np.linspace(1, 4, 4)), 'y': list(2 * np.ones(4))}
    J = jacobian([x * y, x**2, x + y], ['x', 'y'], v_mapping)