Skip to content
Open
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
24 changes: 17 additions & 7 deletions minitorch/datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,29 @@ def circle(N):


def spiral(N):

def x(t):
return t * math.cos(t) / 20.0

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)]

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)
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,
}
21 changes: 17 additions & 4 deletions minitorch/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,16 @@ 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 m in self._modules.values():
m.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 m in self._modules.values():
m.eval()

def named_parameters(self) -> Sequence[Tuple[str, Parameter]]:
"""Collect all the parameters of this module and its descendents.
Expand All @@ -48,12 +52,21 @@ def named_parameters(self) -> Sequence[Tuple[str, Parameter]]:

"""
# TODO: Implement for Task 0.4.
raise NotImplementedError("Need to implement for Task 0.4")
out = []
for n, p in self._parameters.items():
out.append((n, p))
for n, p in self._modules.items():
for cn, cp in p.named_parameters():
out.append((f"{n}.{cn}", cp))
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")
out = []
for _, p in self.named_parameters():
out.append(p)
return out

def add_parameter(self, k: str, v: Any) -> Parameter:
"""Manually add a parameter. Useful helper for scalar parameters.
Expand Down
142 changes: 142 additions & 0 deletions minitorch/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,90 @@


# TODO: Implement for Task 0.1.
def mul(x: float, y: float) -> float:
"""Multiplies two numbers"""
return x * y


def id(x: float) -> float:
"""Returns the input unchanged"""
return x


def add(x: float, y: float) -> float:
"""Adds two numbers"""
return x + y


def neg(x: float) -> float:
"""Negates a number"""
return -x


def lt(x: float, y: float) -> bool:
"""Checks if one number is less than another"""
return x < y


def eq(x: float, y: float) -> bool:
"""Checks if two numbers are equal"""
return x == y


def max(x: float, y: float) -> float:
"""Returns the larger of two numbers"""
if x >= y:
return x
else:
return y


def is_close(x: float, y: float) -> bool:
"""Checks if two numbers are close in value"""
return abs(x - y) < 0.01


def sigmoid(x: float) -> float:
"""Calculates the sigmoid function"""
return 1 / (1 + math.exp(-x))


def relu(x: float) -> float:
"""Applies the ReLU activation function"""
return max(0, x)


def log(x: float) -> float:
"""Calculates the natural logarithm"""
return math.log(x)


def exp(x: float) -> float:
"""Calculates the exponential function"""
return math.exp(x)


def inv(x: float) -> float:
"""Calculates the reciprocal"""
return 1 / x


def log_back(x: float, y: float) -> float:
"""Computes the derivative of log times a second arg"""
return y / x


def inv_back(x: float, y: float) -> float:
"""Computes the derivative of reciprocal times a second arg"""
return -y / x**2


def relu_back(x: float, y: float) -> float:
"""Computes the derivative of ReLU times a second arg"""
if x > 0:
return y
else:
return 0


# ## Task 0.3
Expand All @@ -52,3 +136,61 @@


# TODO: Implement for Task 0.3.
def map(fn: Callable[[float], float]) -> Callable[[Iterable[float]], list[float]]:
"""Higher-order function that applies a given function to each element of an iterable"""

def apply(xx: Iterable[float]) -> list[float]:
cur = []
for x in xx:
cur.append(fn(x))
return cur

return apply


def zipWith(
fn: Callable[[float, float], float],
) -> Callable[[Iterable[float], Iterable[float]], list[float]]:
"""Higher-order function that combines elements from two iterables using a given function"""

def apply(xx: Iterable[float], yy: Iterable[float]) -> list[float]:
cur = []
for x, y in zip(xx, yy):
cur.append(fn(x, y))
return cur

return apply


def reduce(
fn: Callable[[float, float], float], s: float
) -> Callable[[Iterable[float]], float]:
"""Higher-order function that reduces an iterable to a single value using a given function"""

def apply(xx: Iterable[float]) -> float:
out = s
for x in xx:
out = fn(out, x)
return out

return apply


def negList(xx: Iterable[float]) -> list[float]:
"""Negate all elements in a list using map"""
return map(neg)(xx)


def addLists(xx: Iterable[float], yy: Iterable[float]) -> list[float]:
"""Add corresponding elements from two lists using zipWith"""
return zipWith(add)(xx, yy)


def sum(xx: Iterable[float]) -> float:
"""Sum all elements in a list using reduce"""
return reduce(add, 0.0)(xx)


def prod(xx: Iterable[float]) -> float:
"""Calculate the product of all elements in a list using reduce"""
return reduce(mul, 1.0)(xx)
4 changes: 2 additions & 2 deletions requirements.extra.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
altair==4.2.2
datasets==2.4.0
embeddings==0.0.8
networkx==3.3
plotly==4.14.3
pydot==1.4.1
python-mnist
streamlit==1.12.0
streamlit-ace
torch
watchdog==1.0.2
altair==4.2.2
networkx==3.3
32 changes: 21 additions & 11 deletions tests/test_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,49 +99,59 @@ def test_eq(a: float) -> None:


@pytest.mark.task0_2
@given(small_floats)
def test_sigmoid(a: float) -> None:
@given(small_floats, small_floats)
def test_sigmoid(a: float, b: float) -> None:
"""Check properties of the sigmoid function, specifically
* It is always between 0.0 and 1.0.
* one minus sigmoid is the same as sigmoid of the negative
* 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")
assert sigmoid(a) <= 1.0
assert sigmoid(a) >= 0.0
assert_close(1 - sigmoid(a), sigmoid(-a))
assert sigmoid(0.0) == 0.5
if a + 1e-6 < b:
assert sigmoid(a) < sigmoid(b)


@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) and lt(b, c):
assert lt(a, c)


@pytest.mark.task0_2
def test_symmetric() -> None:
@given(small_floats, small_floats)
def test_symmetric(a: float, b: 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 mul(a, b) == mul(b, a)


@pytest.mark.task0_2
def test_distribute() -> None:
@given(small_floats, small_floats, small_floats)
def test_distribute(a: float, b: float, c: 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")
assert_close(mul(a, add(b, c)), add(mul(a, b), mul(a, c)))


@pytest.mark.task0_2
def test_other() -> None:
@given(small_floats, small_floats, small_floats)
def test_other(a: float, b: float, c: 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")
if a > 1e-6 or a < -1e-6:
assert_close(inv_back(a, b + c), inv_back(a, b) + inv_back(a, c))


# ## Task 0.3 - Higher-order functions
Expand Down Expand Up @@ -169,7 +179,7 @@ def test_sum_distribute(ls1: List[float], ls2: List[float]) -> None:
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")
assert_close(sum(ls1 + ls2), sum(ls1) + sum(ls2))


@pytest.mark.task0_3
Expand Down