mirror of
https://github.com/MLSysBook/TinyTorch.git
synced 2026-04-28 07:47:32 -05:00
Implement comprehensive inline testing for Tensor module
- Replace basic inline tests with comprehensive educational tests - Add thorough tensor creation testing (8 test cases) - Add comprehensive property testing (6 test cases) - Add complete arithmetic testing (8 test cases) - Add ML integration test with realistic scenarios - Provide detailed feedback, hints, and progress tracking - Follow inline-first testing approach for immediate feedback
This commit is contained in:
@@ -440,198 +440,604 @@ class Tensor:
|
||||
|
||||
# %% [markdown]
|
||||
"""
|
||||
### 🧪 Quick Test: Tensor Creation
|
||||
### 🧪 Unit Test: Tensor Creation
|
||||
|
||||
Let's test your tensor creation implementation right away! This gives you immediate feedback on whether your `__init__` method works correctly.
|
||||
|
||||
**This is a unit test** - it tests one specific function (tensor creation) in isolation.
|
||||
"""
|
||||
|
||||
# %% nbgrader={"grade": true, "grade_id": "test-tensor-creation-immediate", "locked": true, "points": 10, "schema_version": 3, "solution": false, "task": false}
|
||||
# Test tensor creation immediately after implementation
|
||||
print("🔬 Testing tensor creation...")
|
||||
|
||||
# Test scalar
|
||||
try:
|
||||
scalar = Tensor(5.0)
|
||||
print(f"✅ Scalar creation: {scalar}")
|
||||
assert hasattr(scalar, '_data'), "Tensor should have _data attribute"
|
||||
assert scalar._data.shape == (), f"Scalar should have shape (), got {scalar._data.shape}"
|
||||
except Exception as e:
|
||||
print(f"❌ Scalar creation failed: {e}")
|
||||
raise
|
||||
|
||||
# Test list
|
||||
try:
|
||||
vector = Tensor([1, 2, 3])
|
||||
print(f"✅ Vector creation: {vector}")
|
||||
assert vector._data.shape == (3,), f"Vector should have shape (3,), got {vector._data.shape}"
|
||||
except Exception as e:
|
||||
print(f"❌ Vector creation failed: {e}")
|
||||
raise
|
||||
|
||||
# Test matrix
|
||||
try:
|
||||
matrix = Tensor([[1, 2], [3, 4]])
|
||||
print(f"✅ Matrix creation: {matrix}")
|
||||
assert matrix._data.shape == (2, 2), f"Matrix should have shape (2, 2), got {matrix._data.shape}"
|
||||
except Exception as e:
|
||||
print(f"❌ Matrix creation failed: {e}")
|
||||
raise
|
||||
|
||||
print("🎉 Tensor creation works! You can create scalars, vectors, and matrices.")
|
||||
print("📈 Progress: Tensor creation ✓")
|
||||
|
||||
# %% [markdown]
|
||||
"""
|
||||
### 🧪 Quick Test: Tensor Properties
|
||||
### 🧪 Comprehensive Test: Tensor Creation
|
||||
|
||||
Now let's test the tensor properties (shape, size, dtype, data access) to make sure they work correctly.
|
||||
Let's thoroughly test your tensor creation to make sure it handles all the cases you'll encounter in ML.
|
||||
This tests the foundation of everything else we'll build.
|
||||
"""
|
||||
|
||||
# %% nbgrader={"grade": true, "grade_id": "test-tensor-properties-immediate", "locked": true, "points": 10, "schema_version": 3, "solution": false, "task": false}
|
||||
# Test tensor properties immediately after implementation
|
||||
print("🔬 Testing tensor properties...")
|
||||
# %% nbgrader={"grade": true, "grade_id": "test-tensor-creation-comprehensive", "locked": true, "points": 15, "schema_version": 3, "solution": false, "task": false}
|
||||
def test_tensor_creation_comprehensive():
|
||||
"""Comprehensive test of tensor creation with all data types and shapes."""
|
||||
print("🔬 Testing comprehensive tensor creation...")
|
||||
|
||||
tests_passed = 0
|
||||
total_tests = 8
|
||||
|
||||
# Test 1: Scalar creation (0D tensor)
|
||||
try:
|
||||
scalar_int = Tensor(42)
|
||||
scalar_float = Tensor(3.14)
|
||||
scalar_zero = Tensor(0)
|
||||
|
||||
assert hasattr(scalar_int, '_data'), "Tensor should have _data attribute"
|
||||
assert scalar_int._data.shape == (), f"Scalar should have shape (), got {scalar_int._data.shape}"
|
||||
assert scalar_float._data.shape == (), f"Float scalar should have shape (), got {scalar_float._data.shape}"
|
||||
assert scalar_zero._data.shape == (), f"Zero scalar should have shape (), got {scalar_zero._data.shape}"
|
||||
|
||||
print("✅ Scalar creation: integers, floats, and zero")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Scalar creation failed: {e}")
|
||||
|
||||
# Test 2: Vector creation (1D tensor)
|
||||
try:
|
||||
vector_int = Tensor([1, 2, 3, 4, 5])
|
||||
vector_float = Tensor([1.0, 2.5, 3.7])
|
||||
vector_single = Tensor([42])
|
||||
vector_empty = Tensor([])
|
||||
|
||||
assert vector_int._data.shape == (5,), f"Int vector should have shape (5,), got {vector_int._data.shape}"
|
||||
assert vector_float._data.shape == (3,), f"Float vector should have shape (3,), got {vector_float._data.shape}"
|
||||
assert vector_single._data.shape == (1,), f"Single element vector should have shape (1,), got {vector_single._data.shape}"
|
||||
assert vector_empty._data.shape == (0,), f"Empty vector should have shape (0,), got {vector_empty._data.shape}"
|
||||
|
||||
print("✅ Vector creation: integers, floats, single element, and empty")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Vector creation failed: {e}")
|
||||
|
||||
# Test 3: Matrix creation (2D tensor)
|
||||
try:
|
||||
matrix_2x2 = Tensor([[1, 2], [3, 4]])
|
||||
matrix_3x2 = Tensor([[1, 2], [3, 4], [5, 6]])
|
||||
matrix_1x3 = Tensor([[1, 2, 3]])
|
||||
|
||||
assert matrix_2x2._data.shape == (2, 2), f"2x2 matrix should have shape (2, 2), got {matrix_2x2._data.shape}"
|
||||
assert matrix_3x2._data.shape == (3, 2), f"3x2 matrix should have shape (3, 2), got {matrix_3x2._data.shape}"
|
||||
assert matrix_1x3._data.shape == (1, 3), f"1x3 matrix should have shape (1, 3), got {matrix_1x3._data.shape}"
|
||||
|
||||
print("✅ Matrix creation: 2x2, 3x2, and 1x3 matrices")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Matrix creation failed: {e}")
|
||||
|
||||
# Test 4: Data type handling
|
||||
try:
|
||||
int_tensor = Tensor([1, 2, 3])
|
||||
float_tensor = Tensor([1.0, 2.0, 3.0])
|
||||
mixed_tensor = Tensor([1, 2.5, 3]) # Should convert to float
|
||||
|
||||
# Check that data types are reasonable
|
||||
assert int_tensor._data.dtype in [np.int32, np.int64], f"Int tensor has unexpected dtype: {int_tensor._data.dtype}"
|
||||
assert float_tensor._data.dtype in [np.float32, np.float64], f"Float tensor has unexpected dtype: {float_tensor._data.dtype}"
|
||||
assert mixed_tensor._data.dtype in [np.float32, np.float64], f"Mixed tensor should be float, got: {mixed_tensor._data.dtype}"
|
||||
|
||||
print("✅ Data type handling: integers, floats, and mixed types")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Data type handling failed: {e}")
|
||||
|
||||
# Test 5: NumPy array input
|
||||
try:
|
||||
np_array = np.array([1, 2, 3, 4])
|
||||
tensor_from_np = Tensor(np_array)
|
||||
|
||||
assert tensor_from_np._data.shape == (4,), f"Tensor from NumPy should have shape (4,), got {tensor_from_np._data.shape}"
|
||||
assert np.array_equal(tensor_from_np._data, np_array), "Tensor from NumPy should preserve data"
|
||||
|
||||
print("✅ NumPy array input: conversion works correctly")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ NumPy array input failed: {e}")
|
||||
|
||||
# Test 6: Large tensor creation
|
||||
try:
|
||||
large_tensor = Tensor(list(range(1000)))
|
||||
assert large_tensor._data.shape == (1000,), f"Large tensor should have shape (1000,), got {large_tensor._data.shape}"
|
||||
assert large_tensor._data[0] == 0, "Large tensor should start with 0"
|
||||
assert large_tensor._data[-1] == 999, "Large tensor should end with 999"
|
||||
|
||||
print("✅ Large tensor creation: 1000 elements")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Large tensor creation failed: {e}")
|
||||
|
||||
# Test 7: Negative numbers
|
||||
try:
|
||||
negative_tensor = Tensor([-1, -2, -3])
|
||||
mixed_signs = Tensor([-1, 0, 1])
|
||||
|
||||
assert negative_tensor._data.shape == (3,), f"Negative tensor should have shape (3,), got {negative_tensor._data.shape}"
|
||||
assert np.array_equal(negative_tensor._data, np.array([-1, -2, -3])), "Negative numbers should be preserved"
|
||||
assert np.array_equal(mixed_signs._data, np.array([-1, 0, 1])), "Mixed signs should be preserved"
|
||||
|
||||
print("✅ Negative numbers: handled correctly")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Negative numbers failed: {e}")
|
||||
|
||||
# Test 8: Edge cases
|
||||
try:
|
||||
# Very large numbers
|
||||
big_tensor = Tensor([1e6, 1e-6])
|
||||
assert big_tensor._data.shape == (2,), "Big numbers tensor should have correct shape"
|
||||
|
||||
# Zero tensor
|
||||
zero_tensor = Tensor([0, 0, 0])
|
||||
assert np.all(zero_tensor._data == 0), "Zero tensor should contain all zeros"
|
||||
|
||||
print("✅ Edge cases: large numbers and zeros")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Edge cases failed: {e}")
|
||||
|
||||
# Results summary
|
||||
print(f"\n📊 Tensor Creation Results: {tests_passed}/{total_tests} tests passed")
|
||||
|
||||
if tests_passed == total_tests:
|
||||
print("🎉 All tensor creation tests passed! Your Tensor class can handle:")
|
||||
print(" • Scalars, vectors, and matrices")
|
||||
print(" • Different data types (int, float)")
|
||||
print(" • NumPy arrays")
|
||||
print(" • Large tensors and edge cases")
|
||||
print("📈 Progress: Tensor Creation ✓")
|
||||
return True
|
||||
else:
|
||||
print("⚠️ Some tensor creation tests failed. Common issues:")
|
||||
print(" • Check your __init__ method implementation")
|
||||
print(" • Make sure you're storing data in self._data")
|
||||
print(" • Verify NumPy array conversion works correctly")
|
||||
print(" • Test with different input types (int, float, list, np.array)")
|
||||
return False
|
||||
|
||||
# Test properties on different tensor types
|
||||
scalar = Tensor(5.0)
|
||||
vector = Tensor([1, 2, 3])
|
||||
matrix = Tensor([[1, 2], [3, 4]])
|
||||
|
||||
# Test shape property
|
||||
try:
|
||||
assert scalar.shape == (), f"Scalar shape should be (), got {scalar.shape}"
|
||||
assert vector.shape == (3,), f"Vector shape should be (3,), got {vector.shape}"
|
||||
assert matrix.shape == (2, 2), f"Matrix shape should be (2, 2), got {matrix.shape}"
|
||||
print("✅ Shape property works correctly")
|
||||
except Exception as e:
|
||||
print(f"❌ Shape property failed: {e}")
|
||||
raise
|
||||
|
||||
# Test size property
|
||||
try:
|
||||
assert scalar.size == 1, f"Scalar size should be 1, got {scalar.size}"
|
||||
assert vector.size == 3, f"Vector size should be 3, got {vector.size}"
|
||||
assert matrix.size == 4, f"Matrix size should be 4, got {matrix.size}"
|
||||
print("✅ Size property works correctly")
|
||||
except Exception as e:
|
||||
print(f"❌ Size property failed: {e}")
|
||||
raise
|
||||
|
||||
# Test data access
|
||||
try:
|
||||
assert scalar.data.item() == 5.0, f"Scalar data should be 5.0, got {scalar.data.item()}"
|
||||
assert np.array_equal(vector.data, np.array([1, 2, 3])), "Vector data mismatch"
|
||||
assert np.array_equal(matrix.data, np.array([[1, 2], [3, 4]])), "Matrix data mismatch"
|
||||
print("✅ Data access works correctly")
|
||||
except Exception as e:
|
||||
print(f"❌ Data access failed: {e}")
|
||||
raise
|
||||
|
||||
print("🎉 Tensor properties work! You can access shape, size, and data.")
|
||||
print("📈 Progress: Tensor creation ✓, Properties ✓")
|
||||
# Run the comprehensive test
|
||||
success = test_tensor_creation_comprehensive()
|
||||
|
||||
# %% [markdown]
|
||||
"""
|
||||
### 🧪 Quick Test: Basic Arithmetic
|
||||
### 🧪 Comprehensive Test: Tensor Properties
|
||||
|
||||
Let's test the basic arithmetic methods (add, multiply) before moving to operator overloading.
|
||||
Now let's test all the properties your tensor should have. These properties are essential for ML operations.
|
||||
"""
|
||||
|
||||
# %% nbgrader={"grade": true, "grade_id": "test-basic-arithmetic-immediate", "locked": true, "points": 10, "schema_version": 3, "solution": false, "task": false}
|
||||
# Test basic arithmetic methods immediately after implementation
|
||||
print("🔬 Testing basic arithmetic methods...")
|
||||
# %% nbgrader={"grade": true, "grade_id": "test-tensor-properties-comprehensive", "locked": true, "points": 15, "schema_version": 3, "solution": false, "task": false}
|
||||
def test_tensor_properties_comprehensive():
|
||||
"""Comprehensive test of tensor properties (shape, size, dtype, data access)."""
|
||||
print("🔬 Testing comprehensive tensor properties...")
|
||||
|
||||
tests_passed = 0
|
||||
total_tests = 6
|
||||
|
||||
# Test 1: Shape property
|
||||
try:
|
||||
scalar = Tensor(5.0)
|
||||
vector = Tensor([1, 2, 3])
|
||||
matrix = Tensor([[1, 2], [3, 4]])
|
||||
tensor_3d = Tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
|
||||
|
||||
assert scalar.shape == (), f"Scalar shape should be (), got {scalar.shape}"
|
||||
assert vector.shape == (3,), f"Vector shape should be (3,), got {vector.shape}"
|
||||
assert matrix.shape == (2, 2), f"Matrix shape should be (2, 2), got {matrix.shape}"
|
||||
assert tensor_3d.shape == (2, 2, 2), f"3D tensor shape should be (2, 2, 2), got {tensor_3d.shape}"
|
||||
|
||||
print("✅ Shape property: scalar, vector, matrix, and 3D tensor")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Shape property failed: {e}")
|
||||
|
||||
# Test 2: Size property
|
||||
try:
|
||||
scalar = Tensor(5.0)
|
||||
vector = Tensor([1, 2, 3])
|
||||
matrix = Tensor([[1, 2], [3, 4]])
|
||||
empty = Tensor([])
|
||||
|
||||
assert scalar.size == 1, f"Scalar size should be 1, got {scalar.size}"
|
||||
assert vector.size == 3, f"Vector size should be 3, got {vector.size}"
|
||||
assert matrix.size == 4, f"Matrix size should be 4, got {matrix.size}"
|
||||
assert empty.size == 0, f"Empty tensor size should be 0, got {empty.size}"
|
||||
|
||||
print("✅ Size property: scalar, vector, matrix, and empty tensor")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Size property failed: {e}")
|
||||
|
||||
# Test 3: Data type property
|
||||
try:
|
||||
int_tensor = Tensor([1, 2, 3])
|
||||
float_tensor = Tensor([1.0, 2.0, 3.0])
|
||||
|
||||
# Check that dtype is accessible and reasonable
|
||||
assert hasattr(int_tensor, 'dtype'), "Tensor should have dtype property"
|
||||
assert hasattr(float_tensor, 'dtype'), "Tensor should have dtype property"
|
||||
|
||||
# Data types should be NumPy dtypes
|
||||
assert isinstance(int_tensor.dtype, np.dtype), f"dtype should be np.dtype, got {type(int_tensor.dtype)}"
|
||||
assert isinstance(float_tensor.dtype, np.dtype), f"dtype should be np.dtype, got {type(float_tensor.dtype)}"
|
||||
|
||||
print(f"✅ Data type property: int tensor is {int_tensor.dtype}, float tensor is {float_tensor.dtype}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Data type property failed: {e}")
|
||||
|
||||
# Test 4: Data access property
|
||||
try:
|
||||
scalar = Tensor(5.0)
|
||||
vector = Tensor([1, 2, 3])
|
||||
matrix = Tensor([[1, 2], [3, 4]])
|
||||
|
||||
# Test data access
|
||||
assert hasattr(scalar, 'data'), "Tensor should have data property"
|
||||
assert hasattr(vector, 'data'), "Tensor should have data property"
|
||||
assert hasattr(matrix, 'data'), "Tensor should have data property"
|
||||
|
||||
# Test data content
|
||||
assert scalar.data.item() == 5.0, f"Scalar data should be 5.0, got {scalar.data.item()}"
|
||||
assert np.array_equal(vector.data, np.array([1, 2, 3])), "Vector data mismatch"
|
||||
assert np.array_equal(matrix.data, np.array([[1, 2], [3, 4]])), "Matrix data mismatch"
|
||||
|
||||
print("✅ Data access: scalar, vector, and matrix data retrieval")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Data access failed: {e}")
|
||||
|
||||
# Test 5: String representation
|
||||
try:
|
||||
scalar = Tensor(5.0)
|
||||
vector = Tensor([1, 2, 3])
|
||||
|
||||
# Test that __repr__ works
|
||||
scalar_str = str(scalar)
|
||||
vector_str = str(vector)
|
||||
|
||||
assert isinstance(scalar_str, str), "Tensor string representation should be a string"
|
||||
assert isinstance(vector_str, str), "Tensor string representation should be a string"
|
||||
assert len(scalar_str) > 0, "Tensor string representation should not be empty"
|
||||
assert len(vector_str) > 0, "Tensor string representation should not be empty"
|
||||
|
||||
print(f"✅ String representation: scalar={scalar_str[:50]}{'...' if len(scalar_str) > 50 else ''}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ String representation failed: {e}")
|
||||
|
||||
# Test 6: Property consistency
|
||||
try:
|
||||
test_cases = [
|
||||
Tensor(42),
|
||||
Tensor([1, 2, 3, 4, 5]),
|
||||
Tensor([[1, 2, 3], [4, 5, 6]]),
|
||||
Tensor([])
|
||||
]
|
||||
|
||||
for i, tensor in enumerate(test_cases):
|
||||
# Size should equal product of shape
|
||||
expected_size = np.prod(tensor.shape) if tensor.shape else 1
|
||||
assert tensor.size == expected_size, f"Test case {i}: size {tensor.size} doesn't match shape {tensor.shape}"
|
||||
|
||||
# Data shape should match tensor shape
|
||||
assert tensor.data.shape == tensor.shape, f"Test case {i}: data shape {tensor.data.shape} doesn't match tensor shape {tensor.shape}"
|
||||
|
||||
print("✅ Property consistency: size matches shape, data shape matches tensor shape")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Property consistency failed: {e}")
|
||||
|
||||
# Results summary
|
||||
print(f"\n📊 Tensor Properties Results: {tests_passed}/{total_tests} tests passed")
|
||||
|
||||
if tests_passed == total_tests:
|
||||
print("🎉 All tensor property tests passed! Your tensor has:")
|
||||
print(" • Correct shape property for all dimensions")
|
||||
print(" • Accurate size calculation")
|
||||
print(" • Proper data type handling")
|
||||
print(" • Working data access")
|
||||
print(" • Good string representation")
|
||||
print("📈 Progress: Tensor Creation ✓, Properties ✓")
|
||||
return True
|
||||
else:
|
||||
print("⚠️ Some property tests failed. Common issues:")
|
||||
print(" • Check your @property decorators")
|
||||
print(" • Verify shape returns self._data.shape")
|
||||
print(" • Make sure size returns self._data.size")
|
||||
print(" • Ensure dtype returns self._data.dtype")
|
||||
print(" • Test your __repr__ method")
|
||||
return False
|
||||
|
||||
# Test addition method
|
||||
try:
|
||||
a = Tensor([1, 2, 3])
|
||||
b = Tensor([4, 5, 6])
|
||||
c = a.add(b)
|
||||
expected = np.array([5, 7, 9])
|
||||
assert np.array_equal(c.data, expected), f"Addition method failed: expected {expected}, got {c.data}"
|
||||
print(f"✅ Addition method: {a.data} + {b.data} = {c.data}")
|
||||
except Exception as e:
|
||||
print(f"❌ Addition method failed: {e}")
|
||||
raise
|
||||
|
||||
# Test multiplication method
|
||||
try:
|
||||
d = a.multiply(b)
|
||||
expected = np.array([4, 10, 18])
|
||||
assert np.array_equal(d.data, expected), f"Multiplication method failed: expected {expected}, got {d.data}"
|
||||
print(f"✅ Multiplication method: {a.data} * {b.data} = {d.data}")
|
||||
except Exception as e:
|
||||
print(f"❌ Multiplication method failed: {e}")
|
||||
raise
|
||||
|
||||
print("🎉 Basic arithmetic methods work! You can add and multiply tensors.")
|
||||
print("📈 Progress: Tensor creation ✓, Properties ✓, Basic arithmetic ✓")
|
||||
# Run the comprehensive test
|
||||
success = test_tensor_properties_comprehensive() and success
|
||||
|
||||
# %% [markdown]
|
||||
"""
|
||||
### 🧪 Quick Test: Operator Overloading
|
||||
### 🧪 Comprehensive Test: Tensor Arithmetic
|
||||
|
||||
Finally, let's test the Python operators (+, -, *, /) to make sure they work with natural syntax.
|
||||
Let's test all arithmetic operations. These are the foundation of neural network computations!
|
||||
"""
|
||||
|
||||
# %% nbgrader={"grade": true, "grade_id": "test-operators-immediate", "locked": true, "points": 10, "schema_version": 3, "solution": false, "task": false}
|
||||
# Test operator overloading immediately after implementation
|
||||
print("🔬 Testing operator overloading...")
|
||||
# %% nbgrader={"grade": true, "grade_id": "test-tensor-arithmetic-comprehensive", "locked": true, "points": 20, "schema_version": 3, "solution": false, "task": false}
|
||||
def test_tensor_arithmetic_comprehensive():
|
||||
"""Comprehensive test of tensor arithmetic operations."""
|
||||
print("🔬 Testing comprehensive tensor arithmetic...")
|
||||
|
||||
tests_passed = 0
|
||||
total_tests = 8
|
||||
|
||||
# Test 1: Basic addition method
|
||||
try:
|
||||
a = Tensor([1, 2, 3])
|
||||
b = Tensor([4, 5, 6])
|
||||
c = a.add(b)
|
||||
|
||||
expected = np.array([5, 7, 9])
|
||||
assert np.array_equal(c.data, expected), f"Addition method failed: expected {expected}, got {c.data}"
|
||||
assert isinstance(c, Tensor), "Addition should return a Tensor"
|
||||
|
||||
print(f"✅ Addition method: {a.data} + {b.data} = {c.data}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Addition method failed: {e}")
|
||||
|
||||
# Test 2: Basic multiplication method
|
||||
try:
|
||||
a = Tensor([1, 2, 3])
|
||||
b = Tensor([4, 5, 6])
|
||||
c = a.multiply(b)
|
||||
|
||||
expected = np.array([4, 10, 18])
|
||||
assert np.array_equal(c.data, expected), f"Multiplication method failed: expected {expected}, got {c.data}"
|
||||
assert isinstance(c, Tensor), "Multiplication should return a Tensor"
|
||||
|
||||
print(f"✅ Multiplication method: {a.data} * {b.data} = {c.data}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Multiplication method failed: {e}")
|
||||
|
||||
# Test 3: Addition operator (+)
|
||||
try:
|
||||
a = Tensor([1, 2, 3])
|
||||
b = Tensor([4, 5, 6])
|
||||
c = a + b
|
||||
|
||||
expected = np.array([5, 7, 9])
|
||||
assert np.array_equal(c.data, expected), f"+ operator failed: expected {expected}, got {c.data}"
|
||||
assert isinstance(c, Tensor), "+ operator should return a Tensor"
|
||||
|
||||
print(f"✅ + operator: {a.data} + {b.data} = {c.data}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ + operator failed: {e}")
|
||||
|
||||
# Test 4: Multiplication operator (*)
|
||||
try:
|
||||
a = Tensor([1, 2, 3])
|
||||
b = Tensor([4, 5, 6])
|
||||
c = a * b
|
||||
|
||||
expected = np.array([4, 10, 18])
|
||||
assert np.array_equal(c.data, expected), f"* operator failed: expected {expected}, got {c.data}"
|
||||
assert isinstance(c, Tensor), "* operator should return a Tensor"
|
||||
|
||||
print(f"✅ * operator: {a.data} * {b.data} = {c.data}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ * operator failed: {e}")
|
||||
|
||||
# Test 5: Subtraction operator (-)
|
||||
try:
|
||||
a = Tensor([1, 2, 3])
|
||||
b = Tensor([4, 5, 6])
|
||||
c = b - a
|
||||
|
||||
expected = np.array([3, 3, 3])
|
||||
assert np.array_equal(c.data, expected), f"- operator failed: expected {expected}, got {c.data}"
|
||||
assert isinstance(c, Tensor), "- operator should return a Tensor"
|
||||
|
||||
print(f"✅ - operator: {b.data} - {a.data} = {c.data}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ - operator failed: {e}")
|
||||
|
||||
# Test 6: Division operator (/)
|
||||
try:
|
||||
a = Tensor([1, 2, 4])
|
||||
b = Tensor([2, 4, 8])
|
||||
c = b / a
|
||||
|
||||
expected = np.array([2.0, 2.0, 2.0])
|
||||
assert np.allclose(c.data, expected), f"/ operator failed: expected {expected}, got {c.data}"
|
||||
assert isinstance(c, Tensor), "/ operator should return a Tensor"
|
||||
|
||||
print(f"✅ / operator: {b.data} / {a.data} = {c.data}")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ / operator failed: {e}")
|
||||
|
||||
# Test 7: Scalar operations
|
||||
try:
|
||||
a = Tensor([1, 2, 3])
|
||||
|
||||
# Addition with scalar
|
||||
b = a + 10
|
||||
expected_add = np.array([11, 12, 13])
|
||||
assert np.array_equal(b.data, expected_add), f"Scalar addition failed: expected {expected_add}, got {b.data}"
|
||||
|
||||
# Multiplication with scalar
|
||||
c = a * 2
|
||||
expected_mul = np.array([2, 4, 6])
|
||||
assert np.array_equal(c.data, expected_mul), f"Scalar multiplication failed: expected {expected_mul}, got {c.data}"
|
||||
|
||||
# Subtraction with scalar
|
||||
d = a - 1
|
||||
expected_sub = np.array([0, 1, 2])
|
||||
assert np.array_equal(d.data, expected_sub), f"Scalar subtraction failed: expected {expected_sub}, got {d.data}"
|
||||
|
||||
# Division with scalar
|
||||
e = a / 2
|
||||
expected_div = np.array([0.5, 1.0, 1.5])
|
||||
assert np.allclose(e.data, expected_div), f"Scalar division failed: expected {expected_div}, got {e.data}"
|
||||
|
||||
print(f"✅ Scalar operations: +10, *2, -1, /2 all work correctly")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Scalar operations failed: {e}")
|
||||
|
||||
# Test 8: Matrix operations
|
||||
try:
|
||||
matrix_a = Tensor([[1, 2], [3, 4]])
|
||||
matrix_b = Tensor([[5, 6], [7, 8]])
|
||||
|
||||
# Matrix addition
|
||||
c = matrix_a + matrix_b
|
||||
expected = np.array([[6, 8], [10, 12]])
|
||||
assert np.array_equal(c.data, expected), f"Matrix addition failed: expected {expected}, got {c.data}"
|
||||
assert c.shape == (2, 2), f"Matrix addition should preserve shape, got {c.shape}"
|
||||
|
||||
# Matrix multiplication (element-wise)
|
||||
d = matrix_a * matrix_b
|
||||
expected_mul = np.array([[5, 12], [21, 32]])
|
||||
assert np.array_equal(d.data, expected_mul), f"Matrix multiplication failed: expected {expected_mul}, got {d.data}"
|
||||
|
||||
print(f"✅ Matrix operations: 2x2 matrix addition and multiplication")
|
||||
tests_passed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ Matrix operations failed: {e}")
|
||||
|
||||
# Results summary
|
||||
print(f"\n📊 Tensor Arithmetic Results: {tests_passed}/{total_tests} tests passed")
|
||||
|
||||
if tests_passed == total_tests:
|
||||
print("🎉 All tensor arithmetic tests passed! Your tensor supports:")
|
||||
print(" • Basic methods: add(), multiply()")
|
||||
print(" • Python operators: +, -, *, /")
|
||||
print(" • Scalar operations: tensor + number")
|
||||
print(" • Matrix operations: element-wise operations")
|
||||
print("📈 Progress: Tensor Creation ✓, Properties ✓, Arithmetic ✓")
|
||||
return True
|
||||
else:
|
||||
print("⚠️ Some arithmetic tests failed. Common issues:")
|
||||
print(" • Check your add() and multiply() methods")
|
||||
print(" • Verify operator overloading (__add__, __mul__, __sub__, __truediv__)")
|
||||
print(" • Make sure scalar operations work (convert scalar to Tensor)")
|
||||
print(" • Test with different tensor shapes")
|
||||
return False
|
||||
|
||||
a = Tensor([1, 2, 3])
|
||||
b = Tensor([4, 5, 6])
|
||||
# Run the comprehensive test
|
||||
success = test_tensor_arithmetic_comprehensive() and success
|
||||
|
||||
# Test addition operator
|
||||
try:
|
||||
c = a + b
|
||||
expected = np.array([5, 7, 9])
|
||||
assert np.array_equal(c.data, expected), f"+ operator failed: expected {expected}, got {c.data}"
|
||||
print(f"✅ + operator: {a.data} + {b.data} = {c.data}")
|
||||
except Exception as e:
|
||||
print(f"❌ + operator failed: {e}")
|
||||
raise
|
||||
# %% [markdown]
|
||||
"""
|
||||
### 🧪 Final Integration Test: Real ML Scenario
|
||||
|
||||
# Test multiplication operator
|
||||
try:
|
||||
d = a * b
|
||||
expected = np.array([4, 10, 18])
|
||||
assert np.array_equal(d.data, expected), f"* operator failed: expected {expected}, got {d.data}"
|
||||
print(f"✅ * operator: {a.data} * {b.data} = {d.data}")
|
||||
except Exception as e:
|
||||
print(f"❌ * operator failed: {e}")
|
||||
raise
|
||||
Let's test your tensor with a realistic machine learning scenario to make sure everything works together.
|
||||
"""
|
||||
|
||||
# Test subtraction operator
|
||||
try:
|
||||
e = b - a
|
||||
expected = np.array([3, 3, 3])
|
||||
assert np.array_equal(e.data, expected), f"- operator failed: expected {expected}, got {e.data}"
|
||||
print(f"✅ - operator: {b.data} - {a.data} = {e.data}")
|
||||
except Exception as e:
|
||||
print(f"❌ - operator failed: {e}")
|
||||
raise
|
||||
# %% nbgrader={"grade": true, "grade_id": "test-tensor-integration", "locked": true, "points": 10, "schema_version": 3, "solution": false, "task": false}
|
||||
def test_tensor_integration():
|
||||
"""Integration test with realistic ML scenario."""
|
||||
print("🔬 Testing tensor integration with ML scenario...")
|
||||
|
||||
try:
|
||||
print("🧠 Simulating a simple neural network forward pass...")
|
||||
|
||||
# Simulate input data (batch of 2 samples, 3 features each)
|
||||
X = Tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
|
||||
print(f"📊 Input data shape: {X.shape}")
|
||||
|
||||
# Simulate weights (3 input features, 2 output neurons)
|
||||
W = Tensor([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]])
|
||||
print(f"🎯 Weights shape: {W.shape}")
|
||||
|
||||
# Simulate bias (2 output neurons)
|
||||
b = Tensor([0.1, 0.2])
|
||||
print(f"⚖️ Bias shape: {b.shape}")
|
||||
|
||||
# Simple linear transformation: y = X * W + b
|
||||
# Note: This is a simplified version - real matrix multiplication would be different
|
||||
# But we can test element-wise operations
|
||||
|
||||
# Test that we can do basic operations needed for ML
|
||||
sample = Tensor([1.0, 2.0, 3.0]) # Single sample
|
||||
weight_col = Tensor([0.1, 0.3, 0.5]) # First column of weights
|
||||
|
||||
# Compute dot product manually using element-wise operations
|
||||
products = sample * weight_col # Element-wise multiplication
|
||||
print(f"✅ Element-wise multiplication works: {products.data}")
|
||||
|
||||
# Test addition for bias
|
||||
result = products + Tensor([0.1, 0.1, 0.1])
|
||||
print(f"✅ Bias addition works: {result.data}")
|
||||
|
||||
# Test with different shapes
|
||||
matrix_a = Tensor([[1, 2], [3, 4]])
|
||||
matrix_b = Tensor([[0.1, 0.2], [0.3, 0.4]])
|
||||
matrix_result = matrix_a * matrix_b
|
||||
print(f"✅ Matrix operations work: {matrix_result.data}")
|
||||
|
||||
# Test scalar operations (common in ML)
|
||||
scaled = sample * 0.5 # Learning rate scaling
|
||||
print(f"✅ Scalar scaling works: {scaled.data}")
|
||||
|
||||
# Test normalization-like operations
|
||||
mean_val = Tensor([2.0, 2.0, 2.0]) # Simulate mean
|
||||
normalized = sample - mean_val
|
||||
print(f"✅ Mean subtraction works: {normalized.data}")
|
||||
|
||||
print("\n🎉 Integration test passed! Your tensor class can handle:")
|
||||
print(" • Multi-dimensional data (batches, features)")
|
||||
print(" • Element-wise operations needed for ML")
|
||||
print(" • Scalar operations (learning rates, normalization)")
|
||||
print(" • Matrix operations (weights, transformations)")
|
||||
print("📈 Progress: All tensor functionality ✓")
|
||||
print("🚀 Ready for neural network layers!")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Integration test failed: {e}")
|
||||
print("\n💡 This suggests an issue with:")
|
||||
print(" • Basic tensor operations not working together")
|
||||
print(" • Shape handling problems")
|
||||
print(" • Arithmetic operation implementation")
|
||||
print(" • Check your tensor creation and arithmetic methods")
|
||||
return False
|
||||
|
||||
# Test division operator
|
||||
try:
|
||||
f = b / a
|
||||
expected = np.array([4.0, 2.5, 2.0])
|
||||
assert np.allclose(f.data, expected), f"/ operator failed: expected {expected}, got {f.data}"
|
||||
print(f"✅ / operator: {b.data} / {a.data} = {f.data}")
|
||||
except Exception as e:
|
||||
print(f"❌ / operator failed: {e}")
|
||||
raise
|
||||
# Run the integration test
|
||||
success = test_tensor_integration() and success
|
||||
|
||||
# Test scalar operations
|
||||
try:
|
||||
g = a + 10
|
||||
expected = np.array([11, 12, 13])
|
||||
assert np.array_equal(g.data, expected), f"Scalar + failed: expected {expected}, got {g.data}"
|
||||
print(f"✅ Scalar operations: {a.data} + 10 = {g.data}")
|
||||
except Exception as e:
|
||||
print(f"❌ Scalar operations failed: {e}")
|
||||
raise
|
||||
# Print final summary
|
||||
print(f"\n{'='*60}")
|
||||
print("🎯 TENSOR MODULE TESTING COMPLETE")
|
||||
print(f"{'='*60}")
|
||||
|
||||
print("🎉 All operators work! You can use +, -, *, / with natural Python syntax.")
|
||||
print("📈 Progress: Tensor creation ✓, Properties ✓, Basic arithmetic ✓, Operators ✓")
|
||||
print("🚀 Ready for the comprehensive tests!")
|
||||
if success:
|
||||
print("🎉 CONGRATULATIONS! All tensor tests passed!")
|
||||
print("\n✅ Your Tensor class successfully implements:")
|
||||
print(" • Comprehensive tensor creation (scalars, vectors, matrices)")
|
||||
print(" • All essential properties (shape, size, dtype, data access)")
|
||||
print(" • Complete arithmetic operations (methods and operators)")
|
||||
print(" • Scalar and matrix operations")
|
||||
print(" • Real ML scenario compatibility")
|
||||
print("\n🚀 You're ready to move to the next module!")
|
||||
print("📈 Final Progress: Tensor Module ✓ COMPLETE")
|
||||
else:
|
||||
print("⚠️ Some tests failed. Please review the error messages above.")
|
||||
print("\n🔧 To fix issues:")
|
||||
print(" 1. Check the specific test that failed")
|
||||
print(" 2. Review the error message and hints")
|
||||
print(" 3. Fix your implementation")
|
||||
print(" 4. Re-run the notebook cells")
|
||||
print("\n💪 Don't give up! Debugging is part of learning.")
|
||||
|
||||
# %% [markdown]
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user