From 79a7f2c05ed3fff48756a89a78f4e627429161e6 Mon Sep 17 00:00:00 2001 From: Laetitia Papaxanthos Date: Fri, 18 Mar 2022 10:35:57 +0000 Subject: [PATCH] Project import generated by Copybara. PiperOrigin-RevId: 435593406 --- docs/conf.py | 14 + docs/core.rst | 7 + docs/index.rst | 1 + docs/notebooks/neural_dual.ipynb | 502 ++++++++++++++++++ ott/__init__.py | 14 + ott/core/__init__.py | 15 + ott/core/anderson.py | 14 + ott/core/dataclasses.py | 14 + ott/core/discrete_barycenter.py | 14 + ott/core/fixed_point_loop.py | 14 + ott/core/gromov_wasserstein.py | 14 + ott/core/icnn.py | 24 +- ott/core/implicit_differentiation.py | 14 + ott/core/momentum.py | 14 + ott/core/neuraldual.py | 359 +++++++++++++ ott/core/problems.py | 14 + ott/core/quad_problems.py | 14 + ott/core/sinkhorn.py | 14 + ott/core/sinkhorn_lr.py | 14 + ott/core/unbalanced_functions.py | 14 + ott/examples/fairness/config.py | 14 + ott/examples/fairness/data.py | 14 + ott/examples/fairness/losses.py | 14 + ott/examples/fairness/main.py | 14 + ott/examples/fairness/models.py | 14 + ott/examples/fairness/train.py | 14 + ott/examples/soft_error/config.py | 14 + ott/examples/soft_error/data.py | 14 + ott/examples/soft_error/losses.py | 14 + ott/examples/soft_error/main.py | 14 + ott/examples/soft_error/model.py | 14 + ott/examples/soft_error/train.py | 14 + ott/geometry/__init__.py | 14 + ott/geometry/costs.py | 14 + ott/geometry/epsilon_scheduler.py | 14 + ott/geometry/geometry.py | 14 + ott/geometry/grid.py | 14 + ott/geometry/low_rank.py | 14 + ott/geometry/matrix_square_root.py | 14 + ott/geometry/ops.py | 14 + ott/geometry/pointcloud.py | 14 + ott/tools/__init__.py | 14 + ott/tools/gaussian_mixture/__init__.py | 14 + ott/tools/gaussian_mixture/fit_gmm.py | 14 + ott/tools/gaussian_mixture/fit_gmm_pair.py | 14 + ott/tools/gaussian_mixture/gaussian.py | 14 + .../gaussian_mixture/gaussian_mixture.py | 14 + .../gaussian_mixture/gaussian_mixture_pair.py | 14 + ott/tools/gaussian_mixture/linalg.py | 14 + ott/tools/gaussian_mixture/probabilities.py | 14 + ott/tools/gaussian_mixture/scale_tril.py | 14 + ott/tools/plot.py | 14 + ott/tools/sinkhorn_divergence.py | 14 + ott/tools/soft_sort.py | 14 + ott/tools/transport.py | 14 + ott/version.py | 14 + requirements.txt | 3 +- setup.py | 14 + tests/core/discrete_barycenter_test.py | 14 + tests/core/fused_gromov_wasserstein_test.py | 14 + tests/core/gromov_wasserstein_test.py | 14 + .../gromov_wasserstein_unbalanced_test.py | 14 + tests/core/icnn_test.py | 14 + tests/core/neuraldual_test.py | 139 +++++ .../sinkhorn_anderson_acceleration_test.py | 14 + tests/core/sinkhorn_bures_test.py | 14 + tests/core/sinkhorn_diff_grid_loc_test.py | 14 + tests/core/sinkhorn_diff_grid_weights_test.py | 14 + tests/core/sinkhorn_diff_precond_test.py | 14 + tests/core/sinkhorn_diff_test.py | 14 + tests/core/sinkhorn_grid_test.py | 14 + tests/core/sinkhorn_hessian_test.py | 14 + tests/core/sinkhorn_implicit_lse_test.py | 14 + tests/core/sinkhorn_implicit_test.py | 14 + tests/core/sinkhorn_jacobian_apply_test.py | 14 + tests/core/sinkhorn_jit_test.py | 14 + tests/core/sinkhorn_lr_test.py | 14 + tests/core/sinkhorn_online_large_test.py | 14 + .../core/sinkhorn_potentials_jacobian_test.py | 14 + tests/core/sinkhorn_test.py | 14 + tests/core/sinkhorn_unbalanced_test.py | 14 + tests/geometry/geometry_costs_test.py | 14 + tests/geometry/geometry_lr_test.py | 14 + tests/geometry/geometry_lse_test.py | 14 + .../geometry_pointcloud_apply_test.py | 14 + tests/geometry/matrix_square_root_test.py | 14 + .../gaussian_mixture/fit_gmm_pair_test.py | 14 + tests/tools/gaussian_mixture/fit_gmm_test.py | 14 + .../gaussian_mixture_pair_test.py | 14 + .../gaussian_mixture/gaussian_mixture_test.py | 14 + tests/tools/gaussian_mixture/gaussian_test.py | 14 + tests/tools/gaussian_mixture/linalg_test.py | 14 + .../gaussian_mixture/probabilities_test.py | 14 + .../tools/gaussian_mixture/scale_tril_test.py | 14 + ...khorn_divergence_differentiability_test.py | 14 + tests/tools/sinkhorn_divergence_test.py | 14 + tests/tools/soft_sort_test.py | 14 + tests/tools/transport_test.py | 14 + 98 files changed, 2307 insertions(+), 3 deletions(-) create mode 100644 docs/notebooks/neural_dual.ipynb create mode 100644 ott/core/neuraldual.py create mode 100644 tests/core/neuraldual_test.py diff --git a/docs/conf.py b/docs/conf.py index 03f4b1ac0..c0cf8dbd0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full diff --git a/docs/core.rst b/docs/core.rst index b993da195..e7e625c44 100644 --- a/docs/core.rst +++ b/docs/core.rst @@ -43,6 +43,13 @@ Neural Potentials icnn.ICNN +Neural Potentials +----------------- +.. autosummary:: + :toctree: _autosummary + + neuraldual.NeuralDualSolver + neuraldual.NeuralDual References ---------- diff --git a/docs/index.rst b/docs/index.rst index 74d9096d5..279773788 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -63,6 +63,7 @@ There are currently three packages, ``geometry``, ``core`` and ``tools``, playin notebooks/soft_sort.ipynb notebooks/application_biology.ipynb notebooks/fairness.ipynb + notebooks/neural_dual.ipynb .. toctree:: diff --git a/docs/notebooks/neural_dual.ipynb b/docs/notebooks/neural_dual.ipynb new file mode 100644 index 000000000..da3e9b44a --- /dev/null +++ b/docs/notebooks/neural_dual.ipynb @@ -0,0 +1,502 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Learning the Kantorovich Dual using Input Convex Neural Networks" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this tutorial, we explore how to learn the solution of the Kantorovich dual based on parameterizing the two dual potentials $f$ and $g$ with two [input convex neural networks (ICNN)](http://proceedings.mlr.press/v70/amos17b/amos17b.pdf), a method developed by [Makkuva et al. (2020)](http://proceedings.mlr.press/v119/makkuva20a/makkuva20a.pdf). For more insights on the approach itself, we refer the user to the original publication.\n", + "Given dataloaders containing samples of the *source* and the *target* distribution, `OTT`'s `NeuralDualSolver` finds the pair of optimal potentials $f$ and $g$ to solve the corresponding dual of the optimal transport problem. Once a solution has been found, this can be used to transport unseen source data samples to its target distribution (or vice-versa) or compute the corresponding distance between new source and target distribution." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/bunnech/miniforge3/lib/python3.9/site-packages/jax/_src/lib/__init__.py:33: UserWarning: JAX on Mac ARM machines is experimental and minimally tested. Please see https://github.com/google/jax/issues/5501 in the event of problems.\n", + " warnings.warn(\"JAX on Mac ARM machines is experimental and minimally tested. \"\n" + ] + } + ], + "source": [ + "import jax\n", + "import jax.numpy as jnp\n", + "import numpy as np\n", + "import optax\n", + "import matplotlib.pyplot as plt\n", + "from torch.utils.data import IterableDataset\n", + "from torch.utils.data import DataLoader\n", + "from ott.tools.sinkhorn_divergence import sinkhorn_divergence\n", + "from ott.geometry import pointcloud\n", + "from ott.core.neuraldual import NeuralDualSolver\n", + "from ott.core import icnn" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Helper Functions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let us define some helper functions which we use for the subsequent analysis." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_ot_map(neural_dual, source, target, inverse=False):\n", + " \"\"\"Plot data and learned optimal transport map.\"\"\"\n", + "\n", + " def draw_arrows(a, b):\n", + " plt.arrow(a[0], a[1], b[0] - a[0], b[1] - a[1],\n", + " color=[0.5, 0.5, 1], alpha=0.3)\n", + "\n", + " if not inverse:\n", + " grad_state_s = neural_dual.transport(source)\n", + " else:\n", + " grad_state_s = neural_dual.inverse_transport(target)\n", + "\n", + " fig = plt.figure()\n", + " ax = fig.add_subplot(111)\n", + "\n", + " ax.scatter(target[:, 0], target[:, 1], color='#A7BED3',\n", + " alpha=0.5, label=r'$target$')\n", + " ax.scatter(source[:, 0], source[:, 1], color='#1A254B',\n", + " alpha=0.5, label=r'$source$')\n", + " if not inverse:\n", + " ax.scatter(grad_state_s[:, 0], grad_state_s[:, 1], color='#F2545B',\n", + " alpha=0.5, label=r'$\\nabla g(source)$')\n", + " else:\n", + " ax.scatter(grad_state_s[:, 0], grad_state_s[:, 1], color='#F2545B',\n", + " alpha=0.5, label=r'$\\nabla f(target)$')\n", + "\n", + " plt.legend()\n", + "\n", + " for i in range(source.shape[0]):\n", + " draw_arrows(source[i, :], grad_state_s[i, :])" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def get_optimizer(optimizer, lr, b1, b2, eps):\n", + " \"\"\"Returns a flax optimizer object based on `config`.\"\"\"\n", + "\n", + " if optimizer == 'Adam':\n", + " optimizer = optax.adam(learning_rate=lr, b1=b1, b2=b2, eps=eps)\n", + " elif optimizer == 'SGD':\n", + " optimizer = optax.sgd(learning_rate=lr, momentum=None, nesterov=False)\n", + " else:\n", + " raise NotImplementedError(\n", + " f'Optimizer {optimizer} not supported yet!')\n", + "\n", + " return optimizer" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "@jax.jit\n", + "def sinkhorn_loss(x, y, epsilon=0.1, power=2.0):\n", + " \"\"\"Computes transport between (x, a) and (y, b) via Sinkhorn algorithm.\"\"\"\n", + " a = jnp.ones(len(x)) / len(x)\n", + " b = jnp.ones(len(y)) / len(y)\n", + "\n", + " sdiv = sinkhorn_divergence(pointcloud.PointCloud, x, y, power=power,\n", + " epsilon=epsilon, a=a, b=b)\n", + " return sdiv.divergence" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup Training and Validation Datasets" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We apply the `NeuralDual` to compute the transport between toy datasets. In this tutorial, the user can choose between the datasets `simple` (data clustered in one center), `circle` (two-dimensional Gaussians arranged on a circle), `square_five` (two-dimensional Gaussians on a square with one Gaussian in the center), and `square_four` (two-dimensional Gaussians in the corners of a rectangle)." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "class ToyDataset(IterableDataset):\n", + " def __init__(self, name):\n", + " self.name = name\n", + "\n", + " def __iter__(self):\n", + " return self.create_sample_generators()\n", + "\n", + " def create_sample_generators(self, scale=5.0, variance=0.5):\n", + " # given name of dataset, select centers\n", + " if self.name == \"simple\":\n", + " centers = np.array([0, 0])\n", + "\n", + " elif self.name == \"circle\":\n", + " centers = np.array(\n", + " [\n", + " (1, 0),\n", + " (-1, 0),\n", + " (0, 1),\n", + " (0, -1),\n", + " (1.0 / np.sqrt(2), 1.0 / np.sqrt(2)),\n", + " (1.0 / np.sqrt(2), -1.0 / np.sqrt(2)),\n", + " (-1.0 / np.sqrt(2), 1.0 / np.sqrt(2)),\n", + " (-1.0 / np.sqrt(2), -1.0 / np.sqrt(2)),\n", + " ]\n", + " )\n", + "\n", + " elif self.name == \"square_five\":\n", + " centers = np.array([[0, 0], [1, 1], [-1, 1], [-1, -1], [1, -1]])\n", + "\n", + " elif self.name == \"square_four\":\n", + " centers = np.array([[1, 0], [0, 1], [-1, 0], [0, -1]])\n", + "\n", + " else:\n", + " raise NotImplementedError()\n", + "\n", + " # create generator which randomly picks center and adds noise\n", + " centers = scale * centers\n", + " while True:\n", + " center = centers[np.random.choice(len(centers))]\n", + " point = center + variance**2 * np.random.randn(2)\n", + "\n", + " yield point\n", + "\n", + "\n", + "def load_toy_data(name_source: str,\n", + " name_target: str,\n", + " batch_size: int = 1024,\n", + " valid_batch_size: int = 1024):\n", + " dataloaders = (\n", + " iter(DataLoader(ToyDataset(name_source), batch_size=batch_size)),\n", + " iter(DataLoader(ToyDataset(name_target), batch_size=batch_size)),\n", + " iter(DataLoader(ToyDataset(name_source), batch_size=valid_batch_size)),\n", + " iter(DataLoader(ToyDataset(name_target), batch_size=valid_batch_size)),\n", + " )\n", + " input_dim = 2\n", + " return dataloaders, input_dim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Solve Neural Dual" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to solve the neural dual, we need to define our dataloaders. The only requirement is that the corresponding source and target train and validation datasets are *iterators*." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "(dataloader_source, dataloader_target, _, _), input_dim = load_toy_data('simple', 'circle')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we define the architectures parameterizing the dual potentials $f$ and $g$. These need to be parameterized by ICNNs. You can adapt the size of the ICNNs by passing a sequence containing hidden layer sizes. While ICNNs are by default containing partially positive weights, we can solve the `NeuralDual` using approximations to this positivity constraint (via weight clipping and a weight penalization). For this, set `positive weights` to True in both the `ICNN` architecture and `NeuralDualSolver` configuration. For more details on how to customize the ICNN architectures, we refer you to the documentation." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# initialize models\n", + "neural_f = icnn.ICNN(dim_hidden=[64, 64, 64, 64])\n", + "neural_g = icnn.ICNN(dim_hidden=[64, 64, 64, 64])\n", + "\n", + "# initialize optimizers\n", + "optimizer_f = get_optimizer('Adam', lr=0.0001, b1=0.5, b2=0.9, eps=0.00000001)\n", + "optimizer_g = get_optimizer('Adam', lr=0.0001, b1=0.5, b2=0.9, eps=0.00000001)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We then initialize the `NeuralDualSolver` by passing the two ICNN models parameterizing $f$ and $g$, as well as by specifying the input dimensions of the data and the number of training iterations to execute. Once the `NeuralDualSolver` is initialized, we can obtain the `NeuralDual` by passing the corresponding dataloaders to it, which will subsequently return the optimal `NeuralDual` for the problem. As here our training and validation datasets do not differ, we pass (`dataloader_source`, `dataloader_target`) for both training and validation steps. For more details on how to configer the `NeuralDualSolver`, we refer you to the documentation." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:absl:No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)\n", + "100%|██████████████████████████████████████████████████████████████████████████████████████████████████| 5000/5000 [16:55<00:00, 4.92it/s]\n" + ] + } + ], + "source": [ + "neural_dual_solver = NeuralDualSolver(\n", + " input_dim, neural_f, neural_g, optimizer_f, optimizer_g, num_train_iters=5000)\n", + "neural_dual = neural_dual_solver(\n", + " dataloader_source, dataloader_target, dataloader_source, dataloader_target)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Evaluate Neural Dual" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After training has completed successfully, we can evaluate the `NeuralDual` on unseen incoming data. We first sample a new batch from the source and target distribution." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "data_source = next(dataloader_source).numpy()\n", + "data_target = next(dataloader_target).numpy()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can plot the corresponding transport from source to target using the gradient of the learning potential `NeuralDual.g`, i.e., $\\nabla g(\\text{source})$, or from target to source via the gradient of the learning potential `NeuralDual.f`, i.e., $\\nabla f(\\text{target})$." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plot_ot_map(neural_dual, data_source, data_target, inverse=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plot_ot_map(neural_dual, data_target, data_source, inverse=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We further test, how close the predicted samples are to the sampled data." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First for potential $g$, transporting source to target samples. Ideally the resulting Sinkhorn distance is close to 0." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sinkhorn distance between predictions and data samples: 1.4347131252288818\n" + ] + } + ], + "source": [ + "pred_target = neural_dual.transport(data_source)\n", + "print(f'Sinkhorn distance between predictions and data samples: {sinkhorn_loss(pred_target, data_target)}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then for potential $f$, transporting target to source samples. Again, the resulting Sinkhorn distance needs to be close to 0." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sinkhorn distance between predictions and data samples: 0.03713676333427429\n" + ] + } + ], + "source": [ + "pred_source = neural_dual.inverse_transport(data_target)\n", + "print(f'Sinkhorn distance between predictions and data samples: {sinkhorn_loss(pred_source, data_source)}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Besides computing the transport and mapping source to target samples or vice versa, we can also compute the overall distance between new source and target samples." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Neural dual distance between source and target data: 22.301618576049805\n" + ] + } + ], + "source": [ + "neural_dual_dist = neural_dual.distance(data_source, data_target)\n", + "print(f'Neural dual distance between source and target data: {neural_dual_dist}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Which compares to the primal Sinkhorn distance in the following." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sinkhorn distance between source and target data: 22.258913040161133\n" + ] + } + ], + "source": [ + "sinkhorn_dist = sinkhorn_loss(data_source, data_target)\n", + "print(f'Sinkhorn distance between source and target data: {sinkhorn_dist}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "interpreter": { + "hash": "b37caf44d0318b4f4d9ee96c84a0e4fe372b1526393be3417b3365184e480b09" + }, + "kernelspec": { + "display_name": "Python 3.9.10 ('base')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/ott/__init__.py b/ott/__init__.py index 24e4f814f..ad67c93a3 100644 --- a/ott/__init__.py +++ b/ott/__init__.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """OTT library.""" from . import core diff --git a/ott/core/__init__.py b/ott/core/__init__.py index a3d004d73..8b66471d9 100644 --- a/ott/core/__init__.py +++ b/ott/core/__init__.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """OTT core libraries: the engines behind most computations happening in OTT.""" # pytype: disable=import-error # kwargs-checking @@ -11,6 +25,7 @@ from . import problems from . import sinkhorn from . import sinkhorn_lr +from . import neuraldual from .implicit_differentiation import ImplicitDiff from .problems import LinearProblem from .sinkhorn import Sinkhorn diff --git a/ott/core/anderson.py b/ott/core/anderson.py index 455ded1ba..644e36443 100644 --- a/ott/core/anderson.py +++ b/ott/core/anderson.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Tools for Anderson acceleration.""" from typing import Any import jax diff --git a/ott/core/dataclasses.py b/ott/core/dataclasses.py index 37c2ee1c9..8318a35ec 100644 --- a/ott/core/dataclasses.py +++ b/ott/core/dataclasses.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """pytree_nodes Dataclasses.""" import dataclasses diff --git a/ott/core/discrete_barycenter.py b/ott/core/discrete_barycenter.py index 73b267953..0a90b9fc1 100644 --- a/ott/core/discrete_barycenter.py +++ b/ott/core/discrete_barycenter.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Implementation of Janati+(2020) Wasserstein barycenter algorithm.""" diff --git a/ott/core/fixed_point_loop.py b/ott/core/fixed_point_loop.py index 006293f9b..0d54f570d 100644 --- a/ott/core/fixed_point_loop.py +++ b/ott/core/fixed_point_loop.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """jheek@ backprop-friendly implementation of fixed point loop.""" from typing import Callable, Any diff --git a/ott/core/gromov_wasserstein.py b/ott/core/gromov_wasserstein.py index 1eae732d5..c57077201 100644 --- a/ott/core/gromov_wasserstein.py +++ b/ott/core/gromov_wasserstein.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """A Jax version of the regularised GW Solver (Peyre et al. 2016).""" import functools diff --git a/ott/core/icnn.py b/ott/core/icnn.py index 163e205bf..b581ee202 100644 --- a/ott/core/icnn.py +++ b/ott/core/icnn.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Implementation of Amos+(2017) input convex neural networks (ICNN).""" @@ -77,17 +91,23 @@ class ICNN(nn.Module): init_std: float = 0.1 init_fn: Callable = jax.nn.initializers.normal act_fn: Callable = nn.leaky_relu + pos_weights: bool = True def setup(self): num_hidden = len(self.dim_hidden) w_zs = list() + if self.pos_weights: + Dense = PositiveDense + else: + Dense = nn.Dense + for i in range(1, num_hidden): - w_zs.append(PositiveDense( + w_zs.append(Dense( self.dim_hidden[i], kernel_init=self.init_fn(self.init_std), use_bias=False)) - w_zs.append(PositiveDense( + w_zs.append(Dense( 1, kernel_init=self.init_fn(self.init_std), use_bias=False)) self.w_zs = w_zs diff --git a/ott/core/implicit_differentiation.py b/ott/core/implicit_differentiation.py index 9be0d674c..de2088467 100644 --- a/ott/core/implicit_differentiation.py +++ b/ott/core/implicit_differentiation.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Functions entering the implicit differentiation of Sinkhorn.""" from typing import Callable, Optional, Tuple diff --git a/ott/core/momentum.py b/ott/core/momentum.py index 9716ec957..d3ff998e6 100644 --- a/ott/core/momentum.py +++ b/ott/core/momentum.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Functions related to momemtum.""" import jax.numpy as jnp diff --git a/ott/core/neuraldual.py b/ott/core/neuraldual.py new file mode 100644 index 000000000..0a9934b2d --- /dev/null +++ b/ott/core/neuraldual.py @@ -0,0 +1,359 @@ +# coding=utf-8 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Lint as: python3 +"""A Jax implementation of the ICNN based Kantorovich dual.""" + +from typing import Iterator, Optional +import jax +import jax.numpy as jnp +import optax +import flax.linen as nn +from flax.training import train_state +from flax.core import freeze +from optax._src import base +import warnings +from tqdm import tqdm +from ott.core import icnn + + +class NeuralDualSolver: + r"""Solver of the ICNN-based Kantorovich dual. + + The algorithm is described in: + Optimal transport mapping via input convex neural networks, + Makkuva-Taghvaei-Lee-Oh, ICML'20. + http://proceedings.mlr.press/v119/makkuva20a/makkuva20a.pdf + + Args: + input_dim: input dimensionality of data required for network init + neural_f: network architecture for potential f + neural_g: network architecture for potential g + optimizer_f: optimizer function for potential f + optimizer_g: optimizer function for potential g + num_train_iters: number of total training iterations + num_inner_iters: number of training iterations of g per iteration of f + valid_freq: frequency with which model is validated + log_freq: frequency with training and validation are logged + logging: option to return logs + seed: random seed for network initialiations + pos_weights: option to train networks with potitive weights or regularizer + beta: regularization parameter when not training with positive weights + + Returns: + the `NeuralDual` containing the optimal dual potentials f and g + """ + + def __init__(self, + input_dim: int, + neural_f: Optional[nn.Module] = None, + neural_g: Optional[nn.Module] = None, + optimizer_f: Optional[base.GradientTransformation] = None, + optimizer_g: Optional[base.GradientTransformation] = None, + num_train_iters: int = 100, + num_inner_iters: int = 10, + valid_freq: int = 100, + log_freq: int = 100, + logging: bool = False, + seed: int = 0, + pos_weights: bool = True, + beta: int = 1.0): + self.num_train_iters = num_train_iters + self.num_inner_iters = num_inner_iters + self.valid_freq = valid_freq + self.log_freq = log_freq + self.logging = logging + self.pos_weights = pos_weights + self.beta = beta + + # set random key + rng = jax.random.PRNGKey(seed) + + # set default optimizers + if optimizer_f is None: + optimizer_f = optax.adam(learning_rate=0.0001, b1=0.5, b2=0.9, eps=1e-8) + if optimizer_g is None: + optimizer_g = optax.adam(learning_rate=0.0001, b1=0.5, b2=0.9, eps=1e-8) + + # set default neural architectures + if neural_f is None: + neural_f = icnn.ICNN(dim_hidden=[64, 64, 64, 64]) + if neural_g is None: + neural_g = icnn.ICNN(dim_hidden=[64, 64, 64, 64]) + + # set optimizer and networks + self.setup(rng, neural_f, neural_g, input_dim, optimizer_f, optimizer_g) + + def setup(self, rng, neural_f, neural_g, input_dim, optimizer_f, optimizer_g): + """Setup all components required to train the `NeuralDual`.""" + # split random key + rng, rng_f, rng_g = jax.random.split(rng, 3) + + # check setting of network architectures + if (neural_f.pos_weights != self.pos_weights + or neural_g.pos_weights != self.pos_weights): + warnings.warn(f"Setting of ICNN and the positive weights setting of the \ + `NeuralDualSolver` are not consistent. Proceeding with \ + the `NeuralDualSolver` setting, with positive weigths \ + being {self.positive_weights}.") + neural_f.pos_weights = self.pos_weights + neural_g.pos_weights = self.pos_weights + + self.state_f = self.create_train_state( + rng_f, neural_f, optimizer_f, input_dim) + self.state_g = self.create_train_state( + rng_g, neural_g, optimizer_g, input_dim) + + # define train and valid step functions + self.train_step_f = self.get_step_fn(train=True, to_optimize='f') + self.valid_step_f = self.get_step_fn(train=False, to_optimize='f') + + self.train_step_g = self.get_step_fn(train=True, to_optimize='g') + self.valid_step_g = self.get_step_fn(train=False, to_optimize='g') + + def __call__(self, + trainloader_source: Iterator[jnp.ndarray], + trainloader_target: Iterator[jnp.ndarray], + validloader_source: Iterator[jnp.ndarray], + validloader_target: Iterator[jnp.ndarray],) -> 'NeuralDual': + logs = self.train_neuraldual( + trainloader_source, trainloader_target, + validloader_source, validloader_target + ) + if self.logging: + return NeuralDual(self.state_f, self.state_g), logs + else: + return NeuralDual(self.state_f, self.state_g) + + def train_neuraldual(self, trainloader_source, trainloader_target, + validloader_source, validloader_target): + """Implementation of the training and validation script.""" + + # define dict to contain source and target batch + batch_g = {} + batch_f = {} + valid_batch = {} + + # set logging dictionaries + train_logs = { + 'train_loss_f': [], + 'train_loss_g': [], + 'train_w_dist': [] + } + valid_logs = { + 'valid_loss_f': [], + 'valid_loss_g': [], + 'valid_w_dist': [] + } + + for step in tqdm(range(self.num_train_iters)): + # execute training steps + for _ in range(self.num_inner_iters): + # get train batch for potential g + batch_g['source'] = jnp.array(next(trainloader_source)) + batch_g['target'] = jnp.array(next(trainloader_target)) + + self.state_g, loss_g, _ = self.train_step_g( + self.state_f, self.state_g, batch_g) + + # get train batch for potential f + batch_f['source'] = jnp.array(next(trainloader_source)) + batch_f['target'] = jnp.array(next(trainloader_target)) + + self.state_f, loss_f, w_dist = self.train_step_f( + self.state_f, self.state_g, batch_f) + if not self.pos_weights: + self.state_f = self.state_f.replace( + params=self.clip_weights_icnn(self.state_f.params)) + + # log to wandb + if self.logging and step % self.log_freq == 0: + train_logs['train_loss_f'].append(float(loss_f)) + train_logs['train_loss_g'].append(float(loss_g)) + train_logs['train_w_dist'].append(float(w_dist)) + + # report the loss on an validuation dataset periodically + if (step != 0 and step % self.valid_freq == 0): + # get batch + valid_batch['source'] = jnp.array(next(validloader_source)) + valid_batch['target'] = jnp.array(next(validloader_target)) + + valid_loss_f, _ = self.valid_step_f( + self.state_f, self.state_g, valid_batch) + valid_loss_g, valid_w_dist = self.valid_step_g( + self.state_f, self.state_g, valid_batch) + + if self.logging: + # log training progress + valid_logs['valid_loss_f'].append(float(valid_loss_f)) + valid_logs['valid_loss_g'].append(float(valid_loss_g)) + valid_logs['valid_w_dist'].append(float(valid_w_dist)) + + return {'train_logs': train_logs, 'valid_logs': valid_logs} + + def get_step_fn(self, train, to_optimize='g'): + """Create a one-step training and evaluation function.""" + + def loss_fn(params_f, params_g, f, g, batch): + """Loss function for potential f.""" + # get two distributions + source, target = batch['source'], batch['target'] + + # get loss terms of kantorovich dual + f_t = f({'params': params_f}, batch['target']) + + grad_g_s = jax.vmap(lambda x: jax.grad(g, argnums=1)( + {'params': params_g}, x))(batch['source']) + + f_grad_g_s = f({'params': params_f}, grad_g_s) + + s_dot_grad_g_s = jnp.sum(source * grad_g_s, axis=1) + + s_sq = jnp.sum(source * source, axis=1) + t_sq = jnp.sum(target * target, axis=1) + + # compute final wasserstein distance + dist = 2 * jnp.mean(f_grad_g_s - f_t - s_dot_grad_g_s + + 0.5 * t_sq + 0.5 * s_sq) + + loss_f = jnp.mean(f_t - f_grad_g_s) + loss_g = jnp.mean(f_grad_g_s - s_dot_grad_g_s) + + if to_optimize == 'f': + return loss_f, dist + elif to_optimize == 'g': + if not self.pos_weights: + penalty = self.penalize_weights_icnn(params_g) + loss_g += self.beta * penalty + return loss_g, dist + else: + raise ValueError('Optimization target has been misspecified.') + + @jax.jit + def step_fn(state_f, state_g, batch): + """Step function of either training or validation.""" + + if to_optimize == 'f': + grad_fn = jax.value_and_grad(loss_fn, argnums=0, has_aux=True) + state = state_f + elif to_optimize == 'g': + grad_fn = jax.value_and_grad(loss_fn, argnums=1, has_aux=True) + state = state_g + else: + raise ValueError('Potential to be optimize might be misspecified.') + + if train: + # compute loss and gradients + (loss, dist), grads = grad_fn( + state_f.params, state_g.params, + state_f.apply_fn, state_g.apply_fn, batch) + + # update state + return state.apply_gradients(grads=grads), loss, dist + + else: + # compute loss and gradients + (loss, dist), _ = grad_fn( + state_f.params, state_g.params, + state_f.apply_fn, state_g.apply_fn, batch) + + # do not update state + return loss, dist + + return step_fn + + def create_train_state(self, rng, model, optimizer, input): + """Creates initial `TrainState`.""" + + params = model.init(rng, jnp.ones(input))['params'] + return train_state.TrainState.create( + apply_fn=model.apply, params=params, tx=optimizer) + + def clip_weights_icnn(params): + params = params.unfreeze() + for k in params.keys(): + if (k.startswith('w_z')): + params[k]['kernel'] = jnp.clip(params[k]['kernel'], a_min=0) + + return freeze(params) + + def penalize_weights_icnn(self, params): + penalty = 0 + for k in params.keys(): + if (k.startswith('w_z')): + penalty += jnp.linalg.norm(jax.nn.relu(-params[k]['kernel'])) + return penalty + + +@jax.tree_util.register_pytree_node_class +class NeuralDual: + r"""Neural Kantorovich dual. + + Attributes: + state_f: optimal potential f + state_g: optimal potential g + """ + + def __init__(self, state_f, state_g): + self.state_f = state_f + self.state_g = state_g + + def tree_flatten(self): + return ((self.state_f, self.state_g), None) + + @classmethod + def tree_unflatten(cls, aux_data, children): + return cls(*children, **aux_data) + + @property + def f(self): + return self.state_f + + @property + def g(self): + return self.state_g + + def transport(self, data: jnp.ndarray) -> jnp.ndarray: + """Transport source data samples with potential g.""" + + return jax.vmap(lambda x: jax.grad(self.g.apply_fn, argnums=1)( + {'params': self.g.params}, x))(data) + + def inverse_transport(self, data: jnp.ndarray) -> jnp.ndarray: + """Transport source data samples with potential g.""" + + return jax.vmap(lambda x: jax.grad(self.f.apply_fn, argnums=1)( + {'params': self.f.params}, x))(data) + + def distance(self, + source: jnp.ndarray, + target: jnp.ndarray) -> float: + """Given potentials f and g, compute the overall distance.""" + + f_t = self.f.apply_fn({'params': self.f.params}, target) + + grad_g_s = jax.vmap(lambda x: jax.grad(self.g.apply_fn, argnums=1)( + {'params': self.g.params}, x))(source) + + f_grad_g_s = self.f.apply_fn({'params': self.f.params}, grad_g_s) + + s_dot_grad_g_s = jnp.sum(source * grad_g_s, axis=1) + + s_sq = jnp.sum(source * source, axis=1) + t_sq = jnp.sum(target * target, axis=1) + + # compute final wasserstein distance + dist = 2 * jnp.mean(f_grad_g_s - f_t - s_dot_grad_g_s + + 0.5 * t_sq + 0.5 * s_sq) + return dist diff --git a/ott/core/problems.py b/ott/core/problems.py index 815911622..a1caeb164 100644 --- a/ott/core/problems.py +++ b/ott/core/problems.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Classes defining OT problem(s) (objective function + utilities).""" from typing import Optional, Tuple diff --git a/ott/core/quad_problems.py b/ott/core/quad_problems.py index beb746cfd..1a1ca3d93 100644 --- a/ott/core/quad_problems.py +++ b/ott/core/quad_problems.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Classes defining OT problem(s) (objective function + utilities).""" from typing import Callable, Optional, Tuple, Union diff --git a/ott/core/sinkhorn.py b/ott/core/sinkhorn.py index 1c1b5ede4..1682e60b3 100644 --- a/ott/core/sinkhorn.py +++ b/ott/core/sinkhorn.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """A Jax implementation of the Sinkhorn algorithm.""" from typing import Optional, Callable, NamedTuple, Sequence, Tuple diff --git a/ott/core/sinkhorn_lr.py b/ott/core/sinkhorn_lr.py index 2b1bbfe0e..b4c68923d 100644 --- a/ott/core/sinkhorn_lr.py +++ b/ott/core/sinkhorn_lr.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """A Jax implementation of the Low-Rank Sinkhorn algorithm.""" from typing import Optional, NamedTuple, Tuple, Any diff --git a/ott/core/unbalanced_functions.py b/ott/core/unbalanced_functions.py index fd2d7b9f6..784e71457 100644 --- a/ott/core/unbalanced_functions.py +++ b/ott/core/unbalanced_functions.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Functions useful to define unbalanced OT problems.""" diff --git a/ott/examples/fairness/config.py b/ott/examples/fairness/config.py index 504139444..e6f87f72b 100644 --- a/ott/examples/fairness/config.py +++ b/ott/examples/fairness/config.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Configuration to train a fairness aware classifier on the adult dataset.""" import ml_collections diff --git a/ott/examples/fairness/data.py b/ott/examples/fairness/data.py index 6ac74b361..e5f4cbde5 100644 --- a/ott/examples/fairness/data.py +++ b/ott/examples/fairness/data.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Loads the adult dataset data.""" import os diff --git a/ott/examples/fairness/losses.py b/ott/examples/fairness/losses.py index 40f6fde61..1a4812b67 100644 --- a/ott/examples/fairness/losses.py +++ b/ott/examples/fairness/losses.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Losses for the fairness example.""" import functools diff --git a/ott/examples/fairness/main.py b/ott/examples/fairness/main.py index 5383368e2..db71e44a6 100644 --- a/ott/examples/fairness/main.py +++ b/ott/examples/fairness/main.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Runs the training of the network on CIFAR10.""" from typing import Sequence diff --git a/ott/examples/fairness/models.py b/ott/examples/fairness/models.py index 7465e3620..f0136a2d1 100644 --- a/ott/examples/fairness/models.py +++ b/ott/examples/fairness/models.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """A model for to embed structured features.""" from typing import Any, Tuple diff --git a/ott/examples/fairness/train.py b/ott/examples/fairness/train.py index 09cb39292..79df353df 100644 --- a/ott/examples/fairness/train.py +++ b/ott/examples/fairness/train.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Training a network on the adult dataset with fairnes constraints.""" import collections diff --git a/ott/examples/soft_error/config.py b/ott/examples/soft_error/config.py index d3226c02f..4e8a420e9 100644 --- a/ott/examples/soft_error/config.py +++ b/ott/examples/soft_error/config.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Default Hyperparameter configuration.""" import ml_collections diff --git a/ott/examples/soft_error/data.py b/ott/examples/soft_error/data.py index f140b0a9f..2ca4ba44d 100644 --- a/ott/examples/soft_error/data.py +++ b/ott/examples/soft_error/data.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Data loading and data augmentation.""" from flax import jax_utils diff --git a/ott/examples/soft_error/losses.py b/ott/examples/soft_error/losses.py index d6b3110c7..a2b31d8ae 100644 --- a/ott/examples/soft_error/losses.py +++ b/ott/examples/soft_error/losses.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Defines classification losses.""" import functools diff --git a/ott/examples/soft_error/main.py b/ott/examples/soft_error/main.py index 4eeca5c16..3d7c12655 100644 --- a/ott/examples/soft_error/main.py +++ b/ott/examples/soft_error/main.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Runs the training of the network on CIFAR10.""" from typing import Sequence diff --git a/ott/examples/soft_error/model.py b/ott/examples/soft_error/model.py index b1db6209a..9370077d5 100644 --- a/ott/examples/soft_error/model.py +++ b/ott/examples/soft_error/model.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Flax CNN model.""" from typing import Any diff --git a/ott/examples/soft_error/train.py b/ott/examples/soft_error/train.py index 3b4b9983e..a5fcf70ee 100644 --- a/ott/examples/soft_error/train.py +++ b/ott/examples/soft_error/train.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Train for the soft-error loss.""" import collections diff --git a/ott/geometry/__init__.py b/ott/geometry/__init__.py index 879ef6e27..f32306b07 100644 --- a/ott/geometry/__init__.py +++ b/ott/geometry/__init__.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """OTT ground geometries: Classes and cost functions to instantiate them.""" from . import costs from . import low_rank diff --git a/ott/geometry/costs.py b/ott/geometry/costs.py index 738607ca7..1269ed21d 100644 --- a/ott/geometry/costs.py +++ b/ott/geometry/costs.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Several cost/norm functions for relevant vector types.""" import abc diff --git a/ott/geometry/epsilon_scheduler.py b/ott/geometry/epsilon_scheduler.py index 0784554d1..f3d59025e 100644 --- a/ott/geometry/epsilon_scheduler.py +++ b/ott/geometry/epsilon_scheduler.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """A class to define a scheduler for the entropic regularization epsilon.""" from typing import Optional diff --git a/ott/geometry/geometry.py b/ott/geometry/geometry.py index 1f63f36cd..d36643720 100644 --- a/ott/geometry/geometry.py +++ b/ott/geometry/geometry.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """A class describing operations used to instantiate and use a geometry.""" import functools diff --git a/ott/geometry/grid.py b/ott/geometry/grid.py index fc0a6afd9..8698786b9 100644 --- a/ott/geometry/grid.py +++ b/ott/geometry/grid.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Implements a geometry class for points supported on a cartesian product.""" import itertools diff --git a/ott/geometry/low_rank.py b/ott/geometry/low_rank.py index 250b7e2a9..95460be5f 100644 --- a/ott/geometry/low_rank.py +++ b/ott/geometry/low_rank.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """A class describing low-rank geometries.""" import jax diff --git a/ott/geometry/matrix_square_root.py b/ott/geometry/matrix_square_root.py index d9690d113..6bf63b45b 100644 --- a/ott/geometry/matrix_square_root.py +++ b/ott/geometry/matrix_square_root.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """A Jax backprop friendly version of Matrix square root.""" diff --git a/ott/geometry/ops.py b/ott/geometry/ops.py index ed1be8b02..8e2227ef6 100644 --- a/ott/geometry/ops.py +++ b/ott/geometry/ops.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Low level functions used within the scope of Geometric processing.""" import functools diff --git a/ott/geometry/pointcloud.py b/ott/geometry/pointcloud.py index 0c72a4ce6..25cc21930 100644 --- a/ott/geometry/pointcloud.py +++ b/ott/geometry/pointcloud.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """A geometry defined using 2 point clouds and a cost function between them.""" from typing import Optional, Union diff --git a/ott/tools/__init__.py b/ott/tools/__init__.py index f936f0cfe..7bc804c59 100644 --- a/ott/tools/__init__.py +++ b/ott/tools/__init__.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """OTT tools: A set of tools to use OT in differentiable ML pipelines.""" #from . import plot diff --git a/ott/tools/gaussian_mixture/__init__.py b/ott/tools/gaussian_mixture/__init__.py index c345ac607..5791c0c1e 100644 --- a/ott/tools/gaussian_mixture/__init__.py +++ b/ott/tools/gaussian_mixture/__init__.py @@ -1,2 +1,16 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """OTT tools: A set of tools to use OT in differentiable ML pipelines.""" diff --git a/ott/tools/gaussian_mixture/fit_gmm.py b/ott/tools/gaussian_mixture/fit_gmm.py index 97589d9bb..17e25ae17 100644 --- a/ott/tools/gaussian_mixture/fit_gmm.py +++ b/ott/tools/gaussian_mixture/fit_gmm.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + r"""Fit a Gaussian mixture model. Sample usage: diff --git a/ott/tools/gaussian_mixture/fit_gmm_pair.py b/ott/tools/gaussian_mixture/fit_gmm_pair.py index 583700ca7..47eeae92f 100644 --- a/ott/tools/gaussian_mixture/fit_gmm_pair.py +++ b/ott/tools/gaussian_mixture/fit_gmm_pair.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + r"""Fit 2 GMMs to 2 point clouds using likelihood and (approx) W2 distance. Suppose we have two large point clouds and want to estimate a coupling and a diff --git a/ott/tools/gaussian_mixture/gaussian.py b/ott/tools/gaussian_mixture/gaussian.py index 43a86dd86..186b79699 100644 --- a/ott/tools/gaussian_mixture/gaussian.py +++ b/ott/tools/gaussian_mixture/gaussian.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Pytree for a normal distribution.""" import math diff --git a/ott/tools/gaussian_mixture/gaussian_mixture.py b/ott/tools/gaussian_mixture/gaussian_mixture.py index bd1f40e82..5f285d84c 100644 --- a/ott/tools/gaussian_mixture/gaussian_mixture.py +++ b/ott/tools/gaussian_mixture/gaussian_mixture.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python 3 """Pytree for a Gaussian mixture model.""" diff --git a/ott/tools/gaussian_mixture/gaussian_mixture_pair.py b/ott/tools/gaussian_mixture/gaussian_mixture_pair.py index 033f83dc3..3841cb293 100644 --- a/ott/tools/gaussian_mixture/gaussian_mixture_pair.py +++ b/ott/tools/gaussian_mixture/gaussian_mixture_pair.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Pytree containing parameters for a pair of coupled Gaussian mixture models. """ import jax diff --git a/ott/tools/gaussian_mixture/linalg.py b/ott/tools/gaussian_mixture/linalg.py index 60a69cae8..3da58dbb3 100644 --- a/ott/tools/gaussian_mixture/linalg.py +++ b/ott/tools/gaussian_mixture/linalg.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Linear algebra utility methods for optimal transport of Gaussian mixtures.""" from typing import Callable, Iterable, List, Optional, Tuple diff --git a/ott/tools/gaussian_mixture/probabilities.py b/ott/tools/gaussian_mixture/probabilities.py index fd1b99520..afd82c260 100644 --- a/ott/tools/gaussian_mixture/probabilities.py +++ b/ott/tools/gaussian_mixture/probabilities.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Pytree for a vector of probabilities.""" from typing import Optional diff --git a/ott/tools/gaussian_mixture/scale_tril.py b/ott/tools/gaussian_mixture/scale_tril.py index 1f3e70f1e..6f1e35d00 100644 --- a/ott/tools/gaussian_mixture/scale_tril.py +++ b/ott/tools/gaussian_mixture/scale_tril.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Pytree for a lower triangular Cholesky factored covariance matrix.""" from typing import Optional, Tuple diff --git a/ott/tools/plot.py b/ott/tools/plot.py index bba49ebfb..60f386653 100644 --- a/ott/tools/plot.py +++ b/ott/tools/plot.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Plotting utils.""" from typing import List, Optional, Sequence, Union diff --git a/ott/tools/sinkhorn_divergence.py b/ott/tools/sinkhorn_divergence.py index fa6017bfb..6d12db341 100644 --- a/ott/tools/sinkhorn_divergence.py +++ b/ott/tools/sinkhorn_divergence.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Implements the sinkhorn divergence.""" import collections diff --git a/ott/tools/soft_sort.py b/ott/tools/soft_sort.py index c09c207b4..ba66ca6c4 100644 --- a/ott/tools/soft_sort.py +++ b/ott/tools/soft_sort.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Soft sort operators.""" import functools diff --git a/ott/tools/transport.py b/ott/tools/transport.py index b672d4b96..976feeaa1 100644 --- a/ott/tools/transport.py +++ b/ott/tools/transport.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Some utility functions for transport computation. This module is primarily made for new users who are looking for one-liners. diff --git a/ott/version.py b/ott/version.py index 6341afd2e..fe025177d 100644 --- a/ott/version.py +++ b/ott/version.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Current ott version.""" __version__ = "0.2.3" diff --git a/requirements.txt b/requirements.txt index 21cccf4a9..58eeb82c5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ jaxlib>=0.1.47 numpy>=1.18.4 matplotlib>=2.0.1 flax>=0.3.6 -optax>=0.0.9 +optax>=0.1.1 +tqdm>=4.63.0 diff --git a/setup.py b/setup.py index 2a3d709e0..eb02d95d8 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Setup script for installing ott as a pip module.""" import os import setuptools diff --git a/tests/core/discrete_barycenter_test.py b/tests/core/discrete_barycenter_test.py index 9bc66332d..39bb9f670 100644 --- a/tests/core/discrete_barycenter_test.py +++ b/tests/core/discrete_barycenter_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Policy.""" diff --git a/tests/core/fused_gromov_wasserstein_test.py b/tests/core/fused_gromov_wasserstein_test.py index fdfbf2503..2fc493ea4 100644 --- a/tests/core/fused_gromov_wasserstein_test.py +++ b/tests/core/fused_gromov_wasserstein_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Fused Gromov Wasserstein.""" diff --git a/tests/core/gromov_wasserstein_test.py b/tests/core/gromov_wasserstein_test.py index f8839b416..6ee1f453a 100644 --- a/tests/core/gromov_wasserstein_test.py +++ b/tests/core/gromov_wasserstein_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Gromov Wasserstein.""" diff --git a/tests/core/gromov_wasserstein_unbalanced_test.py b/tests/core/gromov_wasserstein_unbalanced_test.py index 6111defa6..f24367ea1 100644 --- a/tests/core/gromov_wasserstein_unbalanced_test.py +++ b/tests/core/gromov_wasserstein_unbalanced_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Gromov Wasserstein.""" diff --git a/tests/core/icnn_test.py b/tests/core/icnn_test.py index 09634b149..be0c1d8b8 100644 --- a/tests/core/icnn_test.py +++ b/tests/core/icnn_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for ICNN network architecture.""" diff --git a/tests/core/neuraldual_test.py b/tests/core/neuraldual_test.py new file mode 100644 index 000000000..3f4456696 --- /dev/null +++ b/tests/core/neuraldual_test.py @@ -0,0 +1,139 @@ +# coding=utf-8 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Lint as: python3 +"""Tests for implementation of ICNN-based Kantorovich dual by Makkuva+(2020).""" + +from absl.testing import absltest +from absl.testing import parameterized +import jax +import jax.test_util +import numpy as np +from ott.core.neuraldual import NeuralDualSolver + + +class ToyDataset(): + def __init__(self, name): + self.name = name + + def __iter__(self): + return self.create_sample_generators() + + def create_sample_generators(self, scale=5.0, variance=0.5): + # given name of dataset, select centers + if self.name == "simple": + centers = np.array([0, 0]) + + elif self.name == "circle": + centers = np.array( + [ + (1, 0), + (-1, 0), + (0, 1), + (0, -1), + (1.0 / np.sqrt(2), 1.0 / np.sqrt(2)), + (1.0 / np.sqrt(2), -1.0 / np.sqrt(2)), + (-1.0 / np.sqrt(2), 1.0 / np.sqrt(2)), + (-1.0 / np.sqrt(2), -1.0 / np.sqrt(2)), + ] + ) + + elif self.name == "square_five": + centers = np.array([[0, 0], [1, 1], [-1, 1], [-1, -1], [1, -1]]) + + elif self.name == "square_four": + centers = np.array([[1, 0], [0, 1], [-1, 0], [0, -1]]) + + else: + raise NotImplementedError() + + # create generator which randomly picks center and adds noise + centers = scale * centers + while True: + center = centers[np.random.choice(len(centers))] + point = center + variance**2 * np.random.randn(2) + + yield np.expand_dims(point, 0) + + +def load_toy_data(name_source: str, + name_target: str): + dataloaders = ( + iter(ToyDataset(name_source)), + iter(ToyDataset(name_target)), + iter(ToyDataset(name_source)), + iter(ToyDataset(name_target)), + ) + input_dim = 2 + return dataloaders, input_dim + + +class NeuralDualTest(jax.test_util.JaxTestCase): + def setUp(self): + super().setUp() + self.rng = jax.random.PRNGKey(0) + + @parameterized.parameters({"num_train_iters": 100, "log_freq": 100}) + def test_neural_dual_convergence(self, num_train_iters, log_freq): + """Tests convergence of learning the Kantorovich dual using ICNNs.""" + def increasing(losses): + return all(x <= y for x, y in zip(losses, losses[1:])) + + def decreasing(losses): + return all(x >= y for x, y in zip(losses, losses[1:])) + + # initialize dataloaders + (dataloader_source, dataloader_target, _, _), input_dim = load_toy_data( + 'simple', 'circle') + + # inizialize neural dual + neural_dual_solver = NeuralDualSolver( + input_dim=input_dim, num_train_iters=num_train_iters, + logging=True, log_freq=log_freq) + neural_dual, logs = neural_dual_solver( + dataloader_source, dataloader_target, + dataloader_source, dataloader_target) + + # check if training loss of f is increasing and g is decreasing + self.assertTrue( + increasing(logs['train_logs']['train_loss_f']) + and decreasing(logs['train_logs']['train_loss_g'])) + + @parameterized.parameters({"num_train_iters": 10}) + def test_neural_dual_jit(self, num_train_iters): + + # initialize dataloaders + (dataloader_source, dataloader_target, _, _), input_dim = load_toy_data( + 'simple', 'circle') + # inizialize neural dual + neural_dual_solver = NeuralDualSolver( + input_dim=input_dim, num_train_iters=num_train_iters) + neural_dual = neural_dual_solver( + dataloader_source, dataloader_target, + dataloader_source, dataloader_target) + + data_source = next(dataloader_source) + pred_target = neural_dual.transport(data_source) + + compute_transport = jax.jit(lambda data_source: neural_dual.transport( + data_source)) + pred_target_jit = compute_transport(data_source) + + # ensure epsilon and optimal f's are a scale^2 apart (^2 comes from ^2 cost) + self.assertAllClose(pred_target, pred_target_jit, + rtol=1e-3, atol=1e-3) + + +if __name__ == "__main__": + absltest.main() diff --git a/tests/core/sinkhorn_anderson_acceleration_test.py b/tests/core/sinkhorn_anderson_acceleration_test.py index f3d255174..51c29c2a8 100644 --- a/tests/core/sinkhorn_anderson_acceleration_test.py +++ b/tests/core/sinkhorn_anderson_acceleration_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests Anderson acceleration for sinkhorn.""" diff --git a/tests/core/sinkhorn_bures_test.py b/tests/core/sinkhorn_bures_test.py index d8c0ef6ab..e27139e97 100644 --- a/tests/core/sinkhorn_bures_test.py +++ b/tests/core/sinkhorn_bures_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Bures cost between Gaussian distributions.""" diff --git a/tests/core/sinkhorn_diff_grid_loc_test.py b/tests/core/sinkhorn_diff_grid_loc_test.py index 85e729349..93cd4630c 100644 --- a/tests/core/sinkhorn_diff_grid_loc_test.py +++ b/tests/core/sinkhorn_diff_grid_loc_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Test gradient of Sinkhorn applied to grid w.r.t. location.""" from absl.testing import absltest diff --git a/tests/core/sinkhorn_diff_grid_weights_test.py b/tests/core/sinkhorn_diff_grid_weights_test.py index c8c11f61d..f63843e3e 100644 --- a/tests/core/sinkhorn_diff_grid_weights_test.py +++ b/tests/core/sinkhorn_diff_grid_weights_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Test gradient of Sinkhorn applied to grid w.r.t. probability weights.""" diff --git a/tests/core/sinkhorn_diff_precond_test.py b/tests/core/sinkhorn_diff_precond_test.py index 024d7dbe6..a39e67ea8 100644 --- a/tests/core/sinkhorn_diff_precond_test.py +++ b/tests/core/sinkhorn_diff_precond_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Jacobian of optimal potential.""" import functools diff --git a/tests/core/sinkhorn_diff_test.py b/tests/core/sinkhorn_diff_test.py index 23e8d09dd..46b7ac710 100644 --- a/tests/core/sinkhorn_diff_test.py +++ b/tests/core/sinkhorn_diff_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the differentiability of reg_ot_cost w.r.t weights/locations.""" diff --git a/tests/core/sinkhorn_grid_test.py b/tests/core/sinkhorn_grid_test.py index 7c4f3fffe..0c5d6491a 100644 --- a/tests/core/sinkhorn_grid_test.py +++ b/tests/core/sinkhorn_grid_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for Sinkhorn when applied on a grid.""" diff --git a/tests/core/sinkhorn_hessian_test.py b/tests/core/sinkhorn_hessian_test.py index bd4b14b9e..7246ff64f 100644 --- a/tests/core/sinkhorn_hessian_test.py +++ b/tests/core/sinkhorn_hessian_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Policy.""" diff --git a/tests/core/sinkhorn_implicit_lse_test.py b/tests/core/sinkhorn_implicit_lse_test.py index 172d6cc02..69d3ccd0d 100644 --- a/tests/core/sinkhorn_implicit_lse_test.py +++ b/tests/core/sinkhorn_implicit_lse_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Policy.""" diff --git a/tests/core/sinkhorn_implicit_test.py b/tests/core/sinkhorn_implicit_test.py index 77f259e33..5a0296cd7 100644 --- a/tests/core/sinkhorn_implicit_test.py +++ b/tests/core/sinkhorn_implicit_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Policy.""" diff --git a/tests/core/sinkhorn_jacobian_apply_test.py b/tests/core/sinkhorn_jacobian_apply_test.py index f628cb655..666228c0a 100644 --- a/tests/core/sinkhorn_jacobian_apply_test.py +++ b/tests/core/sinkhorn_jacobian_apply_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Jacobian of Apply OT.""" diff --git a/tests/core/sinkhorn_jit_test.py b/tests/core/sinkhorn_jit_test.py index 55de292c6..fb13a95a7 100644 --- a/tests/core/sinkhorn_jit_test.py +++ b/tests/core/sinkhorn_jit_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Jitting test for Sinkhorn.""" import functools diff --git a/tests/core/sinkhorn_lr_test.py b/tests/core/sinkhorn_lr_test.py index ee1ff5c8f..9a6f53e40 100644 --- a/tests/core/sinkhorn_lr_test.py +++ b/tests/core/sinkhorn_lr_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Policy.""" diff --git a/tests/core/sinkhorn_online_large_test.py b/tests/core/sinkhorn_online_large_test.py index 68d25598b..f59c836c0 100644 --- a/tests/core/sinkhorn_online_large_test.py +++ b/tests/core/sinkhorn_online_large_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests Online option for PointCloud geometry.""" from functools import partial diff --git a/tests/core/sinkhorn_potentials_jacobian_test.py b/tests/core/sinkhorn_potentials_jacobian_test.py index a909fdde4..18d75dd8e 100644 --- a/tests/core/sinkhorn_potentials_jacobian_test.py +++ b/tests/core/sinkhorn_potentials_jacobian_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Jacobian of optimal potential.""" diff --git a/tests/core/sinkhorn_test.py b/tests/core/sinkhorn_test.py index 43068a921..9b7ab3f5f 100644 --- a/tests/core/sinkhorn_test.py +++ b/tests/core/sinkhorn_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Policy.""" diff --git a/tests/core/sinkhorn_unbalanced_test.py b/tests/core/sinkhorn_unbalanced_test.py index 66db1f101..d818e3620 100644 --- a/tests/core/sinkhorn_unbalanced_test.py +++ b/tests/core/sinkhorn_unbalanced_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Policy.""" diff --git a/tests/geometry/geometry_costs_test.py b/tests/geometry/geometry_costs_test.py index a2a24ff67..fd26321fd 100644 --- a/tests/geometry/geometry_costs_test.py +++ b/tests/geometry/geometry_costs_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the cost/norm functions.""" diff --git a/tests/geometry/geometry_lr_test.py b/tests/geometry/geometry_lr_test.py index 78f08ba68..faf2682d8 100644 --- a/tests/geometry/geometry_lr_test.py +++ b/tests/geometry/geometry_lr_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Test Low-Rank Geometry.""" diff --git a/tests/geometry/geometry_lse_test.py b/tests/geometry/geometry_lse_test.py index 078b39189..eb68f3364 100644 --- a/tests/geometry/geometry_lse_test.py +++ b/tests/geometry/geometry_lse_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the jvp of a custom implementation of lse.""" diff --git a/tests/geometry/geometry_pointcloud_apply_test.py b/tests/geometry/geometry_pointcloud_apply_test.py index ba615c891..2b3ab7b59 100644 --- a/tests/geometry/geometry_pointcloud_apply_test.py +++ b/tests/geometry/geometry_pointcloud_apply_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for apply_cost and apply_kernel.""" diff --git a/tests/geometry/matrix_square_root_test.py b/tests/geometry/matrix_square_root_test.py index dfa3d0e4a..c5038280c 100644 --- a/tests/geometry/matrix_square_root_test.py +++ b/tests/geometry/matrix_square_root_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for matrix square roots.""" from typing import Callable diff --git a/tests/tools/gaussian_mixture/fit_gmm_pair_test.py b/tests/tools/gaussian_mixture/fit_gmm_pair_test.py index a3c337c80..1a42b5bab 100644 --- a/tests/tools/gaussian_mixture/fit_gmm_pair_test.py +++ b/tests/tools/gaussian_mixture/fit_gmm_pair_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Tests for fit_gmm_pair.""" from absl.testing import absltest diff --git a/tests/tools/gaussian_mixture/fit_gmm_test.py b/tests/tools/gaussian_mixture/fit_gmm_test.py index ba3128150..694f20d0f 100644 --- a/tests/tools/gaussian_mixture/fit_gmm_test.py +++ b/tests/tools/gaussian_mixture/fit_gmm_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Tests for fit_gmm_pair.""" from absl.testing import absltest diff --git a/tests/tools/gaussian_mixture/gaussian_mixture_pair_test.py b/tests/tools/gaussian_mixture/gaussian_mixture_pair_test.py index 051c49246..e833c3fba 100644 --- a/tests/tools/gaussian_mixture/gaussian_mixture_pair_test.py +++ b/tests/tools/gaussian_mixture/gaussian_mixture_pair_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python 3 """Tests for gaussian_mixture_pair.""" diff --git a/tests/tools/gaussian_mixture/gaussian_mixture_test.py b/tests/tools/gaussian_mixture/gaussian_mixture_test.py index 33951762e..fff96a55c 100644 --- a/tests/tools/gaussian_mixture/gaussian_mixture_test.py +++ b/tests/tools/gaussian_mixture/gaussian_mixture_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python 3 """Tests for gaussian_mixture.""" diff --git a/tests/tools/gaussian_mixture/gaussian_test.py b/tests/tools/gaussian_mixture/gaussian_test.py index acd06d30c..b0bc0fb2c 100644 --- a/tests/tools/gaussian_mixture/gaussian_test.py +++ b/tests/tools/gaussian_mixture/gaussian_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Tests for gaussian.""" from absl.testing import absltest diff --git a/tests/tools/gaussian_mixture/linalg_test.py b/tests/tools/gaussian_mixture/linalg_test.py index 5df30e116..9aa670854 100644 --- a/tests/tools/gaussian_mixture/linalg_test.py +++ b/tests/tools/gaussian_mixture/linalg_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Tests for linalg.""" from absl.testing import absltest diff --git a/tests/tools/gaussian_mixture/probabilities_test.py b/tests/tools/gaussian_mixture/probabilities_test.py index 00bbcc281..a25f36ebd 100644 --- a/tests/tools/gaussian_mixture/probabilities_test.py +++ b/tests/tools/gaussian_mixture/probabilities_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Tests for probabilities.""" from absl.testing import absltest diff --git a/tests/tools/gaussian_mixture/scale_tril_test.py b/tests/tools/gaussian_mixture/scale_tril_test.py index e8ccab009..77fbd97a3 100644 --- a/tests/tools/gaussian_mixture/scale_tril_test.py +++ b/tests/tools/gaussian_mixture/scale_tril_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Tests for google3.experimental.users.geoffd.contour.clustering.ot.parameters.scale_tril_params.""" from absl.testing import absltest diff --git a/tests/tools/sinkhorn_divergence_differentiability_test.py b/tests/tools/sinkhorn_divergence_differentiability_test.py index eb9138ed2..3062507eb 100644 --- a/tests/tools/sinkhorn_divergence_differentiability_test.py +++ b/tests/tools/sinkhorn_divergence_differentiability_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Sinkhorn divergence.""" diff --git a/tests/tools/sinkhorn_divergence_test.py b/tests/tools/sinkhorn_divergence_test.py index 2f458b0ea..d3a56112a 100644 --- a/tests/tools/sinkhorn_divergence_test.py +++ b/tests/tools/sinkhorn_divergence_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the Sinkhorn divergence.""" diff --git a/tests/tools/soft_sort_test.py b/tests/tools/soft_sort_test.py index fbc657fc2..427f85262 100644 --- a/tests/tools/soft_sort_test.py +++ b/tests/tools/soft_sort_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Lint as: python3 """Tests for the soft sort tools.""" import functools diff --git a/tests/tools/transport_test.py b/tests/tools/transport_test.py index bb449e877..78aa323f3 100644 --- a/tests/tools/transport_test.py +++ b/tests/tools/transport_test.py @@ -1,4 +1,18 @@ # coding=utf-8 +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Tests for ott.tools.transport.""" from absl.testing import absltest