# autocast

[![PyPI version](https://badge.fury.io/py/autocast.svg)](https://badge.fury.io/py/autocast)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python Versions](https://img.shields.io/pypi/pyversions/autocast.svg)](https://pypi.org/project/autocast/)
[![Downloads](https://static.pepy.tech/badge/autocast)](https://pepy.tech/project/autocast)

**Automatic type conversion for Python.**

`autocast` is a lightweight library that automatically converts objects from one type to another using a graph of registered transformation functions. If a direct conversion isn't available, `autocast` finds the shortest path through intermediate types to get you the result you need.

It comes with built-in support for **NumPy**, **PyTorch**, **Open3D**, and **OmegaConf**, but it is designed to be easily extensible for your own classes.

## 📦 Installation

```bash
pip install acaster
```

To install with support for all optional backends (PyTorch, Open3D, etc.):

```bash
pip install "acaster[all]"
```

## 🚀 Quick Start
Basic Usage
The core function is acast(object, target_type).

```python
import torch
import numpy as np
from autocast import acast

# Create a Torch tensor
tensor = torch.ones((3, 3))

# Automatically convert to NumPy array
array = acast(tensor, np.ndarray)

print(type(array)) 
# <class 'numpy.ndarray'>
```

A more complex example: Building a Open3D Point Cloud directly from PyTorch `tensors` or Numpy `arrays` without having to manually convert them to Open3D `Vector3dVector`:

```python
rgb_image = torch.tensor(...) # H x W x 3
predicted_pts = ... # N x 3 
pcd = acast((predicted_pts, rgb_image), o3d.geometry.PointCloud)
print(type(pcd))
# <class 'o3d.geometry.PointCloud'> 
o3d.io.write_point_cloud(save_path, pcd)
```

### Recursive Collection Handling
autocast automatically handles nested lists and dictionaries.

```python
data = {
    "points": torch.rand(100, 3),
    "colors": torch.rand(100, 3),
    "meta": [torch.tensor(1.0), torch.tensor(2.0)]
}

# Convert everything inside the dict to NumPy arrays
numpy_data = acast(data, np.ndarray)

print(type(numpy_data['points'])) 
# <class 'numpy.ndarray'>
```

## 🛠 Features
- **Graph-Based Casting**: Uses Breadth-First Search (BFS) to find conversion paths. If you have A -> B and B -> C, autocast can do A -> C automatically.
- **Zero-Overhead Imports**: Backend modules (like torch or open3d) are only imported if they are installed in your environment.
- **Extensible**: Register your own types and conversion functions easily.

| From | To | Notes |
| :--- | :--- | :--- |
| `torch.Tensor` | `numpy.ndarray` | Zero-copy where possible |
| `numpy.ndarray` | `torch.Tensor` | |
| `list`/`tuple` | `np.ndarray` | |
| `open3d.geometry` | `numpy.ndarray` | Vector3dVector support |
| `omegaconf.ListConfig` | `list`/`dict` | Converts to native containers |

## 🧩 Extending autocast
You can register your own casts using the add_acast function or the @acaster decorator.

### Option 1: The Decorator

```python
from autocast import acaster, acast

class Cat:
    def speak(self): return "Meow"

class Dog:
    def speak(self): return "Woof"

# Register a function that converts Cat -> Dog
@acaster(Cat, Dog)
def cat_to_dog(cat_instance):
    return Dog()

my_cat = Cat()
my_dog = acast(my_cat, Dog)
```

### Option 2: Manual Registration

```python
from autocast import add_acast

def str_to_int(s):
    return int(s)

add_acast(str, int, str_to_int)

print(acast("123", int)) # 123
```

## 🤝 Contributing
We welcome contributions! If you want to add support for a new library (e.g., Pandas, Polars, JAX), please submit a Pull Request.

1. Fork the repository
2. Create your feature branch (git checkout -b feature/new-cast)
3. Commit your changes
4. Push to the branch
5. Open a Pull Request

To run the tests, install `pytests` and run `python -m pytest`.

## 📄 License
Distributed under the MIT License. See [LICENSE](https://github.com/marcbenedi/autocast/blob/main/LICENSE) for more information.

## Acknowledgements

The idea for this library was inspired by [erezsh/autocast](https://github.com/erezsh/autocast).
