.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/sliced-wasserstein/plot_uniortho_high_dim.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_sliced-wasserstein_plot_uniortho_high_dim.py: ========================================================= UnifOrtho Sliced Wasserstein in high dimension ========================================================= This example illustrates the UnifOrtho sampling scheme for Sliced Wasserstein directions, introduced in [97] and recommended for large dimensions by a recent numerical and theoretical study [98], and compares it to the default uniform (Monte Carlo) sampling of slicing directions. Sliced Wasserstein (SWD) approximates the Wasserstein distance by averaging 1D Wasserstein distances over projections onto random directions :math:`\theta` drawn uniformly on the sphere. By default these directions are sampled purely at random (Monte Carlo), which introduces some variance in the estimate for a given number of projections. UnifOrtho takes a different route that works in *any* dimension: instead of drawing directions independently, it draws them in blocks of ``dim`` directions, each block is a random orthonormal basis (a draw from the Haar measure on the special orthogonal group :math:`\mathrm{SO}(\mathrm{dim})`). Directions within a block are therefore exactly mutually orthogonal, spreading them out much more evenly than independent draws would. We first visualize this block structure on the ordinary 3D sphere, purely for intuition -- dimension 3 is precisely where QSW/RQSW should be preferred in practice, not UnifOrtho, as the convergence comparison right after the visualization confirms numerically. We then measure convergence to the true Sliced Wasserstein distance in a genuinely high dimension, where UnifOrtho is the recommended choice. .. [97] Rowland, M., Hron, J., Tang, Y., Choromanski, K., Sarlos, T., & Weller, A. (2019). Orthogonal Estimation of Wasserstein Distances. Proceedings of the 22nd International Conference on Artificial Intelligence and Statistics (AISTATS), PMLR 89. .. [98] Petrovic, V., Bardenet, R., & Desolneux, A. (2026). Repulsive Monte Carlo on the sphere for the sliced Wasserstein distance. Transactions on Machine Learning Research. .. [99] Sisouk, K., Delon, J., & Tierny, J. (2025). A User's Guide to Sampling Strategies for Sliced Optimal Transport. Transactions on Machine Learning Research. .. GENERATED FROM PYTHON SOURCE LINES 43-57 .. code-block:: Python # Author: Samuel Vangu # # License: MIT License # sphinx_gallery_thumbnail_number = 1 import numpy as np import matplotlib.pylab as pl from mpl_toolkits.mplot3d import Axes3D # noqa: F401 (registers the 3D projection) import ot from ot.sliced import get_random_projections, get_random_orthogonal_directions .. GENERATED FROM PYTHON SOURCE LINES 58-76 Visualize the block structure on the sphere (d=3, for intuition only) ----------------------------------------------------------------------- We draw 15 directions on :math:`S^2` with each scheme: - ``uniform``: directions are Gaussian vectors normalized to unit norm (standard Monte Carlo sampling of the sphere) -- no structure between the points. - ``unif_ortho``: 5 independent blocks of 3 mutually orthogonal directions each. Each block is colored separately below, so that same-colored points are exactly orthogonal to one another -- this is the structure that is not visible in the uniform sample. A translucent sphere is drawn behind the points purely to make the geometry easier to read. Dimension 3 is used here only because it is the one human beings can actually look at. It is *not* the dimension UnifOrtho is recommended for -- see the convergence experiment below. .. GENERATED FROM PYTHON SOURCE LINES 76-131 .. code-block:: Python d = 3 n_blocks = 15 n_projections = n_blocks * d seed = 42 theta_uniform = get_random_projections(d, n_projections, seed=seed) theta_uniortho = get_random_orthogonal_directions(d, n_projections, seed=seed) # A plain unit sphere surface, drawn behind the scattered points below. u_sphere = np.linspace(0, 2 * np.pi, 40) v_sphere = np.linspace(0, np.pi, 20) sphere_x = np.outer(np.cos(u_sphere), np.sin(v_sphere)) sphere_y = np.outer(np.sin(u_sphere), np.sin(v_sphere)) sphere_z = np.outer(np.ones_like(u_sphere), np.cos(v_sphere)) fig = pl.figure(1, figsize=(10, 5)) ax1 = fig.add_subplot(1, 2, 1, projection="3d") ax1.scatter( theta_uniform[0], theta_uniform[1], theta_uniform[2], c="gray", s=25, alpha=0.8 ) ax1.set_title("Uniform (Monte Carlo)") ax2 = fig.add_subplot(1, 2, 2, projection="3d") block_ids = np.repeat(np.arange(n_blocks), d) ax2.scatter( theta_uniortho[0], theta_uniortho[1], theta_uniortho[2], c=block_ids, cmap="tab10", s=25, alpha=0.9, ) ax2.set_title("UnifOrtho (one color per orthogonal block)") for ax in (ax1, ax2): ax.plot_surface( sphere_x, sphere_y, sphere_z, color="lightgray", alpha=0.15, linewidth=0 ) ax.set_box_aspect([1, 1, 1]) ax.view_init(elev=20, azim=45) ax.set_xticks([]) ax.set_yticks([]) ax.set_zticks([]) pl.tight_layout() pl.show() # Every triple of same-colored points on the right is an exact orthogonal # basis of R^3 -- three mutually perpendicular directions. The uniform # sample on the left has no such guarantee: any two of its points can end # up arbitrarily close to each other. .. image-sg:: /auto_examples/sliced-wasserstein/images/sphx_glr_plot_uniortho_high_dim_001.png :alt: Uniform (Monte Carlo), UnifOrtho (one color per orthogonal block) :srcset: /auto_examples/sliced-wasserstein/images/sphx_glr_plot_uniortho_high_dim_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 132-139 Convergence at d=3: UnifOrtho is not the right tool here ------------------------------------------------------------ Before moving to high dimension, it is worth checking numerically that UnifOrtho is indeed NOT the best choice at d=3 -- randomized spiral points (RQSW, see the companion example "Quasi-Monte Carlo Sliced Wasserstein in 3D") are. We use the same exact closed-form reference as below (a pure translation). .. GENERATED FROM PYTHON SOURCE LINES 139-184 .. code-block:: Python from ot.sliced import get_projections_spiral # noqa: E402 (kept local to this section) d3 = 3 rng3 = np.random.RandomState(1) delta3 = rng3.normal(size=d3) * 1.2 Xs3 = rng3.uniform(-2, 2, (100, d3)) Xt3 = Xs3 + delta3 sw_true3 = np.linalg.norm(delta3) / np.sqrt(d3) n_proj_list3 = [10, 20, 50, 100] n_trials3 = 10 errors3 = {"uniform": [], "unif_ortho": [], "randomized_spiral_qmc": []} for n_proj in n_proj_list3: row = {k: [] for k in errors3} for t in range(n_trials3): for method in errors3: val = ot.sliced_wasserstein_distance( Xs3, Xt3, n_projections=n_proj, sampling_slices=method, seed=t ) row[method].append(abs(val - sw_true3)) for method in errors3: errors3[method].append(np.mean(row[method])) pl.figure(2, figsize=(6, 5)) for method, marker, label in [ ("uniform", "o-", "Uniform (MC)"), ("unif_ortho", "s-", "UnifOrtho"), ("randomized_spiral_qmc", "^-", "RQSW"), ]: pl.plot(n_proj_list3, errors3[method], marker, label=label) pl.xscale("log") pl.yscale("log") pl.xlabel("Number of projections") pl.ylabel("Absolute error to the true SWD") pl.title("Convergence of the Sliced Wasserstein estimate (d=3)") pl.legend() pl.show() # RQSW is consistently the most accurate here, UnifOrtho a clear second # (still better than plain uniform sampling, but not the recommended # choice), and uniform sampling the least accurate -- exactly the # low-dimensional ordering the literature [99] describes. .. image-sg:: /auto_examples/sliced-wasserstein/images/sphx_glr_plot_uniortho_high_dim_002.png :alt: Convergence of the Sliced Wasserstein estimate (d=3) :srcset: /auto_examples/sliced-wasserstein/images/sphx_glr_plot_uniortho_high_dim_002.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 185-203 Convergence to the true Sliced Wasserstein distance, in high dimension -------------------------------------------------------------------------- As with the QSW/RQSW example, we build ``Xt`` as a pure translation of ``Xs`` by a fixed vector :math:`\delta`, which makes the true Sliced Wasserstein distance known exactly, with zero approximation error left except from the number of projections: .. math:: \mathcal{SWD}_2(\mu, \nu) = \frac{\|\delta\|}{\sqrt{d}} This time we work in dimension 30 -- the regime UnifOrtho is recommended for . The list of projection counts below is deliberately chosen to never be a multiple of ``d``: when ``n_projections`` is an exact multiple of 30, UnifOrtho draws a whole number of *complete* orthogonal bases, which for this particular translation experiment happens to recover the exact answer up to floating-point precision (an identity, not an approximation) -- an interesting fact in its own right, but not representative of the typical case we want to illustrate here. .. GENERATED FROM PYTHON SOURCE LINES 203-269 .. code-block:: Python d = 30 rng = np.random.RandomState(0) n_samples = 200 delta = rng.normal(size=d) * 1.2 Xs = rng.uniform(-2, 2, (n_samples, d)) Xt = Xs + delta # Exact reference: no approximation at all, at any cost. sw_true = np.linalg.norm(delta) / np.sqrt(d) n_proj_list = [35, 65, 95, 190, 380, 760] # all >= d, none a multiple of d n_trials = 10 errors_uniform = np.zeros((n_trials, len(n_proj_list))) errors_uniortho = np.zeros((n_trials, len(n_proj_list))) for j, n_proj in enumerate(n_proj_list): for t in range(n_trials): sw_uniform = ot.sliced_wasserstein_distance( Xs, Xt, n_projections=n_proj, sampling_slices="uniform", seed=t ) sw_uniortho = ot.sliced_wasserstein_distance( Xs, Xt, n_projections=n_proj, sampling_slices="unif_ortho", seed=t ) errors_uniform[t, j] = np.abs(sw_uniform - sw_true) errors_uniortho[t, j] = np.abs(sw_uniortho - sw_true) mean_err_uniform = errors_uniform.mean(axis=0) std_err_uniform = errors_uniform.std(axis=0) mean_err_uniortho = errors_uniortho.mean(axis=0) std_err_uniortho = errors_uniortho.std(axis=0) pl.figure(3, figsize=(6, 5)) pl.plot(n_proj_list, mean_err_uniform, "o-", label="Uniform (MC)") pl.fill_between( n_proj_list, mean_err_uniform - std_err_uniform, mean_err_uniform + std_err_uniform, alpha=0.3, ) pl.plot(n_proj_list, mean_err_uniortho, "s-", label="UnifOrtho") pl.fill_between( n_proj_list, mean_err_uniortho - std_err_uniortho, mean_err_uniortho + std_err_uniortho, alpha=0.3, ) pl.xscale("log") pl.yscale("log") pl.xlabel("Number of projections") pl.ylabel("Absolute error to the true SWD") pl.title(f"Convergence of the Sliced Wasserstein estimate (d={d})") pl.legend() pl.show() # UnifOrtho consistently reaches a given accuracy with markedly fewer # projections than uniform sampling in this high-dimensional setting -- # the opposite of the low-dimensional case above, where RQSW is the # better choice. As a rule of thumb from the literature [98, 99]: prefer # RQSW in low dimension, UnifOrtho in high dimension, and either may do # in between. Since UnifOrtho remains an unbiased, stochastic estimator # (like RQSW), it is also a drop-in replacement for uniform sampling in # stochastic optimization settings. .. image-sg:: /auto_examples/sliced-wasserstein/images/sphx_glr_plot_uniortho_high_dim_003.png :alt: Convergence of the Sliced Wasserstein estimate (d=30) :srcset: /auto_examples/sliced-wasserstein/images/sphx_glr_plot_uniortho_high_dim_003.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 2.410 seconds) .. _sphx_glr_download_auto_examples_sliced-wasserstein_plot_uniortho_high_dim.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_uniortho_high_dim.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_uniortho_high_dim.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_uniortho_high_dim.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_