GithubHelp home page GithubHelp logo

metaopt / torchopt Goto Github PK

View Code? Open in Web Editor NEW
518.0 12.0 35.0 5.34 MB

TorchOpt is an efficient library for differentiable optimization built upon PyTorch.

Home Page: https://torchopt.readthedocs.io

License: Apache License 2.0

CMake 1.67% Python 87.98% C++ 4.70% Cuda 4.09% Makefile 1.15% Dockerfile 0.41%
functional-programming optimizer pytorch meta-reinforcement-learning deep-learning meta-learning meta-rl bilevel-optimization differentiable-optimization implicit-differentiation optimization differentiable-programming automatic-differentiation

torchopt's Introduction

Python 3.8+ PyPI GitHub Workflow Status CodeCov Documentation Status Downloads License

Installation | Documentation | Tutorials | Examples | Paper | Citation

TorchOpt is an efficient library for differentiable optimization built upon PyTorch. TorchOpt is:

  • Comprehensive: TorchOpt provides three differentiation modes - explicit differentiation, implicit differentiation, and zero-order differentiation for handling different differentiable optimization situations.
  • Flexible: TorchOpt provides both functional and objective-oriented API for users' different preferences. Users can implement differentiable optimization in JAX-like or PyTorch-like style.
  • Efficient: TorchOpt provides (1) CPU/GPU acceleration differentiable optimizer (2) RPC-based distributed training framework (3) Fast Tree Operations, to largely increase the training efficiency for bi-level optimization problems.

Beyond differentiable optimization, TorchOpt can also be regarded as a functional optimizer that enables JAX-like composable functional optimizer for PyTorch. With TorchOpt, users can easily conduct neural network optimization in PyTorch with a functional style optimizer, similar to Optax in JAX.


The README is organized as follows:


TorchOpt as Functional Optimizer

The design of TorchOpt follows the philosophy of functional programming. Aligned with functorch, users can conduct functional style programming with models, optimizers and training in PyTorch. We use the Adam optimizer as an example in the following illustration. You can also check out the tutorial notebook Functional Optimizer for more details.

Optax-Like API

For those users who prefer fully functional programming, we offer Optax-Like API by passing gradients and optimizer states to the optimizer function. Here is an example coupled with functorch:

class Net(nn.Module): ...

class Loader(DataLoader): ...

net = Net()  # init
loader = Loader()
optimizer = torchopt.adam()

model, params = functorch.make_functional(net)           # use functorch extract network parameters
opt_state = optimizer.init(params)                       # init optimizer

xs, ys = next(loader)                                    # get data
pred = model(params, xs)                                 # forward
loss = F.cross_entropy(pred, ys)                         # compute loss

grads = torch.autograd.grad(loss, params)                # compute gradients
updates, opt_state = optimizer.update(grads, opt_state)  # get updates
params = torchopt.apply_updates(params, updates)         # update network parameters

We also provide a wrapper torchopt.FuncOptimizer to make maintaining the optimizer state easier:

net = Net()  # init
loader = Loader()
optimizer = torchopt.FuncOptimizer(torchopt.adam())      # wrap with `torchopt.FuncOptimizer`

model, params = functorch.make_functional(net)           # use functorch extract network parameters

for xs, ys in loader:                                    # get data
    pred = model(params, xs)                             # forward
    loss = F.cross_entropy(pred, ys)                     # compute loss

    params = optimizer.step(loss, params)                # update network parameters

PyTorch-Like API

We also design a base class torchopt.Optimizer that has the same interface as torch.optim.Optimizer. We offer origin PyTorch APIs (e.g. zero_grad() or step()) by wrapping our Optax-Like API for traditional PyTorch users.

net = Net()  # init
loader = Loader()
optimizer = torchopt.Adam(net.parameters())

xs, ys = next(loader)             # get data
pred = net(xs)                    # forward
loss = F.cross_entropy(pred, ys)  # compute loss

optimizer.zero_grad()             # zero gradients
loss.backward()                   # backward
optimizer.step()                  # step updates

Differentiable

On top of the same optimization function as torch.optim, an important benefit of the functional optimizer is that one can implement differentiable optimization easily. This is particularly helpful when the algorithm requires differentiation through optimization updates (such as meta-learning practices). We take as the inputs the gradients and optimizer states, and use non-in-place operators to compute and output the updates. The processes can be automatically implemented, with the only need from users being to pass the argument inplace=False to the functions. Check out the section Explicit Gradient (EG) functional API for example.


TorchOpt for Differentiable Optimization

We design a bilevel-optimization updating scheme, which can be easily extended to realize various differentiable optimization processes.

As shown above, the scheme contains an outer level that has parameters $\phi$ that can be learned end-to-end through the inner level parameters solution $\theta^{\prime}(\phi)$ by using the best-response derivatives $\partial \theta^{\prime}(\phi) / \partial \phi$. TorchOpt supports three differentiation modes. It can be seen that the key component of this algorithm is to calculate the best-response (BR) Jacobian. From the BR-based perspective, existing gradient methods can be categorized into three groups: explicit gradient over unrolled optimization, implicit differentiation, and zero-order gradient differentiation.

Explicit Gradient (EG)

The idea of the explicit gradient is to treat the gradient step as a differentiable function and try to backpropagate through the unrolled optimization path. This differentiation mode is suitable for algorithms when the inner-level optimization solution is obtained by a few gradient steps, such as MAML and MGRL. TorchOpt offers both functional and object-oriented API for EG to fit different user applications.

Functional API

The functional API is to conduct optimization in a functional programming style. Note that we pass the argument inplace=False to the functions to make the optimization differentiable. Refer to the tutorial notebook Functional Optimizer for more guidance.

# Define functional optimizer
optimizer = torchopt.adam()
# Define meta and inner parameters
meta_params = ...
fmodel, params = make_functional(model)
# Initial state
state = optimizer.init(params)

for iter in range(iter_times):
    loss = inner_loss(fmodel, params, meta_params)
    grads = torch.autograd.grad(loss, params)
    # Apply non-inplace parameter update
    updates, state = optimizer.update(grads, state, inplace=False)
    params = torchopt.apply_updates(params, updates)

loss = outer_loss(fmodel, params, meta_params)
meta_grads = torch.autograd.grad(loss, meta_params)

OOP API

TorchOpt also provides OOP API compatible with the PyTorch programming style. Refer to the example and the tutorial notebook Meta-Optimizer, Stop Gradient for more guidance.

# Define meta and inner parameters
meta_params = ...
model = ...
# Define differentiable optimizer
optimizer = torchopt.MetaAdam(model)  # a model instance as the argument instead of model.parameters()

for iter in range(iter_times):
    # Perform inner update
    loss = inner_loss(model, meta_params)
    optimizer.step(loss)

loss = outer_loss(model, meta_params)
loss.backward()

Implicit Gradient (IG)

By treating the solution $\theta^{\prime}$ as an implicit function of $\phi$, the idea of IG is to directly get analytical best-response derivatives $\partial \theta^{\prime} (\phi) / \partial \phi$ by implicit function theorem. This is suitable for algorithms when the inner-level optimal solution is achieved ${\left. \frac{\partial F (\theta, \phi)}{\partial \theta} \right\rvert}_{\theta=\theta^{\prime}} = 0$ or reaches some stationary conditions $F (\theta^{\prime}, \phi) = 0$, such as iMAML and DEQ. TorchOpt offers both functional and OOP APIs for supporting both conjugate gradient-based and Neumann series-based IG methods. Refer to the example iMAML and the notebook Implicit Gradient for more guidance.

Functional API

For the implicit gradient, similar to JAXopt, users need to define the stationary condition and TorchOpt provides the decorator to wrap the solve function for enabling implicit gradient computation.

# The stationary condition for the inner-loop
def stationary(params, meta_params, data):
    # Stationary condition construction
    return stationary condition

# Decorator for wrapping the function
# Optionally specify the linear solver (conjugate gradient or Neumann series)
@torchopt.diff.implicit.custom_root(stationary, solve=linear_solver)
def solve(params, meta_params, data):
    # Forward optimization process for params
    return output

# Define params, meta_params and get data
params, meta_prams, data = ..., ..., ...
optimal_params = solve(params, meta_params, data)
loss = outer_loss(optimal_params)

meta_grads = torch.autograd.grad(loss, meta_params)

OOP API

TorchOpt also offers an OOP API, which users need to inherit from the class torchopt.nn.ImplicitMetaGradientModule to construct the inner-loop network. Users need to define the stationary condition/objective function and the inner-loop solve function to enable implicit gradient computation.

# Inherited from the class ImplicitMetaGradientModule
# Optionally specify the linear solver (conjugate gradient or Neumann series)
class InnerNet(ImplicitMetaGradientModule, linear_solve=linear_solver):
    def __init__(self, meta_param):
        super().__init__()
        self.meta_param = meta_param
        ...

    def forward(self, batch):
        # Forward process
        ...

    def optimality(self, batch, labels):
        # Stationary condition construction for calculating implicit gradient
        # NOTE: If this method is not implemented, it will be automatically
        # derived from the gradient of the `objective` function.
        ...

    def objective(self, batch, labels):
        # Define the inner-loop optimization objective
        ...

    def solve(self, batch, labels):
        # Conduct the inner-loop optimization
        ...

# Get meta_params and data
meta_params, data = ..., ...
inner_net = InnerNet(meta_params)

# Solve for inner-loop process related to the meta-parameters
optimal_inner_net = inner_net.solve(data)

# Get outer loss and solve for meta-gradient
loss = outer_loss(optimal_inner_net)
meta_grads = torch.autograd.grad(loss, meta_params)

Zero-order Differentiation (ZD)

When the inner-loop process is non-differentiable or one wants to eliminate the heavy computation burdens in the previous two modes (brought by Hessian), one can choose Zero-order Differentiation (ZD). ZD typically gets gradients based on zero-order estimation, such as finite-difference, or Evolutionary Strategy. Instead of optimizing the objective $F$, ES optimizes a smoothed objective. TorchOpt provides both functional and OOP APIs for the ES method. Refer to the tutorial notebook Zero-order Differentiation for more guidance.

Functional API

For zero-order differentiation, users need to define the forward pass calculation and the noise sampling procedure. TorchOpt provides the decorator to wrap the forward function for enabling zero-order differentiation.

