Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Project knowledge
- **Tech Stack:** Python3
- **File Structure:**
- `src/common` – Application source code (autograd engine in here)
- `src/tests/` – All tests in here

## Commands you can use
Run tests: `pytest src/tests/differential_test.py`

## Documentation practices
Don't make PR's to big, be smart and prioritize readability over efficiency.
Write so that a new developer to this codebase can understand your writing, don’t assume your audience are experts in the topic/area you are writing about.

## Boundaries
- ✅ **Always do:** Make sure you are actually using the `autograd.py` and `Module.py` files in the implementation of any models.
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,9 @@ disallow_untyped_defs = true
explicit_package_bases = true
namespace_packages = true
disallow_untyped_calls = false
disable_error_code = ["import-untyped"]
disable_error_code = ["import-untyped"]
mypy_path = ["src"]

[[tool.mypy.overrides]]
module = ["cupy"]
ignore_missing_imports = true
6 changes: 3 additions & 3 deletions src/common/MLP.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from common.Module import Module


class Neuron(Module): # type: ignore[misc]
class Neuron(Module):
def __init__(self, nin: int, nonlin: bool = True) -> None:
super().__init__()
self.w = [Value(random.uniform(-1, 1)) for _ in range(nin)]
Expand All @@ -23,7 +23,7 @@ def __repr__(self) -> str:
return f"{'ReLU' if self.nonlin else 'Linear'}Neuron({len(self.w)})"


class Layer(Module): # type: ignore[misc]
class Layer(Module):
def __init__(self, nin: int, nout: int, **kwargs: Any) -> None:
super().__init__()
self.neurons = [Neuron(nin, **kwargs) for _ in range(nout)]
Expand All @@ -39,7 +39,7 @@ def __repr__(self) -> str:
return f"Layer of [{', '.join(str(n) for n in self.neurons)}]"


class MLP(Module): # type: ignore[misc]
class MLP(Module):
def __init__(self, nin: int, nouts: list[int]) -> None:
super().__init__()
sz = [nin] + nouts
Expand Down
17 changes: 10 additions & 7 deletions src/common/Module.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from abc import ABC, abstractmethod
from typing import Any, Generator
from collections.abc import Iterable
from typing import Any

from common.autograd import Value

Expand Down Expand Up @@ -34,17 +35,17 @@ def forward(self, x):
must be made before assignment on the child."""

def __init__(self) -> None:
self.params: dict[
str, Value | "Module"
] = {} # real param store: name -> Value or Module
self.params: dict[str, Value | "Module"] = (
{}
) # real param store: name -> Value or Module

@abstractmethod
def forward(self, *args: Any, **kwargs: Any) -> Value:
def forward(self, *args: Any, **kwargs: Any) -> Any:
"""Compute the forward pass for this module."""

raise NotImplementedError

def __call__(self, *args: Any, **kwargs: Any) -> Value:
def __call__(self, *args: Any, **kwargs: Any) -> Any:
# will call self.forward!
return self.forward(*args, **kwargs)

Expand All @@ -67,8 +68,10 @@ def __setattr__(self, key: str, value: Any) -> None:
# registry in sync with the current state of the object.
if isinstance(value, (Value, Module)):
self.params[key] = value
elif key in self.params:
del self.params[key]

def parameters(self) -> Generator[Value, None, None]:
def parameters(self) -> Iterable[Value]:
# recursively collects parameters and yields them (use a Generator)
for key, param in self.params.items():
if isinstance(param, Module):
Expand Down
26 changes: 12 additions & 14 deletions src/common/autograd.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
from typing import Union
from __future__ import annotations

try:
import cupy as np
except ImportError:
print("CUDA not available, defaulting to numpy")
import numpy as np

Scalar = float | int


class Value:
"""stores value's and its gradient's"""

def __init__(
self,
data: float | np.ndarray,
data: Scalar | np.ndarray,
_children: tuple["Value", ...] = (),
_op: str = "",
) -> None:
Expand Down Expand Up @@ -65,7 +67,7 @@ def sum_to_shape(
result = result.sum(axis=i, keepdims=True)
return result

def __add__(self, other: "Value") -> "Value":
def __add__(self, other: Value | Scalar) -> Value:
other = other if isinstance(other, Value) else Value(other)
out = Value(self.data + other.data, (self, other), "+")

Expand All @@ -86,11 +88,7 @@ def _backward() -> None:

return out

def __mul__(
self, other: Union["Value", int]
) -> (
"Value"
): # some weird python black thing, so we made it use the union from typing instead(cuz it thinks its a string)
def __mul__(self, other: Value | Scalar) -> Value:
other = other if isinstance(other, Value) else Value(other)
out = Value(self.data * other.data, (self, other), "*")

Expand Down Expand Up @@ -281,22 +279,22 @@ def build_topo(v: "Value") -> None:
def __neg__(self) -> "Value": # -self
return self * -1

def __radd__(self, other: "Value") -> "Value": # other + self
def __radd__(self, other: Value | Scalar) -> Value: # other + self
return self + other

def __sub__(self, other: "Value") -> "Value": # self - other
def __sub__(self, other: Value | Scalar) -> Value: # self - other
return self + (-other)

def __rsub__(self, other: "Value") -> "Value": # other - self
def __rsub__(self, other: Value | Scalar) -> Value: # other - self
return other + (-self)

def __rmul__(self, other: "Value") -> "Value": # other * self
def __rmul__(self, other: Value | Scalar) -> Value: # other * self
return self * other

def __truediv__(self, other: "Value") -> "Value": # self / other
def __truediv__(self, other: Value | Scalar) -> Value: # self / other
return self * other**-1

def __rtruediv__(self, other: "Value") -> "Value": # other / self
def __rtruediv__(self, other: Value | Scalar) -> Value: # other / self
return other * self**-1

def __repr__(self) -> "str":
Expand Down
Loading