Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
1ae8472
Add TBA section to README
dcoeurjo Oct 15, 2025
e991d04
Merge branch 'master' into master
rflamary Nov 18, 2025
4e9f1a4
add files
rflamary Nov 18, 2025
5db0676
cleanup files
rflamary Nov 18, 2025
a42a0cf
remove readme line
rflamary Nov 18, 2025
ca8711b
debug cython now build fail because of c++ !
rflamary Nov 18, 2025
da9cc30
fix h file
rflamary Nov 18, 2025
f9b9c91
Adding static Eigen 3.4.0
dcoeurjo Jan 28, 2026
bf485bc
Merge branch 'master' into master
dcoeurjo Jan 28, 2026
60f3ec3
up BSP-OT wrapper
baptiste-genest Jan 28, 2026
15683c9
up BSP-OT wrapper
baptiste-genest Jan 28, 2026
d05597d
up BSP-OT wrapper for merging
baptiste-genest Jan 28, 2026
3ed3b14
Merge branch 'master' into master
rflamary Jan 29, 2026
b64f5e5
Merging conflict
dcoeurjo Jan 29, 2026
7e4962a
Detecting openmp flags on macOS (with libomp from brew) --lint
dcoeurjo Jan 28, 2026
777ec24
Merging conflict
dcoeurjo Jan 29, 2026
fd0053f
std=c++17
dcoeurjo Jan 29, 2026
d9d92f0
RELEASE.md
dcoeurjo Jan 29, 2026
e6dbc3f
Removing static eigen
dcoeurjo Jan 29, 2026
bf96176
Eigen as submodule
dcoeurjo Jan 29, 2026
88c795d
submodules in the GithubActions
dcoeurjo Jan 29, 2026
81af86f
add norm as cost, change metric parameter name
baptiste-genest Jan 29, 2026
6210214
fix merge
baptiste-genest Jan 29, 2026
85c0755
fix merging
baptiste-genest Jan 29, 2026
59ec8a4
Missing 'with submodule' in GA
dcoeurjo Jan 29, 2026
13daaae
Moving eigen to deps
dcoeurjo Jan 29, 2026
7bd21f1
Moving eigen to deps
dcoeurjo Jan 29, 2026
06d6b37
Merge branch 'master' into master
dcoeurjo Jan 29, 2026
1bdd23c
add initial plan in bijection compute
baptiste-genest Jan 29, 2026
3cc66ea
add bsp_wrap.cpp to gitignore
baptiste-genest Jan 29, 2026
b29b46e
remove bsp_wrap.cpp
baptiste-genest Jan 29, 2026
f9daf2f
fix eigen path
baptiste-genest Jan 29, 2026
b153ae2
undo
baptiste-genest Jan 29, 2026
eec2629
remove filesystem and timing code
baptiste-genest Jan 30, 2026
b248092
Merge branch 'master' into master
rflamary Feb 13, 2026
7f3dc42
Reverting openmp compilation flags
dcoeurjo Feb 18, 2026
a50746a
Reverting openmp compilation flags (ruff-format)
dcoeurjo Feb 18, 2026
969c604
Removing deadcode
dcoeurjo Feb 18, 2026
70bec56
Merge branch 'master' into master
dcoeurjo Feb 18, 2026
1fa6bb9
Merge branch 'openmpFixMacos'
dcoeurjo Feb 18, 2026
2b7fa53
Merging from PR#797
dcoeurjo Feb 18, 2026
866befb
Manifest fix
dcoeurjo Feb 18, 2026
560262e
Manifest (eigen)
dcoeurjo Feb 18, 2026
135df1c
std++17 for windows
dcoeurjo Feb 18, 2026
c1f5848
Fixing path
dcoeurjo Feb 18, 2026
fee71ae
add example code and tests for bsp-ot
baptiste-genest Mar 19, 2026
232f69e
Merge branch 'master' of github.com:dcoeurjo/POT
baptiste-genest Mar 19, 2026
1476be6
fix file name in manifest
baptiste-genest Mar 19, 2026
e90453b
Merge branch 'master' into master
dcoeurjo Mar 19, 2026
5d922f0
Merge branch 'master' of github.com:dcoeurjo/POT
baptiste-genest Mar 19, 2026
1fea14e
fix compile_args in setup file
baptiste-genest Mar 19, 2026
e2849a2
fix formatting
baptiste-genest Mar 19, 2026
f61d618
remove parallelism in bijection merging, not needed and problems with…
baptiste-genest Mar 19, 2026
d367798
try to avoid openmp over range based for loop
baptiste-genest Mar 20, 2026
19cd81f
upadte config.yml
rflamary Mar 20, 2026
c12374f
switch to 3.12
rflamary Mar 20, 2026
f115fb4
fix manifet
rflamary Mar 20, 2026
567c4ca
verbose build for not being kille don circleci
rflamary Mar 20, 2026
cc80e72
try better ressource
rflamary Mar 20, 2026
1a83fd8
micro fixes
baptiste-genest Mar 23, 2026
f659065
change wrapper code to handle generic backends
baptiste-genest Mar 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ version: 2
jobs:
build_docs:
docker:
- image: cimg/python:3.10
resource_class: medium
- image: cimg/python:3.12
resource_class: large
steps:
- checkout
- run:
Expand All @@ -27,6 +27,7 @@ jobs:
git remote add upstream https://github.com/PythonOT/POT.git;
git pull --ff-only upstream "refs/pull/$(cat merge.txt)/merge";
git fetch upstream master;
git submodule update --init --recursive
fi
# Load our data
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/build_doc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
submodules: true
# Standard drop-in approach that should work for most people.
- name: Free Disk Space (Ubuntu)
uses: insightsengineering/disk-space-reclaimer@v1
Expand All @@ -32,7 +34,7 @@ jobs:
python -m pip install --user --upgrade --progress-bar off -r requirements_all.txt
python -m pip install --user --upgrade --progress-bar off -r docs/requirements.txt
python -m pip install --user --upgrade --progress-bar off ipython sphinx-gallery memory_profiler
python -m pip install --user -e .
python -m pip install -v --user -e .
# Look at what we have and fail early if there is some library conflict
- name: Check installation
run: |
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/build_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ jobs:

