Skip to content

Commit f4f1e2a

Browse files
authored
Merge pull request #88 from discovery-unicamp/merge-from-dev
Merge from development - 12/04/2025
2 parents 4fc2360 + 23181e3 commit f4f1e2a

File tree

94 files changed

+4920
-1123
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+4920
-1123
lines changed

.github/workflows/continuous-testing.yml

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,26 @@ on:
44
push:
55
branches: ["main"]
66
paths-ignore:
7-
- "**/README.md"
8-
- "**/CONTRIBUTING.md"
9-
- "**/CODE_OF_CONDUCT.md"
10-
- "**/SECURITY.md"
11-
- "**/.github/ISSUE_TEMPLATE/*"
12-
- "**/.github/PULL_REQUEST_TEMPLATE/*"
13-
- "**/LICENSE"
7+
- '**/README.md'
8+
- '**/CONTRIBUTING.md'
9+
- '**/CODE_OF_CONDUCT.md'
10+
- '**/SECURITY.md'
11+
- '**/.github/ISSUE_TEMPLATE/*'
12+
- '**/.github/PULL_REQUEST_TEMPLATE/*'
13+
- '**/LICENSE'
1414
pull_request:
1515
branches: ["main"]
1616
paths-ignore:
17-
- "**/README.md"
18-
- "**/CONTRIBUTING.md"
19-
- "**/CODE_OF_CONDUCT.md"
20-
- "**/SECURITY.md"
21-
- "**/.github/ISSUE_TEMPLATE/*"
22-
- "**/.github/PULL_REQUEST_TEMPLATE/*"
23-
- "**/LICENSE"
17+
- '**/README.md'
18+
- '**/CONTRIBUTING.md'
19+
- '**/CODE_OF_CONDUCT.md'
20+
- '**/SECURITY.md'
21+
- '**/.github/ISSUE_TEMPLATE/*'
22+
- '**/.github/PULL_REQUEST_TEMPLATE/*'
23+
- '**/LICENSE'
2424

2525
permissions:
26-
contents: read # No write permission needed
26+
contents: read # No write permission needed
2727

2828
jobs:
2929
continuous-testing:
@@ -37,7 +37,7 @@ jobs:
3737
uses: actions/setup-python@v5
3838
with:
3939
python-version: "3.10"
40-
cache: "pip" # Cache pip packages to improve workflow runtime
40+
cache: "pip" # Cache pip packages to improve workflow runtime
4141

4242
- name: Install Dependencies
4343
run: |
@@ -56,18 +56,19 @@ jobs:
5656
uses: actions/upload-artifact@v4
5757
with:
5858
name: html-coverage-report
59-
path: htmlcov/ # Upload the generated HTML report directory
60-
59+
path: htmlcov/ # Upload the generated HTML report directory
60+
6161
- name: Creating interrogate folder
6262
run: |
6363
mkdir interrogate
6464
65+
6566
- name: Docstring Coverage
6667
run: interrogate minerva -vv --fail-under=80 --generate-badge interrogate/interrogate_badge.svg --badge-format svg -o interrogate/simple-report.md
6768
continue-on-error: true
68-
69+
6970
- name: Upload Docstring Coverage Report
7071
uses: actions/upload-artifact@v4
7172
with:
7273
name: docstring-coverage-files
73-
path: interrogate/ # Upload the generated docstring coverage report
74+
path: interrogate/ # Upload the generated docstring coverage report
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Black Format Check
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
paths:
7+
- '**.py'
8+
9+
pull_request:
10+
branches: ["main"]
11+
paths:
12+
- '**.py'
13+
14+
jobs:
15+
black-check:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v3
20+
21+
- name: Set up Python
22+
uses: actions/setup-python@v4
23+
with:
24+
python-version: '3.10'
25+
26+
- name: Install dependencies
27+
run: |
28+
pip install black nbqa
29+
30+
- name: Check Python file formatting
31+
run: |
32+
echo "🔍 Checking Python files..."
33+
black --check --diff . || (
34+
echo "❌ Black found formatting issues in Python files." && exit 1
35+
)

