Skip to content

Commit c604323

Browse files
Didannygithub-actions[bot]mikeheddes
authored
Add tests for datasets and coverage report (#130)
* Update readme * Add test for uci benchmark metadata * Add test for all dataset downloads * [github-action] formatting fixes * Add test for report and score * [github-action] formatting fixes * Remove main * [github-action] formatting fixes * Remove parenthesis * Remove try block * Exclude UCIHAR * Add escape characters * Add quotes * Exclude UCIHAR * Excelude CCPP * Excliude collection * [github-action] formatting fixes * Debug dataset downloads * Debug dataset downloads * Debug dataset downloads * [github-action] formatting fixes * Debug dataset downloads * [github-action] formatting fixes * Debug dataset downloads * Debug dataset downloads * [github-action] formatting fixes * Add openpyxl dependency * Fix error in UCIHAR dataset * [github-action] formatting fixes * Add coverage to the dev requirements * Update readme * Fix command in readme * Split dataset download test * Add tests for base class * [github-action] formatting fixes * Add test for resonator * [github-action] formatting fixes * Delete data dir only ones --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Mike Heddes <[email protected]>
1 parent d5c9c5f commit c604323

File tree

12 files changed

+429
-39
lines changed

12 files changed

+429
-39
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@ To create a clean build, remove the `/build` and `/docs/generated` directories.
118118
2. Create a new GitHub release. Set the tag according to [PEP 440](https://peps.python.org/pep-0440/), e.g., v1.5.2, and provide a clear description of the changes. You can use GitHub's "auto-generate release notes" button. Look at previous releases for examples.
119119
3. A GitHub release triggers a GitHub action that builds the library and publishes it to PyPi and Conda in addition to the documentation website.
120120

121+
### Running tests
122+
123+
To run the unit tests located in [`torchhd/tests`](https://github.com/hyperdimensional-computing/torchhd/tree/main/torchhd/tests) do the following:
124+
1. Use `pip install -r dev-requirements.txt` to install the required development packages.
125+
2. Then run the tests using just `pytest`.
126+
127+
Optionally, to measure the code coverage use `coverage run -m --omit="torchhd/tests/**" pytest` to create the coverage report. You can then view this report with `coverage report`.
128+
121129
### License
122130

123131
This library is [MIT licensed](https://github.com/hyperdimensional-computing/torchhd/blob/main/LICENSE).

conda/meta.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ requirements:
2323
- scipy
2424
- pandas
2525
- requests
26+
- openpyxl
2627
- tqdm
2728

2829
test:

dev-requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ numpy
77
flake8
88
pytest
99
black
10-
tqdm
10+
tqdm
11+
openpyxl
12+
coverage

docs/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ scipy
55
requests
66
tqdm
77
numpy
8+
openpyxl
89
sphinx
910
sphinx-rtd-theme

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"numpy",
2626
"requests",
2727
"tqdm",
28+
"openpyxl",
2829
],
2930
packages=find_packages(exclude=["docs", "torchhd.tests", "examples"]),
3031
python_requires=">=3.6, <4",

torchhd/datasets/beijing_air_quality.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,14 +177,14 @@ def _load_data(self):
177177
# Map the wind directions to a category identifier (int)
178178
# # Save the mapping for user referencing
179179
self.wind_directions = tuple(sorted(list(set(data.wind_direction))))
180-
data.loc[:, "wind_direction"] = data.wind_direction.apply(
180+
data.wind_direction = data.wind_direction.apply(
181181
lambda x: self.wind_directions.index(x)
182182
)
183183

184184
# Map the stations to a category identifier (int)
185185
# Save the mapping for user referencing
186186
self.stations = tuple(sorted(list(set(data.station))))
187-
data.loc[:, "station"] = data.station.apply(lambda x: self.stations.index(x))
187+
data.station = data.station.apply(lambda x: self.stations.index(x))
188188

189189
categorical = data[self.categorical_columns]
190190
self.categorical_data = torch.tensor(categorical.values, dtype=torch.long)

torchhd/datasets/ucihar.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#
2424
import os
2525
import os.path as path
26+
import shutil
2627
from typing import Callable, Optional, List
2728
import torch
2829
from torch.utils import data
@@ -132,19 +133,19 @@ def _check_integrity(self) -> bool:
132133
test_dir = os.path.join(self.root, "test")
133134
has_test_dir = os.path.isdir(test_dir)
134135

135-
if not has_train_dir and not has_test_dir:
136+
if (not has_train_dir) or (not has_test_dir):
136137
return False
137138

138139
has_train_x = os.path.isfile(os.path.join(train_dir, "X_train.txt"))
139140
has_train_y = os.path.isfile(os.path.join(train_dir, "y_train.txt"))
140141

141-
if not has_train_x and not has_train_y:
142+
if (not has_train_x) or (not has_train_y):
142143
return False
143144

144145
has_test_x = os.path.isfile(os.path.join(test_dir, "X_test.txt"))
145-
has_test_y = os.path.isfile(os.path.join(train_dir, "y_test.txt"))
146+
has_test_y = os.path.isfile(os.path.join(test_dir, "y_test.txt"))
146147

147-
if not has_test_x or not has_test_y:
148+
if (not has_test_x) or (not has_test_y):
148149
return False
149150

150151
return True
@@ -154,15 +155,11 @@ def _load_data(self):
154155
data_file = "X_train.txt" if self.train else "X_test.txt"
155156
target_file = "y_train.txt" if self.train else "y_test.txt"
156157

157-
data = pd.read_csv(
158-
os.path.join(data_dir, data_file), delim_whitespace=True, header=None
159-
)
160-
targets = np.loadtxt(
161-
path.join(data_dir, target_file), delimiter="\n", dtype="int64"
162-
).tolist()
158+
data = np.loadtxt(os.path.join(data_dir, data_file), dtype="float32")
159+
targets = np.loadtxt(path.join(data_dir, target_file), dtype="int64")
163160

164-
self.data = torch.tensor(data.values, dtype=torch.float)
165-
self.targets = torch.tensor(targets, dtype=torch.long) - 1
161+
self.data = torch.from_numpy(data)
162+
self.targets = torch.from_numpy(targets) - 1
166163

167164
def download(self):
168165
"""Downloads the dataset if it doesn't exist already"""
@@ -183,8 +180,8 @@ def download(self):
183180
source_dir = os.path.join(self.root, "UCI HAR Dataset")
184181
data_files = os.listdir(source_dir)
185182
for filename in data_files:
186-
os.rename(
187-
os.path.join(source_dir, filename), os.path.join(self.root, filename)
188-
)
183+
src = os.path.join(source_dir, filename)
184+
dest = os.path.join(self.root, filename)
185+
os.rename(src, dest)
189186

190187
os.rmdir(source_dir)

torchhd/embeddings.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,10 @@ def __init__(
956956
out_features + 1, out_features, vsa, low=low, high=high, **factory_kwargs
957957
)
958958

959+
def reset_parameters(self) -> None:
960+
self.key.reset_parameters()
961+
self.density_encoding.reset_parameters()
962+
959963
# Specify the steps needed to perform the encoding
960964
def forward(self, input: Tensor) -> Tensor:
961965
# Perform binding of key and value vectors
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#
2+
# MIT License
3+
#
4+
# Copyright (c) 2023 Mike Heddes, Igor Nunes, Pere Vergés, Denis Kleyko, and Danny Abraham
5+
#
6+
# Permission is hereby granted, free of charge, to any person obtaining a copy
7+
# of this software and associated documentation files (the "Software"), to deal
8+
# in the Software without restriction, including without limitation the rights
9+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
# copies of the Software, and to permit persons to whom the Software is
11+
# furnished to do so, subject to the following conditions:
12+
#
13+
# The above copyright notice and this permission notice shall be included in all
14+
# copies or substantial portions of the Software.
15+
#
16+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
# SOFTWARE.
23+
#
24+
import pytest
25+
import torch
26+
27+
from torchhd import VSATensor
28+
29+
30+
class TestVSATensor:
31+
def test_empty(self):
32+
with pytest.raises(NotImplementedError):
33+
VSATensor.empty(4, 525)
34+
35+
def test_identity(self):
36+
with pytest.raises(NotImplementedError):
37+
VSATensor.identity(4, 525)
38+
39+
def test_random(self):
40+
with pytest.raises(NotImplementedError):
41+
VSATensor.random(4, 525)
42+
43+
def test_bundle(self):
44+
a = torch.randn(100).as_subclass(VSATensor)
45+
b = torch.randn(100).as_subclass(VSATensor)
46+
47+
with pytest.raises(NotImplementedError):
48+
a.bundle(b)
49+
50+
def test_multibundle(self):
51+
a = torch.randn(10, 100).as_subclass(VSATensor)
52+
53+
with pytest.raises(NotImplementedError):
54+
a.multibundle()
55+
56+
def test_bind(self):
57+
a = torch.randn(100).as_subclass(VSATensor)
58+
b = torch.randn(100).as_subclass(VSATensor)
59+
60+
with pytest.raises(NotImplementedError):
61+
a.bind(b)
62+
63+
def test_multibind(self):
64+
a = torch.randn(10, 100).as_subclass(VSATensor)
65+
66+
with pytest.raises(NotImplementedError):
67+
a.multibind()
68+
69+
def test_inverse(self):
70+
a = torch.randn(100).as_subclass(VSATensor)
71+
72+
with pytest.raises(NotImplementedError):
73+
a.inverse()
74+
75+
def test_negative(self):
76+
a = torch.randn(100).as_subclass(VSATensor)
77+
78+
with pytest.raises(NotImplementedError):
79+
a.negative()
80+
81+
def test_permute(self):
82+
a = torch.randn(100).as_subclass(VSATensor)
83+
84+
with pytest.raises(NotImplementedError):
85+
a.permute()
86+
87+
def test_dot_similarity(self):
88+
a = torch.randn(100).as_subclass(VSATensor)
89+
b = torch.randn(100).as_subclass(VSATensor)
90+
91+
with pytest.raises(NotImplementedError):
92+
a.dot_similarity(b)
93+
94+
def test_cosine_similarity(self):
95+
a = torch.randn(100).as_subclass(VSATensor)
96+
b = torch.randn(100).as_subclass(VSATensor)
97+
98+
with pytest.raises(NotImplementedError):
99+
a.cosine_similarity(b)

0 commit comments

Comments
 (0)