mirror of
https://github.com/macaodha/batdetect2.git
synced 2026-08-22 03:00:10 +02:00
Compare commits
No commits in common. "586e78814f0ca62f29f987773f4a4d8efef636ad" and "1bab80f8c6cd880adfc306d4632e582ddfe45653" have entirely different histories.
586e78814f
...
1bab80f8c6
@ -15,9 +15,6 @@ Defined in `batdetect2.api_v2`.
|
|||||||
- `BatDetect2API.from_config(model_config=..., targets_config=..., ...)`
|
- `BatDetect2API.from_config(model_config=..., targets_config=..., ...)`
|
||||||
- build a full model stack from config objects.
|
- build a full model stack from config objects.
|
||||||
|
|
||||||
Both constructors accept `compile_model=True` to compile the detector after the
|
|
||||||
API is built.
|
|
||||||
|
|
||||||
## Common tasks
|
## Common tasks
|
||||||
|
|
||||||
- Load a checkpoint and run prediction on one file.
|
- Load a checkpoint and run prediction on one file.
|
||||||
@ -25,8 +22,6 @@ API is built.
|
|||||||
- Save predictions in one of the supported output formats.
|
- Save predictions in one of the supported output formats.
|
||||||
- Evaluate a model on labelled data.
|
- Evaluate a model on labelled data.
|
||||||
- Fine-tune an existing checkpoint on new targets.
|
- Fine-tune an existing checkpoint on new targets.
|
||||||
- Compile the detector explicitly with `BatDetect2API.compile()` when you want
|
|
||||||
to opt into PyTorch runtime compilation from Python.
|
|
||||||
|
|
||||||
## Generated reference
|
## Generated reference
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,6 @@ Defined in `batdetect2.inference.config`.
|
|||||||
|
|
||||||
## Top-level fields
|
## Top-level fields
|
||||||
|
|
||||||
- `compile_model`
|
|
||||||
- compile the detector before batch prediction. This is off by default.
|
|
||||||
- `loader`
|
- `loader`
|
||||||
- data-loader settings for inference.
|
- data-loader settings for inference.
|
||||||
- `clipping`
|
- `clipping`
|
||||||
@ -36,19 +34,8 @@ Override `InferenceConfig` when:
|
|||||||
|
|
||||||
- long recordings need different clipping behavior,
|
- long recordings need different clipping behavior,
|
||||||
- you want to tune batch size for your hardware,
|
- you want to tune batch size for your hardware,
|
||||||
- you want to opt into runtime model compilation for repeated predictions,
|
|
||||||
- you need reproducible prediction settings across runs.
|
- you need reproducible prediction settings across runs.
|
||||||
|
|
||||||
## Runtime compilation
|
|
||||||
|
|
||||||
Set `compile_model: true` to compile the detector before batch inference. This
|
|
||||||
can help when you run repeated predictions with stable input shapes. For a
|
|
||||||
single short run, the compile step can cost more time than it saves.
|
|
||||||
|
|
||||||
In Python, you can also compile explicitly with `BatDetect2API.compile()` or by
|
|
||||||
passing `compile_model=True` to `BatDetect2API.from_checkpoint(...)` or
|
|
||||||
`BatDetect2API.from_config(...)`.
|
|
||||||
|
|
||||||
## Related pages
|
## Related pages
|
||||||
|
|
||||||
- Tune inference clipping:
|
- Tune inference clipping:
|
||||||
|
|||||||
@ -7,8 +7,6 @@ Defined in `batdetect2.train.config`.
|
|||||||
|
|
||||||
## Top-level fields
|
## Top-level fields
|
||||||
|
|
||||||
- `compile_model`
|
|
||||||
- compile the detector before training starts. This is off by default.
|
|
||||||
- `train_loader`
|
- `train_loader`
|
||||||
- training data loading and clipping settings.
|
- training data loading and clipping settings.
|
||||||
- `val_loader`
|
- `val_loader`
|
||||||
@ -35,18 +33,10 @@ Use `TrainingConfig` when you want to change things like:
|
|||||||
- batch size,
|
- batch size,
|
||||||
- augmentation,
|
- augmentation,
|
||||||
- optimiser and scheduler settings,
|
- optimiser and scheduler settings,
|
||||||
- runtime options such as model compilation,
|
|
||||||
- number of epochs,
|
- number of epochs,
|
||||||
- validation frequency,
|
- validation frequency,
|
||||||
- checkpoint behaviour.
|
- checkpoint behaviour.
|
||||||
|
|
||||||
## Runtime options
|
|
||||||
|
|
||||||
Use `compile_model: true` to call `torch.compile` on the detector used during
|
|
||||||
training. This can help on longer runs with stable tensor shapes, but it may be
|
|
||||||
slower for short CPU-only experiments because PyTorch has to compile the graph
|
|
||||||
before it can reuse it.
|
|
||||||
|
|
||||||
Example files live under `example_data/configs/`, including
|
Example files live under `example_data/configs/`, including
|
||||||
`example_data/configs/training.yaml`.
|
`example_data/configs/training.yaml`.
|
||||||
|
|
||||||
|
|||||||
2
justfile
2
justfile
@ -136,7 +136,7 @@ clean: clean-build clean-pyc clean-test clean-docs
|
|||||||
|
|
||||||
# Train on example data.
|
# Train on example data.
|
||||||
example-train OPTIONS="":
|
example-train OPTIONS="":
|
||||||
uv run batdetect2 -v train \
|
uv run batdetect2 train \
|
||||||
--val-dataset example_data/dataset.yaml \
|
--val-dataset example_data/dataset.yaml \
|
||||||
--base-dir . \
|
--base-dir . \
|
||||||
--targets example_data/targets.yaml \
|
--targets example_data/targets.yaml \
|
||||||
|
|||||||
@ -25,8 +25,8 @@ dependencies = [
|
|||||||
"soundevent[audio,geometry,plot]>=2.10.0",
|
"soundevent[audio,geometry,plot]>=2.10.0",
|
||||||
"soundfile>=0.12.1",
|
"soundfile>=0.12.1",
|
||||||
"tensorboard>=2.16.2",
|
"tensorboard>=2.16.2",
|
||||||
"torch>=2.0.0",
|
"torch>=1.13.1",
|
||||||
"torchaudio>=2.0.0",
|
"torchaudio>=1.13.1",
|
||||||
"xarray>=2024.0.0",
|
"xarray>=2024.0.0",
|
||||||
]
|
]
|
||||||
requires-python = ">=3.10,<3.14"
|
requires-python = ">=3.10,<3.14"
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
from lightning.pytorch.loggers import Logger
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, Literal
|
from typing import TYPE_CHECKING, Literal
|
||||||
@ -8,7 +9,6 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
from lightning.pytorch.loggers import Logger
|
|
||||||
from soundevent import data
|
from soundevent import data
|
||||||
|
|
||||||
from batdetect2.audio import AudioConfig, AudioLoader
|
from batdetect2.audio import AudioConfig, AudioLoader
|
||||||
@ -153,19 +153,6 @@ class BatDetect2API:
|
|||||||
|
|
||||||
self.model.eval()
|
self.model.eval()
|
||||||
|
|
||||||
def compile(self) -> "BatDetect2API":
|
|
||||||
"""Compile the detector path used by inference.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
BatDetect2API
|
|
||||||
This API instance with the detector compiled.
|
|
||||||
"""
|
|
||||||
from batdetect2.models import compile_model
|
|
||||||
|
|
||||||
compile_model(self.model)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def load_annotations(
|
def load_annotations(
|
||||||
self,
|
self,
|
||||||
path: data.PathLike,
|
path: data.PathLike,
|
||||||
@ -241,9 +228,6 @@ class BatDetect2API:
|
|||||||
Training logger config override.
|
Training logger config override.
|
||||||
logging_callbacks : Sequence[LoggingCallback[TrainLoggingContext]], optional
|
logging_callbacks : Sequence[LoggingCallback[TrainLoggingContext]], optional
|
||||||
Extra logging callbacks to run during training setup.
|
Extra logging callbacks to run during training setup.
|
||||||
train_logger : Logger | None, optional
|
|
||||||
Pre-built Lightning logger to use for training. If omitted, one is
|
|
||||||
built from ``logger_config``.
|
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -998,7 +982,6 @@ class BatDetect2API:
|
|||||||
inference_config: InferenceConfig | None = None,
|
inference_config: InferenceConfig | None = None,
|
||||||
outputs_config: OutputsConfig | None = None,
|
outputs_config: OutputsConfig | None = None,
|
||||||
logging_config: AppLoggingConfig | None = None,
|
logging_config: AppLoggingConfig | None = None,
|
||||||
compile_model: bool = False,
|
|
||||||
) -> "BatDetect2API":
|
) -> "BatDetect2API":
|
||||||
"""Build an API instance from config objects.
|
"""Build an API instance from config objects.
|
||||||
|
|
||||||
@ -1024,8 +1007,6 @@ class BatDetect2API:
|
|||||||
Output config. If omitted, the default outputs config is used.
|
Output config. If omitted, the default outputs config is used.
|
||||||
logging_config : AppLoggingConfig | None, optional
|
logging_config : AppLoggingConfig | None, optional
|
||||||
Logging config. If omitted, the default logging config is used.
|
Logging config. If omitted, the default logging config is used.
|
||||||
compile_model : bool, optional
|
|
||||||
If ``True``, compile the detector path after building the API.
|
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -1108,7 +1089,7 @@ class BatDetect2API:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
api = cls(
|
return cls(
|
||||||
model_config=model_config,
|
model_config=model_config,
|
||||||
audio_config=audio_config,
|
audio_config=audio_config,
|
||||||
train_config=train_config,
|
train_config=train_config,
|
||||||
@ -1127,11 +1108,6 @@ class BatDetect2API:
|
|||||||
output_transform=output_transform,
|
output_transform=output_transform,
|
||||||
)
|
)
|
||||||
|
|
||||||
if compile_model:
|
|
||||||
api.compile()
|
|
||||||
|
|
||||||
return api
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_checkpoint(
|
def from_checkpoint(
|
||||||
cls,
|
cls,
|
||||||
@ -1142,7 +1118,6 @@ class BatDetect2API:
|
|||||||
inference_config: InferenceConfig | None = None,
|
inference_config: InferenceConfig | None = None,
|
||||||
outputs_config: OutputsConfig | None = None,
|
outputs_config: OutputsConfig | None = None,
|
||||||
logging_config: AppLoggingConfig | None = None,
|
logging_config: AppLoggingConfig | None = None,
|
||||||
compile_model: bool = False,
|
|
||||||
) -> "BatDetect2API":
|
) -> "BatDetect2API":
|
||||||
"""Build an API instance from a saved checkpoint.
|
"""Build an API instance from a saved checkpoint.
|
||||||
|
|
||||||
@ -1163,8 +1138,6 @@ class BatDetect2API:
|
|||||||
Output config override.
|
Output config override.
|
||||||
logging_config : AppLoggingConfig | None, optional
|
logging_config : AppLoggingConfig | None, optional
|
||||||
Logging config override.
|
Logging config override.
|
||||||
compile_model : bool, optional
|
|
||||||
If ``True``, compile the detector path after building the API.
|
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -1248,7 +1221,7 @@ class BatDetect2API:
|
|||||||
transform=output_transform,
|
transform=output_transform,
|
||||||
)
|
)
|
||||||
|
|
||||||
api = cls(
|
return cls(
|
||||||
model_config=model_config,
|
model_config=model_config,
|
||||||
audio_config=audio_config,
|
audio_config=audio_config,
|
||||||
train_config=train_config,
|
train_config=train_config,
|
||||||
@ -1267,11 +1240,6 @@ class BatDetect2API:
|
|||||||
output_transform=output_transform,
|
output_transform=output_transform,
|
||||||
)
|
)
|
||||||
|
|
||||||
if compile_model:
|
|
||||||
api.compile()
|
|
||||||
|
|
||||||
return api
|
|
||||||
|
|
||||||
def _set_trainable_parameters(
|
def _set_trainable_parameters(
|
||||||
self,
|
self,
|
||||||
trainable: Literal["all", "heads", "classifier_head", "size_head"],
|
trainable: Literal["all", "heads", "classifier_head", "size_head"],
|
||||||
|
|||||||
@ -10,7 +10,6 @@ from batdetect2.inference.clips import get_clips_from_files
|
|||||||
from batdetect2.inference.config import InferenceConfig
|
from batdetect2.inference.config import InferenceConfig
|
||||||
from batdetect2.inference.dataset import build_inference_loader
|
from batdetect2.inference.dataset import build_inference_loader
|
||||||
from batdetect2.inference.lightning import InferenceModule
|
from batdetect2.inference.lightning import InferenceModule
|
||||||
from batdetect2.models import compile_model
|
|
||||||
from batdetect2.models.types import ModelProtocol
|
from batdetect2.models.types import ModelProtocol
|
||||||
from batdetect2.outputs import (
|
from batdetect2.outputs import (
|
||||||
OutputsConfig,
|
OutputsConfig,
|
||||||
@ -72,9 +71,6 @@ def run_batch_inference(
|
|||||||
batch_size=batch_size,
|
batch_size=batch_size,
|
||||||
)
|
)
|
||||||
|
|
||||||
if inference_config.compile_model:
|
|
||||||
compile_model(model)
|
|
||||||
|
|
||||||
module = InferenceModule(
|
module = InferenceModule(
|
||||||
model,
|
model,
|
||||||
output_transform=output_transform,
|
output_transform=output_transform,
|
||||||
|
|||||||
@ -15,7 +15,6 @@ class ClipingConfig(BaseConfig):
|
|||||||
|
|
||||||
|
|
||||||
class InferenceConfig(BaseConfig):
|
class InferenceConfig(BaseConfig):
|
||||||
compile_model: bool = False
|
|
||||||
loader: InferenceLoaderConfig = Field(
|
loader: InferenceLoaderConfig = Field(
|
||||||
default_factory=InferenceLoaderConfig
|
default_factory=InferenceLoaderConfig
|
||||||
)
|
)
|
||||||
|
|||||||
@ -100,7 +100,6 @@ __all__ = [
|
|||||||
"ModelConfig",
|
"ModelConfig",
|
||||||
"build_model",
|
"build_model",
|
||||||
"build_model_with_new_targets",
|
"build_model_with_new_targets",
|
||||||
"compile_model",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -113,6 +112,9 @@ class ModelConfig(BaseConfig):
|
|||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
----------
|
----------
|
||||||
|
compile : bool
|
||||||
|
If ``True``, compile the model before training. Defaults to
|
||||||
|
``False``.
|
||||||
samplerate : int
|
samplerate : int
|
||||||
Expected input audio sample rate in Hz. Audio must be resampled
|
Expected input audio sample rate in Hz. Audio must be resampled
|
||||||
to this rate before being passed to the model. Defaults to
|
to this rate before being passed to the model. Defaults to
|
||||||
@ -130,6 +132,7 @@ class ModelConfig(BaseConfig):
|
|||||||
``PostprocessConfig()``.
|
``PostprocessConfig()``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
compile: bool = False
|
||||||
samplerate: int = Field(default=TARGET_SAMPLERATE_HZ, gt=0)
|
samplerate: int = Field(default=TARGET_SAMPLERATE_HZ, gt=0)
|
||||||
architecture: BackboneConfig = Field(default_factory=UNetBackboneConfig)
|
architecture: BackboneConfig = Field(default_factory=UNetBackboneConfig)
|
||||||
preprocess: PreprocessingConfig = Field(
|
preprocess: PreprocessingConfig = Field(
|
||||||
@ -320,15 +323,3 @@ def build_model_with_new_targets(
|
|||||||
dimension_names=roi_mapper.dimension_names,
|
dimension_names=roi_mapper.dimension_names,
|
||||||
config=model.get_config(),
|
config=model.get_config(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def compile_model(model: ModelProtocol) -> ModelProtocol:
|
|
||||||
"""Compile the detector path used by training and inference."""
|
|
||||||
if not isinstance(model.detector, torch.nn.Module):
|
|
||||||
raise TypeError("Detector must be a torch.nn.Module to compile.")
|
|
||||||
|
|
||||||
if getattr(model.detector, "_compiled_call_impl", None) is not None:
|
|
||||||
return model
|
|
||||||
|
|
||||||
model.detector.compile()
|
|
||||||
return model
|
|
||||||
|
|||||||
@ -70,16 +70,11 @@ __all__ = [
|
|||||||
"FreqCoordConvUpBlock",
|
"FreqCoordConvUpBlock",
|
||||||
"StandardConvUpBlock",
|
"StandardConvUpBlock",
|
||||||
"SelfAttention",
|
"SelfAttention",
|
||||||
"EfficientSelfAttention",
|
|
||||||
"VerticalMean",
|
|
||||||
"ConvConfig",
|
"ConvConfig",
|
||||||
"EfficientSelfAttentionConfig",
|
|
||||||
"FreqCoordConvDownConfig",
|
"FreqCoordConvDownConfig",
|
||||||
"StandardConvDownConfig",
|
"StandardConvDownConfig",
|
||||||
"FreqCoordConvUpConfig",
|
"FreqCoordConvUpConfig",
|
||||||
"StandardConvUpConfig",
|
"StandardConvUpConfig",
|
||||||
"VerticalConvConfig",
|
|
||||||
"VerticalMeanConfig",
|
|
||||||
"LayerConfig",
|
"LayerConfig",
|
||||||
"build_layer",
|
"build_layer",
|
||||||
]
|
]
|
||||||
@ -150,8 +145,8 @@ class SelfAttentionConfig(BaseConfig):
|
|||||||
attention_channels : int
|
attention_channels : int
|
||||||
Dimensionality of the query, key, and value projections.
|
Dimensionality of the query, key, and value projections.
|
||||||
temperature : float
|
temperature : float
|
||||||
Divisor applied together with ``attention_channels`` when scaling
|
Scaling factor applied to the weighted values before the final
|
||||||
dot-product attention logits. Defaults to ``1``.
|
linear projection. Defaults to ``1``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name: Literal["SelfAttention"] = "SelfAttention"
|
name: Literal["SelfAttention"] = "SelfAttention"
|
||||||
@ -311,126 +306,6 @@ class SelfAttention(Block):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class EfficientSelfAttentionConfig(BaseConfig):
|
|
||||||
"""Configuration for an ``EfficientSelfAttention`` block.
|
|
||||||
|
|
||||||
Attributes
|
|
||||||
----------
|
|
||||||
name : str
|
|
||||||
Discriminator field; always ``"EfficientSelfAttention"``.
|
|
||||||
attention_channels : int
|
|
||||||
Dimensionality of the query, key, and value projections.
|
|
||||||
temperature : float
|
|
||||||
Divisor applied together with ``attention_channels`` when scaling
|
|
||||||
dot-product attention logits. Defaults to ``1``.
|
|
||||||
"""
|
|
||||||
|
|
||||||
name: Literal["EfficientSelfAttention"] = "EfficientSelfAttention"
|
|
||||||
attention_channels: int
|
|
||||||
temperature: float = 1.0
|
|
||||||
|
|
||||||
|
|
||||||
class EfficientSelfAttention(Block):
|
|
||||||
"""An optimized self-attention block operating along the time axis.
|
|
||||||
|
|
||||||
Applies a scaled dot-product self-attention mechanism across the time
|
|
||||||
steps of an input feature map. This version uses a fused QKV linear
|
|
||||||
projection and PyTorch's native scaled dot-product attention (SDPA)
|
|
||||||
for optimal memory usage and execution speed.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
in_channels : int
|
|
||||||
Number of input channels (features per time step).
|
|
||||||
attention_channels : int
|
|
||||||
Dimensionality of the query, key, and value projections.
|
|
||||||
temperature : float, default=1.0
|
|
||||||
Divisor applied together with ``attention_channels`` when scaling
|
|
||||||
the dot-product scores before softmax.
|
|
||||||
|
|
||||||
Attributes
|
|
||||||
----------
|
|
||||||
qkv_proj : nn.Linear
|
|
||||||
Fused linear projection for queries, keys, and values.
|
|
||||||
pro_fun : nn.Linear
|
|
||||||
Final linear projection applied to the attended values.
|
|
||||||
temperature : float
|
|
||||||
Scaling divisor used when computing attention scores.
|
|
||||||
att_dim : int
|
|
||||||
Dimensionality of the attention space (``attention_channels``).
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
in_channels: int,
|
|
||||||
attention_channels: int,
|
|
||||||
temperature: float = 1.0,
|
|
||||||
):
|
|
||||||
super().__init__()
|
|
||||||
self.in_channels = in_channels
|
|
||||||
self.out_channels = in_channels
|
|
||||||
self.temperature = temperature
|
|
||||||
self.att_dim = attention_channels
|
|
||||||
self.output_channels = in_channels
|
|
||||||
|
|
||||||
self.qkv_proj = nn.Linear(in_channels, 3 * attention_channels)
|
|
||||||
self.pro_fun = nn.Linear(attention_channels, in_channels)
|
|
||||||
|
|
||||||
self.scale_factor = 1.0 / (self.temperature * self.att_dim)
|
|
||||||
|
|
||||||
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
||||||
"""Apply self-attention along the time dimension."""
|
|
||||||
x = x.squeeze(2).permute(0, 2, 1)
|
|
||||||
|
|
||||||
# Single projection pass
|
|
||||||
qkv = self.qkv_proj(x)
|
|
||||||
|
|
||||||
# Split along the last dimension into Q, K, V
|
|
||||||
query, key, value = torch.chunk(qkv, 3, dim=-1)
|
|
||||||
|
|
||||||
att = F.scaled_dot_product_attention(
|
|
||||||
query,
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
attn_mask=None,
|
|
||||||
dropout_p=0.0,
|
|
||||||
is_causal=False,
|
|
||||||
scale=self.scale_factor,
|
|
||||||
)
|
|
||||||
|
|
||||||
op = self.pro_fun(att)
|
|
||||||
|
|
||||||
return op.permute(0, 2, 1).unsqueeze(2)
|
|
||||||
|
|
||||||
def compute_attention_weights(self, x: torch.Tensor) -> torch.Tensor:
|
|
||||||
"""Return the softmax attention weight matrix.
|
|
||||||
|
|
||||||
Useful for visualising which time steps attend to which others.
|
|
||||||
"""
|
|
||||||
x = x.squeeze(2).permute(0, 2, 1)
|
|
||||||
|
|
||||||
qkv = self.qkv_proj(x)
|
|
||||||
query, key, _ = torch.chunk(qkv, 3, dim=-1)
|
|
||||||
|
|
||||||
kk_qq = torch.bmm(key, query.permute(0, 2, 1)) * self.scale_factor
|
|
||||||
att_weights = F.softmax(kk_qq, dim=1)
|
|
||||||
|
|
||||||
return att_weights
|
|
||||||
|
|
||||||
@block_registry.register(EfficientSelfAttentionConfig)
|
|
||||||
@staticmethod
|
|
||||||
def from_config(
|
|
||||||
config: EfficientSelfAttentionConfig,
|
|
||||||
input_channels: int,
|
|
||||||
input_height: int,
|
|
||||||
) -> "EfficientSelfAttention":
|
|
||||||
return EfficientSelfAttention(
|
|
||||||
in_channels=input_channels,
|
|
||||||
attention_channels=config.attention_channels,
|
|
||||||
temperature=config.temperature,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ConvConfig(BaseConfig):
|
class ConvConfig(BaseConfig):
|
||||||
"""Configuration for a basic ConvBlock."""
|
"""Configuration for a basic ConvBlock."""
|
||||||
|
|
||||||
@ -585,10 +460,6 @@ class VerticalConv(Block):
|
|||||||
"""
|
"""
|
||||||
return F.relu_(self.bn(self.conv(x)))
|
return F.relu_(self.bn(self.conv(x)))
|
||||||
|
|
||||||
def get_output_height(self, input_height: int) -> int:
|
|
||||||
"""Return the collapsed output height."""
|
|
||||||
return 1
|
|
||||||
|
|
||||||
@block_registry.register(VerticalConvConfig)
|
@block_registry.register(VerticalConvConfig)
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_config(
|
def from_config(
|
||||||
@ -603,94 +474,6 @@ class VerticalConv(Block):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VerticalMeanConfig(BaseConfig):
|
|
||||||
"""Configuration for a ``VerticalMean`` block.
|
|
||||||
|
|
||||||
Attributes
|
|
||||||
----------
|
|
||||||
name : str
|
|
||||||
Discriminator field; always ``"VerticalMean"``.
|
|
||||||
"""
|
|
||||||
|
|
||||||
name: Literal["VerticalMean"] = "VerticalMean"
|
|
||||||
"""Discriminator field indicating the block type."""
|
|
||||||
|
|
||||||
channels: int
|
|
||||||
"""Number of output channels."""
|
|
||||||
|
|
||||||
|
|
||||||
class VerticalMean(Block):
|
|
||||||
"""Mean pooling block operating along the height dimension.
|
|
||||||
|
|
||||||
Applies a 2D mean pooling operation, followed by a 2D convolution,
|
|
||||||
followed by a batch normalization and ReLU activation.
|
|
||||||
|
|
||||||
Sequence: Mean Pool -> Conv -> BN -> ReLU.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
in_channels : int
|
|
||||||
Number of channels in the input tensor.
|
|
||||||
out_channels : int
|
|
||||||
Number of output channels after the mean pooling.
|
|
||||||
input_height : int
|
|
||||||
The height (H dimension) of the input tensor. The convolutional kernel
|
|
||||||
will be sized `(1, 1)`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
in_channels: int,
|
|
||||||
out_channels: int,
|
|
||||||
input_height: int,
|
|
||||||
):
|
|
||||||
super().__init__()
|
|
||||||
self.in_channels = in_channels
|
|
||||||
self.out_channels = out_channels
|
|
||||||
self.input_height = input_height
|
|
||||||
self.conv = nn.Conv2d(
|
|
||||||
in_channels,
|
|
||||||
out_channels,
|
|
||||||
kernel_size=(1, 1),
|
|
||||||
padding=0,
|
|
||||||
)
|
|
||||||
self.batch_norm = nn.BatchNorm2d(out_channels)
|
|
||||||
|
|
||||||
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
||||||
"""Apply avg pooling -> Conv -> BN -> ReLU.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
x : torch.Tensor
|
|
||||||
Input tensor, shape `(B, C_in, H, W)`.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
torch.Tensor
|
|
||||||
Output tensor, shape `(B, C_out, 1, W)`.
|
|
||||||
"""
|
|
||||||
x = x.mean(dim=2, keepdim=True)
|
|
||||||
x = self.conv(x)
|
|
||||||
return F.relu(self.batch_norm(x), inplace=True)
|
|
||||||
|
|
||||||
def get_output_height(self, input_height: int) -> int:
|
|
||||||
"""Return the collapsed output height."""
|
|
||||||
return 1
|
|
||||||
|
|
||||||
@block_registry.register(VerticalMeanConfig)
|
|
||||||
@staticmethod
|
|
||||||
def from_config(
|
|
||||||
config: VerticalMeanConfig,
|
|
||||||
input_channels: int,
|
|
||||||
input_height: int,
|
|
||||||
):
|
|
||||||
return VerticalMean(
|
|
||||||
in_channels=input_channels,
|
|
||||||
out_channels=config.channels,
|
|
||||||
input_height=input_height,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class FreqCoordConvDownConfig(BaseConfig):
|
class FreqCoordConvDownConfig(BaseConfig):
|
||||||
"""Configuration for a FreqCoordConvDownBlock."""
|
"""Configuration for a FreqCoordConvDownBlock."""
|
||||||
|
|
||||||
@ -1168,16 +951,13 @@ LayerConfig = Annotated[
|
|||||||
| FreqCoordConvUpConfig
|
| FreqCoordConvUpConfig
|
||||||
| StandardConvUpConfig
|
| StandardConvUpConfig
|
||||||
| SelfAttentionConfig
|
| SelfAttentionConfig
|
||||||
| EfficientSelfAttentionConfig
|
|
||||||
| VerticalConvConfig
|
|
||||||
| VerticalMeanConfig
|
|
||||||
| LayerGroupConfig,
|
| LayerGroupConfig,
|
||||||
Field(discriminator="name"),
|
Field(discriminator="name"),
|
||||||
]
|
]
|
||||||
"""Type alias for the discriminated union of block configuration models."""
|
"""Type alias for the discriminated union of block configuration models."""
|
||||||
|
|
||||||
|
|
||||||
class LayerGroup(Block):
|
class LayerGroup(nn.Module):
|
||||||
"""Sequential chain of blocks that acts as a single composite block.
|
"""Sequential chain of blocks that acts as a single composite block.
|
||||||
|
|
||||||
Wraps multiple ``Block`` instances in an ``nn.Sequential`` container,
|
Wraps multiple ``Block`` instances in an ``nn.Sequential`` container,
|
||||||
|
|||||||
@ -21,18 +21,14 @@ This module provides:
|
|||||||
from typing import Annotated, List
|
from typing import Annotated, List
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
from pydantic import Field, model_validator
|
from pydantic import Field
|
||||||
from torch import nn
|
from torch import nn
|
||||||
|
|
||||||
from batdetect2.core.configs import BaseConfig
|
from batdetect2.core.configs import BaseConfig
|
||||||
from batdetect2.models.blocks import (
|
from batdetect2.models.blocks import (
|
||||||
Block,
|
Block,
|
||||||
EfficientSelfAttentionConfig,
|
|
||||||
SelfAttentionConfig,
|
SelfAttentionConfig,
|
||||||
VerticalConv,
|
VerticalConv,
|
||||||
VerticalConvConfig,
|
|
||||||
VerticalMean,
|
|
||||||
VerticalMeanConfig,
|
|
||||||
build_layer,
|
build_layer,
|
||||||
)
|
)
|
||||||
from batdetect2.models.types import BottleneckProtocol
|
from batdetect2.models.types import BottleneckProtocol
|
||||||
@ -98,7 +94,6 @@ class Bottleneck(Block):
|
|||||||
in_channels: int,
|
in_channels: int,
|
||||||
out_channels: int,
|
out_channels: int,
|
||||||
bottleneck_channels: int | None = None,
|
bottleneck_channels: int | None = None,
|
||||||
frequency_aggregator: Block | None = None,
|
|
||||||
layers: List[torch.nn.Module] | None = None,
|
layers: List[torch.nn.Module] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialise the Bottleneck layer.
|
"""Initialise the Bottleneck layer.
|
||||||
@ -130,14 +125,11 @@ class Bottleneck(Block):
|
|||||||
)
|
)
|
||||||
self.layers = nn.ModuleList(layers or [])
|
self.layers = nn.ModuleList(layers or [])
|
||||||
|
|
||||||
if frequency_aggregator is None:
|
self.conv_vert = VerticalConv(
|
||||||
frequency_aggregator = VerticalConv(
|
in_channels=in_channels,
|
||||||
in_channels=in_channels,
|
out_channels=self.bottleneck_channels,
|
||||||
out_channels=self.bottleneck_channels,
|
input_height=input_height,
|
||||||
input_height=input_height,
|
)
|
||||||
)
|
|
||||||
|
|
||||||
self.conv_vert = frequency_aggregator
|
|
||||||
|
|
||||||
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||||
"""Process the encoder's bottleneck features.
|
"""Process the encoder's bottleneck features.
|
||||||
@ -168,19 +160,12 @@ class Bottleneck(Block):
|
|||||||
|
|
||||||
|
|
||||||
BottleneckLayerConfig = Annotated[
|
BottleneckLayerConfig = Annotated[
|
||||||
SelfAttentionConfig | EfficientSelfAttentionConfig,
|
SelfAttentionConfig,
|
||||||
Field(discriminator="name"),
|
Field(discriminator="name"),
|
||||||
]
|
]
|
||||||
"""Type alias for the discriminated union of block configs usable in the Bottleneck."""
|
"""Type alias for the discriminated union of block configs usable in the Bottleneck."""
|
||||||
|
|
||||||
|
|
||||||
FrequencyAggregationLayerConfig = Annotated[
|
|
||||||
(VerticalConvConfig | VerticalMeanConfig),
|
|
||||||
Field(discriminator="name"),
|
|
||||||
]
|
|
||||||
"""Type alias for the discriminated union of block configs usable in the FrequencyAggregation."""
|
|
||||||
|
|
||||||
|
|
||||||
class BottleneckConfig(BaseConfig):
|
class BottleneckConfig(BaseConfig):
|
||||||
"""Configuration for the bottleneck component.
|
"""Configuration for the bottleneck component.
|
||||||
|
|
||||||
@ -197,79 +182,17 @@ class BottleneckConfig(BaseConfig):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
channels: int
|
channels: int
|
||||||
frequency_aggregation: FrequencyAggregationLayerConfig | None = None
|
|
||||||
layers: List[BottleneckLayerConfig] = Field(default_factory=list)
|
layers: List[BottleneckLayerConfig] = Field(default_factory=list)
|
||||||
|
|
||||||
@model_validator(mode="after")
|
|
||||||
def set_default_frequency_aggregation(self) -> "BottleneckConfig":
|
|
||||||
"""Default frequency aggregation to the bottleneck channel count."""
|
|
||||||
if self.frequency_aggregation is None:
|
|
||||||
self.frequency_aggregation = VerticalConvConfig(
|
|
||||||
channels=self.channels
|
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_BOTTLENECK_CONFIG: BottleneckConfig = BottleneckConfig(
|
DEFAULT_BOTTLENECK_CONFIG: BottleneckConfig = BottleneckConfig(
|
||||||
channels=256,
|
channels=256,
|
||||||
frequency_aggregation=VerticalConvConfig(channels=256),
|
|
||||||
layers=[
|
layers=[
|
||||||
SelfAttentionConfig(attention_channels=256),
|
SelfAttentionConfig(attention_channels=256),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def build_frequency_aggregation(
|
|
||||||
input_height: int,
|
|
||||||
in_channels: int,
|
|
||||||
config: FrequencyAggregationLayerConfig,
|
|
||||||
) -> Block:
|
|
||||||
"""Build a block for aggregating frequency information.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
input_height : int
|
|
||||||
Height (number of frequency bins) of the input tensor from the
|
|
||||||
encoder. Must be positive.
|
|
||||||
in_channels : int
|
|
||||||
Number of channels in the input tensor from the encoder. Must be
|
|
||||||
positive.
|
|
||||||
config : FrequencyAggregationLayerConfig, optional
|
|
||||||
Configuration specifying the output channel count and any
|
|
||||||
additional layers. Uses ``VerticalConvConfig`` if ``None``.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Block
|
|
||||||
An initialised ``VerticalConv`` module.
|
|
||||||
|
|
||||||
Raises
|
|
||||||
------
|
|
||||||
AssertionError
|
|
||||||
If any configured layer changes the height of the feature map
|
|
||||||
(bottleneck layers must preserve height so that it can be restored
|
|
||||||
by repetition).
|
|
||||||
"""
|
|
||||||
if config.name == "VerticalConv":
|
|
||||||
return VerticalConv(
|
|
||||||
in_channels=in_channels,
|
|
||||||
out_channels=config.channels,
|
|
||||||
input_height=input_height,
|
|
||||||
)
|
|
||||||
|
|
||||||
if config.name == "VerticalMean":
|
|
||||||
return VerticalMean(
|
|
||||||
in_channels=in_channels,
|
|
||||||
out_channels=config.channels,
|
|
||||||
input_height=input_height,
|
|
||||||
)
|
|
||||||
|
|
||||||
raise NotImplementedError(
|
|
||||||
f"Unknown frequency aggregation layer: {config.name}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def build_bottleneck(
|
def build_bottleneck(
|
||||||
input_height: int,
|
input_height: int,
|
||||||
in_channels: int,
|
in_channels: int,
|
||||||
@ -308,26 +231,13 @@ def build_bottleneck(
|
|||||||
by repetition).
|
by repetition).
|
||||||
"""
|
"""
|
||||||
config = config or DEFAULT_BOTTLENECK_CONFIG
|
config = config or DEFAULT_BOTTLENECK_CONFIG
|
||||||
frequency_aggregation = config.frequency_aggregation
|
|
||||||
if frequency_aggregation is None:
|
|
||||||
raise ValueError("frequency_aggregation must be configured.")
|
|
||||||
|
|
||||||
frequency_aggregator = build_frequency_aggregation(
|
current_channels = in_channels
|
||||||
input_height=input_height,
|
current_height = input_height
|
||||||
in_channels=in_channels,
|
|
||||||
config=frequency_aggregation,
|
|
||||||
)
|
|
||||||
|
|
||||||
current_channels = frequency_aggregator.out_channels
|
|
||||||
current_height = frequency_aggregator.get_output_height(input_height)
|
|
||||||
assert current_height == 1, (
|
|
||||||
"Bottleneck frequency aggregation should collapse spectrogram height"
|
|
||||||
)
|
|
||||||
|
|
||||||
layers = []
|
layers = []
|
||||||
|
|
||||||
for layer_config in config.layers:
|
for layer_config in config.layers:
|
||||||
previous_height = current_height
|
|
||||||
layer = build_layer(
|
layer = build_layer(
|
||||||
input_height=current_height,
|
input_height=current_height,
|
||||||
in_channels=current_channels,
|
in_channels=current_channels,
|
||||||
@ -335,7 +245,7 @@ def build_bottleneck(
|
|||||||
)
|
)
|
||||||
current_height = layer.get_output_height(current_height)
|
current_height = layer.get_output_height(current_height)
|
||||||
current_channels = layer.out_channels
|
current_channels = layer.out_channels
|
||||||
assert current_height == previous_height, (
|
assert current_height == input_height, (
|
||||||
"Bottleneck layers should not change the spectrogram height"
|
"Bottleneck layers should not change the spectrogram height"
|
||||||
)
|
)
|
||||||
layers.append(layer)
|
layers.append(layer)
|
||||||
@ -343,7 +253,6 @@ def build_bottleneck(
|
|||||||
return Bottleneck(
|
return Bottleneck(
|
||||||
input_height=input_height,
|
input_height=input_height,
|
||||||
in_channels=in_channels,
|
in_channels=in_channels,
|
||||||
out_channels=current_channels,
|
out_channels=config.channels,
|
||||||
frequency_aggregator=frequency_aggregator,
|
|
||||||
layers=layers,
|
layers=layers,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
import json
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from multiprocessing import Pool
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Literal, Sequence
|
from typing import List, Literal, Sequence
|
||||||
from uuid import UUID, uuid4
|
from uuid import UUID, uuid4
|
||||||
@ -10,7 +8,6 @@ import xarray as xr
|
|||||||
from loguru import logger
|
from loguru import logger
|
||||||
from soundevent import data
|
from soundevent import data
|
||||||
from soundevent.geometry import compute_bounds
|
from soundevent.geometry import compute_bounds
|
||||||
from tqdm import tqdm
|
|
||||||
|
|
||||||
from batdetect2.core import BaseConfig
|
from batdetect2.core import BaseConfig
|
||||||
from batdetect2.outputs.formats.base import (
|
from batdetect2.outputs.formats.base import (
|
||||||
@ -28,8 +25,6 @@ class RawOutputConfig(BaseConfig):
|
|||||||
include_class_scores: bool = True
|
include_class_scores: bool = True
|
||||||
include_features: bool = True
|
include_features: bool = True
|
||||||
include_geometry: bool = True
|
include_geometry: bool = True
|
||||||
n_jobs: int = 1
|
|
||||||
show_progress: bool = False
|
|
||||||
|
|
||||||
|
|
||||||
class RawFormatter(OutputFormatterProtocol[ClipDetections]):
|
class RawFormatter(OutputFormatterProtocol[ClipDetections]):
|
||||||
@ -40,19 +35,12 @@ class RawFormatter(OutputFormatterProtocol[ClipDetections]):
|
|||||||
include_features: bool = True,
|
include_features: bool = True,
|
||||||
include_geometry: bool = True,
|
include_geometry: bool = True,
|
||||||
parse_full_geometry: bool = False,
|
parse_full_geometry: bool = False,
|
||||||
n_jobs: int = 1,
|
|
||||||
show_progress: bool = False,
|
|
||||||
):
|
):
|
||||||
self.targets = targets
|
self.targets = targets
|
||||||
self.include_class_scores = include_class_scores
|
self.include_class_scores = include_class_scores
|
||||||
self.include_features = include_features
|
self.include_features = include_features
|
||||||
self.include_geometry = include_geometry
|
self.include_geometry = include_geometry
|
||||||
self.parse_full_geometry = parse_full_geometry
|
self.parse_full_geometry = parse_full_geometry
|
||||||
self.n_jobs = n_jobs
|
|
||||||
self.show_progress = show_progress
|
|
||||||
|
|
||||||
if n_jobs < 1:
|
|
||||||
raise ValueError("n_jobs must be >= 1")
|
|
||||||
|
|
||||||
def format(
|
def format(
|
||||||
self,
|
self,
|
||||||
@ -80,40 +68,15 @@ class RawFormatter(OutputFormatterProtocol[ClipDetections]):
|
|||||||
def load(self, path: data.PathLike) -> List[ClipDetections]:
|
def load(self, path: data.PathLike) -> List[ClipDetections]:
|
||||||
path = Path(path)
|
path = Path(path)
|
||||||
files = list(path.glob("*.nc"))
|
files = list(path.glob("*.nc"))
|
||||||
|
predictions: List[ClipDetections] = []
|
||||||
|
|
||||||
if self.n_jobs == 1:
|
for filepath in files:
|
||||||
return self._load_sequential(files)
|
logger.debug(f"Loading clip predictions {filepath}")
|
||||||
|
clip_data = xr.load_dataset(filepath)
|
||||||
|
prediction = self.pred_from_xr(clip_data)
|
||||||
|
predictions.append(prediction)
|
||||||
|
|
||||||
return self._load_parallel(files)
|
return predictions
|
||||||
|
|
||||||
def _load_sequential(
|
|
||||||
self, files: Sequence[data.PathLike]
|
|
||||||
) -> List[ClipDetections]:
|
|
||||||
|
|
||||||
iterable = files
|
|
||||||
if self.show_progress:
|
|
||||||
iterable = tqdm(files, total=len(files))
|
|
||||||
|
|
||||||
return [self.load_single_file(filepath) for filepath in iterable]
|
|
||||||
|
|
||||||
def _load_parallel(
|
|
||||||
self, files: Sequence[data.PathLike]
|
|
||||||
) -> List[ClipDetections]:
|
|
||||||
with Pool(self.n_jobs) as pool:
|
|
||||||
if not self.show_progress:
|
|
||||||
return pool.map(self.load_single_file, files)
|
|
||||||
|
|
||||||
return list(
|
|
||||||
tqdm(
|
|
||||||
pool.imap(self.load_single_file, files),
|
|
||||||
total=len(files),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def load_single_file(self, filepath: data.PathLike) -> ClipDetections:
|
|
||||||
logger.debug(f"Loading clip predictions {filepath}")
|
|
||||||
clip_data = xr.load_dataset(filepath)
|
|
||||||
return self.pred_from_xr(clip_data)
|
|
||||||
|
|
||||||
def pred_to_xr(
|
def pred_to_xr(
|
||||||
self,
|
self,
|
||||||
@ -177,7 +140,7 @@ class RawFormatter(OutputFormatterProtocol[ClipDetections]):
|
|||||||
"clip_id": str(clip.uuid),
|
"clip_id": str(clip.uuid),
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.include_class_scores and values["class_scores"]:
|
if self.include_class_scores:
|
||||||
class_scores = np.stack(values["class_scores"], axis=0)
|
class_scores = np.stack(values["class_scores"], axis=0)
|
||||||
data_vars["class_scores"] = (
|
data_vars["class_scores"] = (
|
||||||
["detection", "classes"],
|
["detection", "classes"],
|
||||||
@ -185,7 +148,7 @@ class RawFormatter(OutputFormatterProtocol[ClipDetections]):
|
|||||||
)
|
)
|
||||||
coords["classes"] = ("classes", self.targets.class_names)
|
coords["classes"] = ("classes", self.targets.class_names)
|
||||||
|
|
||||||
if self.include_features and values["features"]:
|
if self.include_features:
|
||||||
features = np.stack(values["features"], axis=0)
|
features = np.stack(values["features"], axis=0)
|
||||||
data_vars["features"] = (["detection", "feature"], features)
|
data_vars["features"] = (["detection", "feature"], features)
|
||||||
coords["feature"] = ("feature", np.arange(num_features))
|
coords["feature"] = ("feature", np.arange(num_features))
|
||||||
@ -204,81 +167,59 @@ class RawFormatter(OutputFormatterProtocol[ClipDetections]):
|
|||||||
def pred_from_xr(self, dataset: xr.Dataset) -> ClipDetections:
|
def pred_from_xr(self, dataset: xr.Dataset) -> ClipDetections:
|
||||||
clip_data = dataset
|
clip_data = dataset
|
||||||
|
|
||||||
recording = data.Recording.model_validate(
|
recording = data.Recording.model_validate_json(
|
||||||
json.loads(clip_data.attrs["recording"])
|
clip_data.attrs["recording"]
|
||||||
)
|
)
|
||||||
|
|
||||||
clip_id = clip_data.clip_id.item()
|
clip_id = clip_data.clip_id.item()
|
||||||
clip = data.Clip.model_construct(
|
clip = data.Clip(
|
||||||
recording=recording,
|
recording=recording,
|
||||||
uuid=UUID(clip_id),
|
uuid=UUID(clip_id),
|
||||||
start_time=float(clip_data.clip_start),
|
start_time=clip_data.clip_start,
|
||||||
end_time=float(clip_data.clip_end),
|
end_time=clip_data.clip_end,
|
||||||
)
|
)
|
||||||
|
|
||||||
sound_events = []
|
sound_events = []
|
||||||
|
|
||||||
num_detections = len(clip_data.coords["detection"])
|
for detection in clip_data.coords["detection"]:
|
||||||
|
detection_data = clip_data.sel(detection=detection)
|
||||||
|
score = detection_data.score.item()
|
||||||
|
|
||||||
scores = clip_data.score.data
|
if "geometry" in clip_data and self.parse_full_geometry:
|
||||||
start_times = clip_data.start_time.data
|
geometry = data.geometry_validate(
|
||||||
end_times = clip_data.end_time.data
|
detection_data.geometry.item()
|
||||||
low_freqs = clip_data.low_freq.data
|
)
|
||||||
high_freqs = clip_data.high_freq.data
|
|
||||||
|
|
||||||
top_class_scores = clip_data.top_class_score.data
|
|
||||||
top_class = clip_data.top_class.data
|
|
||||||
|
|
||||||
num_classes = len(self.targets.class_names)
|
|
||||||
class_map = dict(
|
|
||||||
zip(
|
|
||||||
self.targets.class_names,
|
|
||||||
range(num_classes),
|
|
||||||
strict=True,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
geometries = None
|
|
||||||
if self.parse_full_geometry and "geometry" in clip_data:
|
|
||||||
geometries = clip_data.geometry.data
|
|
||||||
|
|
||||||
class_scores = None
|
|
||||||
if "class_scores" in clip_data:
|
|
||||||
class_scores = clip_data.class_scores.data
|
|
||||||
|
|
||||||
features = None
|
|
||||||
if "features" in clip_data:
|
|
||||||
features = clip_data.features.data
|
|
||||||
|
|
||||||
for index in range(num_detections):
|
|
||||||
score = scores[index]
|
|
||||||
|
|
||||||
if geometries is not None:
|
|
||||||
geometry = data.geometry_validate(geometries[index])
|
|
||||||
else:
|
else:
|
||||||
start_time = start_times[index]
|
start_time = detection_data.start_time.item()
|
||||||
end_time = end_times[index]
|
end_time = detection_data.end_time.item()
|
||||||
low_freq = low_freqs[index]
|
low_freq = detection_data.low_freq.item()
|
||||||
high_freq = high_freqs[index]
|
high_freq = detection_data.high_freq.item()
|
||||||
geometry = data.BoundingBox.model_construct(
|
geometry = data.BoundingBox.model_construct(
|
||||||
coordinates=[start_time, low_freq, end_time, high_freq]
|
coordinates=[start_time, low_freq, end_time, high_freq]
|
||||||
)
|
)
|
||||||
|
|
||||||
if class_scores is not None:
|
if "class_scores" in detection_data:
|
||||||
class_score = class_scores[index]
|
class_scores = detection_data.class_scores.data
|
||||||
else:
|
else:
|
||||||
class_score = np.zeros(num_classes)
|
class_scores = np.zeros(len(self.targets.class_names))
|
||||||
class_index = class_map[top_class[index]]
|
class_index = self.targets.class_names.index(
|
||||||
class_score[class_index] = top_class_scores[index]
|
detection_data.top_class.item()
|
||||||
|
)
|
||||||
|
class_scores[class_index] = (
|
||||||
|
detection_data.top_class_score.item()
|
||||||
|
)
|
||||||
|
|
||||||
feats = features[index] if features is not None else np.zeros(0)
|
if "features" in detection_data:
|
||||||
|
features = detection_data.features.data
|
||||||
|
else:
|
||||||
|
features = np.zeros(0)
|
||||||
|
|
||||||
sound_events.append(
|
sound_events.append(
|
||||||
Detection(
|
Detection(
|
||||||
geometry=geometry,
|
geometry=geometry,
|
||||||
detection_score=score,
|
detection_score=score,
|
||||||
class_scores=class_score,
|
class_scores=class_scores,
|
||||||
features=feats,
|
features=features,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -295,6 +236,4 @@ class RawFormatter(OutputFormatterProtocol[ClipDetections]):
|
|||||||
include_class_scores=config.include_class_scores,
|
include_class_scores=config.include_class_scores,
|
||||||
include_features=config.include_features,
|
include_features=config.include_features,
|
||||||
include_geometry=config.include_geometry,
|
include_geometry=config.include_geometry,
|
||||||
n_jobs=config.n_jobs,
|
|
||||||
show_progress=config.show_progress,
|
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from typing import Literal
|
||||||
|
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
|
||||||
from batdetect2.core.configs import BaseConfig
|
from batdetect2.core.configs import BaseConfig
|
||||||
@ -39,7 +41,7 @@ class PLTrainerConfig(BaseConfig):
|
|||||||
|
|
||||||
|
|
||||||
class TrainingConfig(BaseConfig):
|
class TrainingConfig(BaseConfig):
|
||||||
compile_model: bool = False
|
precision: Literal["medium", "high"] | None = None
|
||||||
train_loader: TrainLoaderConfig = Field(default_factory=TrainLoaderConfig)
|
train_loader: TrainLoaderConfig = Field(default_factory=TrainLoaderConfig)
|
||||||
val_loader: ValLoaderConfig = Field(default_factory=ValLoaderConfig)
|
val_loader: ValLoaderConfig = Field(default_factory=ValLoaderConfig)
|
||||||
optimizer: OptimizerConfig = Field(default_factory=AdamOptimizerConfig)
|
optimizer: OptimizerConfig = Field(default_factory=AdamOptimizerConfig)
|
||||||
|
|||||||
@ -2,6 +2,7 @@ from collections.abc import Sequence
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
import torch
|
||||||
from lightning import Trainer, seed_everything
|
from lightning import Trainer, seed_everything
|
||||||
from lightning.pytorch.loggers import Logger
|
from lightning.pytorch.loggers import Logger
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@ -15,7 +16,7 @@ from batdetect2.logging import (
|
|||||||
LoggingCallback,
|
LoggingCallback,
|
||||||
build_logger,
|
build_logger,
|
||||||
)
|
)
|
||||||
from batdetect2.models import ModelConfig, build_model, compile_model
|
from batdetect2.models import ModelConfig, build_model
|
||||||
from batdetect2.models.types import ModelProtocol
|
from batdetect2.models.types import ModelProtocol
|
||||||
from batdetect2.preprocess import PreprocessorProtocol, build_preprocessor
|
from batdetect2.preprocess import PreprocessorProtocol, build_preprocessor
|
||||||
from batdetect2.targets import (
|
from batdetect2.targets import (
|
||||||
@ -207,9 +208,16 @@ def run_train(
|
|||||||
run_name=run_name,
|
run_name=run_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
if train_config.compile_model:
|
if model_config.compile:
|
||||||
logger.info("Compiling detector...")
|
logger.info("Compiling model...")
|
||||||
compile_model(module.model)
|
module.compile()
|
||||||
|
|
||||||
|
if train_config.precision is not None:
|
||||||
|
logger.info(
|
||||||
|
"Setting precision float precision to {}",
|
||||||
|
train_config.precision,
|
||||||
|
)
|
||||||
|
torch.set_float32_matmul_precision(train_config.precision)
|
||||||
|
|
||||||
logger.info("Starting main training loop...")
|
logger.info("Starting main training loop...")
|
||||||
trainer.fit(
|
trainer.fit(
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import uuid
|
import uuid
|
||||||
from dataclasses import dataclass
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, List, Optional, cast
|
from typing import Callable, List, Optional
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
import lightning as L
|
import lightning as L
|
||||||
@ -16,7 +15,6 @@ from batdetect2.audio.clips import build_clipper
|
|||||||
from batdetect2.audio.types import AudioLoader, ClipperProtocol
|
from batdetect2.audio.types import AudioLoader, ClipperProtocol
|
||||||
from batdetect2.data import DatasetConfig, load_dataset
|
from batdetect2.data import DatasetConfig, load_dataset
|
||||||
from batdetect2.data.annotations.batdetect2 import BatDetect2FilesAnnotations
|
from batdetect2.data.annotations.batdetect2 import BatDetect2FilesAnnotations
|
||||||
from batdetect2.models.types import ModelProtocol
|
|
||||||
from batdetect2.preprocess import build_preprocessor
|
from batdetect2.preprocess import build_preprocessor
|
||||||
from batdetect2.preprocess.types import PreprocessorProtocol
|
from batdetect2.preprocess.types import PreprocessorProtocol
|
||||||
from batdetect2.targets import (
|
from batdetect2.targets import (
|
||||||
@ -33,12 +31,6 @@ from batdetect2.train.lightning import build_training_module
|
|||||||
from batdetect2.train.types import ClipLabeller
|
from batdetect2.train.types import ClipLabeller
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class DetectorCompileRecorder:
|
|
||||||
compile_count: int = 0
|
|
||||||
call_count: int = 0
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def example_data_dir() -> Path:
|
def example_data_dir() -> Path:
|
||||||
pkg_dir = Path(__file__).parent.parent
|
pkg_dir = Path(__file__).parent.parent
|
||||||
@ -164,15 +156,12 @@ def generate_whistle(tmp_path: Path):
|
|||||||
|
|
||||||
offset = int((time - duration / 2) * samplerate)
|
offset = int((time - duration / 2) * samplerate)
|
||||||
t = np.linspace(-duration / 2, duration / 2, frames, endpoint=False)
|
t = np.linspace(-duration / 2, duration / 2, frames, endpoint=False)
|
||||||
pulse = np.asarray(
|
data = signal.gausspulse(
|
||||||
signal.gausspulse(
|
t,
|
||||||
t,
|
fc=frequency,
|
||||||
fc=frequency,
|
bw=2 / (frequency * whistle_duration),
|
||||||
bw=2 / (frequency * whistle_duration),
|
|
||||||
),
|
|
||||||
dtype=np.float64,
|
|
||||||
)
|
)
|
||||||
wave = (np.roll(pulse, offset) * np.iinfo(np.int16).max).astype(
|
wave = (np.roll(data, offset) * np.iinfo(np.int16).max).astype(
|
||||||
np.int16
|
np.int16
|
||||||
)
|
)
|
||||||
sf.write(str(path), wave, samplerate, subtype="PCM_16")
|
sf.write(str(path), wave, samplerate, subtype="PCM_16")
|
||||||
@ -374,30 +363,6 @@ def sample_audio_loader() -> AudioLoader:
|
|||||||
return build_audio_loader()
|
return build_audio_loader()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def record_detector_compilation(
|
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
|
||||||
) -> Callable[[ModelProtocol], DetectorCompileRecorder]:
|
|
||||||
def factory(model: ModelProtocol) -> DetectorCompileRecorder:
|
|
||||||
recorder = DetectorCompileRecorder()
|
|
||||||
detector = cast(Any, model.detector)
|
|
||||||
original_call_impl = detector._call_impl
|
|
||||||
|
|
||||||
def compile_detector() -> None:
|
|
||||||
recorder.compile_count += 1
|
|
||||||
|
|
||||||
def compiled_call(*args, **kwargs):
|
|
||||||
recorder.call_count += 1
|
|
||||||
return original_call_impl(*args, **kwargs)
|
|
||||||
|
|
||||||
detector._compiled_call_impl = compiled_call
|
|
||||||
|
|
||||||
monkeypatch.setattr(detector, "compile", compile_detector)
|
|
||||||
return recorder
|
|
||||||
|
|
||||||
return factory
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def bat_tag() -> data.Tag:
|
def bat_tag() -> data.Tag:
|
||||||
return data.Tag(key="class", value="bat")
|
return data.Tag(key="class", value="bat")
|
||||||
|
|||||||
@ -153,61 +153,6 @@ def test_process_spectrogram_rejects_batched_input(
|
|||||||
api_v2.process_spectrogram(spec)
|
api_v2.process_spectrogram(spec)
|
||||||
|
|
||||||
|
|
||||||
def test_user_can_compile_api_detector(
|
|
||||||
api_v2: BatDetect2API,
|
|
||||||
example_audio_files: list[Path],
|
|
||||||
record_detector_compilation,
|
|
||||||
) -> None:
|
|
||||||
recorder = record_detector_compilation(api_v2.model)
|
|
||||||
audio = api_v2.load_audio(example_audio_files[0])
|
|
||||||
spec = api_v2.generate_spectrogram(audio)
|
|
||||||
|
|
||||||
api_v2.compile()
|
|
||||||
api_v2.compile()
|
|
||||||
api_v2.process_spectrogram(spec)
|
|
||||||
|
|
||||||
assert recorder.compile_count == 1
|
|
||||||
assert recorder.call_count == 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_api_from_config_compiles_detector_when_requested(
|
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
|
||||||
) -> None:
|
|
||||||
compiled_models = []
|
|
||||||
|
|
||||||
def compile_model(model):
|
|
||||||
compiled_models.append(model)
|
|
||||||
return model
|
|
||||||
|
|
||||||
monkeypatch.setattr("batdetect2.models.compile_model", compile_model)
|
|
||||||
|
|
||||||
api = BatDetect2API.from_config(
|
|
||||||
compile_model=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert compiled_models == [api.model]
|
|
||||||
|
|
||||||
|
|
||||||
def test_api_from_checkpoint_compiles_detector_when_requested(
|
|
||||||
tiny_checkpoint_path: Path,
|
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
|
||||||
) -> None:
|
|
||||||
compiled_models = []
|
|
||||||
|
|
||||||
def compile_model(model):
|
|
||||||
compiled_models.append(model)
|
|
||||||
return model
|
|
||||||
|
|
||||||
monkeypatch.setattr("batdetect2.models.compile_model", compile_model)
|
|
||||||
|
|
||||||
api = BatDetect2API.from_checkpoint(
|
|
||||||
tiny_checkpoint_path,
|
|
||||||
compile_model=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert compiled_models == [api.model]
|
|
||||||
|
|
||||||
|
|
||||||
def test_user_can_read_top_class_and_other_class_scores(
|
def test_user_can_read_top_class_and_other_class_scores(
|
||||||
api_v2: BatDetect2API,
|
api_v2: BatDetect2API,
|
||||||
example_audio_files: list[Path],
|
example_audio_files: list[Path],
|
||||||
|
|||||||
@ -61,102 +61,3 @@ def test_roundtrip(
|
|||||||
).all()
|
).all()
|
||||||
assert (recovered_prediction.features == detection.features).all()
|
assert (recovered_prediction.features == detection.features).all()
|
||||||
assert recovered_prediction.geometry == detection.geometry
|
assert recovered_prediction.geometry == detection.geometry
|
||||||
|
|
||||||
|
|
||||||
def test_roundtrip_recovers_recording_metadata(
|
|
||||||
sample_formatter,
|
|
||||||
create_recording,
|
|
||||||
create_clip,
|
|
||||||
sample_targets: TargetProtocol,
|
|
||||||
tmp_path: Path,
|
|
||||||
):
|
|
||||||
recording = create_recording(
|
|
||||||
tags=[data.Tag(key="source", value="test-recorder")],
|
|
||||||
duration=2,
|
|
||||||
samplerate=384_000,
|
|
||||||
time_expansion=10,
|
|
||||||
)
|
|
||||||
clip = create_clip(recording=recording, start_time=0.25, end_time=0.75)
|
|
||||||
detection = Detection(
|
|
||||||
geometry=data.BoundingBox(
|
|
||||||
coordinates=[0.3, 45_000, 0.4, 70_000],
|
|
||||||
),
|
|
||||||
detection_score=0.5,
|
|
||||||
class_scores=np.ones(len(sample_targets.class_names)),
|
|
||||||
features=np.ones(32),
|
|
||||||
)
|
|
||||||
prediction = ClipDetections(clip=clip, detections=[detection])
|
|
||||||
|
|
||||||
path = tmp_path / "predictions"
|
|
||||||
|
|
||||||
sample_formatter.save(predictions=[prediction], path=path)
|
|
||||||
recovered = sample_formatter.load(path=path)
|
|
||||||
|
|
||||||
assert len(recovered) == 1
|
|
||||||
assert recovered[0].clip.recording.model_dump(mode="json") == (
|
|
||||||
recording.model_dump(mode="json")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_roundtrip_empty_detections(
|
|
||||||
sample_formatter,
|
|
||||||
clip: data.Clip,
|
|
||||||
tmp_path: Path,
|
|
||||||
):
|
|
||||||
prediction = ClipDetections(clip=clip, detections=[])
|
|
||||||
|
|
||||||
path = tmp_path / "predictions"
|
|
||||||
|
|
||||||
sample_formatter.save(predictions=[prediction], path=path)
|
|
||||||
recovered = sample_formatter.load(path=path)
|
|
||||||
|
|
||||||
assert len(recovered) == 1
|
|
||||||
assert recovered[0].detections == []
|
|
||||||
assert recovered[0].clip.uuid == prediction.clip.uuid
|
|
||||||
assert recovered[0].clip.start_time == prediction.clip.start_time
|
|
||||||
assert recovered[0].clip.end_time == prediction.clip.end_time
|
|
||||||
|
|
||||||
|
|
||||||
def test_roundtrip_loads_with_multiprocessing(
|
|
||||||
clip: data.Clip,
|
|
||||||
sample_targets: TargetProtocol,
|
|
||||||
tmp_path: Path,
|
|
||||||
):
|
|
||||||
save_formatter = build_output_formatter(
|
|
||||||
config=RawOutputConfig(),
|
|
||||||
targets=sample_targets,
|
|
||||||
)
|
|
||||||
load_formatter = build_output_formatter(
|
|
||||||
config=RawOutputConfig(n_jobs=2),
|
|
||||||
targets=sample_targets,
|
|
||||||
)
|
|
||||||
predictions = [
|
|
||||||
ClipDetections(
|
|
||||||
clip=data.Clip(
|
|
||||||
recording=clip.recording,
|
|
||||||
start_time=index,
|
|
||||||
end_time=index + 0.5,
|
|
||||||
),
|
|
||||||
detections=[
|
|
||||||
Detection(
|
|
||||||
geometry=data.BoundingBox(
|
|
||||||
coordinates=[index, 45_000, index + 0.1, 70_000],
|
|
||||||
),
|
|
||||||
detection_score=0.5,
|
|
||||||
class_scores=np.ones(len(sample_targets.class_names)),
|
|
||||||
features=np.ones(32),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
for index in range(2)
|
|
||||||
]
|
|
||||||
|
|
||||||
path = tmp_path / "predictions"
|
|
||||||
|
|
||||||
save_formatter.save(predictions=predictions, path=path)
|
|
||||||
recovered = load_formatter.load(path=path)
|
|
||||||
|
|
||||||
assert len(recovered) == len(predictions)
|
|
||||||
assert {item.clip.uuid for item in recovered} == {
|
|
||||||
item.clip.uuid for item in predictions
|
|
||||||
}
|
|
||||||
|
|||||||
@ -3,8 +3,6 @@ from pathlib import Path
|
|||||||
import pytest
|
import pytest
|
||||||
from soundevent import data
|
from soundevent import data
|
||||||
|
|
||||||
from batdetect2.api_v2 import BatDetect2API
|
|
||||||
from batdetect2.inference import InferenceConfig
|
|
||||||
from batdetect2.inference.batch import run_batch_inference
|
from batdetect2.inference.batch import run_batch_inference
|
||||||
from batdetect2.targets import build_roi_mapping, build_targets
|
from batdetect2.targets import build_roi_mapping, build_targets
|
||||||
from batdetect2.train import load_model_from_checkpoint
|
from batdetect2.train import load_model_from_checkpoint
|
||||||
@ -55,54 +53,3 @@ def test_run_batch_inference_matches_single_clip_inference(
|
|||||||
strict=True,
|
strict=True,
|
||||||
):
|
):
|
||||||
assert_clip_detections_equal(batched, single)
|
assert_clip_detections_equal(batched, single)
|
||||||
|
|
||||||
|
|
||||||
def test_run_batch_inference_compiles_detector_when_config_requests_compile(
|
|
||||||
example_annotations: list[data.ClipAnnotation],
|
|
||||||
record_detector_compilation,
|
|
||||||
) -> None:
|
|
||||||
api = BatDetect2API.from_config()
|
|
||||||
recorder = record_detector_compilation(api.model)
|
|
||||||
|
|
||||||
predictions = run_batch_inference(
|
|
||||||
api.model,
|
|
||||||
[example_annotations[0].clip],
|
|
||||||
targets=api.targets,
|
|
||||||
roi_mapper=api.roi_mapper,
|
|
||||||
audio_loader=api.audio_loader,
|
|
||||||
preprocessor=api.preprocessor,
|
|
||||||
output_transform=api.output_transform,
|
|
||||||
inference_config=InferenceConfig(compile_model=True),
|
|
||||||
batch_size=1,
|
|
||||||
num_workers=0,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert predictions
|
|
||||||
assert recorder.compile_count == 1
|
|
||||||
assert recorder.call_count > 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_run_batch_inference_does_not_recompile_compiled_detector(
|
|
||||||
example_annotations: list[data.ClipAnnotation],
|
|
||||||
record_detector_compilation,
|
|
||||||
) -> None:
|
|
||||||
api = BatDetect2API.from_config()
|
|
||||||
recorder = record_detector_compilation(api.model)
|
|
||||||
api.compile()
|
|
||||||
|
|
||||||
predictions = run_batch_inference(
|
|
||||||
api.model,
|
|
||||||
[example_annotations[0].clip],
|
|
||||||
targets=api.targets,
|
|
||||||
roi_mapper=api.roi_mapper,
|
|
||||||
audio_loader=api.audio_loader,
|
|
||||||
preprocessor=api.preprocessor,
|
|
||||||
output_transform=api.output_transform,
|
|
||||||
inference_config=InferenceConfig(compile_model=True),
|
|
||||||
batch_size=1,
|
|
||||||
num_workers=0,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert predictions
|
|
||||||
assert recorder.compile_count == 1
|
|
||||||
assert recorder.call_count > 0
|
|
||||||
|
|||||||
@ -10,8 +10,6 @@ from hypothesis import strategies as st
|
|||||||
|
|
||||||
from batdetect2 import api
|
from batdetect2 import api
|
||||||
from batdetect2.detector import parameters
|
from batdetect2.detector import parameters
|
||||||
from batdetect2.models.backbones import UNetBackboneConfig
|
|
||||||
from batdetect2.train import load_model_from_checkpoint
|
|
||||||
|
|
||||||
|
|
||||||
@settings(deadline=None, max_examples=5)
|
@settings(deadline=None, max_examples=5)
|
||||||
@ -74,16 +72,3 @@ def test_can_import_model_without_pickle_on_test_data(
|
|||||||
model=model_with_pickle,
|
model=model_with_pickle,
|
||||||
)
|
)
|
||||||
assert predictions_without_pickle == predictions_with_pickle
|
assert predictions_without_pickle == predictions_with_pickle
|
||||||
|
|
||||||
|
|
||||||
def test_bundled_checkpoint_loads_with_current_config_schema() -> None:
|
|
||||||
"""Bundled checkpoints remain loadable after config schema changes."""
|
|
||||||
model, configs = load_model_from_checkpoint()
|
|
||||||
|
|
||||||
assert model.class_names
|
|
||||||
assert isinstance(configs.model.architecture, UNetBackboneConfig)
|
|
||||||
frequency_aggregation = (
|
|
||||||
configs.model.architecture.bottleneck.frequency_aggregation
|
|
||||||
)
|
|
||||||
assert frequency_aggregation is not None
|
|
||||||
assert frequency_aggregation.name
|
|
||||||
|
|||||||
@ -4,8 +4,6 @@ import torch
|
|||||||
from batdetect2.models.blocks import (
|
from batdetect2.models.blocks import (
|
||||||
ConvBlock,
|
ConvBlock,
|
||||||
ConvConfig,
|
ConvConfig,
|
||||||
EfficientSelfAttention,
|
|
||||||
EfficientSelfAttentionConfig,
|
|
||||||
FreqCoordConvDownBlock,
|
FreqCoordConvDownBlock,
|
||||||
FreqCoordConvDownConfig,
|
FreqCoordConvDownConfig,
|
||||||
FreqCoordConvUpBlock,
|
FreqCoordConvUpBlock,
|
||||||
@ -20,8 +18,6 @@ from batdetect2.models.blocks import (
|
|||||||
StandardConvUpConfig,
|
StandardConvUpConfig,
|
||||||
VerticalConv,
|
VerticalConv,
|
||||||
VerticalConvConfig,
|
VerticalConvConfig,
|
||||||
VerticalMean,
|
|
||||||
VerticalMeanConfig,
|
|
||||||
build_layer,
|
build_layer,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -103,20 +99,6 @@ def test_vertical_conv_forward_shape(dummy_input):
|
|||||||
assert block.out_channels == out_channels
|
assert block.out_channels == out_channels
|
||||||
|
|
||||||
|
|
||||||
def test_vertical_mean_forward_shape(dummy_input):
|
|
||||||
"""Test that VerticalMean collapses the height dimension to 1."""
|
|
||||||
in_channels = dummy_input.size(1)
|
|
||||||
input_height = dummy_input.size(2)
|
|
||||||
out_channels = 32
|
|
||||||
|
|
||||||
block = VerticalMean(in_channels, out_channels, input_height)
|
|
||||||
output = block(dummy_input)
|
|
||||||
|
|
||||||
assert output.shape == (2, out_channels, 1, 32)
|
|
||||||
assert block.out_channels == out_channels
|
|
||||||
assert block.get_output_height(input_height) == 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_self_attention_forward_shape(dummy_bottleneck_input):
|
def test_self_attention_forward_shape(dummy_bottleneck_input):
|
||||||
"""Test that SelfAttention maintains the exact shape."""
|
"""Test that SelfAttention maintains the exact shape."""
|
||||||
in_channels = dummy_bottleneck_input.size(1)
|
in_channels = dummy_bottleneck_input.size(1)
|
||||||
@ -131,20 +113,6 @@ def test_self_attention_forward_shape(dummy_bottleneck_input):
|
|||||||
assert block.out_channels == in_channels
|
assert block.out_channels == in_channels
|
||||||
|
|
||||||
|
|
||||||
def test_efficient_self_attention_forward_shape(dummy_bottleneck_input):
|
|
||||||
"""Test that EfficientSelfAttention maintains the exact shape."""
|
|
||||||
in_channels = dummy_bottleneck_input.size(1)
|
|
||||||
attention_channels = 32
|
|
||||||
|
|
||||||
block = EfficientSelfAttention(
|
|
||||||
in_channels=in_channels, attention_channels=attention_channels
|
|
||||||
)
|
|
||||||
output = block(dummy_bottleneck_input)
|
|
||||||
|
|
||||||
assert output.shape == dummy_bottleneck_input.shape
|
|
||||||
assert block.out_channels == in_channels
|
|
||||||
|
|
||||||
|
|
||||||
def test_self_attention_weights(dummy_bottleneck_input):
|
def test_self_attention_weights(dummy_bottleneck_input):
|
||||||
"""Test that attention weights sum to 1 over the time sequence."""
|
"""Test that attention weights sum to 1 over the time sequence."""
|
||||||
in_channels = dummy_bottleneck_input.size(1)
|
in_channels = dummy_bottleneck_input.size(1)
|
||||||
@ -163,51 +131,6 @@ def test_self_attention_weights(dummy_bottleneck_input):
|
|||||||
assert torch.allclose(sum_weights, torch.ones_like(sum_weights), atol=1e-5)
|
assert torch.allclose(sum_weights, torch.ones_like(sum_weights), atol=1e-5)
|
||||||
|
|
||||||
|
|
||||||
def test_efficient_self_attention_matches_self_attention_with_copied_weights(
|
|
||||||
dummy_bottleneck_input,
|
|
||||||
):
|
|
||||||
"""Temporarily compare efficient and original attention outputs."""
|
|
||||||
in_channels = dummy_bottleneck_input.size(1)
|
|
||||||
attention_channels = 32
|
|
||||||
|
|
||||||
block = SelfAttention(
|
|
||||||
in_channels=in_channels,
|
|
||||||
attention_channels=attention_channels,
|
|
||||||
)
|
|
||||||
efficient_block = EfficientSelfAttention(
|
|
||||||
in_channels=in_channels,
|
|
||||||
attention_channels=attention_channels,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Match the fused QKV projection to the original separate projections.
|
|
||||||
with torch.no_grad():
|
|
||||||
efficient_block.qkv_proj.weight[:attention_channels].copy_(
|
|
||||||
block.query_fun.weight
|
|
||||||
)
|
|
||||||
efficient_block.qkv_proj.bias[:attention_channels].copy_(
|
|
||||||
block.query_fun.bias
|
|
||||||
)
|
|
||||||
efficient_block.qkv_proj.weight[
|
|
||||||
attention_channels : 2 * attention_channels
|
|
||||||
].copy_(block.key_fun.weight)
|
|
||||||
efficient_block.qkv_proj.bias[
|
|
||||||
attention_channels : 2 * attention_channels
|
|
||||||
].copy_(block.key_fun.bias)
|
|
||||||
efficient_block.qkv_proj.weight[2 * attention_channels :].copy_(
|
|
||||||
block.value_fun.weight
|
|
||||||
)
|
|
||||||
efficient_block.qkv_proj.bias[2 * attention_channels :].copy_(
|
|
||||||
block.value_fun.bias
|
|
||||||
)
|
|
||||||
efficient_block.pro_fun.weight.copy_(block.pro_fun.weight)
|
|
||||||
efficient_block.pro_fun.bias.copy_(block.pro_fun.bias)
|
|
||||||
|
|
||||||
output = block(dummy_bottleneck_input)
|
|
||||||
efficient_output = efficient_block(dummy_bottleneck_input)
|
|
||||||
|
|
||||||
torch.testing.assert_close(output, efficient_output)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"layer_config, expected_type",
|
"layer_config, expected_type",
|
||||||
[
|
[
|
||||||
@ -217,12 +140,7 @@ def test_efficient_self_attention_matches_self_attention_with_copied_weights(
|
|||||||
(FreqCoordConvDownConfig(out_channels=32), FreqCoordConvDownBlock),
|
(FreqCoordConvDownConfig(out_channels=32), FreqCoordConvDownBlock),
|
||||||
(FreqCoordConvUpConfig(out_channels=32), FreqCoordConvUpBlock),
|
(FreqCoordConvUpConfig(out_channels=32), FreqCoordConvUpBlock),
|
||||||
(SelfAttentionConfig(attention_channels=32), SelfAttention),
|
(SelfAttentionConfig(attention_channels=32), SelfAttention),
|
||||||
(
|
|
||||||
EfficientSelfAttentionConfig(attention_channels=32),
|
|
||||||
EfficientSelfAttention,
|
|
||||||
),
|
|
||||||
(VerticalConvConfig(channels=32), VerticalConv),
|
(VerticalConvConfig(channels=32), VerticalConv),
|
||||||
(VerticalMeanConfig(channels=32), VerticalMean),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_build_layer_factory(layer_config, expected_type):
|
def test_build_layer_factory(layer_config, expected_type):
|
||||||
|
|||||||
@ -1,55 +0,0 @@
|
|||||||
import torch
|
|
||||||
|
|
||||||
from batdetect2.models.blocks import (
|
|
||||||
SelfAttention,
|
|
||||||
SelfAttentionConfig,
|
|
||||||
VerticalMeanConfig,
|
|
||||||
)
|
|
||||||
from batdetect2.models.bottleneck import (
|
|
||||||
Bottleneck,
|
|
||||||
BottleneckConfig,
|
|
||||||
build_bottleneck,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_bottleneck_layers_use_frequency_aggregation_channels() -> None:
|
|
||||||
"""Layers after frequency aggregation are built for aggregated channels."""
|
|
||||||
config = BottleneckConfig(
|
|
||||||
channels=128,
|
|
||||||
frequency_aggregation=VerticalMeanConfig(channels=128),
|
|
||||||
layers=[SelfAttentionConfig(attention_channels=32)],
|
|
||||||
)
|
|
||||||
|
|
||||||
bottleneck = build_bottleneck(
|
|
||||||
input_height=8,
|
|
||||||
in_channels=64,
|
|
||||||
config=config,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert isinstance(bottleneck, Bottleneck)
|
|
||||||
attention = bottleneck.layers[0]
|
|
||||||
assert isinstance(attention, SelfAttention)
|
|
||||||
assert attention.in_channels == 128
|
|
||||||
|
|
||||||
output = bottleneck(torch.randn(2, 64, 8, 10))
|
|
||||||
|
|
||||||
assert output.shape == (2, 128, 8, 10)
|
|
||||||
|
|
||||||
|
|
||||||
def test_bottleneck_default_frequency_aggregation_matches_channels() -> None:
|
|
||||||
"""Minimal configs keep advertised and actual output channels in sync."""
|
|
||||||
config = BottleneckConfig(channels=128, layers=[])
|
|
||||||
|
|
||||||
bottleneck = build_bottleneck(
|
|
||||||
input_height=8,
|
|
||||||
in_channels=64,
|
|
||||||
config=config,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert isinstance(bottleneck, Bottleneck)
|
|
||||||
assert bottleneck.out_channels == 128
|
|
||||||
assert bottleneck.conv_vert.out_channels == 128
|
|
||||||
|
|
||||||
output = bottleneck(torch.randn(2, 64, 8, 10))
|
|
||||||
|
|
||||||
assert output.shape == (2, bottleneck.out_channels, 8, 10)
|
|
||||||
@ -49,16 +49,6 @@ def build_default_module(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def build_fast_train_config() -> TrainingConfig:
|
|
||||||
train_config = TrainingConfig()
|
|
||||||
train_config.trainer.limit_train_batches = 1
|
|
||||||
train_config.trainer.limit_val_batches = 1
|
|
||||||
train_config.trainer.log_every_n_steps = 1
|
|
||||||
train_config.train_loader.batch_size = 1
|
|
||||||
train_config.train_loader.augmentations.enabled = False
|
|
||||||
return train_config
|
|
||||||
|
|
||||||
|
|
||||||
def test_can_initialize_default_module():
|
def test_can_initialize_default_module():
|
||||||
module = build_default_module()
|
module = build_default_module()
|
||||||
assert isinstance(module, L.LightningModule)
|
assert isinstance(module, L.LightningModule)
|
||||||
@ -281,7 +271,19 @@ def test_train_smoke_produces_loadable_checkpoint(
|
|||||||
sample_audio_loader: AudioLoader,
|
sample_audio_loader: AudioLoader,
|
||||||
):
|
):
|
||||||
# Given
|
# Given
|
||||||
train_config = build_fast_train_config()
|
train_config = TrainingConfig.model_validate(
|
||||||
|
{
|
||||||
|
"trainer": {
|
||||||
|
"limit_train_batches": 1,
|
||||||
|
"limit_val_batches": 1,
|
||||||
|
"log_every_n_steps": 1,
|
||||||
|
},
|
||||||
|
"train_loader": {
|
||||||
|
"batch_size": 1,
|
||||||
|
"augmentations": {"enabled": False},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# When
|
# When
|
||||||
run_train(
|
run_train(
|
||||||
@ -308,47 +310,6 @@ def test_train_smoke_produces_loadable_checkpoint(
|
|||||||
assert outputs is not None
|
assert outputs is not None
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.slow
|
|
||||||
def test_run_train_compiles_detector_when_train_config_requests_compile(
|
|
||||||
tmp_path: Path,
|
|
||||||
example_annotations: list[data.ClipAnnotation],
|
|
||||||
record_detector_compilation,
|
|
||||||
) -> None:
|
|
||||||
targets_config = TargetConfig()
|
|
||||||
targets = build_targets(targets_config)
|
|
||||||
roi_mapper = build_roi_mapping(targets_config.roi)
|
|
||||||
model = build_model(
|
|
||||||
ModelConfig(),
|
|
||||||
class_names=targets.class_names,
|
|
||||||
dimension_names=roi_mapper.dimension_names,
|
|
||||||
)
|
|
||||||
train_config = build_fast_train_config()
|
|
||||||
train_config.compile_model = True
|
|
||||||
recorder = record_detector_compilation(model)
|
|
||||||
|
|
||||||
module = run_train(
|
|
||||||
train_annotations=example_annotations[:1],
|
|
||||||
val_annotations=example_annotations[:1],
|
|
||||||
model=model,
|
|
||||||
targets=targets,
|
|
||||||
roi_mapper=roi_mapper,
|
|
||||||
targets_config=targets_config,
|
|
||||||
train_config=train_config,
|
|
||||||
num_epochs=1,
|
|
||||||
train_workers=0,
|
|
||||||
val_workers=0,
|
|
||||||
checkpoint_dir=tmp_path / "checkpoints",
|
|
||||||
log_dir=tmp_path / "logs",
|
|
||||||
seed=0,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (
|
|
||||||
getattr(module.model.detector, "_compiled_call_impl", None) is not None
|
|
||||||
)
|
|
||||||
assert recorder.compile_count == 1
|
|
||||||
assert recorder.call_count > 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_build_training_module_uses_provided_model() -> None:
|
def test_build_training_module_uses_provided_model() -> None:
|
||||||
targets = build_targets(TargetConfig())
|
targets = build_targets(TargetConfig())
|
||||||
roi_mapper = build_roi_mapping(TargetConfig().roi)
|
roi_mapper = build_roi_mapping(TargetConfig().roi)
|
||||||
|
|||||||
394
uv.lock
generated
394
uv.lock
generated
@ -453,7 +453,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "batdetect2"
|
name = "batdetect2"
|
||||||
version = "2.0.0b2"
|
version = "2.0.0b1"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "click" },
|
{ name = "click" },
|
||||||
@ -557,8 +557,8 @@ requires-dist = [
|
|||||||
{ name = "soundevent", extras = ["audio", "geometry", "plot"], specifier = ">=2.10.0" },
|
{ name = "soundevent", extras = ["audio", "geometry", "plot"], specifier = ">=2.10.0" },
|
||||||
{ name = "soundfile", specifier = ">=0.12.1" },
|
{ name = "soundfile", specifier = ">=0.12.1" },
|
||||||
{ name = "tensorboard", specifier = ">=2.16.2" },
|
{ name = "tensorboard", specifier = ">=2.16.2" },
|
||||||
{ name = "torch", specifier = ">=2.0.0" },
|
{ name = "torch", specifier = ">=1.13.1" },
|
||||||
{ name = "torchaudio", specifier = ">=2.0.0" },
|
{ name = "torchaudio", specifier = ">=1.13.1" },
|
||||||
{ name = "xarray", specifier = ">=2024.0.0" },
|
{ name = "xarray", specifier = ">=2024.0.0" },
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -626,10 +626,10 @@ name = "bitsandbytes"
|
|||||||
version = "0.49.2"
|
version = "0.49.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
|
||||||
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" },
|
||||||
{ name = "packaging" },
|
{ name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||||
{ name = "torch" },
|
{ name = "torch", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||||
]
|
]
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/d8/7d/f1fe0992334b18cd8494f89aeec1dcc674635584fcd9f115784fea3a1d05/bitsandbytes-0.49.2-py3-none-macosx_14_0_arm64.whl", hash = "sha256:87be5975edeac5396d699ecbc39dfc47cf2c026daaf2d5852a94368611a6823f", size = 131940, upload-time = "2026-02-16T21:26:04.572Z" },
|
{ url = "https://files.pythonhosted.org/packages/d8/7d/f1fe0992334b18cd8494f89aeec1dcc674635584fcd9f115784fea3a1d05/bitsandbytes-0.49.2-py3-none-macosx_14_0_arm64.whl", hash = "sha256:87be5975edeac5396d699ecbc39dfc47cf2c026daaf2d5852a94368611a6823f", size = 131940, upload-time = "2026-02-16T21:26:04.572Z" },
|
||||||
@ -1070,7 +1070,7 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
|
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -1151,7 +1151,7 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } },
|
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -1345,7 +1345,7 @@ name = "cuda-bindings"
|
|||||||
version = "13.2.0"
|
version = "13.2.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "cuda-pathfinder" },
|
{ name = "cuda-pathfinder", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556", size = 5744254, upload-time = "2026-03-11T00:12:29.798Z" },
|
{ url = "https://files.pythonhosted.org/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556", size = 5744254, upload-time = "2026-03-11T00:12:29.798Z" },
|
||||||
@ -1376,37 +1376,37 @@ wheels = [
|
|||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
cublas = [
|
cublas = [
|
||||||
{ name = "nvidia-cublas" },
|
{ name = "nvidia-cublas", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
cudart = [
|
cudart = [
|
||||||
{ name = "nvidia-cuda-runtime" },
|
{ name = "nvidia-cuda-runtime", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
cufft = [
|
cufft = [
|
||||||
{ name = "nvidia-cufft" },
|
{ name = "nvidia-cufft", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
cufile = [
|
cufile = [
|
||||||
{ name = "nvidia-cufile" },
|
{ name = "nvidia-cufile", marker = "sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
cupti = [
|
cupti = [
|
||||||
{ name = "nvidia-cuda-cupti" },
|
{ name = "nvidia-cuda-cupti", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
curand = [
|
curand = [
|
||||||
{ name = "nvidia-curand" },
|
{ name = "nvidia-curand", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
cusolver = [
|
cusolver = [
|
||||||
{ name = "nvidia-cusolver" },
|
{ name = "nvidia-cusolver", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
cusparse = [
|
cusparse = [
|
||||||
{ name = "nvidia-cusparse" },
|
{ name = "nvidia-cusparse", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
nvjitlink = [
|
nvjitlink = [
|
||||||
{ name = "nvidia-nvjitlink" },
|
{ name = "nvidia-nvjitlink", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
nvrtc = [
|
nvrtc = [
|
||||||
{ name = "nvidia-cuda-nvrtc" },
|
{ name = "nvidia-cuda-nvrtc", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
nvtx = [
|
nvtx = [
|
||||||
{ name = "nvidia-nvtx" },
|
{ name = "nvidia-nvtx", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1875,7 +1875,7 @@ name = "exceptiongroup"
|
|||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -2417,7 +2417,7 @@ name = "gunicorn"
|
|||||||
version = "25.3.0"
|
version = "25.3.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "packaging" },
|
{ name = "packaging", marker = "platform_machine != 'ARM64' or sys_platform != 'win32'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/c4/f4/e78fa054248fab913e2eab0332c6c2cb07421fca1ce56d8fe43b6aef57a4/gunicorn-25.3.0.tar.gz", hash = "sha256:f74e1b2f9f76f6cd1ca01198968bd2dd65830edc24b6e8e4d78de8320e2fe889", size = 634883, upload-time = "2026-03-27T00:00:26.092Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/c4/f4/e78fa054248fab913e2eab0332c6c2cb07421fca1ce56d8fe43b6aef57a4/gunicorn-25.3.0.tar.gz", hash = "sha256:f74e1b2f9f76f6cd1ca01198968bd2dd65830edc24b6e8e4d78de8320e2fe889", size = 634883, upload-time = "2026-03-27T00:00:26.092Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -2638,17 +2638,17 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
{ name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" },
|
||||||
{ name = "decorator" },
|
{ name = "decorator", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "exceptiongroup" },
|
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "jedi" },
|
{ name = "jedi", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "matplotlib-inline" },
|
{ name = "matplotlib-inline", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
{ name = "pexpect", marker = "python_full_version < '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||||
{ name = "prompt-toolkit" },
|
{ name = "prompt-toolkit", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "pygments" },
|
{ name = "pygments", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "stack-data" },
|
{ name = "stack-data", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "traitlets" },
|
{ name = "traitlets", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/40/18/f8598d287006885e7136451fdea0755af4ebcbfe342836f24deefaed1164/ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624", size = 5513971, upload-time = "2026-03-27T10:02:13.94Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/40/18/f8598d287006885e7136451fdea0755af4ebcbfe342836f24deefaed1164/ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624", size = 5513971, upload-time = "2026-03-27T10:02:13.94Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -2674,18 +2674,18 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
{ name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" },
|
||||||
{ name = "decorator" },
|
{ name = "decorator", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "ipython-pygments-lexers" },
|
{ name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "jedi" },
|
{ name = "jedi", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "matplotlib-inline" },
|
{ name = "matplotlib-inline", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
{ name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||||
{ name = "prompt-toolkit" },
|
{ name = "prompt-toolkit", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "psutil" },
|
{ name = "psutil", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "pygments" },
|
{ name = "pygments", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "stack-data" },
|
{ name = "stack-data", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "traitlets" },
|
{ name = "traitlets", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "typing-extensions", marker = "python_full_version < '3.12'" },
|
{ name = "typing-extensions", marker = "python_full_version == '3.11.*'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/87cda5842cf5c31837c06ddb588e11c3c35d8ece89b7a0108c06b8c9b00a/ipython-9.13.0.tar.gz", hash = "sha256:7e834b6afc99f020e3f05966ced34792f40267d64cb1ea9043886dab0dde5967", size = 4430549, upload-time = "2026-04-24T12:24:55.221Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/87cda5842cf5c31837c06ddb588e11c3c35d8ece89b7a0108c06b8c9b00a/ipython-9.13.0.tar.gz", hash = "sha256:7e834b6afc99f020e3f05966ced34792f40267d64cb1ea9043886dab0dde5967", size = 4430549, upload-time = "2026-04-24T12:24:55.221Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -2697,7 +2697,7 @@ name = "ipython-pygments-lexers"
|
|||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "pygments" },
|
{ name = "pygments", marker = "python_full_version >= '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -3460,7 +3460,7 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "mdurl" },
|
{ name = "mdurl", marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -3486,7 +3486,7 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "mdurl" },
|
{ name = "mdurl", marker = "python_full_version >= '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -3961,12 +3961,12 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" } },
|
{ name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
{ name = "jinja2" },
|
{ name = "jinja2", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" } },
|
{ name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
{ name = "mdit-py-plugins" },
|
{ name = "mdit-py-plugins", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "pyyaml" },
|
{ name = "pyyaml", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } },
|
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985, upload-time = "2025-02-12T10:53:03.833Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985, upload-time = "2025-02-12T10:53:03.833Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -3992,12 +3992,12 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" } },
|
{ name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "jinja2" },
|
{ name = "jinja2", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "markdown-it-py", version = "4.2.0", source = { registry = "https://pypi.org/simple" } },
|
{ name = "markdown-it-py", version = "4.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "mdit-py-plugins" },
|
{ name = "mdit-py-plugins", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "pyyaml" },
|
{ name = "pyyaml", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/33/fa/7b45eef11b7971f0beb29d27b7bfe0d747d063aa29e170d9edd004733c8a/myst_parser-5.0.0.tar.gz", hash = "sha256:f6f231452c56e8baa662cc352c548158f6a16fcbd6e3800fc594978002b94f3a", size = 98535, upload-time = "2026-01-15T09:08:18.036Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/33/fa/7b45eef11b7971f0beb29d27b7bfe0d747d063aa29e170d9edd004733c8a/myst_parser-5.0.0.tar.gz", hash = "sha256:f6f231452c56e8baa662cc352c548158f6a16fcbd6e3800fc594978002b94f3a", size = 98535, upload-time = "2026-01-15T09:08:18.036Z" }
|
||||||
@ -4086,9 +4086,9 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and platform_machine == 'ARM64' and sys_platform == 'win32'",
|
"python_full_version < '3.11' and platform_machine == 'ARM64' and sys_platform == 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "certifi" },
|
{ name = "certifi", marker = "python_full_version < '3.11' and platform_machine == 'ARM64' and sys_platform == 'win32'" },
|
||||||
{ name = "cftime" },
|
{ name = "cftime", marker = "python_full_version < '3.11' and platform_machine == 'ARM64' and sys_platform == 'win32'" },
|
||||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
|
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and platform_machine == 'ARM64' and sys_platform == 'win32'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/0e/76/7bc801796dee752c1ce9cd6935564a6ee79d5c9d9ef9192f57b156495a35/netcdf4-1.7.3.tar.gz", hash = "sha256:83f122fc3415e92b1d4904fd6a0898468b5404c09432c34beb6b16c533884673", size = 836095, upload-time = "2025-10-13T18:38:00.76Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/0e/76/7bc801796dee752c1ce9cd6935564a6ee79d5c9d9ef9192f57b156495a35/netcdf4-1.7.3.tar.gz", hash = "sha256:83f122fc3415e92b1d4904fd6a0898468b5404c09432c34beb6b16c533884673", size = 836095, upload-time = "2025-10-13T18:38:00.76Z" }
|
||||||
|
|
||||||
@ -4114,8 +4114,8 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "certifi" },
|
{ name = "certifi", marker = "python_full_version >= '3.11' or platform_machine != 'ARM64' or sys_platform != 'win32'" },
|
||||||
{ name = "cftime" },
|
{ name = "cftime", marker = "python_full_version >= '3.11' or platform_machine != 'ARM64' or sys_platform != 'win32'" },
|
||||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'ARM64') or (python_full_version < '3.11' and sys_platform != 'win32')" },
|
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'ARM64') or (python_full_version < '3.11' and sys_platform != 'win32')" },
|
||||||
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||||
]
|
]
|
||||||
@ -4427,7 +4427,7 @@ name = "nvidia-cudnn-cu13"
|
|||||||
version = "9.19.0.56"
|
version = "9.19.0.56"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "nvidia-cublas" },
|
{ name = "nvidia-cublas", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" },
|
{ url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" },
|
||||||
@ -4439,7 +4439,7 @@ name = "nvidia-cufft"
|
|||||||
version = "12.0.0.61"
|
version = "12.0.0.61"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "nvidia-nvjitlink" },
|
{ name = "nvidia-nvjitlink", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" },
|
{ url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" },
|
||||||
@ -4469,9 +4469,9 @@ name = "nvidia-cusolver"
|
|||||||
version = "12.0.4.66"
|
version = "12.0.4.66"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "nvidia-cublas" },
|
{ name = "nvidia-cublas", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
{ name = "nvidia-cusparse" },
|
{ name = "nvidia-cusparse", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
{ name = "nvidia-nvjitlink" },
|
{ name = "nvidia-nvjitlink", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" },
|
{ url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" },
|
||||||
@ -4483,7 +4483,7 @@ name = "nvidia-cusparse"
|
|||||||
version = "12.6.3.3"
|
version = "12.6.3.3"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "nvidia-nvjitlink" },
|
{ name = "nvidia-nvjitlink", marker = "(platform_machine != 'ARM64' and sys_platform == 'win32') or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" },
|
{ url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" },
|
||||||
@ -4762,8 +4762,8 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
|
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
{ name = "types-pytz" },
|
{ name = "types-pytz", marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/92/5d/be23854a73fda69f1dbdda7bc10fbd6f930bd1fa87aaec389f00c901c1e8/pandas_stubs-2.3.3.260113.tar.gz", hash = "sha256:076e3724bcaa73de78932b012ec64b3010463d377fa63116f4e6850643d93800", size = 116131, upload-time = "2026-01-13T22:30:16.704Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/92/5d/be23854a73fda69f1dbdda7bc10fbd6f930bd1fa87aaec389f00c901c1e8/pandas_stubs-2.3.3.260113.tar.gz", hash = "sha256:076e3724bcaa73de78932b012ec64b3010463d377fa63116f4e6850643d93800", size = 116131, upload-time = "2026-01-13T22:30:16.704Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -4789,7 +4789,7 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } },
|
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/27/1d/297ff2c7ea50a768a2247621d6451abb2a07c0e9be7ca6d36ebe371658e5/pandas_stubs-3.0.0.260204.tar.gz", hash = "sha256:bf9294b76352effcffa9cb85edf0bed1339a7ec0c30b8e1ac3d66b4228f1fbc3", size = 109383, upload-time = "2026-02-04T15:17:17.247Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/27/1d/297ff2c7ea50a768a2247621d6451abb2a07c0e9be7ca6d36ebe371658e5/pandas_stubs-3.0.0.260204.tar.gz", hash = "sha256:bf9294b76352effcffa9cb85edf0bed1339a7ec0c30b8e1ac3d66b4228f1fbc3", size = 109383, upload-time = "2026-02-04T15:17:17.247Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -4828,7 +4828,7 @@ name = "pexpect"
|
|||||||
version = "4.9.0"
|
version = "4.9.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "ptyprocess" },
|
{ name = "ptyprocess", marker = "platform_machine != 'ARM64' or sys_platform != 'win32'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -5311,14 +5311,14 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "accessible-pygments" },
|
{ name = "accessible-pygments", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "babel" },
|
{ name = "babel", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "beautifulsoup4" },
|
{ name = "beautifulsoup4", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" } },
|
{ name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
{ name = "packaging" },
|
{ name = "packaging", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "pygments" },
|
{ name = "pygments", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } },
|
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/67/ea/3ab478cccacc2e8ef69892c42c44ae547bae089f356c4b47caf61730958d/pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d", size = 2400673, upload-time = "2024-06-25T19:28:45.041Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/67/ea/3ab478cccacc2e8ef69892c42c44ae547bae089f356c4b47caf61730958d/pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d", size = 2400673, upload-time = "2024-06-25T19:28:45.041Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -5344,14 +5344,14 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "accessible-pygments" },
|
{ name = "accessible-pygments", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "babel" },
|
{ name = "babel", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "beautifulsoup4" },
|
{ name = "beautifulsoup4", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" } },
|
{ name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "pygments" },
|
{ name = "pygments", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions", marker = "python_full_version >= '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/00/20/bb50f9de3a6de69e6abd6b087b52fa2418a0418b19597601605f855ad044/pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7", size = 2412693, upload-time = "2024-12-17T10:53:39.537Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/00/20/bb50f9de3a6de69e6abd6b087b52fa2418a0418b19597601605f855ad044/pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7", size = 2412693, upload-time = "2024-12-17T10:53:39.537Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -5390,7 +5390,7 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "cffi" },
|
{ name = "cffi", marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/2e/ea/762d00f6f518423cd889e39b12028844cc95f91a6413cf7136e184864821/pygit2-1.18.2.tar.gz", hash = "sha256:eca87e0662c965715b7f13491d5e858df2c0908341dee9bde2bc03268e460f55", size = 797200, upload-time = "2025-08-16T13:52:36.853Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/2e/ea/762d00f6f518423cd889e39b12028844cc95f91a6413cf7136e184864821/pygit2-1.18.2.tar.gz", hash = "sha256:eca87e0662c965715b7f13491d5e858df2c0908341dee9bde2bc03268e460f55", size = 797200, upload-time = "2025-08-16T13:52:36.853Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -5451,7 +5451,7 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "cffi" },
|
{ name = "cffi", marker = "python_full_version >= '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/3a/a4/10ce00feef5c43eddacab19ae6610c4d4ef3ab77e544e9ee938772cd1c17/pygit2-1.19.2.tar.gz", hash = "sha256:cbeb3dbca9ca6ee3d5ea5d02f5e844c2d6084a2d5d6621e3e06aa2b11c645bfd", size = 803448, upload-time = "2026-03-29T14:57:27.565Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/3a/a4/10ce00feef5c43eddacab19ae6610c4d4ef3ab77e544e9ee938772cd1c17/pygit2-1.19.2.tar.gz", hash = "sha256:cbeb3dbca9ca6ee3d5ea5d02f5e844c2d6084a2d5d6621e3e06aa2b11c645bfd", size = 803448, upload-time = "2026-03-29T14:57:27.565Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -5880,15 +5880,15 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "affine" },
|
{ name = "affine", marker = "python_full_version < '3.12'" },
|
||||||
{ name = "attrs" },
|
{ name = "attrs", marker = "python_full_version < '3.12'" },
|
||||||
{ name = "certifi" },
|
{ name = "certifi", marker = "python_full_version < '3.12'" },
|
||||||
{ name = "click" },
|
{ name = "click", marker = "python_full_version < '3.12'" },
|
||||||
{ name = "click-plugins" },
|
{ name = "click-plugins", marker = "python_full_version < '3.12'" },
|
||||||
{ name = "cligj" },
|
{ name = "cligj", marker = "python_full_version < '3.12'" },
|
||||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version != '3.11.*'" },
|
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
|
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "pyparsing" },
|
{ name = "pyparsing", marker = "python_full_version < '3.12'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/ec/fa/fce8dc9f09e5bc6520b6fc1b4ecfa510af9ca06eb42ad7bdff9c9b8989d0/rasterio-1.4.4.tar.gz", hash = "sha256:c95424e2c7f009b8f7df1095d645c52895cd332c0c2e1b4c2e073ea28b930320", size = 445004, upload-time = "2025-12-12T18:01:08.971Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/ec/fa/fce8dc9f09e5bc6520b6fc1b4ecfa510af9ca06eb42ad7bdff9c9b8989d0/rasterio-1.4.4.tar.gz", hash = "sha256:c95424e2c7f009b8f7df1095d645c52895cd332c0c2e1b4c2e073ea28b930320", size = 445004, upload-time = "2025-12-12T18:01:08.971Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -5938,13 +5938,13 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "affine" },
|
{ name = "affine", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "attrs" },
|
{ name = "attrs", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "certifi" },
|
{ name = "certifi", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "click" },
|
{ name = "click", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "cligj" },
|
{ name = "cligj", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } },
|
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "pyparsing" },
|
{ name = "pyparsing", marker = "python_full_version >= '3.12'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/f6/88/edb4b66b6cb2c13f123af5a3896bf70c0cbe73ab3cd4243cb4eb0212a0f6/rasterio-1.5.0.tar.gz", hash = "sha256:1e0ea56b02eea4989b36edf8e58a5a3ef40e1b7edcb04def2603accd5ab3ee7b", size = 452184, upload-time = "2026-01-05T16:06:47.169Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/f6/88/edb4b66b6cb2c13f123af5a3896bf70c0cbe73ab3cd4243cb4eb0212a0f6/rasterio-1.5.0.tar.gz", hash = "sha256:1e0ea56b02eea4989b36edf8e58a5a3ef40e1b7edcb04def2603accd5ab3ee7b", size = 452184, upload-time = "2026-01-05T16:06:47.169Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6228,10 +6228,10 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "joblib" },
|
{ name = "joblib", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
|
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
{ name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" } },
|
{ name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
{ name = "threadpoolctl" },
|
{ name = "threadpoolctl", marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6281,10 +6281,10 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "joblib" },
|
{ name = "joblib", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } },
|
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" } },
|
{ name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "threadpoolctl" },
|
{ name = "threadpoolctl", marker = "python_full_version >= '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6325,7 +6325,7 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
|
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6395,7 +6395,7 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } },
|
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6748,23 +6748,23 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "alabaster" },
|
{ name = "alabaster", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "babel" },
|
{ name = "babel", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
{ name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" },
|
||||||
{ name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" } },
|
{ name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
{ name = "imagesize" },
|
{ name = "imagesize", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "jinja2" },
|
{ name = "jinja2", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "packaging" },
|
{ name = "packaging", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "pygments" },
|
{ name = "pygments", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "requests" },
|
{ name = "requests", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "snowballstemmer" },
|
{ name = "snowballstemmer", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "sphinxcontrib-applehelp" },
|
{ name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "sphinxcontrib-devhelp" },
|
{ name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "sphinxcontrib-htmlhelp" },
|
{ name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "sphinxcontrib-jsmath" },
|
{ name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "sphinxcontrib-qthelp" },
|
{ name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "sphinxcontrib-serializinghtml" },
|
{ name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "tomli" },
|
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6782,23 +6782,23 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "alabaster" },
|
{ name = "alabaster", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "babel" },
|
{ name = "babel", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
{ name = "colorama", marker = "python_full_version == '3.11.*' and sys_platform == 'win32'" },
|
||||||
{ name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" } },
|
{ name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "imagesize" },
|
{ name = "imagesize", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "jinja2" },
|
{ name = "jinja2", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "packaging" },
|
{ name = "packaging", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "pygments" },
|
{ name = "pygments", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "requests" },
|
{ name = "requests", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "roman-numerals" },
|
{ name = "roman-numerals", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "snowballstemmer" },
|
{ name = "snowballstemmer", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "sphinxcontrib-applehelp" },
|
{ name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "sphinxcontrib-devhelp" },
|
{ name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "sphinxcontrib-htmlhelp" },
|
{ name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "sphinxcontrib-jsmath" },
|
{ name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "sphinxcontrib-qthelp" },
|
{ name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "sphinxcontrib-serializinghtml" },
|
{ name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.11.*'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502, upload-time = "2025-12-04T07:45:27.343Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502, upload-time = "2025-12-04T07:45:27.343Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6820,23 +6820,23 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "alabaster" },
|
{ name = "alabaster", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "babel" },
|
{ name = "babel", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
{ name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" },
|
||||||
{ name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" } },
|
{ name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "imagesize" },
|
{ name = "imagesize", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "jinja2" },
|
{ name = "jinja2", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "packaging" },
|
{ name = "packaging", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "pygments" },
|
{ name = "pygments", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "requests" },
|
{ name = "requests", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "roman-numerals" },
|
{ name = "roman-numerals", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "snowballstemmer" },
|
{ name = "snowballstemmer", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "sphinxcontrib-applehelp" },
|
{ name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "sphinxcontrib-devhelp" },
|
{ name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "sphinxcontrib-htmlhelp" },
|
{ name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "sphinxcontrib-jsmath" },
|
{ name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "sphinxcontrib-qthelp" },
|
{ name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "sphinxcontrib-serializinghtml" },
|
{ name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.12'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6854,12 +6854,12 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "colorama" },
|
{ name = "colorama", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } },
|
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
{ name = "starlette" },
|
{ name = "starlette", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "uvicorn" },
|
{ name = "uvicorn", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "watchfiles" },
|
{ name = "watchfiles", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "websockets" },
|
{ name = "websockets", marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size = 14023, upload-time = "2024-10-02T23:15:30.172Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size = 14023, upload-time = "2024-10-02T23:15:30.172Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6885,13 +6885,13 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "colorama" },
|
{ name = "colorama", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "starlette" },
|
{ name = "starlette", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "uvicorn" },
|
{ name = "uvicorn", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "watchfiles" },
|
{ name = "watchfiles", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "websockets" },
|
{ name = "websockets", marker = "python_full_version >= '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/e0/3c/a59a3a453d4133777f7ed2e83c80b7dc817d43c74b74298ca0af869662ad/sphinx_autobuild-2025.8.25.tar.gz", hash = "sha256:9cf5aab32853c8c31af572e4fecdc09c997e2b8be5a07daf2a389e270e85b213", size = 15200, upload-time = "2025-08-25T18:44:55.436Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/e0/3c/a59a3a453d4133777f7ed2e83c80b7dc817d43c74b74298ca0af869662ad/sphinx_autobuild-2025.8.25.tar.gz", hash = "sha256:9cf5aab32853c8c31af572e4fecdc09c997e2b8be5a07daf2a389e270e85b213", size = 15200, upload-time = "2025-08-25T18:44:55.436Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6909,7 +6909,7 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } },
|
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/26/f0/43c6a5ff3e7b08a8c3b32f81b859f1b518ccc31e45f22e2b41ced38be7b9/sphinx_autodoc_typehints-3.0.1.tar.gz", hash = "sha256:b9b40dd15dee54f6f810c924f863f9cf1c54f9f3265c495140ea01be7f44fa55", size = 36282, upload-time = "2025-01-16T18:25:30.958Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/26/f0/43c6a5ff3e7b08a8c3b32f81b859f1b518ccc31e45f22e2b41ced38be7b9/sphinx_autodoc_typehints-3.0.1.tar.gz", hash = "sha256:b9b40dd15dee54f6f810c924f863f9cf1c54f9f3265c495140ea01be7f44fa55", size = 36282, upload-time = "2025-01-16T18:25:30.958Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6927,7 +6927,7 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" } },
|
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/1d/f6/bdd93582b2aaad2cfe9eb5695a44883c8bc44572dd3c351a947acbb13789/sphinx_autodoc_typehints-3.6.1.tar.gz", hash = "sha256:fa0b686ae1b85965116c88260e5e4b82faec3687c2e94d6a10f9b36c3743e2fe", size = 37563, upload-time = "2026-01-02T15:23:46.543Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/1d/f6/bdd93582b2aaad2cfe9eb5695a44883c8bc44572dd3c351a947acbb13789/sphinx_autodoc_typehints-3.6.1.tar.gz", hash = "sha256:fa0b686ae1b85965116c88260e5e4b82faec3687c2e94d6a10f9b36c3743e2fe", size = 37563, upload-time = "2026-01-02T15:23:46.543Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6949,7 +6949,7 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" } },
|
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/75/75/9a5695b3d3b848a43cfe91c7d7d3c5ab543eb3bc5c2a12ffaf5003a2a4f6/sphinx_autodoc_typehints-3.10.2.tar.gz", hash = "sha256:34db651eb14343ba16bd9c03e0f5093971f9bbd3cc6b0a1470adecd578940b9c", size = 74241, upload-time = "2026-04-15T22:09:48.87Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/75/75/9a5695b3d3b848a43cfe91c7d7d3c5ab543eb3bc5c2a12ffaf5003a2a4f6/sphinx_autodoc_typehints-3.10.2.tar.gz", hash = "sha256:34db651eb14343ba16bd9c03e0f5093971f9bbd3cc6b0a1470adecd578940b9c", size = 74241, upload-time = "2026-04-15T22:09:48.87Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6967,8 +6967,8 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "pydata-sphinx-theme", version = "0.15.4", source = { registry = "https://pypi.org/simple" } },
|
{ name = "pydata-sphinx-theme", version = "0.15.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" } },
|
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/45/19/d002ed96bdc7738c15847c730e1e88282d738263deac705d5713b4d8fa94/sphinx_book_theme-1.1.4.tar.gz", hash = "sha256:73efe28af871d0a89bd05856d300e61edce0d5b2fbb7984e84454be0fedfe9ed", size = 439188, upload-time = "2025-02-20T16:32:32.581Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/45/19/d002ed96bdc7738c15847c730e1e88282d738263deac705d5713b4d8fa94/sphinx_book_theme-1.1.4.tar.gz", hash = "sha256:73efe28af871d0a89bd05856d300e61edce0d5b2fbb7984e84454be0fedfe9ed", size = 439188, upload-time = "2025-02-20T16:32:32.581Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -6994,8 +6994,8 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "pydata-sphinx-theme", version = "0.16.1", source = { registry = "https://pypi.org/simple" } },
|
{ name = "pydata-sphinx-theme", version = "0.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
|
||||||
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
{ name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/eb/f7/154786f3cfb7692cd7acc24b6dfe4dcd1146b66f376b17df9e47125555e9/sphinx_book_theme-1.2.0.tar.gz", hash = "sha256:4a7ebfc7da4395309ac942ddfc38fbec5c5254c3be22195e99ad12586fbda9e3", size = 443962, upload-time = "2026-03-09T23:20:30.442Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/eb/f7/154786f3cfb7692cd7acc24b6dfe4dcd1146b66f376b17df9e47125555e9/sphinx_book_theme-1.2.0.tar.gz", hash = "sha256:4a7ebfc7da4395309ac942ddfc38fbec5c5254c3be22195e99ad12586fbda9e3", size = 443962, upload-time = "2026-03-09T23:20:30.442Z" }
|
||||||
@ -7163,8 +7163,8 @@ name = "standard-aifc"
|
|||||||
version = "3.13.0"
|
version = "3.13.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "audioop-lts" },
|
{ name = "audioop-lts", marker = "python_full_version >= '3.13'" },
|
||||||
{ name = "standard-chunk" },
|
{ name = "standard-chunk", marker = "python_full_version >= '3.13'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/c4/53/6050dc3dde1671eb3db592c13b55a8005e5040131f7509cef0215212cb84/standard_aifc-3.13.0.tar.gz", hash = "sha256:64e249c7cb4b3daf2fdba4e95721f811bde8bdfc43ad9f936589b7bb2fae2e43", size = 15240, upload-time = "2024-10-30T16:01:31.772Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/c4/53/6050dc3dde1671eb3db592c13b55a8005e5040131f7509cef0215212cb84/standard_aifc-3.13.0.tar.gz", hash = "sha256:64e249c7cb4b3daf2fdba4e95721f811bde8bdfc43ad9f936589b7bb2fae2e43", size = 15240, upload-time = "2024-10-30T16:01:31.772Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -7185,7 +7185,7 @@ name = "standard-sunau"
|
|||||||
version = "3.13.0"
|
version = "3.13.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "audioop-lts" },
|
{ name = "audioop-lts", marker = "python_full_version >= '3.13'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/66/e3/ce8d38cb2d70e05ffeddc28bb09bad77cfef979eb0a299c9117f7ed4e6a9/standard_sunau-3.13.0.tar.gz", hash = "sha256:b319a1ac95a09a2378a8442f403c66f4fd4b36616d6df6ae82b8e536ee790908", size = 9368, upload-time = "2024-10-30T16:01:41.626Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/66/e3/ce8d38cb2d70e05ffeddc28bb09bad77cfef979eb0a299c9117f7ed4e6a9/standard_sunau-3.13.0.tar.gz", hash = "sha256:b319a1ac95a09a2378a8442f403c66f4fd4b36616d6df6ae82b8e536ee790908", size = 9368, upload-time = "2024-10-30T16:01:41.626Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -7924,9 +7924,9 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
|
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||||
{ name = "packaging" },
|
{ name = "packaging", marker = "python_full_version < '3.11'" },
|
||||||
{ name = "pandas" },
|
{ name = "pandas", marker = "python_full_version < '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/19/ec/e50d833518f10b0c24feb184b209bb6856f25b919ba8c1f89678b930b1cd/xarray-2025.6.1.tar.gz", hash = "sha256:a84f3f07544634a130d7dc615ae44175419f4c77957a7255161ed99c69c7c8b0", size = 3003185, upload-time = "2025-06-12T03:04:09.099Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/19/ec/e50d833518f10b0c24feb184b209bb6856f25b919ba8c1f89678b930b1cd/xarray-2025.6.1.tar.gz", hash = "sha256:a84f3f07544634a130d7dc615ae44175419f4c77957a7255161ed99c69c7c8b0", size = 3003185, upload-time = "2025-06-12T03:04:09.099Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@ -7952,9 +7952,9 @@ resolution-markers = [
|
|||||||
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
"python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } },
|
{ name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "packaging" },
|
{ name = "packaging", marker = "python_full_version >= '3.11'" },
|
||||||
{ name = "pandas" },
|
{ name = "pandas", marker = "python_full_version >= '3.11'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/4b/a6/6fe936a798a3a38a79c7422d1a31afd2e9a14690fcb0ccff96bc01f04bf2/xarray-2026.4.0.tar.gz", hash = "sha256:c4ac9a01a945d90d5b1628e2af045099a9d4943536d4f2ee3ae963c3b222d15b", size = 3132311, upload-time = "2026-04-13T19:45:36.688Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/4b/a6/6fe936a798a3a38a79c7422d1a31afd2e9a14690fcb0ccff96bc01f04bf2/xarray-2026.4.0.tar.gz", hash = "sha256:c4ac9a01a945d90d5b1628e2af045099a9d4943536d4f2ee3ae963c3b222d15b", size = 3132311, upload-time = "2026-04-13T19:45:36.688Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user