def test_subtraction_with_zero(): A = MathArray([[5, 2, 1], [-2, 4, -3]]) assert equal_as_arrays(A - 0, A) assert equal_as_arrays(0 - A, -A) # pylint: disable=invalid-unary-operand-type u = MathArray([1, 2]) assert equal_as_arrays(u - 0, u) assert equal_as_arrays(0 - u, -u) # pylint: disable=invalid-unary-operand-type
def test_subtraction_with_correct_shapes(): u = MathArray([1, 2, 3]) v = MathArray([10, 20, 30]) w = MathArray([9, 18, 27]) assert equal_as_arrays(v - u, w) A = MathArray([[5, 2, 1], [-2, 4, -3]]) B = MathArray([[2, -1, 4], [3, 0, 1]]) C = MathArray([[3, 3, -3], [-5, 4, -4]]) assert equal_as_arrays(A - B, C)
def test_numpy_functions_preserve_class(): A = MathArray([ [1, 2, 3], [4, 5, 6] ]) A_trans = MathArray([ [1, 4], [2, 5], [3, 6] ]) assert equal_as_arrays(np.transpose(A), A_trans) assert equal_as_arrays(A.T, A_trans)
def test_addition_with_correct_shapes(): u = MathArray([1, 2, 3]) v = MathArray([10, 20, 30]) w = MathArray([11, 22, 33]) assert equal_as_arrays(u + v, w) A = MathArray([[5, 2, 1], [-2, 4, -3]]) B = MathArray([[2, -1, 4], [3, 0, 1]]) C = MathArray([[7, 1, 5], [1, 4, -2]]) assert equal_as_arrays(A + B, C) assert equal_as_arrays(A + B, B + A)
def test_cross(): cross = ARRAY_ONLY_FUNCTIONS['cross'] a = MathArray([2, -1, 3.5]) b = MathArray([1.5, 2.25, -1]) a_cross_b = MathArray([-6.875, 7.25, 6.]) assert equal_as_arrays(cross(a, b), a_cross_b) vec_3 = random_math_array((3, )) vec_4 = random_math_array((4, )) match = ( r"There was an error evaluating function cross\(...\)\n" r"1st input is ok: received a vector of length 3 as expected\n" r"2nd input has an error: received a vector of length 4, expected " r"a vector of length 3") with raises(DomainError, match=match): cross(vec_3, vec_4) match = ( r"There was an error evaluating function cross\(...\)\n" r"1st input has an error: received a vector of length 4, expected " r"a vector of length 3\n" r"2nd input is ok: received a vector of length 3 as expected") with raises(DomainError, match=match): cross(vec_4, vec_3)
def test_in_place_powers(): amounts = [2, -3.0, MathArray(5)] for amount in amounts: A = random_math_array([2, 2]) A_copy = A.copy() A **= amount assert equal_as_arrays(A, A_copy ** amount)
def test_triple_vector_product_raises_error(): # Since vector products are interpretted as dot products, they are # ambiguous, and we disallow them. variables = { 'i': MathArray([1, 0]), 'j': MathArray([0, 1]), } assert equal_as_arrays( evaluator("(i*i)*j", variables)[0], variables['j'] ) match = ("Multiplying three or more vectors is ambiguous. " "Please place parentheses around vector multiplications.") with raises(CalcError, match=match): evaluator("i*i*j", variables)[0] with raises(CalcError, match=match): evaluator("i*2*i*3*j", variables)[0] # Next example should raise an operator shape error, not a triple vec error match='Cannot divide by a vector' with raises(CalcError, match=match): evaluator("i*j/i*i*j", variables)[0]
def test_calc_functions_multiple_arguments(): """Tests parse/eval handling functions with multiple arguments correctly""" def h1(x): return x def h2(x, y): return x * y def h3(x, y, z): return x * y * z assert evaluator("h(2)", {}, {"h": h1}, {})[0] == 2.0 assert evaluator("h(2, 3)", {}, {"h": h2}, {})[0] == 6.0 assert evaluator("h(2, 3, 4)", {}, {"h": h3}, {})[0] == 24.0 assert equal_as_arrays( evaluator("h(2, [1, 2, 3])", {}, {"h": h2})[0], MathArray([2, 4, 6])) with raises(ArgumentError): evaluator("h(2, 1)", {}, {"h": h1}, {}) with raises(UnableToParse): evaluator("h()", {}, {"h": h1}, {}) with raises(ArgumentError): evaluator("h(1)", {}, {"h": h2}, {}) with raises(ArgumentError): evaluator("h(1,2,3)", {}, {"h": h2}, {}) with raises(UnableToParse): evaluator("h()", {}, {"h": h3}, {}) with raises(ArgumentError): evaluator("h(1)", {}, {"h": h3}, {}) with raises(ArgumentError): evaluator("h(1,2)", {}, {"h": h3}, {})
def test_matrix_times_matrix_multiplication(): A = MathArray([ [5, 2, 3], [-4, 3, -1] ]) B = MathArray([ [0, 4, 5, 2], [5, -2, 6, -3], [-7, 5, 8, 4] ]) C = MathArray([ [-11, 31, 61, 16], [22, -27, -10, -21] ]) assert equal_as_arrays(A*B, C) X = random_math_array([3, 5]) Y = random_math_array([4, 2]) match = ("Cannot multiply a matrix of shape \(rows: 3, cols: 5\) with a matrix " "of shape \(rows: 4, cols: 2\)") with raises(ShapeError, match=match): X*Y row = MathArray([[1, 2, 3]]) col = MathArray([[1], [2], [3]]) assert row*col == 14 and isinstance(row*col, Number)
def test_in_place_division(): amounts = [5, MathArray(5)] for amount in amounts: A = random_math_array([2, 2]) A_copy = A.copy() A /= amount assert equal_as_arrays(A, A_copy / amount)
def test_in_place_multiplication(): amounts = [5, random_math_array(tuple()), random_math_array([2, 2])] for amount in amounts: A = random_math_array([2, 2]) A_copy = A.copy() A *= amount assert equal_as_arrays(A, A_copy * amount)
def test_in_place_subtraction(): amounts = [0, random_math_array([2, 2])] for amount in amounts: A = random_math_array([2, 2]) A_copy = A.copy() A -= amount assert equal_as_arrays(A, A_copy - amount)
def test_correct_arguments_get_passed_to_function(): f = get_somefunc() w = np.random.uniform(-10, 10) x = random_math_array([2, 3]) y = random_math_array([3]) z = random_math_array([2]) assert equal_as_arrays(f(w, x, y, z), w*x*y + z)
def test_math_arrays(): A = MathArray([[1, 5], [4, -2]]) v = MathArray([3, -2]) n = 3 x = 4.2 z = 2 + 3j variables = {'A': A, 'v': v, 'n': n, 'x': x, 'z': z} expr = '(z*[[1, 5], [4, -2]]^n + 10*A/x)*v' result = evaluator(expr, variables, max_array_dim=2)[0] assert equal_as_arrays(result, (z * A**n + 10 * A / x) * v)
def test_vector_times_matrix_multiplication(): u = MathArray([1, -2, 3]) A = MathArray([[2, 4], [4, -3], [-1, 0]]) b = MathArray([-9, 10]) assert equal_as_arrays(u * A, b) assert b.ndim == 1 # result is a vector X = random_math_array(5) Y = random_math_array([4, 3]) match = (r"Cannot multiply a vector of length 5 with a matrix of shape " r"\(rows: 4, cols: 3\)") with raises(ShapeError, match=match): X * Y
def test_nonpositive_matrix_powers(): A = MathArray([[1, 5], [-4, 3]]) I2 = MathArray([[1, 0], [0, 1]]) # B is not invertible B = MathArray([[2, 1], [6, 3]]) assert equal_as_arrays(A**0, I2) # Slight numerical errors in the inversion assert approx_equal_as_arrays(A**-2 * A**3, A, tol=1e-15) match = 'Cannot raise singular matrix to negative powers.' with raises(MathArrayError, match=match): B**-1
def test_addition_with_zero(): A = MathArray([[5, 2, 1], [-2, 4, -3]]) assert equal_as_arrays(A + 0, A) assert equal_as_arrays(0 + A, A) z0 = MathArray(0) assert equal_as_arrays(A + z0, A) assert equal_as_arrays(z0 + A, A) # This is ugly, but convenient for logic and implementation z3 = MathArray([[[0]]]) assert equal_as_arrays(A + z3, A) assert equal_as_arrays(z3 + A, A) u = MathArray([1, 2]) assert equal_as_arrays(u + 0, u) assert equal_as_arrays(0 + u, u)
def test_matrix_times_vector_multiplication(): u = MathArray([1, -2, 3]) A = MathArray([[2, 4, -1], [4, -3, 0]]) b = MathArray([-9, 10]) assert equal_as_arrays(A * u, b) assert b.ndim == 1 # result is a vector X = random_math_array([4, 3]) Y = random_math_array(5) match = ( r"Cannot multiply a matrix of shape \(rows: 4, cols: 3\) with a vector " r"of length 5.") with raises(ShapeError, match=match): X * Y
def test_array_input(): """Test that vectors/matrices can be inputted into calc's evaluator""" result = evaluator("[1, 2, 3]", {}, {}, {}, max_array_dim=1)[0] assert equal_as_arrays(result, MathArray([1, 2, 3])) result = evaluator("[[1, 2], [3, 4]]", {}, {}, {}, max_array_dim=2)[0] assert equal_as_arrays(result, MathArray([[1, 2], [3, 4]])) expr = '[v, [4, 5, 6]]' result = evaluator(expr, {'v': MathArray([1, 2, 3])}, max_array_dim=2)[0] assert equal_as_arrays(result, MathArray([[1, 2, 3], [4, 5, 6]])) msg = "Vector and matrix expressions have been forbidden in this entry." with raises(UnableToParse, match=msg): evaluator("[[1, 2], [3, 4]]", {}, {}, {}, max_array_dim=0) msg = "Matrix expressions have been forbidden in this entry." with raises(UnableToParse, match=msg): evaluator("[[1, 2], [3, 4]]", {}, {}, {}, max_array_dim=1) msg = "Tensor expressions have been forbidden in this entry." with raises(UnableToParse, match=msg): evaluator("[[[1, 2], [3, 4]]]", {}, {}, {}, max_array_dim=2) # By default, this is fine evaluator("[[[1, 2], [3, 4]]]")
def test_scalar_product(): X = MathArray([[1, 2, 3], [10, 20, 30]]) two = MathArray(2) two3 = MathArray([[[2]]]) Y = MathArray([[2, 4, 6], [20, 40, 60]]) assert equal_as_arrays(2 * X, Y) assert equal_as_arrays(X * 2, Y) assert equal_as_arrays(two * X, Y) assert equal_as_arrays(X * two, Y) # This is ugly, but convenient for logic and implementation assert equal_as_arrays(two3 * X, Y) assert equal_as_arrays(X * two3, Y)
def test_matrix_power_works_with_floats_and_scalars_if_integral(): A = random_math_array([3, 3]) assert equal_as_arrays(A**2, A**2.0) assert equal_as_arrays(A**2, A**MathArray(2.0))
def test_identity_dim_provides_identity(): no_identity = MatrixGrader() assert no_identity.constants.get('I', None) is None grader = MatrixGrader(identity_dim=4) assert equal_as_arrays(grader.constants['I'], identity(4))
def test_division_by_scalar(): A = MathArray([4, 8]) assert equal_as_arrays(A/2, MathArray([2, 4])) assert equal_as_arrays(A/MathArray(2), MathArray([2, 4])) assert 4/MathArray(2) == MathArray(2)
def test_copying(): A = MathArray([1, 2, 3]) assert equal_as_arrays(A, A.copy())