minerva/analysis/complexity_performance_analysis.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ class ComplexityPerformanceAnalysis(_ModelAnalysis):
1717
If necessary, random data can be employed.
1818
"""
1919

20-
def __init__(self, path: Optional[PathLike] = None, custom_input_size: Optional[Tuple] = None):
20+
def __init__(
21+
self, path: Optional[PathLike] = None, custom_input_size: Optional[Tuple] = None
22+
):
2123
super().__init__()
2224
self._path = path
2325
self._custom_input_size = custom_input_size
@@ -41,7 +43,9 @@ def compute(self, model: L.LightningModule, data: L.LightningDataModule):
4143
evaluation_data = torch.tensor(evaluation_data, dtype=torch.float32)
4244
# Computing MACs, parameters, and energy consumption
4345
macs, params = profile(model, inputs=(evaluation_data,))
44-
carbonTracker = EmissionsTracker(project_name="basic_measurement", measure_power_secs=10, save_to_file=False)
46+
carbonTracker = EmissionsTracker(
47+
project_name="basic_measurement", measure_power_secs=10, save_to_file=False
48+
)
4549
try:
4650
carbonTracker.start_task("measure_inference")
4751
_ = model(evaluation_data)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from .pixel_accuracy import PixelAccuracy
22
from .balanced_accuracy import BalancedAccuracy
33

4-
__all__ = ["PixelAccuracy","BalancedAccuracy"]
4+
__all__ = ["PixelAccuracy", "BalancedAccuracy"]

minerva/analysis/metrics/balanced_accuracy.py

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,54 @@
44
from torchmetrics.functional import confusion_matrix
55
import warnings
66

7+
78
class BalancedAccuracy(Metric):
8-
def __init__(
9-
self,
10-
num_classes: int,
11-
task: str,
12-
adjusted:
13-
bool = False
14-
):
9+
def __init__(self, num_classes: int, task: str, adjusted: bool = False):
1510
"""
16-
Compute the balanced accuracy.
11+
Compute the balanced accuracy.
12+
13+
The balanced accuracy in binary, multiclass, and multilabel classification problems
14+
deals with imbalanced datasets. It is defined as the average of recall obtained on each class.
15+
16+
Parameters
17+
----------
18+
num_classes : int
19+
The number of classes in the target data.
1720
18-
The balanced accuracy in binary, multiclass, and multilabel classification problems
19-
deals with imbalanced datasets. It is defined as the average of recall obtained on each class.
21+
task : str
22+
The type of classification task, should be one of 'binary' or 'multiclass'
2023
21-
Parameters
22-
----------
23-
num_classes : int
24-
The number of classes in the target data.
25-
26-
task : str
27-
The type of classification task, should be one of 'binary' or 'multiclass'
28-
29-
adjusted : bool, optional (default=False)
30-
When true, the result is adjusted for chance, so that random performance would score 0,
31-
while keeping perfect performance at a score of 1.
24+
adjusted : bool, optional (default=False)
25+
When true, the result is adjusted for chance, so that random performance would score 0,
26+
while keeping perfect performance at a score of 1.
3227
33-
Attributes
34-
----------
35-
confmat : torch.Tensor
36-
Confusion matrix to keep track of true positives, false positives, true negatives, and false negatives.
28+
Attributes
29+
----------
30+
confmat : torch.Tensor
31+
Confusion matrix to keep track of true positives, false positives, true negatives, and false negatives.
3732
38-
Examples
39-
--------
40-
>>> y_true = torch.tensor([0, 1, 0, 0, 1, 0])
41-
>>> y_pred = torch.tensor([0, 1, 0, 0, 0, 1])
42-
>>> metric = BalancedAccuracy(num_classes=2, task='binary')
43-
>>> metric(y_pred, y_true)
44-
0.625
33+
Examples
34+
--------
35+
>>> y_true = torch.tensor([0, 1, 0, 0, 1, 0])
36+
>>> y_pred = torch.tensor([0, 1, 0, 0, 0, 1])
37+
>>> metric = BalancedAccuracy(num_classes=2, task='binary')
38+
>>> metric(y_pred, y_true)
39+
0.625
4540
"""
4641
super().__init__()
4742
self.num_classes = num_classes
4843
self.adjusted = adjusted
4944
self.task = task
50-
self.add_state("confmat", default=torch.zeros((num_classes, num_classes)), dist_reduce_fx="sum")
45+
self.add_state(
46+
"confmat",
47+
default=torch.zeros((num_classes, num_classes)),
48+
dist_reduce_fx="sum",
49+
)
5150

5251
def update(self, preds: torch.Tensor, target: torch.Tensor):
53-
self.confmat += confusion_matrix(preds, target, num_classes=self.num_classes, task=self.task)
52+
self.confmat += confusion_matrix(
53+
preds, target, num_classes=self.num_classes, task=self.task
54+
)
5455

5556
def compute(self):
5657
with torch.no_grad():

minerva/callback/embedding_logger_callback.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@
77
class EmbeddingLoggerCallback(Callback):
88

99
def __init__(
10-
self,
11-
data_X: Tensor,
12-
logger: CSVLogger,
13-
data_Y: Tensor = None,
14-
feature_preffix: str = 'EMB-',
15-
backbone_names_list: List[str] = ['backbone', 'encoder'],
16-
) -> None:
17-
'''
10+
self,
11+
data_X: Tensor,
12+
logger: CSVLogger,
13+
data_Y: Tensor = None,
14+
feature_preffix: str = "EMB-",
15+
backbone_names_list: List[str] = ["backbone", "encoder"],
16+
) -> None:
17+
"""
1818
Callback to extract and log embeddings from some data using the model's backbone.
19-
19+
2020
Parameters
2121
----------
2222
data_X : torch.Tensor
@@ -29,29 +29,34 @@ def __init__(
2929
The preffix to use for the feature names, by default 'EMB-'.
3030
backbone_names_list : List[str], optional
3131
List with the names of the backbones in the model, by default ['backbone', 'encoder'].
32-
'''
32+
"""
3333
super().__init__()
3434
self.data_X = data_X
3535
self.data_Y = data_Y
3636
self.logger = logger
3737
self.feature_preffix = feature_preffix
3838
self.backbone_names_list = backbone_names_list
39-
39+
4040
def on_train_start(self, trainer: Trainer, pl_module: LightningModule) -> None:
41-
filtered_names_list = [name for name in self.backbone_names_list if hasattr(pl_module, name)]
41+
filtered_names_list = [
42+
name for name in self.backbone_names_list if hasattr(pl_module, name)
43+
]
4244
if len(filtered_names_list) == 0:
43-
raise ValueError('No backbone found in the model')
45+
raise ValueError("No backbone found in the model")
4446
self.backbone = getattr(pl_module, filtered_names_list[0])
45-
47+
4648
def on_train_epoch_end(self, trainer: Trainer, pl_module: LightningModule) -> None:
4749
# Obtaining the embeddings
4850
self.backbone.eval()
4951
embeddings = self.backbone(self.data_X).detach().cpu().numpy()
5052
self.backbone.train()
5153
# Logging the embeddings
5254
for row_index, row in enumerate(embeddings):
53-
data_dict = {f'{self.feature_preffix}{str(index).zfill(3)}': value for index, value in enumerate(row)}
54-
data_dict['epoch'] = trainer.current_epoch
55+
data_dict = {
56+
f"{self.feature_preffix}{str(index).zfill(3)}": value
57+
for index, value in enumerate(row)
58+
}
59+
data_dict["epoch"] = trainer.current_epoch
5560
if self.data_Y is not None:
56-
data_dict['y'] = self.data_Y[row_index].item()
57-
self.logger.log_metrics(data_dict)
61+
data_dict["y"] = self.data_Y[row_index].item()
62+
self.logger.log_metrics(data_dict)

minerva/data/data_module_tools.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ def __init__(
6363
else:
6464
delattr(self, "test_dataloader")
6565

66-
6766
def _generate_data(self, num_samples, data_shape, label_shape, num_classes):
6867
data = torch.rand((num_samples, *data_shape), dtype=self.data_dtype)
6968
label = None
@@ -73,9 +72,9 @@ def _generate_data(self, num_samples, data_shape, label_shape, num_classes):
7372
label = torch.rand((num_samples, *label_shape))
7473
elif num_classes is not None:
7574
label = torch.randint(0, num_classes, (num_samples,))
76-
75+
7776
label = label.to(dtype=self.label_dtype)
78-
77+
7978
return data, label
8079

8180
def setup(self, stage):

minerva/data/data_modules/base.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class when creating the dataloaders, by default DataLoader
6666
Name of the data module, by default ""
6767
"""
6868
super().__init__()
69-
69+
7070
self._name = name
7171
self._train_dataset = train_dataset
7272
self._val_dataset = val_dataset
@@ -124,11 +124,11 @@ class when creating the dataloaders, by default DataLoader
124124
# Monkey patch the dataloaders if the datasets are not provided
125125
# It is applyed at instance level to avoid breaking the class signature
126126
if not self._train_dataset:
127-
self.train_dataloader = None # type: ignore
127+
self.train_dataloader = None # type: ignore
128128
if not self._val_dataset:
129-
self.val_dataloader = None # type: ignore
129+
self.val_dataloader = None # type: ignore
130130
if not self._test_dataset:
131-
self.test_dataloader = None # type: ignore
131+
self.test_dataloader = None # type: ignore
132132
if not self._predict_dataset:
133133
self.predict_dataloader = None # type: ignore
134134

@@ -168,19 +168,13 @@ def predict_dataset(self):
168168
return self._predict_dataset
169169

170170
def train_dataloader(self):
171-
return self._dataloader_cls(
172-
self.train_dataset, **self._train_dataloader_kwargs
173-
)
171+
return self._dataloader_cls(self.train_dataset, **self._train_dataloader_kwargs)
174172

175173
def val_dataloader(self):
176-
return self._dataloader_cls(
177-
self.val_dataset, **self._val_dataloader_kwargs
178-
)
174+
return self._dataloader_cls(self.val_dataset, **self._val_dataloader_kwargs)
179175

180176
def test_dataloader(self):
181-
return self._dataloader_cls(
182-
self.test_dataset, **self._test_dataloader_kwargs
183-
)
177+
return self._dataloader_cls(self.test_dataset, **self._test_dataloader_kwargs)
184178

185179
def predict_dataloader(self):
186180
return self._dataloader_cls(
@@ -194,7 +188,12 @@ def indent_text(text, spaces=6, add_line_breaks=True):
194188
return "No data."
195189

196190
return "\n".join(
197-
(" " * int(spaces//2) + "│" if add_line_breaks else " " * int(spaces//2) + " ") + (" " * spaces + line if line.strip() else line)
191+
(
192+
" " * int(spaces // 2) + "│"
193+
if add_line_breaks
194+
else " " * int(spaces // 2) + " "
195+
)
196+
+ (" " * spaces + line if line.strip() else line)
198197
for line in text.split("\n")
199198
)
200199

minerva/data/datasets/base.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,9 @@ def __getitem__(self, idx: int) -> Union[Any, Tuple[Any, ...]]:
171171

172172
def __str__(self) -> str:
173173
readers = self.readers if isinstance(self.readers, list) else [self.readers]
174-
transforms = self.transforms if isinstance(self.transforms, list) else [self.transforms]
174+
transforms = (
175+
self.transforms if isinstance(self.transforms, list) else [self.transforms]
176+
)
175177

176178
readers_info = "\n".join(
177179
[
@@ -192,4 +194,4 @@ def __str__(self) -> str:
192194
)
193195

194196
def __repr__(self) -> str:
195-
return self.__str__()
197+
return self.__str__()

0 commit comments

Comments
 (0)