# Customize the noise sampling function in ES
def distribution(sample_shape):
    # Generate a batch of noise samples
    # NOTE: The distribution should be spherical symmetric and with a constant variance of 1.
    ...
    return noise_batch

# Distribution can also be an instance of `torch.distributions.Distribution`, e.g., `torch.distributions.Normal(...)`
distribution = torch.distributions.Normal(loc=0, scale=1)

# Specify method and hyper-parameter of ES
@torchopt.diff.zero_order(distribution, method)
def forward(params, batch, labels):
    # Forward process
    ...
    return objective  # the returned tensor should be a scalar tensor

OOP API

TorchOpt also offers an OOP API, which users need to inherit from the class torchopt.nn.ZeroOrderGradientModule to construct the network as an nn.Module following a classical PyTorch style. Users need to define the forward process zero-order gradient procedures forward() and a noise sampling function sample().

# Inherited from the class ZeroOrderGradientModule
# Optionally specify the `method` and/or `num_samples` and/or `sigma` used for sampling
class Net(ZeroOrderGradientModule, method=method, num_samples=num_samples, sigma=sigma):
    def __init__(self, ...):
        ...

    def forward(self, batch):
        # Forward process
        ...
        return objective  # the returned tensor should be a scalar tensor

    def sample(self, sample_shape=torch.Size()):
        # Generate a batch of noise samples
        # NOTE: The distribution should be spherical symmetric and with a constant variance of 1.
        ...
        return noise_batch

# Get model and data
net = Net(...)
data = ...

# Forward pass
loss = Net(data)
# Backward pass using zero-order differentiation
grads = torch.autograd.grad(loss, net.parameters())

High-Performance and Distributed Training

CPU/GPU accelerated differentiable optimizer

We take the optimizer as a whole instead of separating it into several basic operators (e.g., sqrt and div). Therefore, by manually writing the forward and backward functions, we can perform the symbolic reduction. In addition, we can store some intermediate data that can be reused during the backpropagation. We write the accelerated functions in C++ OpenMP and CUDA, bind them by pybind11 to allow they can be called by Python, and then define the forward and backward behavior using torch.autograd.Function. Users can use it by simply setting the use_accelerated_op flag as True. Refer to the corresponding sections in the tutorials Functional Optimizer](tutorials/1_Functional_Optimizer.ipynb) and Meta-Optimizer

optimizer = torchopt.MetaAdam(model, lr, use_accelerated_op=True)

Distributed Training

TorchOpt provides distributed training features based on the PyTorch RPC module for better training speed and multi-node multi-GPU support. Different from the MPI-like parallelization paradigm, which uses multiple homogeneous workers and requires carefully designed communication hooks, the RPC APIs allow users to build their optimization pipeline more flexibly. Experimental results show that we achieve an approximately linear relationship between the speed-up ratio and the number of workers. Check out the Distributed Training Documentation and distributed MAML example for more specific guidance.

OpTree

We implement the PyTree to enable fast nested structure flattening using C++. The tree operations (e.g., flatten and unflatten) are very important in enabling functional and Just-In-Time (JIT) features of deep learning frameworks. By implementing it in C++, we can use some cache/memory-friendly structures (e.g., absl::InlinedVector) to improve the performance. For more guidance and comparison results, please refer to our open-source project OpTree.


Visualization

Complex gradient flow in meta-learning brings in a great challenge for managing the gradient flow and verifying its correctness of it. TorchOpt provides a visualization tool that draws variable (e.g., network parameters or meta-parameters) names on the gradient graph for better analysis. The visualization tool is modified from torchviz. Refer to the example visualization code and the tutorial notebook Visualization for more details.

The figure below shows the visualization result. Compared with torchviz, TorchOpt fuses the operations within the Adam together (orange) to reduce the complexity and provide simpler visualization.


Examples

In the examples directory, we offer several examples of functional optimizers and lightweight meta-learning examples with TorchOpt.

Also, check examples for more distributed/visualization/functorch-compatible examples.


Installation

Requirements

  • PyTorch
  • (Optional) For visualizing computation graphs
    • Graphviz (for Linux users use apt/yum install graphviz or conda install -c anaconda python-graphviz)

Please follow the instructions at https://pytorch.org to install PyTorch in your Python environment first. Then run the following command to install TorchOpt from PyPI (PyPI / Status):

pip3 install torchopt

If the minimum version of PyTorch is not satisfied, pip will install/upgrade it for you. Please be careful about the torch build for CPU / CUDA support (e.g. cpu, cu118, cu121). You may need to specify the extra index URL for the torch package:

pip3 install torchopt --extra-index-url https://download.pytorch.org/whl/cu121

See https://pytorch.org for more information about installing PyTorch.

You can also build shared libraries from source, use:

git clone https://github.com/metaopt/torchopt.git
cd torchopt
pip3 install .

We provide a conda environment recipe to install the build toolchain such as cmake, g++, and nvcc. You can use the following commands with conda / mamba to create a new isolated environment.

git clone https://github.com/metaopt/torchopt.git
cd torchopt

# You may need `CONDA_OVERRIDE_CUDA` if conda fails to detect the NVIDIA driver (e.g. in docker or WSL2)
CONDA_OVERRIDE_CUDA=12.1 conda env create --file conda-recipe-minimal.yaml

conda activate torchopt
make install-editable  # or run `pip3 install --no-build-isolation --editable .`

Changelog

See CHANGELOG.md.


Citing TorchOpt

If you find TorchOpt useful, please cite it in your publications.