- name: Checking Out Repository
uses: actions/checkout@v4
with:
submodules: true
# Install Python & Packages
- uses: actions/setup-python@v4
with:
Expand All @@ -43,6 +45,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true

- name: Set up Python
uses: actions/setup-python@v5
with:
Expand Down Expand Up @@ -70,6 +75,9 @@ jobs:
android: true
dotnet: true
- uses: actions/checkout@v4
with:
submodules: true

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
Expand All @@ -95,6 +103,9 @@ jobs:
if: "!contains(github.event.head_commit.message, 'no ci')"
steps:
- uses: actions/checkout@v4
with:
submodules: true

- name: Set up Python
uses: actions/setup-python@v5
with:
Expand Down Expand Up @@ -122,6 +133,8 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
Expand Down Expand Up @@ -150,6 +163,8 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/build_wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
Expand Down Expand Up @@ -54,6 +56,8 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/build_wheels_weekly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ docs/modules/
# Cython output
ot/lp/emd_wrap.cpp
ot/partial/partial_cython.cpp
ot/bsp/bsp_wrap.cpp

# Distribution / packaging
.Python
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "deps/eigen"]
path = deps/eigen
url = https://gitlab.com/libeigen/eigen.git
5 changes: 5 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ include ot/lp/network_simplex_simple.h
include ot/lp/network_simplex_simple_omp.h
include ot/lp/sparse_bipartitegraph.h
include ot/partial/partial_cython.pyx
include ot/bsp/BSP-OT_header_only.h
include ot/bsp/bsp_wrapper.cpp
include ot/bsp/bsp_wrapper.h
include ot/bsp/bsp_wrap.pyx
graft deps/eigen/Eigen
2 changes: 2 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This new release adds support for sparse cost matrices and a new lazy EMD solver
- Migrate backend from deprecated `scipy.sparse.coo_matrix` to modern `scipy.sparse.coo_array` (PR #782)
- Geomloss function now handles both scalar and slice indices for i and j (PR #785)
- Add support for sparse cost matrices in EMD solver (PR #778, Issue #397)
- Add "BSP-OT: Sparse transport plans between discrete measures in loglinear time" (PR #768)
- Added UOT1D with Frank-Wolfe in `ot.unbalanced.uot_1d` (PR #765)
- Add Sliced UOT and Unbalanced Sliced OT in `ot/unbalanced/_sliced.py` (PR #765)

Expand All @@ -27,6 +28,7 @@ This new release adds support for sparse cost matrices and a new lazy EMD solver
- Clean documentation (PR #787)
- Fix code coverage (PR #791)
- Fix test of the version of jax in `ot.backend` (PR #794)
- Reverting the openmp fix on macOS (PR #789) for macOS (PR #796)
- Reverting the openmp fix on macOS (PR #789) for macOS (PR #797)
- Align documentation build dependencies and doc extras (PR #801)

Expand Down
1 change: 1 addition & 0 deletions deps/eigen
Submodule eigen added at d71c30
120 changes: 120 additions & 0 deletions examples/others/plot_bsp_ot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"""
===================
Fast and accurate bijections using BSP-OT example
===================

This example shows how to use the BSP-OT solver to compute a bijection
between two large point clouds, and animate the morphing between them.

[?] Genest, B., Bonneel, N., Nivoliers, V., Coeurjolly, D.
BSP-OT: Sparse transport plans between discrete measures in log-linear time
ACM Transactions on Graphics, Siggraph Asia (2025).

"""

# Author: Baptiste Genest <baptistegenest@gmail.com>
#
# License: MIT License

import ot.bsp
import numpy as np
import time
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation



##############################################################################
# Data ganeration
# ----------------------------------

def sample_ball(n, radius=1.0, center=(0.0, 0.0)):
theta = 2 * np.pi * np.random.rand(n)
r = radius * np.sqrt(np.random.rand(n))

x = r * np.cos(theta) + center[0]
y = r * np.sin(theta) + center[1]

return np.stack((x, y), axis=1)


def sample_two_balls(n, radius=1.0, centers=((-1, 0), (1, 1))):
assert n % 2 == 0, "n must be even"

n_half = n // 2
X1 = sample_ball(n_half, radius, centers[0])
X2 = sample_ball(n_half, radius, centers[1])

return np.vstack((X1, X2))


# ----------------------------
# Load point clouds
# ----------------------------
N = 100000
A = sample_ball(N)
B = sample_two_balls(N, 0.5)

N = A.shape[0]


##############################################################################
# Bijection computation
# ----------------------------------

start = time.time()

# %% call BSP-OT solver
# The solver returns the transport cost, the final bijection and the
# intermediary ones used to compute the final one (here we set k = 64).
# Here we only use the final bijection.
cost, perm, _ = ot.bsp.bsp_solve(A, B, 64,2)
print(
"Bijection computed between {} points, with cost {} in {}s".format(
N, cost, time.time() - start
)
)

# %% Reorder B according to bijection
# such that the points are in correspondence along the morphing animation
# using simply A*(1-t) + B*t
B_perm = B[perm]

##############################################################################
# Setup animation
# ----------------------------

# Animation parameters
FRAMES = 100
INTERVAL = 20

# Plot setup
fig, ax = plt.subplots(figsize=(6, 6))
ax.set_aspect("equal")
ax.set_title("Bijective Point Cloud Morphing")

scat = ax.scatter(A[:, 0], A[:, 1], s=0.05, c="tab:blue")

all_pts = np.vstack([A, B_perm])
pad = 0.5
ax.set_xlim(all_pts[:, 0].min() - pad, all_pts[:, 0].max() + pad)
ax.set_ylim(all_pts[:, 1].min() - pad, all_pts[:, 1].max() + pad)


# ----------------------------
# Animation update
# ----------------------------
def update(frame):
t = frame / (FRAMES - 1)
t = t * t * (3 - 2 * t)
P = (1 - t) * A + t * B_perm
scat.set_offsets(P)
return (scat,)


# ----------------------------
# Run animation
# ----------------------------
ani = FuncAnimation(fig, update, frames=FRAMES, interval=INTERVAL, blit=True)

plt.show()
Loading
Loading