From baeb32b5678e7dab60a360420f449f5674d028f1 Mon Sep 17 00:00:00 2001
From: "github-classroom[bot]"
<66690702+github-classroom[bot]@users.noreply.github.com>
Date: Sun, 28 Sep 2025 20:06:57 +0000
Subject: [PATCH 1/8] GitHub Classroom Autograding Workflow
---
.github/workflows/classroom.yml | 67 +++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
create mode 100644 .github/workflows/classroom.yml
diff --git a/.github/workflows/classroom.yml b/.github/workflows/classroom.yml
new file mode 100644
index 00000000..694e0c44
--- /dev/null
+++ b/.github/workflows/classroom.yml
@@ -0,0 +1,67 @@
+name: Autograding Tests
+'on':
+- workflow_dispatch
+- repository_dispatch
+permissions:
+ checks: write
+ actions: read
+ contents: read
+jobs:
+ run-autograding-tests:
+ runs-on: ubuntu-latest
+ if: github.actor != 'github-classroom[bot]'
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - name: Setup
+ id: setup
+ uses: classroom-resources/autograding-command-grader@v1
+ with:
+ test-name: Setup
+ setup-command: sudo -H pip3 install -qr requirements.txt; sudo -H pip3 install
+ flake8==5.0.4
+ command: flake8 --ignore "N801, E203, E266, E501, W503, F812, E741, N803,
+ N802, N806" minitorch/ tests/ project/; mypy minitorch/*
+ timeout: 10
+ - name: Task 0.1
+ id: task-0-1
+ uses: classroom-resources/autograding-command-grader@v1
+ with:
+ test-name: Task 0.1
+ setup-command: sudo -H pip3 install -qr requirements.txt
+ command: pytest -m task0_1
+ timeout: 10
+ - name: Task 0.2
+ id: task-0-2
+ uses: classroom-resources/autograding-command-grader@v1
+ with:
+ test-name: Task 0.2
+ setup-command: sudo -H pip3 install -qr requirements.txt
+ command: pytest -m task0_2
+ timeout: 10
+ - name: Task 0.3
+ id: task-0-3
+ uses: classroom-resources/autograding-command-grader@v1
+ with:
+ test-name: Task 0.3
+ setup-command: sudo -H pip3 install -qr requirements.txt
+ command: pytest -m task0_3
+ timeout: 10
+ - name: Task 0.4
+ id: task-0-4
+ uses: classroom-resources/autograding-command-grader@v1
+ with:
+ test-name: Task 0.4
+ setup-command: sudo -H pip3 install -qr requirements.txt
+ command: pytest -m task0_4
+ timeout: 10
+ - name: Autograding Reporter
+ uses: classroom-resources/autograding-grading-reporter@v1
+ env:
+ SETUP_RESULTS: "${{steps.setup.outputs.result}}"
+ TASK-0-1_RESULTS: "${{steps.task-0-1.outputs.result}}"
+ TASK-0-2_RESULTS: "${{steps.task-0-2.outputs.result}}"
+ TASK-0-3_RESULTS: "${{steps.task-0-3.outputs.result}}"
+ TASK-0-4_RESULTS: "${{steps.task-0-4.outputs.result}}"
+ with:
+ runners: setup,task-0-1,task-0-2,task-0-3,task-0-4
From a008d3b778a68df2cf0a40895c1148ead3ffe5ab Mon Sep 17 00:00:00 2001
From: "github-classroom[bot]"
<66690702+github-classroom[bot]@users.noreply.github.com>
Date: Sun, 28 Sep 2025 20:06:57 +0000
Subject: [PATCH 2/8] GitHub Classroom Feedback
---
.github/.keep | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 .github/.keep
diff --git a/.github/.keep b/.github/.keep
new file mode 100644
index 00000000..e69de29b
From d55f8ab053aeb091262a1f80a2add3ca1ed10bd8 Mon Sep 17 00:00:00 2001
From: "github-classroom[bot]"
<66690702+github-classroom[bot]@users.noreply.github.com>
Date: Sun, 28 Sep 2025 20:06:58 +0000
Subject: [PATCH 3/8] Setting up GitHub Classroom Feedback
From 631455d658214d1912c623fbcaa434c68aebbfef Mon Sep 17 00:00:00 2001
From: "github-classroom[bot]"
<66690702+github-classroom[bot]@users.noreply.github.com>
Date: Sun, 28 Sep 2025 20:07:00 +0000
Subject: [PATCH 4/8] add online IDE url
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 62e4d6ba..345a3201 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+[](https://classroom.github.com/online_ide?assignment_repo_id=20760858&assignment_repo_type=AssignmentRepo)
# MiniTorch Module 0
From 570ac5ba87398a5062603d14f65105de8db6c477 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=92=D0=B0=D0=B4=D0=B8=D0=BC=20=D0=9F=D0=B0=D0=BB=D1=8B?=
=?UTF-8?q?=D1=81=D0=B0=D0=B5=D0=B2?=
Date: Tue, 30 Sep 2025 23:50:23 +0300
Subject: [PATCH 5/8] module 0
---
minitorch/module.py | 27 ++++++----
minitorch/operators.py | 116 ++++++++++++++++++++++++++++++++++++++--
tests/test_operators.py | 43 +++++++++------
3 files changed, 158 insertions(+), 28 deletions(-)
diff --git a/minitorch/module.py b/minitorch/module.py
index 0a66058c..dcdcffab 100644
--- a/minitorch/module.py
+++ b/minitorch/module.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import Any, Dict, Optional, Sequence, Tuple
+from typing import Any, Dict, Optional, Sequence, Tuple, List
class Module:
@@ -31,13 +31,15 @@ def modules(self) -> Sequence[Module]:
def train(self) -> None:
"""Set the mode of this module and all descendent modules to `train`."""
- # TODO: Implement for Task 0.4.
- raise NotImplementedError("Need to implement for Task 0.4")
+ self.training = True
+ for child in self._modules.values():
+ child.train()
def eval(self) -> None:
"""Set the mode of this module and all descendent modules to `eval`."""
- # TODO: Implement for Task 0.4.
- raise NotImplementedError("Need to implement for Task 0.4")
+ self.training = False
+ for child in self._modules.values():
+ child.eval()
def named_parameters(self) -> Sequence[Tuple[str, Parameter]]:
"""Collect all the parameters of this module and its descendents.
@@ -47,13 +49,20 @@ def named_parameters(self) -> Sequence[Tuple[str, Parameter]]:
The name and `Parameter` of each ancestor parameter.
"""
- # TODO: Implement for Task 0.4.
- raise NotImplementedError("Need to implement for Task 0.4")
+ out: List[Tuple[str, Parameter]] = []
+
+ for name, p in self._parameters.items():
+ out.append((name, p))
+
+ for child_name, child_mod in self._modules.items():
+ for sub_name, p in child_mod.named_parameters():
+ out.append((f"{child_name}.{sub_name}", p))
+
+ return out
def parameters(self) -> Sequence[Parameter]:
"""Enumerate over all the parameters of this module and its descendents."""
- # TODO: Implement for Task 0.4.
- raise NotImplementedError("Need to implement for Task 0.4")
+ return [p for _, p in self.named_parameters()]
def add_parameter(self, k: str, v: Any) -> Parameter:
"""Manually add a parameter. Useful helper for scalar parameters.
diff --git a/minitorch/operators.py b/minitorch/operators.py
index 37cc7c09..efba54d9 100644
--- a/minitorch/operators.py
+++ b/minitorch/operators.py
@@ -3,7 +3,7 @@
import math
# ## Task 0.1
-from typing import Callable, Iterable
+from typing import Callable, Iterable, Iterator, List, TypeVar, Sequence
#
# Implementation of a prelude of elementary functions.
@@ -32,7 +32,73 @@
# $f(x) = |x - y| < 1e-2$
-# TODO: Implement for Task 0.1.
+def mul(x: float, y: float) -> float:
+ return x * y
+
+
+def id(x: float) -> float:
+ return x
+
+
+def add(x: float, y: float) -> float:
+ return x + y
+
+
+def neg(x: float) -> float:
+ return -x
+
+
+def lt(x: float, y: float) -> float:
+ return 1.0 if x < y else 0.0
+
+
+def eq(x: float, y: float) -> float:
+ return 1.0 if x == y else 0.0
+
+
+def max(x: float, y: float) -> float:
+ return x if x > y else y
+
+
+def is_close(x: float, y: float) -> bool:
+ return abs(x - y) < 1e-2
+
+
+def sigmoid(x: float) -> float:
+ if x >= 0.0:
+ z = math.exp(-x)
+ return 1.0 / (1.0 + z)
+ else:
+ z = math.exp(x)
+ return z / (1.0 + z)
+
+
+def relu(x: float) -> float:
+ return x if x > 0.0 else 0.0
+
+
+def log(x: float) -> float:
+ return math.log(x)
+
+
+def exp(x: float) -> float:
+ return math.exp(x)
+
+
+def inv(x: float) -> float:
+ return 1.0 / x
+
+
+def log_back(a: float, b: float) -> float:
+ return b / a
+
+
+def inv_back(a: float, b: float) -> float:
+ return -b / (a * a)
+
+
+def relu_back(a: float, b: float) -> float:
+ return b if a > 0.0 else 0.0
# ## Task 0.3
@@ -51,4 +117,48 @@
# - prod: take the product of lists
-# TODO: Implement for Task 0.3.
+T = TypeVar("T")
+U = TypeVar("U")
+
+
+def map(fn: Callable[[T], U], it: Iterable[T]) -> List[U]:
+ out: List[U] = []
+ for v in it:
+ out.append(fn(v))
+ return out
+
+
+def zipWith(fn: Callable[[T, U], T], a: Iterable[T], b: Iterable[U]) -> List[T]:
+ out: List[T] = []
+ ia = iter(a)
+ ib = iter(b)
+ while True:
+ try:
+ va = next(ia)
+ vb = next(ib)
+ except StopIteration:
+ break
+ out.append(fn(va, vb))
+ return out
+
+
+def reduce(fn: Callable[[T, T], T], it: Iterable[T], start: T) -> T:
+ acc: T = start
+ for v in it:
+ acc = fn(acc, v)
+ return acc
+
+def negList(ls: Iterable[float]) -> List[float]:
+ return map(neg, ls)
+
+
+def addLists(a: Iterable[float], b: Iterable[float]) -> List[float]:
+ return zipWith(add, a, b)
+
+
+def sum(ls: Iterable[float]) -> float:
+ return reduce(add, ls, 0.0)
+
+
+def prod(ls: Iterable[float]) -> float:
+ return reduce(mul, ls, 1.0)
diff --git a/tests/test_operators.py b/tests/test_operators.py
index f6e555af..1f7c6341 100644
--- a/tests/test_operators.py
+++ b/tests/test_operators.py
@@ -1,7 +1,7 @@
from typing import Callable, List, Tuple
import pytest
-from hypothesis import given
+from hypothesis import given, assume
from hypothesis.strategies import lists
from minitorch import MathTest
@@ -107,41 +107,51 @@ def test_sigmoid(a: float) -> None:
* It crosses 0 at 0.5
* It is strictly increasing.
"""
- # TODO: Implement for Task 0.2.
- raise NotImplementedError("Need to implement for Task 0.2")
+ s = sigmoid(a)
+ assert 0.0 <= s <= 1.0
+ assert_close(1.0 - s, sigmoid(-a))
+ assert_close(sigmoid(0.0), 0.5)
+ eps = 1e-3
+ s_left = sigmoid(a - eps)
+ s_right = sigmoid(a + eps)
+ assert s_left <= s <= s_right
@pytest.mark.task0_2
@given(small_floats, small_floats, small_floats)
def test_transitive(a: float, b: float, c: float) -> None:
"""Test the transitive property of less-than (a < b and b < c implies a < c)"""
- # TODO: Implement for Task 0.2.
- raise NotImplementedError("Need to implement for Task 0.2")
+ if lt(a, b) == 1.0 and lt(b, c) == 1.0:
+ assert lt(a, c) == 1.0
@pytest.mark.task0_2
-def test_symmetric() -> None:
+@given(small_floats, small_floats)
+def test_symmetric(x: float, y: float) -> None:
"""Write a test that ensures that :func:`minitorch.operators.mul` is symmetric, i.e.
gives the same value regardless of the order of its input.
"""
- # TODO: Implement for Task 0.2.
- raise NotImplementedError("Need to implement for Task 0.2")
+ assert_close(mul(x, y), mul(y, x))
@pytest.mark.task0_2
-def test_distribute() -> None:
+@given(small_floats, small_floats, small_floats)
+def test_distribute(z: float, x: float, y: float) -> None:
r"""Write a test that ensures that your operators distribute, i.e.
:math:`z \times (x + y) = z \times x + z \times y`
"""
- # TODO: Implement for Task 0.2.
- raise NotImplementedError("Need to implement for Task 0.2")
+ left = mul(z, add(x, y))
+ right = add(mul(z, x), mul(z, y))
+ assert_close(left, right)
@pytest.mark.task0_2
-def test_other() -> None:
+@given(small_floats)
+def test_other(a: float) -> None:
"""Write a test that ensures some other property holds for your functions."""
- # TODO: Implement for Task 0.2.
- raise NotImplementedError("Need to implement for Task 0.2")
+ assert_close(neg(neg(a)), a)
+ b = a + 1.2345
+ assert eq(a, b) == eq(b, a)
# ## Task 0.3 - Higher-order functions
@@ -168,8 +178,9 @@ def test_sum_distribute(ls1: List[float], ls2: List[float]) -> None:
"""Write a test that ensures that the sum of `ls1` plus the sum of `ls2`
is the same as the sum of each element of `ls1` plus each element of `ls2`.
"""
- # TODO: Implement for Task 0.3.
- raise NotImplementedError("Need to implement for Task 0.3")
+ left = minitorch.operators.sum(addLists(ls1, ls2))
+ right = minitorch.operators.sum(ls1) + minitorch.operators.sum(ls2)
+ assert_close(left, right)
@pytest.mark.task0_3
From 56a6b045155c34e32a04768a721b4fb443427086 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=92=D0=B0=D0=B4=D0=B8=D0=BC=20=D0=9F=D0=B0=D0=BB=D1=8B?=
=?UTF-8?q?=D1=81=D0=B0=D0=B5=D0=B2?=
Date: Sun, 19 Oct 2025 23:15:10 +0300
Subject: [PATCH 6/8] add workflow for Module 0 tests
---
.github/workflows/minitorch.yml | 40 +++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
create mode 100644 .github/workflows/minitorch.yml
diff --git a/.github/workflows/minitorch.yml b/.github/workflows/minitorch.yml
new file mode 100644
index 00000000..73ad015e
--- /dev/null
+++ b/.github/workflows/minitorch.yml
@@ -0,0 +1,40 @@
+name: CI (Module 0)
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ tests:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.11"
+ cache: "pip"
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ # если есть requirements.txt — подтянем
+ if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+ # установить сам пакет (editable) + тестовые зависимости
+ pip install -e .
+ pip install pytest hypothesis
+
+ - name: Lint (optional, можно удалить)
+ run: |
+ pip install flake8 pep8-naming
+ flake8 --ignore "N801,E203,E266,E501,W503,F812,F401,F841,E741,N803,N802,N806" minitorch/ tests/ project/
+
+ - name: Run tests (Module 0 only)
+ run: |
+ echo "Module 0"
+ pytest -q -m task0_1
+ pytest -q -m task0_2
+ pytest -q -m task0_3
+ pytest -q -m task0_4
From 28935da039e8d8d9a2cc22c6b90991f4294ed45f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=92=D0=B0=D0=B4=D0=B8=D0=BC=20=D0=9F=D0=B0=D0=BB=D1=8B?=
=?UTF-8?q?=D1=81=D0=B0=D0=B5=D0=B2?=
Date: Sun, 19 Oct 2025 23:40:14 +0300
Subject: [PATCH 7/8] fix linker
---
minitorch/datasets.py | 27 ++++++++++++++++++++-------
minitorch/operators.py | 1 +
2 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/minitorch/datasets.py b/minitorch/datasets.py
index b3bd9faa..91df31cd 100644
--- a/minitorch/datasets.py
+++ b/minitorch/datasets.py
@@ -73,13 +73,26 @@ def x(t):
def y(t):
return t * math.sin(t) / 20.0
- X = [(x(10.0 * (float(i) / (N // 2))) + 0.5, y(10.0 * (float(i) / (N //
- 2))) + 0.5) for i in range(5 + 0, 5 + N // 2)]
- X = X + [(y(-10.0 * (float(i) / (N // 2))) + 0.5, x(-10.0 * (float(i) /
- (N // 2))) + 0.5) for i in range(5 + 0, 5 + N // 2)]
- y2 = [0] * (N // 2) + [1] * (N // 2)
+
+ half = N // 2
+ idx_range = range(5, 5 + half)
+
+ t_pos = [10.0 * (float(i) / half) for i in idx_range]
+ t_neg = [-10.0 * (float(i) / half) for i in idx_range]
+
+ X1 = [(x(t) + 0.5, y(t) + 0.5) for t in t_pos]
+ X2 = [(y(t) + 0.5, x(t) + 0.5) for t in t_neg]
+
+ X = X1 + X2
+ y2 = [0] * half + [1] * half
return Graph(N, X, y2)
-datasets = {'Simple': simple, 'Diag': diag, 'Split': split, 'Xor': xor,
- 'Circle': circle, 'Spiral': spiral}
+datasets = {
+ "Simple": simple,
+ "Diag": diag,
+ "Split": split,
+ "Xor": xor,
+ "Circle": circle,
+ "Spiral": spiral,
+}
diff --git a/minitorch/operators.py b/minitorch/operators.py
index efba54d9..89bb04ab 100644
--- a/minitorch/operators.py
+++ b/minitorch/operators.py
@@ -148,6 +148,7 @@ def reduce(fn: Callable[[T, T], T], it: Iterable[T], start: T) -> T:
acc = fn(acc, v)
return acc
+
def negList(ls: Iterable[float]) -> List[float]:
return map(neg, ls)
From eec7535ee18a3da06111e8c776b34461eb699c92 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=92=D0=B0=D0=B4=D0=B8=D0=BC=20=D0=9F=D0=B0=D0=BB=D1=8B?=
=?UTF-8?q?=D1=81=D0=B0=D0=B5=D0=B2?=
Date: Sun, 19 Oct 2025 23:42:02 +0300
Subject: [PATCH 8/8] fix linker
---
minitorch/datasets.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/minitorch/datasets.py b/minitorch/datasets.py
index 91df31cd..0d45fd44 100644
--- a/minitorch/datasets.py
+++ b/minitorch/datasets.py
@@ -73,7 +73,7 @@ def x(t):
def y(t):
return t * math.sin(t) / 20.0
-
+
half = N // 2
idx_range = range(5, 5 + half)