@article{JMLR:TorchOpt,
  author  = {Jie Ren* and Xidong Feng* and Bo Liu* and Xuehai Pan* and Yao Fu and Luo Mai and Yaodong Yang},
  title   = {TorchOpt: An Efficient Library for Differentiable Optimization},
  journal = {Journal of Machine Learning Research},
  year    = {2023},
  volume  = {24},
  number  = {367},
  pages   = {1--14},
  url     = {http://jmlr.org/papers/v24/23-0191.html}
}

The Team

TorchOpt is a work by Jie Ren, Xidong Feng, Bo Liu, Xuehai Pan, Luo Mai, and Yaodong Yang.

License

TorchOpt is released under the Apache License, Version 2.0.

torchopt's People

Contributors

benjamin-eecs avatar dependabot[bot] avatar eltociear avatar future-xy avatar jieren98 avatar pre-commit-ci[bot] avatar stefanowoerner avatar vmoens avatar waterhorse1 avatar xuehaipan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

torchopt's Issues

Got 'nan' when using MetaAdam as a inner_opt

I just made two modifications on examples/few-shot/maml-omniglot.py:

  1. change the net structure in line 98 to my net:
net = nn.Sequential(
    nn.Conv2d(1, 64, (3, 3), 2, 0), nn.BatchNorm2d(64), nn.ReLU(),
    nn.Conv2d(64, 64, (3, 3), 2, 0), nn.BatchNorm2d(64), nn.ReLU(),
    nn.Conv2d(64, 64, (3, 3), 2, 0), nn.BatchNorm2d(64), nn.ReLU(),
    nn.Conv2d(64, 64, (2, 2), 1, 0), nn.BatchNorm2d(64), nn.ReLU(),
    nn.Flatten(), nn.Linear(64, args.n_way),
).to(device)
  1. change the inner_opt from MetaSGD to MetaAdam in line 120.

Then I got 'nan' parameters after first time of meta_opt.step()

[BUG] Accelerated optimizer test failed on GPU

Describe the bug

CUDA part test failed on GPU machine

Screenshots

torchopt_bug_2

System info

torchopt_bug_1

import torchopt, numpy, sys
print(torchopt.__version__, numpy.__version__, sys.version, sys.platform)
0.4.1 1.22.3 3.8.13 (default, Mar 28 2022, 11:38:47) 
[GCC 7.5.0] linux

Checklist

  • I have checked that there is no similar issue in the repo (required)
  • I have read the documentation (required)

[Feature Request] AdamW support

Motivation

for training big model

Solution

Checklist

  • I have checked that there is no similar issue in the repo (required)

[Question] some problem with stop gradient

Required prerequisites

Questions

system information

>>> print(sys.version, sys.platform)
3.8.18 (default, Sep 11 2023, 13:40:15) 
[GCC 11.2.0] linux
>>> print(torchopt.__version__,torch.__version__,functorch.__version__)
0.7.3 2.1.2+cu121 2.1.2+cu121

problem description

I'm trying to implement MGRL on PPO with a reward network as the hyperparameter to optimize. I meet a problem that I can run the meta-update process for the first time, but in the second round, even though I use 'torchopt.stopgradient' to stop policy, rewardnet and inner_opt, there still comes the error

RuntimeError: Trying to backward through the graph a second time (or directly access saved tensors after they have already been freed). Saved intermediate values of the graph are freed when you call .backward() or autograd.grad(). Specify retain_graph=True if you need to backward through the graph a second time or if you need to access saved tensors after calling backward.

I wonder if it is caused by there is some gradient I haven't stopped or other problem,

inner_opt = torchopt.MetaSGD(self.policy.actor, lr=self.lr_actor,moment_requires_grad=True)
meta_opt = torch.optim.Adam(self.rewardnet.parameters(), lr=self.lr_actor)

# Optimize policy for K epochs
for _ in range(self.K_epochs):
    for _ in range(self.inner_per_meta):
        # Evaluating old actions and values
        logprobs, state_values, state_values_g, dist_entropy = self.policy.evaluate(old_states, old_actions)

        # match state_values tensor dimensions with rewards tensor
        state_values = torch.squeeze(state_values)
        state_values_g = torch.squeeze(state_values_g)
        
        # Finding the ratio (pi_theta / pi_theta__old)
        log_ratios=logprobs - old_logprobs.detach()
        ratios = torch.exp(log_ratios)
        
        # Calculate Approx-KL
        approx_kl=((ratios-1)-log_ratios).mean()

        # Finding Surrogate Loss  
        surr1_g=ratios *advantages_g
        surr2_g=torch.clamp(ratios, 1-self.eps_clip, 1+self.eps_clip) * advantages_g

        # final loss of clipped objective PPO
        policy_loss = -torch.min(surr1_g, surr2_g) + 0.5 * self.MseLoss(state_values_g, rewards_g) - 0.01 * dist_entropy
        
        # backpropagation
        inner_opt.step(policy_loss.mean())
        
        if self.writer:
            self.writer.add_scalar("loss", policy_loss.mean(), self.cnt)
            self.writer.add_scalar("approx_kl", approx_kl.mean(), self.cnt)
            self.cnt += 1
            
    
    logprobs, state_values, state_values_g, dist_entropy = self.policy.evaluate(old_states, old_actions)
    
    sv=torch.squeeze(state_values)
    ratios = torch.exp(logprobs - old_logprobs.detach())
    
    surr1 = ratios * advantages
    surr2 = torch.clamp(ratios, 1-self.eps_clip, 1+self.eps_clip) * advantages
    
    outer_loss=-torch.min(surr1, surr2)+0.5*self.MseLoss(state_values, rewards)-0.01*dist_entropy
    
    meta_opt.zero_grad()
    outer_loss.mean().backward()
    meta_opt.step()
    
    torchopt.stop_gradient(self.policy)
    torchopt.stop_gradient(self.rewardnet)
    torchopt.stop_gradient(inner_opt)

[Feature Request] Task-level Optimization with Distributed Data Parallelization

Motivation

Task-level parallelization for multi-host multi-process optimization.

Batch-level parallelization can be implemented easily by wrapping the network (nn.Module) with:

However, for algorithms that require task-level parallelization, non of the above solutions work. torch.nn.DataParallel and torch.nn.parallel.DistributedDataParallel are used for module-level parallelization. The wrapper will replicate the user module to multiple copies, then do the forward pass in parallel. For task-level parallelization, each task needs to maintain its own model parameters and (optional) training data. The module parameters may be different across tasks.

Solution

functorch.vmap + distributed data parallel optimization.

Example

import torch
import torch.distributed.autograd as dist_autograd
import torch.distributed.rpc as rpc
from torch import optim
from torch.distributed.optim import DistributedOptimizer

with dist_autograd.context() as context_id:
    # Forward pass.
    rref1 = rpc.remote("worker1", torch.add, args=(torch.ones(2), 3))
    rref2 = rpc.remote("worker1", torch.add, args=(torch.ones(2), 1))
    loss = rref1.to_here() + rref2.to_here()
 
    # Backward pass.
    dist_autograd.backward(context_id, [loss.sum()])
 
    # Optimizer.
    dist_optim = DistributedOptimizer(
        optim.SGD,
        [rref1, rref2],
        lr=0.05,
    )
    dist_optim.step(context_id)

Additional context

Resources:

PyTorch:

JAX:

Checklist

  • I have checked that there is no similar issue in the repo (required)

[BUG] Align RMSProp optimizer with the PyTorch implementation

Describe the bug

A clear and concise description of what the bug is.

To Reproduce

Steps to reproduce the behavior.

Please try to provide a minimal example to reproduce the bug. Error messages and stack traces are also helpful.

Please use the markdown code blocks for both code and stack traces.

Use torch.allclose to compare tensors in tests, then run:

make test

Expected behavior

A clear and concise description of what you expected to happen.

Let tests pass.

Screenshots

If applicable, add screenshots to help explain your problem.

image

Additional context

Add any other context about the problem here.

Reason and Possible fixes

If you know or suspect the reason for this bug, paste the code lines and suggest modifications.

Checklist

  • I have checked that there is no similar issue in the repo (required)
  • I have read the documentation (required)
  • I have provided a minimal working example to reproduce the bug (required)

[BUG] Naming of `Adagrad` optimizer

Required prerequisites

What version of TorchOpt are you using?

0.7.1

System information

  • 3.9.16 (main, Jan 11 2023, 16:05:54)
  • 0.7.2.dev44+ga31e7da 1.13.1 1.13.1

Problem description

In torch.optim, it is called Adagrad

image

Reproducible example code

torch.optim.Adagrad
vs
torchopt.AdaGrad

Traceback

No response

Expected behavior

Adagrad

Additional context

No response

[BUG] Separate CPU / CUDA wheels

Describe the bug

A clear and concise description of what the bug is.

Wheels built with torch==1.12.0+cu116 is incompatible with torch==1.12.0+cpu (see CI output https://github.com/metaopt/TorchOpt/runs/7553102052 for more details). Different torch build ships with different libraries:

torch==1.12.0+cu116:

$ ls $SITE_PACKAGES/torch/lib
total 3.3G
-rwxr-xr-x 1 root root 1.2M Jul 28 04:01 libc10_cuda.so
-rwxr-xr-x 1 root root 751K Jul 28 04:01 libc10.so
-rwxr-xr-x 1 root root  25K Jul 28 04:01 libcaffe2_nvrtc.so
-rwxr-xr-x 1 root root 335M Jul 28 04:01 libcublasLt.so.11
-rwxr-xr-x 1 root root 150M Jul 28 04:01 libcublas.so.11
-rwxr-xr-x 1 root root 668K Jul 28 04:01 libcudart-45da57e3.so.11.0
-rwxr-xr-x 1 root root 124M Jul 28 04:01 libcudnn_adv_infer.so.8
-rwxr-xr-x 1 root root  92M Jul 28 04:01 libcudnn_adv_train.so.8
-rwxr-xr-x 1 root root 774M Jul 28 04:01 libcudnn_cnn_infer.so.8
-rwxr-xr-x 1 root root  85M Jul 28 04:01 libcudnn_cnn_train.so.8
-rwxr-xr-x 1 root root  86M Jul 28 04:01 libcudnn_ops_infer.so.8
-rwxr-xr-x 1 root root  68M Jul 28 04:01 libcudnn_ops_train.so.8
-rwxr-xr-x 1 root root 155K Jul 28 04:01 libcudnn.so.8
-rwxr-xr-x 1 root root 165K Jul 28 04:01 libgomp-a34b3233.so.1
-rwxr-xr-x 1 root root  44M Jul 28 04:01 libnvrtc-4dd39364.so.11.2
-rwxr-xr-x 1 root root 6.8M Jul 28 04:01 libnvrtc-builtins.so.11.6
-rwxr-xr-x 1 root root  43K Jul 28 04:01 libnvToolsExt-847d78f2.so.1
-rwxr-xr-x 1 root root  44K Jul 28 04:01 libshm.so
-rwxr-xr-x 1 root root 487M Jul 28 04:01 libtorch_cpu.so
-rwxr-xr-x 1 root root 216M Jul 28 04:01 libtorch_cuda_cpp.so
-rwxr-xr-x 1 root root 653M Jul 28 04:01 libtorch_cuda_cu.so
-rwxr-xr-x 1 root root 209M Jul 28 04:01 libtorch_cuda_linalg.so
-rwxr-xr-x 1 root root 163K Jul 28 04:01 libtorch_cuda.so
-rwxr-xr-x 1 root root  21K Jul 28 04:01 libtorch_global_deps.so
-rwxr-xr-x 1 root root  21M Jul 28 04:01 libtorch_python.so
-rwxr-xr-x 1 root root  16K Jul 28 04:01 libtorch.so

torch==1.12.0+cpu:

$ ls $SITE_PACKAGES/torch/lib
total 496M
-rwxr-xr-x 1 root root 269K Jul 28 04:02 libbackend_with_compiler.so
-rwxr-xr-x 1 root root 766K Jul 28 04:02 libc10.so
-rwxr-xr-x 1 root root 165K Jul 28 04:02 libgomp-a34b3233.so.1
-rwxr-xr-x 1 root root 228K Jul 28 04:02 libjitbackend_test.so
-rwxr-xr-x 1 root root  35K Jul 28 04:02 libshm.so
-rwxr-xr-x 1 root root 588K Jul 28 04:02 libtorchbind_test.so
-rwxr-xr-x 1 root root 476M Jul 28 04:02 libtorch_cpu.so
-rwxr-xr-x 1 root root 8.6K Jul 28 04:02 libtorch_global_deps.so
-rwxr-xr-x 1 root root  19M Jul 28 04:02 libtorch_python.so
-rwxr-xr-x 1 root root 7.1K Jul 28 04:02 libtorch.so

In our .cxx and .cu code, we only have one include directive #include <torch/extension.h> and only referenced torch::Tensor and AT_DISPATCH_FLOATING_TYPES. But the built shared libraries are linking against too many libraries than expected.

$ ldd /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/adam_op.cpython-37m-x86_64-linux-gnu.so
        linux-vdso.so.1 =>  (0x00007ffcd44ea000)
        libc10.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libc10.so (0x00007f75c1243000)
        libc10_cuda.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libc10_cuda.so (0x00007f75c109b000)
        libcaffe2_nvrtc.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libcaffe2_nvrtc.so (0x00007f75c123c000)
        libshm.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libshm.so (0x00007f75c1231000)
        libtorch.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch.so (0x00007f75c122c000)
        libtorch_cpu.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_cpu.so (0x00007f75a704d000)
        libtorch_cuda.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_cuda.so (0x00007f75c120c000)
        libtorch_cuda_cpp.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_cuda_cpp.so (0x00007f7599db0000)
        libtorch_cuda_cu.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_cuda_cu.so (0x00007f757237b000)
        libtorch_cuda_linalg.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_cuda_linalg.so (0x00007f7565905000)
        libtorch_global_deps.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_global_deps.so (0x00007f75c1203000)
        libtorch_python.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_python.so (0x00007f7564957000)
        librt.so.1 => /lib64/librt.so.1 (0x00007f756474f000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f7564533000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f756432f000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f7564027000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f7563d25000)
        libgomp.so.1 => /lib64/libgomp.so.1 (0x00007f7563aff000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f75638e9000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f756351b000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f75c1199000)
        libgomp-a34b3233.so.1 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libgomp-a34b3233.so.1 (0x00007f75632f1000)
        libcuda.so.1 => /lib64/libcuda.so.1 (0x00007f7561e96000)
        libnvrtc-4dd39364.so.11.2 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libnvrtc-4dd39364.so.11.2 (0x00007f755f075000)
        libcudart-45da57e3.so.11.0 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libcudart-45da57e3.so.11.0 (0x00007f755edcd000)
        libnvToolsExt-847d78f2.so.1 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libnvToolsExt-847d78f2.so.1 (0x00007f755ebc2000)
        libcudnn.so.8 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libcudnn.so.8 (0x00007f755e99a000)
        libcublas.so.11 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libcublas.so.11 (0x00007f755521c000)
        libcublasLt.so.11 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libcublasLt.so.11 (0x00007f75401b6000)

To Reproduce

Steps to reproduce the behavior.

Please try to provide a minimal example to reproduce the bug. Error messages and stack traces are also helpful.

Please use the markdown code blocks for both code and stack traces.

See CI output https://github.com/metaopt/TorchOpt/runs/7553102052 for more details.

$ git clone [email protected]:XuehaiPan/TorchOpt.git && cd TorchOpt
$ git checkout cibuildwheel
$ pip3 install --upgrade cibuildwheel
$ PIP_EXTRA_INDEX_URL="https://download.pytorch.org/whl/cu116" python3 -m cibuildwheel --platform linux --config-file=pyproject.toml
...
ls /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torch/lib
total 496M
-rwxr-xr-x 1 root root 269K Jul 28 04:02 libbackend_with_compiler.so
-rwxr-xr-x 1 root root 766K Jul 28 04:02 libc10.so
-rwxr-xr-x 1 root root 165K Jul 28 04:02 libgomp-a34b3233.so.1
-rwxr-xr-x 1 root root 228K Jul 28 04:02 libjitbackend_test.so
-rwxr-xr-x 1 root root  35K Jul 28 04:02 libshm.so
-rwxr-xr-x 1 root root 588K Jul 28 04:02 libtorchbind_test.so
-rwxr-xr-x 1 root root 476M Jul 28 04:02 libtorch_cpu.so
-rwxr-xr-x 1 root root 8.6K Jul 28 04:02 libtorch_global_deps.so
-rwxr-xr-x 1 root root  19M Jul 28 04:02 libtorch_python.so
-rwxr-xr-x 1 root root 7.1K Jul 28 04:02 libtorch.so
ldd /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/adam_op.cpython-37m-x86_64-linux-gnu.so
        linux-vdso.so.1 =>  (0x00007ffd5997a000)
        libc10.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libc10.so (0x00007ff16efde000)
        libc10_cuda.so => not found
        libcaffe2_nvrtc.so => not found
        libshm.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libshm.so (0x00007ff16efce000)
        libtorch.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch.so (0x00007ff16efcb000)
        libtorch_cpu.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_cpu.so (0x00007ff155b9a000)
        libtorch_cuda.so => not found
        libtorch_cuda_cpp.so => not found
        libtorch_cuda_cu.so => not found
        libtorch_cuda_linalg.so => not found
        libtorch_global_deps.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_global_deps.so (0x00007ff16efc5000)
        libtorch_python.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_python.so (0x00007ff154dc0000)
        librt.so.1 => /lib64/librt.so.1 (0x00007ff154bb8000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007ff15499c000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007ff154798000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007ff154490000)
        libm.so.6 => /lib64/libm.so.6 (0x00007ff15418e000)
        libgomp.so.1 => /lib64/libgomp.so.1 (0x00007ff153f68000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007ff153d52000)
        libc.so.6 => /lib64/libc.so.6 (0x00007ff153984000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ff16ef39000)
        libgomp-a34b3233.so.1 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libgomp-a34b3233.so.1 (0x00007ff15375a000)
patchelf --print-rpath /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/adam_op.cpython-37m-x86_64-linux-gnu.so
$ORIGIN/../../torch/lib:$ORIGIN/../../torchopt.libs
make: Entering directory `/project'
/tmp/tmp.3M50Q7bV1d/venv/bin/python3 -m pip show pytest &>/dev/null || (cd && /tmp/tmp.3M50Q7bV1d/venv/bin/python3 -m pip install pytest --upgrade)
/tmp/tmp.3M50Q7bV1d/venv/bin/python3 -m pip show pytest_cov &>/dev/null || (cd && /tmp/tmp.3M50Q7bV1d/venv/bin/python3 -m pip install pytest_cov --upgrade)
/tmp/tmp.3M50Q7bV1d/venv/bin/python3 -m pip show pytest_xdist &>/dev/null || (cd && /tmp/tmp.3M50Q7bV1d/venv/bin/python3 -m pip install pytest_xdist --upgrade)
cd tests && /tmp/tmp.3M50Q7bV1d/venv/bin/python3 -m pytest unit --cov torchopt --durations 0 -v --cov-report term-missing --color=yes
============================= test session starts ==============================
platform linux -- Python 3.7.13, pytest-7.1.2, pluggy-1.0.0 -- /tmp/tmp.3M50Q7bV1d/venv/bin/python3
cachedir: .pytest_cache
rootdir: /project
plugins: forked-1.4.0, cov-3.0.0, xdist-2.5.0
collecting ... collected 0 items / 4 errors

==================================== ERRORS ====================================
___________________ ERROR collecting tests/unit/test_clip.py ___________________
ImportError while importing test module '/project/tests/unit/test_clip.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/opt/python/cp37-cp37m/lib/python3.7/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
/workspace/tests/unit/test_clip.py:25: in <module>
    ???
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/__init__.py:17: in <module>
    from torchopt._src import accelerated_op_available, clip, combine, hook, schedule, visual
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/__init__.py:16: in <module>
    from torchopt._src.accelerated_op import accelerated_op_available
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/__init__.py:20: in <module>
    from torchopt._src.accelerated_op.adam_op import AdamOp
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/adam_op/__init__.py:16: in <module>
    from torchopt._src.accelerated_op.adam_op.adam_op import AdamOp
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/adam_op/adam_op.py:22: in <module>
    from torchopt._lib import adam_op  # pylint: disable=no-name-in-module
E   ImportError: libc10_cuda.so: cannot open shared object file: No such file or directory
_________________ ERROR collecting tests/unit/test_schedule.py _________________
ImportError while importing test module '/project/tests/unit/test_schedule.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/opt/python/cp37-cp37m/lib/python3.7/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
/workspace/tests/unit/test_schedule.py:18: in <module>
    ???
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/__init__.py:17: in <module>
    from torchopt._src import accelerated_op_available, clip, combine, hook, schedule, visual
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/__init__.py:16: in <module>
    from torchopt._src.accelerated_op import accelerated_op_available
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/__init__.py:20: in <module>
    from torchopt._src.accelerated_op.adam_op import AdamOp
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/adam_op/__init__.py:16: in <module>
    from torchopt._src.accelerated_op.adam_op.adam_op import AdamOp
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/adam_op/adam_op.py:22: in <module>
    from torchopt._lib import adam_op  # pylint: disable=no-name-in-module
E   ImportError: libc10_cuda.so: cannot open shared object file: No such file or directory
______ ERROR collecting tests/unit/high_level/test_high_level_inplace.py _______
ImportError while importing test module '/project/tests/unit/high_level/test_high_level_inplace.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/opt/python/cp37-cp37m/lib/python3.7/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
/workspace/tests/unit/high_level/test_high_level_inplace.py:25: in <module>
    ???
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/__init__.py:17: in <module>
    from torchopt._src import accelerated_op_available, clip, combine, hook, schedule, visual
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/__init__.py:16: in <module>
    from torchopt._src.accelerated_op import accelerated_op_available
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/__init__.py:20: in <module>
    from torchopt._src.accelerated_op.adam_op import AdamOp
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/adam_op/__init__.py:16: in <module>
    from torchopt._src.accelerated_op.adam_op.adam_op import AdamOp
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/adam_op/adam_op.py:22: in <module>
    from torchopt._lib import adam_op  # pylint: disable=no-name-in-module
E   ImportError: libc10_cuda.so: cannot open shared object file: No such file or directory
_______ ERROR collecting tests/unit/low_level/test_low_level_inplace.py ________
ImportError while importing test module '/project/tests/unit/low_level/test_low_level_inplace.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/opt/python/cp37-cp37m/lib/python3.7/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
/workspace/tests/unit/low_level/test_low_level_inplace.py:26: in <module>
    ???
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/__init__.py:17: in <module>
    from torchopt._src import accelerated_op_available, clip, combine, hook, schedule, visual
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/__init__.py:16: in <module>
    from torchopt._src.accelerated_op import accelerated_op_available
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/__init__.py:20: in <module>
    from torchopt._src.accelerated_op.adam_op import AdamOp
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/adam_op/__init__.py:16: in <module>
    from torchopt._src.accelerated_op.adam_op.adam_op import AdamOp
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/adam_op/adam_op.py:22: in <module>
    from torchopt._lib import adam_op  # pylint: disable=no-name-in-module
E   ImportError: libc10_cuda.so: cannot open shared object file: No such file or directory

---------- coverage: platform linux, python 3.7.13-final-0 -----------
Name                                                                                                    Stmts   Miss  Cover   Missing
-------------------------------------------------------------------------------------------------------------------------------------
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/__init__.py                                   9      7    22%   18-26
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/__init__.py                              0      0   100%
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/__init__.py                              1      0   100%
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/__init__.py              21     18    14%   23-45
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/adam_op/__init__.py       1      0   100%
/tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_src/accelerated_op/adam_op/adam_op.py       75     72     4%   25-137
make: *** [pytest] Error 2
-------------------------------------------------------------------------------------------------------------------------------------
TOTAL                                                                                                     107     97     9%

=========================== short test summary info ============================
ERROR unit/test_clip.py
ERROR unit/test_schedule.py
ERROR unit/high_level/test_high_level_inplace.py
ERROR unit/low_level/test_low_level_inplace.py
!!!!!!!!!!!!!!!!!!! Interrupted: 4 errors during collection !!!!!!!!!!!!!!!!!!!!
============================== 4 errors in 0.82s ===============================
...

Shared library compiled with torch==1.12.0+cu116:

$ ls /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torch/lib
total 3.3G
-rwxr-xr-x 1 root root 1.2M Jul 28 04:01 libc10_cuda.so
-rwxr-xr-x 1 root root 751K Jul 28 04:01 libc10.so
-rwxr-xr-x 1 root root  25K Jul 28 04:01 libcaffe2_nvrtc.so
-rwxr-xr-x 1 root root 335M Jul 28 04:01 libcublasLt.so.11
-rwxr-xr-x 1 root root 150M Jul 28 04:01 libcublas.so.11
-rwxr-xr-x 1 root root 668K Jul 28 04:01 libcudart-45da57e3.so.11.0
-rwxr-xr-x 1 root root 124M Jul 28 04:01 libcudnn_adv_infer.so.8
-rwxr-xr-x 1 root root  92M Jul 28 04:01 libcudnn_adv_train.so.8
-rwxr-xr-x 1 root root 774M Jul 28 04:01 libcudnn_cnn_infer.so.8
-rwxr-xr-x 1 root root  85M Jul 28 04:01 libcudnn_cnn_train.so.8
-rwxr-xr-x 1 root root  86M Jul 28 04:01 libcudnn_ops_infer.so.8
-rwxr-xr-x 1 root root  68M Jul 28 04:01 libcudnn_ops_train.so.8
-rwxr-xr-x 1 root root 155K Jul 28 04:01 libcudnn.so.8
-rwxr-xr-x 1 root root 165K Jul 28 04:01 libgomp-a34b3233.so.1
-rwxr-xr-x 1 root root  44M Jul 28 04:01 libnvrtc-4dd39364.so.11.2
-rwxr-xr-x 1 root root 6.8M Jul 28 04:01 libnvrtc-builtins.so.11.6
-rwxr-xr-x 1 root root  43K Jul 28 04:01 libnvToolsExt-847d78f2.so.1
-rwxr-xr-x 1 root root  44K Jul 28 04:01 libshm.so
-rwxr-xr-x 1 root root 487M Jul 28 04:01 libtorch_cpu.so
-rwxr-xr-x 1 root root 216M Jul 28 04:01 libtorch_cuda_cpp.so
-rwxr-xr-x 1 root root 653M Jul 28 04:01 libtorch_cuda_cu.so
-rwxr-xr-x 1 root root 209M Jul 28 04:01 libtorch_cuda_linalg.so
-rwxr-xr-x 1 root root 163K Jul 28 04:01 libtorch_cuda.so
-rwxr-xr-x 1 root root  21K Jul 28 04:01 libtorch_global_deps.so
-rwxr-xr-x 1 root root  21M Jul 28 04:01 libtorch_python.so
-rwxr-xr-x 1 root root  16K Jul 28 04:01 libtorch.so
$ ldd /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/adam_op.cpython-37m-x86_64-linux-gnu.so
        linux-vdso.so.1 =>  (0x00007ffcd44ea000)
        libc10.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libc10.so (0x00007f75c1243000)
        libc10_cuda.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libc10_cuda.so (0x00007f75c109b000)
        libcaffe2_nvrtc.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libcaffe2_nvrtc.so (0x00007f75c123c000)
        libshm.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libshm.so (0x00007f75c1231000)
        libtorch.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch.so (0x00007f75c122c000)
        libtorch_cpu.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_cpu.so (0x00007f75a704d000)
        libtorch_cuda.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_cuda.so (0x00007f75c120c000)
        libtorch_cuda_cpp.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_cuda_cpp.so (0x00007f7599db0000)
        libtorch_cuda_cu.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_cuda_cu.so (0x00007f757237b000)
        libtorch_cuda_linalg.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_cuda_linalg.so (0x00007f7565905000)
        libtorch_global_deps.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_global_deps.so (0x00007f75c1203000)
        libtorch_python.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_python.so (0x00007f7564957000)
        librt.so.1 => /lib64/librt.so.1 (0x00007f756474f000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f7564533000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f756432f000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f7564027000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f7563d25000)
        libgomp.so.1 => /lib64/libgomp.so.1 (0x00007f7563aff000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f75638e9000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f756351b000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f75c1199000)
        libgomp-a34b3233.so.1 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libgomp-a34b3233.so.1 (0x00007f75632f1000)
        libcuda.so.1 => /lib64/libcuda.so.1 (0x00007f7561e96000)
        libnvrtc-4dd39364.so.11.2 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libnvrtc-4dd39364.so.11.2 (0x00007f755f075000)
        libcudart-45da57e3.so.11.0 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libcudart-45da57e3.so.11.0 (0x00007f755edcd000)
        libnvToolsExt-847d78f2.so.1 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libnvToolsExt-847d78f2.so.1 (0x00007f755ebc2000)
        libcudnn.so.8 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libcudnn.so.8 (0x00007f755e99a000)
        libcublas.so.11 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libcublas.so.11 (0x00007f755521c000)
        libcublasLt.so.11 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libcublasLt.so.11 (0x00007f75401b6000)
$ patchelf --print-rpath /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/adam_op.cpython-37m-x86_64-linux-gnu.so
$ORIGIN/../../torch/lib:$ORIGIN/../../torchopt.libs

Then deploy with torch==1.12.0+cpu:

$ ls /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torch/lib
total 496M
-rwxr-xr-x 1 root root 269K Jul 28 04:02 libbackend_with_compiler.so
-rwxr-xr-x 1 root root 766K Jul 28 04:02 libc10.so
-rwxr-xr-x 1 root root 165K Jul 28 04:02 libgomp-a34b3233.so.1
-rwxr-xr-x 1 root root 228K Jul 28 04:02 libjitbackend_test.so
-rwxr-xr-x 1 root root  35K Jul 28 04:02 libshm.so
-rwxr-xr-x 1 root root 588K Jul 28 04:02 libtorchbind_test.so
-rwxr-xr-x 1 root root 476M Jul 28 04:02 libtorch_cpu.so
-rwxr-xr-x 1 root root 8.6K Jul 28 04:02 libtorch_global_deps.so
-rwxr-xr-x 1 root root  19M Jul 28 04:02 libtorch_python.so
-rwxr-xr-x 1 root root 7.1K Jul 28 04:02 libtorch.so
$ ldd /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/adam_op.cpython-37m-x86_64-linux-gnu.so
        linux-vdso.so.1 =>  (0x00007ffd5997a000)
        libc10.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libc10.so (0x00007ff16efde000)
        libc10_cuda.so => not found
        libcaffe2_nvrtc.so => not found
        libshm.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libshm.so (0x00007ff16efce000)
        libtorch.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch.so (0x00007ff16efcb000)
        libtorch_cpu.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_cpu.so (0x00007ff155b9a000)
        libtorch_cuda.so => not found
        libtorch_cuda_cpp.so => not found
        libtorch_cuda_cu.so => not found
        libtorch_cuda_linalg.so => not found
        libtorch_global_deps.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_global_deps.so (0x00007ff16efc5000)
        libtorch_python.so => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libtorch_python.so (0x00007ff154dc0000)
        librt.so.1 => /lib64/librt.so.1 (0x00007ff154bb8000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007ff15499c000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007ff154798000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007ff154490000)
        libm.so.6 => /lib64/libm.so.6 (0x00007ff15418e000)
        libgomp.so.1 => /lib64/libgomp.so.1 (0x00007ff153f68000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007ff153d52000)
        libc.so.6 => /lib64/libc.so.6 (0x00007ff153984000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ff16ef39000)
        libgomp-a34b3233.so.1 => /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/../../torch/lib/libgomp-a34b3233.so.1 (0x00007ff15375a000)
$ patchelf --print-rpath /tmp/tmp.3M50Q7bV1d/venv/lib/python3.7/site-packages/torchopt/_lib/adam_op.cpython-37m-x86_64-linux-gnu.so
$ORIGIN/../../torch/lib:$ORIGIN/../../torchopt.libs

Expected behavior

A clear and concise description of what you expected to happen.

Only link libtorch.so and can import shared libraries with different torch dependencies.

Screenshots

If applicable, add screenshots to help explain your problem.

Test torchopt wheels (built with torch==1.12.0+cu116) with torch==1.12.0+cpu.

image

System info

Describe the characteristic of your environment:

  • Describe how the library was installed (pip, source, ...)
  • Python version
  • Versions of any other relevant libraries
import torchopt, numpy, sys
print(torchopt.__version__, numpy.__version__, sys.version, sys.platform)

N/A

Additional context

Add any other context about the problem here.

N/A

Reason and Possible fixes

If you know or suspect the reason for this bug, paste the code lines and suggest modifications.

N/A

Checklist

  • I have checked that there is no similar issue in the repo (required)
  • I have read the documentation (required)
  • I have provided a minimal working example to reproduce the bug (required)

[BUG] torchopt.utils.extract_state_dict has an incorrect overloading annotation

Required prerequisites

What version of TorchOpt are you using?

0.7.0

System information

Problem description

torchopt.utils.extract_state_dict has an incorrect overloading annotation. The detach_buffers argument is missing from the overloading stubs, meaning that the static type checker complains when using detach_buffers.

Reproducible example code

The Python snippets:

torchopt.extract_state_dict(
    nn.Linear(1,1), by="reference", detach_buffers=True
)

Traceback

No response

Expected behavior

Type checker correctly checks the intended usage of extract_state_dict.

Additional context

No response

[Feature Request] Make torchopt.optim.Optimizer compatible with pytorch lightning

Required prerequisites

Motivation

Currently torchopt.optim classes aren't compatible with lightning's configure_optimizers.

This is because lightning doesn't think they are Optimizable

import torchopt
from lightning.fabric.utilities.types import Optimizable
optimizer = torchopt.Adam(model.parameters(), lr=1e-3)

isinstance(optimizer, Optimizable)
# False

For it to be Optimizable it requires defaults and state attributes.

If simply you do

from collections import defaultdict
optimizer.defaults = {}
optimizer.state = defaultdict()

then isinstance(optimizer, Optimizable) passes and torchopt <> lightning works a charm 😍

Solution

Can we add defaults and state attributes to the torchopt.optim.Optimizer class?

Alternatives

No response

Additional context

No response

[Question] Did not manage to install developing version

Questions

I was trying to install the developing version of the package: I cloned the package and ran pip3 install -e as suggested in the documentation. But I obtain the following error:

 gcc -pthread -B /home/qbe/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/qbe/anaconda3/include/python3.7m -c flagcheck.cpp -o flagcheck.o -std=c++17
      cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
      /tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/config/pyprojecttoml.py:108: _BetaConfiguration: Support for `[tool.setuptools]` in `pyproject.toml` is still *beta*.
        warnings.warn(msg, _BetaConfiguration)
      Traceback (most recent call last):
        File "/home/qbe/anaconda3/lib/python3.7/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 351, in <module>
          main()
        File "/home/qbe/anaconda3/lib/python3.7/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 333, in main
          json_out['return_val'] = hook(**hook_input['kwargs'])
        File "/home/qbe/anaconda3/lib/python3.7/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 250, in build_wheel
          metadata_directory)
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/build_meta.py", line 413, in build_wheel
          wheel_directory, config_settings)
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/build_meta.py", line 397, in _build_with_temp_dir
          self.run_setup()
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/build_meta.py", line 335, in run_setup
          exec(code, locals())
        File "<string>", line 104, in <module>
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/__init__.py", line 87, in setup
          return distutils.core.setup(**attrs)
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/_distutils/core.py", line 185, in setup
          return run_commands(dist)
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/_distutils/core.py", line 201, in run_commands
          dist.run_commands()
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/_distutils/dist.py", line 968, in run_commands
          self.run_command(cmd)
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/dist.py", line 1217, in run_command
          super().run_command(command)
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/_distutils/dist.py", line 987, in run_command
          cmd_obj.run()
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/wheel/bdist_wheel.py", line 325, in run
          self.run_command("build")
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/_distutils/cmd.py", line 319, in run_command
          self.distribution.run_command(command)
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/dist.py", line 1217, in run_command
          super().run_command(command)
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/_distutils/dist.py", line 987, in run_command
          cmd_obj.run()
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/_distutils/command/build.py", line 132, in run
          self.run_command(cmd_name)
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/_distutils/cmd.py", line 319, in run_command
          self.distribution.run_command(command)
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/dist.py", line 1217, in run_command
          super().run_command(command)
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/_distutils/dist.py", line 987, in run_command
          cmd_obj.run()
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/command/build_ext.py", line 84, in run
          _build_ext.run(self)
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/_distutils/command/build_ext.py", line 346, in run
          self.build_extensions()
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/pybind11/setup_helpers.py", line 290, in build_extensions
          super().build_extensions()
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/_distutils/command/build_ext.py", line 466, in build_extensions
          self._build_extensions_serial()
        File "/tmp/pip-build-env-7y5hc8wx/overlay/lib/python3.7/site-packages/setuptools/_distutils/command/build_ext.py", line 492, in _build_extensions_serial
          self.build_extension(ext)
        File "<string>", line 42, in build_extension
      RuntimeError: Cannot find CMake executable.
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for torchopt
Failed to build torchopt
ERROR: Could not build wheels for torchopt, which is required to install pyproject.toml-based projects 

Checklist

  • I have checked that there is no similar issue in the repo. (required)
  • I have read the documentation. (required)

[BUG] module 'optree' has no attribute 'PyTreeDef'

Describe the bug

I used torchopt in google colab without any problem untill last week, an error occurred when I tried to import torchopt.

AttributeError: module 'optree' has no attribute 'PyTreeDef'

I have tried reinstalling torchop as well as pytorch, and even replacing a local machine to execute the program, but the problem has not been solved.

To Reproduce

The error occurs when executing the following code on Google Colab:

!pip3 install torchopt
import torchopt

Then

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In [2], line 1
----> 1 import torchopt

File ~/.pyenv/versions/anaconda3-5.3.1/envs/meta_rl/lib/python3.8/site-packages/torchopt/__init__.py:17
      1 # Copyright 2022 MetaOPT Team. All Rights Reserved.
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
   (...)
     13 # limitations under the License.
     14 # ==============================================================================
     15 """TorchOpt: a high-performance optimizer library built upon PyTorch."""
---> 17 from torchopt._src import (
     18     accelerated_op_available,
     19     clip,
     20     combine,
     21     hook,
     22     implicit_diff,
     23     linear_solve,
     24     schedule,
     25     visual,
     26 )
     27 from torchopt._src.alias import adam, adamw, rmsprop, sgd
     28 from torchopt._src.clip import clip_grad_norm

File ~/.pyenv/versions/anaconda3-5.3.1/envs/meta_rl/lib/python3.8/site-packages/torchopt/_src/implicit_diff.py:159
    153     out_kwargs = {k: v for k, (_, _, v) in bound.kwargs.items()}
    154     return out_args, out_kwargs, map_args_back
    157 def _split_tensor_and_others(
    158     mixed_tuple: Tuple,
--> 159 ) -> Tuple[pytree.PyTreeDef, Tuple[bool, ...], Tuple[torch.Tensor, ...], Tuple[Any, ...]]:
    160     flattened, treedef = pytree.tree_flatten(mixed_tuple)
    161     tensors = []

AttributeError: module 'optree' has no attribute 'PyTreeDef'

System info

  • CUDA Version: 11.0
  • python 3.8
  • torch 1.13.0

Checklist

  • I have checked that there is no similar issue in the repo. (required)
  • I have read the documentation. (required)
  • I have provided a minimal working example to reproduce the bug. (required)

[Feature Request] Support building from souce for CPU-only

Motivation

Please outline the motivation for the proposal.
Is your feature request related to a problem? e.g., "I'm always frustrated when [...]".
If this is related to another issue, please link here too.

Allow the user to build the source code without a CUDA distribution installed (nvcc and cudart).

Solution

A clear and concise description of what you want to happen.

Update CMakeList.txt and make CUDA to be optional.

Alternatives

A clear and concise description of any alternative solutions or features you've considered.

Not considered.

Additional context

Add any other context or screenshots about the feature request here.

Checklist

  • I have checked that there is no similar issue in the repo (required)

[Feature Request] Support for functorch transforms

Required prerequisites

Motivation

I am interested in Jacobians and Hessians from implicitly differentiated root finding problems. This is something that regularly comes up in scientific computing. With jax, this is already possible out of the box using function transforms (e.g., jacrev). Is this something you plan to support in torchopt, too?

Solution

I already tried, but apparently, there are some pieces of code that prevent this:

  • missing setup_context for vmap rule in ImplicitMetaGradient (very easy to adapt)
  • .item() in _vdot_real_kernel
  • make_rmatvec in normal_cg
  • conditionals in _cg_solve
  • tree operations in _cg_solve

Alternatives

The jaxopt version
import jax
import jax.numpy as jnp
from jaxopt.implicit_diff import custom_root
from jaxopt import Bisection

jax.config.update("jax_platform_name", "cpu")


def F(x, factor):
  return factor * x ** 3 - x - 2


def bisection_root_solver(init_x, factor):
  bisec = Bisection(optimality_fun=F, lower=1, upper=2)
  return bisec.run(factor=factor).params


@custom_root(F)
def custom_root_solver(init_x, factor):
    """Root solver using gradient descent."""
    maxiter = 100
    lr = 1e-1

    x = init_x
    for _ in range(maxiter):
        grad = F(x, factor)
        x = x - lr * grad

    return x


x_init = jnp.array(3.0)
fac = jnp.array(2.0)

print(custom_root_solver(x_init, fac))
print(bisection_root_solver(x_init, fac))

print(jax.grad(custom_root_solver, argnums=1)(x_init, fac))
print(jax.grad(bisection_root_solver, argnums=1)(x_init, fac))

custom_jac_fcn = jax.jacrev(custom_root_solver, argnums=1)
print(jax.jacrev(custom_jac_fcn, argnums=1)(x_init, fac))
bisection_jac_fcn = jax.jacrev(bisection_root_solver, argnums=1)
print(jax.jacrev(bisection_jac_fcn, argnums=1)(x_init, fac))

Additional context

No response

[BUG] metaopt/optree is not a public repo

Describe the bug

I have trouble installing optree from pypi on our compute clusters, getting undefined symbol errors. I saw this link to an optree github repo on pypi, so I thought I can compile it from source, but the repo either doesn't exist or is not public.

To Reproduce

Visit https://github.com/metaopt/optree without logging into a collaborator account.

Expected behavior

The repo should be accessible

System info

N/A

Additional context

Our compute cluster uses a module system with customized builds, sometimes requiring us to build packages from source.

Checklist

  • I have checked that there is no similar issue in the repo (required)
  • I have read the documentation (required)
  • I have provided a minimal working example to reproduce the bug (required)

[BUG] This error is raised when using Implicit differentiation module.

Describe the bug

The forward is normal, but when I compute gradient, it raised this bug. Seems it does not recognize the parameter that I requires grad, or am I using this function in a wrong way? Please help me in this, Thanks.

To Reproduce

def sgf_stationary(params, meta_params, data):
    gs, ee, s00, h00, s01, h01 = params
    left = meta_params[0]
    if not left:
        gs_ = ee*s00 - h00 - (ee * s01 - h01) @ gs @ (ee * s01.conj().T - h01.conj().T)
        gs_ = tLA.pinv(gs_)
    else:
        gs_ = ee*s00 - h00 - (ee * s01.conj().T - h01.conj().T) @ gs @ (ee * s01 - h01)
        gs_ = tLA.pinv(gs_)
return gs_ - gs
@torchopt.implicit_diff.custom_root(sgf_stationary, argnums=[1,2,3,4,5], solve=torchopt.linear_solve.solve_normal_cg(maxiter=5, atol=0),)
def sgf(params, meta_params, data):
    _, ee, S, H, s01, h01 = params
    left, method = meta_params
    if method == 'GEP':
        gs = calcg0(ee, H, S, h01, s01, left=left)
    else:
        h10 = h01.conj().T
        s10 = s01.conj().T
        alpha, beta = h10 - ee * s10, h01 - ee * s01
        eps, epss = H.clone(), H.clone()
        converged = False
        iteration = 0
        while not converged:
            iteration += 1
            oldeps, oldepss = eps.clone(), epss.clone()
            oldalpha, oldbeta = alpha.clone(), beta.clone()
            tmpa = tLA.solve(ee * S - oldeps, oldalpha)
            tmpb = tLA.solve(ee * S - oldeps, oldbeta)

            alpha, beta = torch.mm(oldalpha, tmpa), torch.mm(oldbeta, tmpb)
            eps = oldeps + torch.mm(oldalpha, tmpb) + torch.mm(oldbeta, tmpa)
            if left:
                epss = oldepss + torch.mm(oldalpha, tmpb)
            else:
                epss = oldepss + torch.mm(oldbeta, tmpa)
            LopezConvTest = torch.max(alpha.abs() + beta.abs())

            if LopezConvTest < 1.0e-40:
                gs = (ee * S - epss).inverse()

                if left:
                    test = ee * S - H - torch.mm(ee * s10 - h10, gs.mm(ee * s01 - h01))
                else:
                    test = ee * S - H - torch.mm(ee * s01 - h01, gs.mm(ee * s10 - h10))
                myConvTest = torch.max((test.mm(gs) - torch.eye(H.shape[0], dtype=h01.dtype)).abs())
                if myConvTest < 1.0e-5:
                    converged = True
                    if myConvTest > 1.0e-8:
                        v = "RIGHT"
                        if left: v = "LEFT"
                        print(
                            "WARNING: Lopez-scheme not-so-well converged for " + v + " electrode at E = %.4f eV:" % ee.real.item(),
                            myConvTest.item())
                else:
                    print("Lopez-Sancho", myConvTest,
                            "Error: gs iteration {0}".format(iteration))
                    raise ArithmeticError("Criteria not met. Please check output...")
    return gs

The code is in above.

Traceback (most recent call last):
  File "/data/ADNEGF/calc/surface_green.py", line 370, in <module>
    torch.autograd.grad(gs.sum(), params)
  File "/root/miniconda3/envs/torch/lib/python3.9/site-packages/torch/autograd/__init__.py", line 300, in grad
    return Variable._execution_engine.run_backward(  # Calls into the C++ engine to run the backward pass
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn

Expected behavior

The backward pass can behave as usual.

import sys, torch, functorch, torchopt
import torch
import torch.linalg as tLA
import scipy.linalg as SLA
import torchopt

Additional context

Add any other context about the problem here.

Reason and Possible fixes

If you know or suspect the reason for this bug, paste the code lines and suggest modifications.

Checklist

  • I have checked that there is no similar issue in the repo. (required)
  • I have read the documentation. (required)
  • I have provided a minimal working example to reproduce the bug. (required)

[BUG] Error in tutorials/3_Meta_Optimizer.ipynb

Describe the bug

Error in running tutorials/3_Meta_Optimizer.ipynb cell [6] (the first cell under section 2.1 Basic API)

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
[<ipython-input-9-ad332932d4e7>](https://localhost:8080/#) in <module>
     15 for i in range(2):
     16     inner_loss = net(x)
---> 17     optim.step(inner_loss)
     18 
     19 print(f'a = {net.a!r}')

6 frames
[/usr/local/lib/python3.7/dist-packages/torchopt/_src/optimizer/meta/base.py](https://localhost:8080/#) in step(self, loss)
     68                 new_state,
     69                 params=flattened_params,
---> 70                 inplace=False,
     71             )
     72             self.state_groups[i] = new_state

[/usr/local/lib/python3.7/dist-packages/torchopt/_src/transform.py](https://localhost:8080/#) in update_fn(updates, state, params, inplace)
     65 
     66         flattened_updates, state = inner.update(
---> 67             flattened_updates, state, params=params, inplace=inplace
     68         )
     69         updates = pytree.tree_unflatten(treedef, flattened_updates)

[/usr/local/lib/python3.7/dist-packages/torchopt/_src/base.py](https://localhost:8080/#) in update_fn(updates, state, params, inplace)
    181             new_state = []
    182             for s, fn in zip(state, update_fns):  # pylint: disable=invalid-name
--> 183                 updates, new_s = fn(updates, s, params=params, inplace=inplace)
    184                 new_state.append(new_s)
    185             return updates, tuple(new_state)

[/usr/local/lib/python3.7/dist-packages/torchopt/_src/transform.py](https://localhost:8080/#) in update_fn(updates, state, params, inplace)
    312     def update_fn(updates, state, *, params=None, inplace=True):  # pylint: disable=unused-argument
    313         mu = _update_moment(
--> 314             updates, state.mu, b1, order=1, inplace=inplace, already_flattened=already_flattened
    315         )
    316         nu = _update_moment(

[/usr/local/lib/python3.7/dist-packages/torchopt/_src/transform.py](https://localhost:8080/#) in _update_moment(updates, moments, decay, order, inplace, already_flattened)
    214 
    215     if already_flattened:
--> 216         return map_flattened(f, updates, moments)
    217     return pytree.tree_map(f, updates, moments)
    218 

[/usr/local/lib/python3.7/dist-packages/torchopt/_src/transform.py](https://localhost:8080/#) in map_flattened(func, *args)
     49 def map_flattened(func: Callable, *args: Any) -> List[Any]:
     50     """Apply a function to each element of a flattened list."""
---> 51     return list(map(func, *args))
     52 
     53 

[/usr/local/lib/python3.7/dist-packages/torchopt/_src/transform.py](https://localhost:8080/#) in f(g, t)
    211 
    212             def f(g, t):
--> 213                 return t.mul(decay).add_(g, alpha=1 - decay) if g is not None else t
    214 
    215     if already_flattened:

RuntimeError: output with shape [] doesn't match the broadcast shape [1]

To Reproduce

See the following notebook for detailed steps to reproduce
https://colab.research.google.com/drive/14S7DzcovrUwDkZuRxdq5OW5KwYqWTJsy?usp=sharing

Expected behavior

Screenshots

System info

Describe the characteristic of your environment:

  • Describe how the library was installed (pip, source, ...)
  • Python version
  • Versions of any other relevant libraries
import torchopt, numpy, sys
print(torchopt.__version__, numpy.__version__, sys.version, sys.platform)
0.5.0 1.21.6 3.7.13 (default, Apr 24 2022, 01:04:09) 
[GCC 7.5.0] linux

Additional context

Add any other context about the problem here.

Reason and Possible fixes

If you know or suspect the reason for this bug, paste the code lines and suggest modifications.

Checklist

  • I have checked that there is no similar issue in the repo (required)
  • I have read the documentation (required)
  • I have provided a minimal working example to reproduce the bug (required)

Does explicit gradient support a self-defined autograd function

Required prerequisites

Questions

Thank you for your project first. I am solving a bilevel optimization using torchopt, but in my problem I have to implement a autograd function in torch, here are my example code

import numpy as np
import torch
import torch.nn as nn
from torch.autograd import Function
import torchopt


class DebugLossFunction(Function):
    @staticmethod
    def forward(ctx, x, r):
        loss = torch.mean(x * r)
        ctx.save_for_backward(x, r)
        return loss

    @staticmethod
    def backward(ctx, grad):
        x, r = ctx.saved_tensors
        grad_x = torch.ones_like(x)
        grad_r = torch.ones_like(r)

        return grad * grad_x, grad * grad_r


class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.x = None

    def set_initial_x(self, r):
        self.x = nn.Parameter(torch.tensor(r, dtype=torch.float32), requires_grad=True)

    def outer_loss(self):
        temp = 2 * self.x
        loss = torch.mean(temp)
        return loss

    def inner_loss(self, r):
        loss = DebugLossFunction.apply(self.x, r)
        return loss

initial_x = np.random.rand(100, 100)
initial_x = 2 * (initial_x - 0.5)

net = Net()
net.set_initial_x(initial_x)

r = nn.Parameter(torch.tensor(initial_x), requires_grad=True)

# High-level API
optim = torchopt.MetaAdam(net, lr=1.0)

for i in range(10):
    inner_loss = net.inner_loss(r)
    optim.step(inner_loss)


outer_loss = net.outer_loss()
outer_loss.backward()

print(f'x.grad = {r.grad!r}')

and the output gradient of r is None.

x.grad = None

I am still new to bilevel optimization, is there any mistakes in my code or torchopt just does not support a self-defined autograd function?

[Feature Request] Implement differentiable discrete randomness

Required prerequisites

Motivation

I would like to request a feature that allows torchopt to differentiate through programs with discrete randomness, such as flipping a coin with probability p of being heads. This would enable gradient-based optimization of stochastic models that involve discrete choices or events. Currently, torchopt does not support automatic differentiation (AD) of such programs because they have a discontinuous dependence on parameters.

Solution

A possible solution is to implement the method proposed by Arya et al. (2022) in their paper "Automatic Differentiation of Programs with Discrete Randomness". This method uses a reparameterization-based technique that generates new programs whose expectation is the derivative of the expectation of the original program. The paper shows how this method gives an unbiased and low-variance estimator which is as automated as traditional AD mechanisms. The paper also demonstrates unbiased forward-mode AD of discrete-time Markov chains, agent-based models such as Conway's Game of Life, and unbiased reverse-mode AD of a particle filter.

Alternatives

One alternative solution is to use score function estimators such as REINFORCE or RELAX, which are based on the log-derivative trick. However, these estimators have high variance and require additional hyperparameters such as baselines or control variates. Another alternative solution is to use discrete variational autoencoders (VAEs), which use a continuous relaxation of discrete variables. However, these methods introduce a bias in the gradient estimation and may not preserve the semantics of the original program.

Additional context

Arya et al., 2022: https://arxiv.org/pdf/2210.08572.pdf

[Feature Request] Zero-order Gradient estimation

Motivation

Zero-order gradient estimation for approximating gradient for non-differentiable optimization problem.

Solution

  • Finite difference
  • Evolutionary Strategy

Checklist

  • I have checked that there is no similar issue in the repo (required)

[Question] Memory keep increase in MetaAdam due to gradient link

Required prerequisites

What version of TorchOpt are you using?

0.7.3

System information

>>> print(sys.version, sys.platform)
3.11.9 | packaged by conda-forge | (main, Apr 19 2024, 18:36:13) [GCC 12.3.0] linux
>>> print(torchopt.__version__, torch.__version__, functorch.__version__)
0.7.3 2.3.0 2.3.0

Problem description

when use torchopt.MetaAdam and step some times, the memory use in gpu are continuously increase. It should not be, will you excute next step, the tensor create in the former step is no need should be release. I find the reason: metaOptimizer not detach the gradient link in optimizer. and former tensor was not release by torch due to dependency.

you can run the test code, the first one memory increase by step increase. and second one (I change the code to detach the grad link) the memory is stable when step increase:
before:
image
after:
image

Reproducible example code

The Python snippets:

import torch
import torch.nn
import torch.nn.functional as F
import time
import torchopt

class test_nn(torch.nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.a = torch.nn.Parameter(torch.randn(768, 512, device="cuda")) # 768 * 512 * 4 / 1024 = 1536 KB
        self.b = torch.nn.Parameter(torch.randn(768, 768, device="cuda")) # 768 * 768 * 4 / 1024 = 2304 KB
        self.test_time = 10
 
    def forward(self):
        from torch.profiler import profile, record_function, ProfilerActivity
        with profile(
            activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
            schedule=torch.profiler.schedule(wait=0, warmup=0, active=1, repeat=1),
            on_trace_ready=torch.profiler.tensorboard_trace_handler('./log_test/'),
            profile_memory=True,
            with_stack=True,
            record_shapes=True,
        ) as prof:
            def test_func1(a, b):
                with torch.enable_grad():
                    c = a * 2
                    d = torch.matmul(b, c)
                    return torch.sum(a + d)
                
            optimizer = torchopt.MetaAdam(self, lr=0.1)
            for _ in range(self.test_time):
                loss = test_func1(self.a, self.b)
                optimizer.step(loss)
                print(torch.cuda.max_memory_allocated())
                
                
def main():
    a = test_nn()
    a.forward()
    
if __name__ == "__main__":
    main()

Command lines:

python test.py

Traceback

current:
62526464
90054144
106309632
122827264
138558464
155600384
171331584
187587072
204104704
219835904

Expected behavior

should be :
57019392
60951552
61737984
63179776
63179776
63179776
63179776
63179776
63179776
63179776

Additional context

No response

[BUG] Got empty meta-parameters using `ImplicitMetaGradientModule`

Required prerequisites

What version of TorchOpt are you using?

0.7.0

System information

System version: 3.9.16 (main, Jan 11 2023, 16:05:54)
System platform: [GCC 11.2.0] linux
Torchopt version: 0.7.0 (installed via conda)
Torch version: 1.13.1
Functorch version: 1.13.1

Problem description

Hi, I am using the implicit model from torchopt.nn.ImplicitMetaGradientModule.

One of my input neural networks contains batch normalization layers, I hope to frozen them when calling implicit_model.solve(). So, I use the network.eval() offered by PyTorch.

However, torchopt throws the following error:

  • ValueError: not enough values to unpack (expected 2, got 0)

from the 140-line meta_params_names, flat_meta_params = tuple(zip(*self.named_meta_parameters())) in /torchopt/diff/implicit/nn/module.py.


What I find about this bug:

  • self.named_meta_parameters() is empty
  • using the following codes to frozen BN layers can avoid the error:
for m in network.modules():
    if isinstance(m, nn.BatchNorm1d):
        m.eval()

Reproducible example code

The Python snippets:

import torch
import torch.nn as nn
import torchopt

_ = torch.manual_seed(123)
torch.set_default_dtype(torch.float64)

class ImplicitModel(torchopt.nn.ImplicitMetaGradientModule):
    def __init__(self, mlp, x0):
        super().__init__()
        self.mlp = mlp
        self.x = nn.Parameter(x0.clone().detach_(), requires_grad=True)

    def objective(self):
        return self.mlp(self.x).mean()

    @torch.enable_grad()
    def solve(self):
        optimizer = torch.optim.Adam([self.x], lr=0.01)
        for epoch in range(100):
            optimizer.zero_grad()
            loss = self.objective()
            loss.backward(inputs=[self.x])
            optimizer. step()

mlp = nn.Sequential(nn.Linear(5,5), nn.BatchNorm1d(5), nn.Tanh(), nn.Linear(5,1))
_ = mlp.eval()

# this will work
# for m in mlp.modules():
#   if isinstance(m, nn.BatchNorm1d):
#       m.eval()

x0 = torch.rand(10,5)
    
model = ImplicitModel(mlp, x0)
model. Solve()

Traceback

╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /home/Miniconda3/envs/torch1.13/lib/python3.9/site-packages/torchopt/diff/implicit/n │
│ n/module.py:140 in wrapped                                                                       │
│                                                                                                  │
│   137 │   def wrapped(self: ImplicitMetaGradientModule, *input, **kwargs) -> Any:                │
│   138 │   │   """Solve the optimization problem."""                                              │
│   139 │   │   params_names, flat_params = tuple(zip(*self.named_parameters()))                   │
│ ❱ 140 │   │   meta_params_names, flat_meta_params = tuple(zip(*self.named_meta_parameters()))    │
│   141 │   │                                                                                      │
│   142 │   │   flat_optimal_params, output = stateless_solver_fn(                                 │
│   143 │   │   │   flat_params,                                                                   │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
ValueError: not enough values to unpack (expected 2, got 0)

Expected behavior

No response

Additional context

No response

[Question] Making the Learning Rate Learnable for the Implicit Gradient Component

Required prerequisites

Questions

Hi @XuehaiPan @Benjamin-eecs @JieRen98,

I have been recently working with your codebase, and I've been trying to modify the implicit gradient component such that the learning rate becomes learnable as well.

As it stands now, the learning rate is a fixed parameter that we need to set before training. I have been trying to implement this change, but I have not been successful so far. Any guidance would be really helpful! Thank you very much for maintaining this project!

[BUG] build wheels for `manylinux`

Describe the bug

Unsupported platform tag for wheels built in GitHub Action.

https://github.com/metaopt/TorchOpt/runs/7430789533?check_suite_focus=true

ERROR    HTTPError: 400 Bad Request from https://test.pypi.org/legacy/
         Binary wheel 'torchopt-0.4.2a0-cp310-cp310-linux_x86_64.whl' has an    
         unsupported platform tag 'linux_x86_64'.                               
Error: Process completed with exit code 1.

To Reproduce

$ python3 -m pip install --upgrade pip setuptools build auditwheel

$ python3 -m build

$ auditwheel show dist/torchopt-0.4.2-cp38-cp38-linux_x86_64.whl

torchopt-0.4.2-cp38-cp38-linux_x86_64.whl is consistent with the
following platform tag: "linux_x86_64".

The wheel references external versioned symbols in these
system-provided shared libraries: libgomp.so.1 with versions
{'OMP_1.0', 'GOMP_4.0'}, librt.so.1 with versions {'GLIBC_2.2.5'},
libgcc_s.so.1 with versions {'GCC_3.0', 'GCC_3.3.1'}, libm.so.6 with
versions {'GLIBC_2.2.5'}, libc.so.6 with versions {'GLIBC_2.4',
'GLIBC_2.2.5', 'GLIBC_2.3', 'GLIBC_2.3.3', 'GLIBC_2.6'},
libstdc++.so.6 with versions {'CXXABI_1.3.5', 'CXXABI_1.3',
'GLIBCXX_3.4.9', 'CXXABI_1.3.3', 'GLIBCXX_3.4.18', 'GLIBCXX_3.4',
'CXXABI_1.3.2'}, libdl.so.2 with versions {'GLIBC_2.2.5'},
libpthread.so.0 with versions {'GLIBC_2.3.2', 'GLIBC_2.3.4',
'GLIBC_2.2.5'}

This constrains the platform tag to "manylinux_2_17_x86_64". In order
to achieve a more compatible tag, you would need to recompile a new
wheel from source on a system with earlier versions of these
libraries, such as a recent manylinux **image.**

References:

Checklist

  • I have checked that there is no similar issue in the repo (required)
  • I have read the documentation (required)
  • I have provided a minimal working example to reproduce the bug (required)

[Feature Request] Implicit gradient support

Motivation

for imaml and model-based meta-rl algos

Solution

  • linear system solver for hessian vector product
  • jaxopt decorator
  • imaml example
  • implicit differentiation tutorial
  • api docs
  • readme

Checklist

  • I have checked that there is no similar issue in the repo (required)

[BUG] Custom root fails for scalar inputs (jaxopt example)

Required prerequisites

What version of TorchOpt are you using?

0.7.3

System information

3.10.6 | packaged by conda-forge | (main, Aug 22 2022, 20:36:39) [GCC 10.4.0] linux
0.7.3 2.1.0 2.1.0

Problem description

I tried to recreate the jaxopt example for root finding with implicit differentiation using a very simple iterative solver. In jax, it just works. With torchopt, however, the gradient is zero for scalar inputs.

The problem stems from:

if maxiter is None:
size = sum(cat_shapes(b))
maxiter = 10 * size # copied from SciPy

Here, the size becomes zero for scalar b and the maxiter is wrongly set to 0. The same piece of code in jax produces a size of 1.

Reproducible example code

The Python snippets:

import torch
import torchopt


# https://jaxopt.github.io/stable/root_finding.html
def F(x, factor):
    return factor * x**3 - x - 2


@torchopt.diff.implicit.custom_root(
    F, argnums=(1,), solve=torchopt.linear_solve.solve_cg()
)
def custom_root_solver(init_x, factor):
    """Root solver using gradient descent."""
    maxiter = 100
    lr = 1e-1

    x = init_x
    for _ in range(maxiter):
        grad = F(x, factor)
        x = x - lr * grad

    return x


def wrapper(fac):
    return custom_root_solver(init_x, fac)


init_x = torch.tensor(1.0)
fac = torch.tensor(2.0, requires_grad=True)

root = wrapper(fac)
root_grad = torch.autograd.grad(root, fac)
print(root_grad)

Traceback

No response

Expected behavior

No response

Additional context

It works upon making the tensors 1D:

init_x = torch.tensor([1.0])
fac = torch.tensor([2.0], requires_grad=True)

It just works in jax.

import jax
import jax.numpy as jnp
from jaxopt.implicit_diff import custom_root
from jaxopt import Bisection

jax.config.update("jax_platform_name", "cpu")


def F(x, factor):
  return factor * x ** 3 - x - 2


def bisection_root_solver(init_x, factor):
  bisec = Bisection(optimality_fun=F, lower=1, upper=2)
  return bisec.run(factor=factor).params


@custom_root(F)
def custom_root_solver(init_x, factor):
    """Root solver using gradient descent."""
    maxiter = 100
    lr = 1e-1

    x = init_x
    for _ in range(maxiter):
        grad = F(x, factor)
        x = x - lr * grad

    return x


x_init = jnp.array(3.0)
fac = jnp.array(2.0)

print(custom_root_solver(x_init, fac))
print(bisection_root_solver(x_init, fac))

print(jax.grad(custom_root_solver, argnums=1)(x_init, fac))
print(jax.grad(bisection_root_solver, argnums=1)(x_init, fac))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.