dlsca-core 0.1.0
Efficient loader for DLSCA datasets with selective downloading
License: MIT
Requires Python: >=3.12
Keywords: cryptography, datasets, deep-learning, huggingface, side-channel

# DLSCA Dataset Loader

[![PyPI version](https://badge.fury.io/py/dlsca-datasets.svg)](https://badge.fury.io/py/dlsca-datasets)
[![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)

A lightweight Python package for efficiently loading DLSCA (Deep Learning Side-Channel Analysis) datasets from HuggingFace with **selective downloading** of traces and samples.

## Why DLSCA Loader?

Standard HuggingFace `datasets` library downloads the **entire dataset** to cache before you can use it. For large cryptographic trace datasets, this wastes:
- ⏱️ Time (downloading gigabytes you don't need)
- 💾 Storage (caching full dataset locally)
- 🌐 Bandwidth (unnecessary data transfer)

**DLSCA loader** downloads **only the tiles you need** based on your trace and sample ranges.

## Installation

```bash
pip install dlsca-datasets
# or
uv add dlsca-datasets
```

## Quick Start

```python
from dlsca_datasets import load_dataset

# Load full dataset
ds = load_dataset("dataset1")

# Load specific traces (e.g., traces 100-200)
ds = load_dataset("dataset1", start_trace=100, end_trace=200)

# Load specific samples (e.g., samples 2000-3000)
ds = load_dataset("dataset1", start_sample=2000, end_sample=3000)

# Load both ranges
ds = load_dataset("dataset1", 
                 start_trace=100, end_trace=200,
                 start_sample=2000, end_sample=3000)

# Load only traces (exclude plaintext/ciphertext)
ds = load_dataset("dataset1",
                 include_plaintext=False,
                 include_ciphertext=False)
```

## API Reference

### `load_dataset()`

```python
load_dataset(
    dataset_name: str,
    start_trace: Optional[int] = None,
    end_trace: Optional[int] = None,
    start_sample: Optional[int] = None,
    end_sample: Optional[int] = None,
    include_trace: bool = True,
    include_plaintext: bool = True,
    include_ciphertext: bool = True,
    chunk_size: int = 100,
    sample_chunk_size: int = 1000,
    cache_dir: Optional[str] = None,
) -> pa.Table
```

#### Parameters

- **`dataset_name`** (str): Name of the dataset on HuggingFace (e.g., 'dataset1')
- **`start_trace`** (int, optional): Start trace index (inclusive), default: 0
- **`end_trace`** (int, optional): End trace index (exclusive), default: all traces
- **`start_sample`** (int, optional): Start sample index (inclusive), default: 0
- **`end_sample`** (int, optional): End sample index (exclusive), default: all samples
- **`include_trace`** (bool): Include trace data, default: True
- **`include_plaintext`** (bool): Include plaintext data, default: True
- **`include_ciphertext`** (bool): Include ciphertext data, default: True
- **`chunk_size`** (int): Traces per row chunk, default: 100
- **`sample_chunk_size`** (int): Samples per column chunk, default: 1000
- **`cache_dir`** (str, optional): Cache directory, default: temporary directory

#### Returns

`pyarrow.Table` with selected columns (trace, plaintext, ciphertext)

## Usage Examples

### Example 1: Training with Subset of Data

```python
from dlsca_datasets import load_dataset
import numpy as np

# Load first 1000 traces for quick experimentation
ds = load_dataset("dataset1", start_trace=0, end_trace=1000)

# Convert to NumPy
traces = np.array([t.as_py() for t in ds['trace']])
plaintexts = np.array([p.as_py() for p in ds['plaintext']])

print(f"Traces shape: {traces.shape}")
# Output: Traces shape: (1000, 5000)
```

### Example 2: Cross-Validation Splits

```python
from dlsca_datasets import load_dataset

# Training set: traces 0-800
train_ds = load_dataset("dataset1", start_trace=0, end_trace=800)

# Validation set: traces 800-1000
val_ds = load_dataset("dataset1", start_trace=800, end_trace=1000)
```

### Example 3: Feature Analysis on Specific Samples

```python
from dlsca_datasets import load_dataset

# Analyze only samples 2000-3000 (middle region of traces)
ds = load_dataset("dataset1", start_sample=2000, end_sample=3000)

# Each trace now has only 1000 samples (2000-2999)
print(len(ds['trace'][0].as_py()))
# Output: 1000
```

### Example 4: Memory-Efficient Loading

```python
from dlsca_datasets import load_dataset

# Load only what you need
ds = load_dataset("dataset1",
                 start_trace=0, end_trace=100,      # Only 100 traces
                 start_sample=0, end_sample=1000,   # Only first 1000 samples
                 include_ciphertext=False)          # Skip ciphertext

# Much smaller memory footprint!
```

### Example 5: PyTorch Integration

```python
from dlsca_datasets import load_dataset
import torch
from torch.utils.data import TensorDataset, DataLoader

# Load data
ds = load_dataset("dataset1", start_trace=0, end_trace=1000)

# Convert to tensors
traces = torch.tensor([t.as_py() for t in ds['trace']], dtype=torch.float32)
plaintexts = torch.tensor([p.as_py() for p in ds['plaintext']], dtype=torch.long)

# Create DataLoader
dataset = TensorDataset(traces, plaintexts)
loader = DataLoader(dataset, batch_size=32, shuffle=True)

# Train
for batch_traces, batch_plaintexts in loader:
    # Your training code here
    pass
```

## How It Works

DLSCA datasets use a 2D tiling structure:

```
dataset1/
├── traces_2d/
│   ├── r0000_c0000.parquet  (traces 0-99, samples 0-999)
│   ├── r0000_c0001.parquet  (traces 0-99, samples 1000-1999)
│   ├── r0001_c0000.parquet  (traces 100-199, samples 0-999)
│   └── ...
├── data-plaintexts.parquet   (all plaintexts)
└── data-ciphertexts.parquet  (all ciphertexts)
```

When you specify ranges:
- **Row chunks** are selected based on `start_trace` and `end_trace`
- **Column chunks** are selected based on `start_sample` and `end_sample`
- Only the needed tiles are downloaded (not the entire dataset!)

## Performance Comparison

| Method | Dataset Size | Download Time | Storage Used |
|--------|-------------|---------------|--------------|
| `datasets.load_dataset()` | 10GB | ~5 min | 10GB |
| `dlsca.load_dataset()` (full) | 10GB | ~5 min | 10GB |
| `dlsca.load_dataset()` (100 traces) | 10GB | ~10 sec | 100MB |
| `dlsca.load_dataset()` (custom range) | 10GB | ~15 sec | 150MB |

**Savings: 98% less time and storage** for typical use cases!

## Requirements

- Python 3.12+
- pyarrow >= 10.0.0
- huggingface-hub >= 0.19.0

## Contributing

Contributions welcome! Please open an issue or submit a pull request.

## License

MIT License

## Links

- [GitHub Repository](https://github.com/DLSCA/DLSCA)
- [HuggingFace Datasets](https://huggingface.co/DLSCA)
- [Documentation](https://github.com/DLSCA/DLSCA#readme)

## Citation

If you use DLSCA datasets in your research, please cite:

```bibtex
@software{dlsca2026,
  title={DLSCA: Deep Learning Side-Channel Analysis Dataset Loader},
  author={DLSCA Team},
  year={2026},
  url={https://github.com/DLSCA/DLSCA}
}
```