Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://github.com/hennesrave/rave2026-multiresolution-replicability
26 July 2026, 13:52:35 UTC
  • Code
  • Branches (1)
  • Releases (0)
  • Visits
    • Branches
    • Releases
    • HEAD
    • refs/heads/master
    No releases to show
  • c556fe0
  • /
  • main.py
Raw File Download Save again
Take a new snapshot of a software origin

If the archived software origin currently browsed is not synchronized with its upstream version (for instance when new commits have been issued), you can explicitly request Software Heritage to take a new snapshot of it.

Use the form below to proceed. Once a request has been submitted and accepted, it will be processed as soon as possible. You can then check its processing state by visiting this dedicated page.
swh spinner

Processing "take a new snapshot" request ...

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
  • revision
  • snapshot
origin badgecontent badge
swh:1:cnt:c3d65e6d2987f9cb65e5c9c1aa0c650dc3939c7e
origin badgedirectory badge
swh:1:dir:c556fe0feeef8f4e04ec625c6c6e705324231763
origin badgerevision badge
swh:1:rev:d758a8ad59192ac036b52dd41c272df725295b31
origin badgesnapshot badge
swh:1:snp:fe245bd2ad4b184eff2efbc7df480420b4106e63

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
  • revision
  • snapshot
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
Tip revision: d758a8ad59192ac036b52dd41c272df725295b31 authored by Hennes Rave on 15 July 2026, 18:46:29 UTC
initial commit
Tip revision: d758a8a
main.py
import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage
import sklearn.datasets

from tqdm import tqdm

# ===== Algorithm implementation (GPU version if CuPy is available, otherwise CPU version)

try:
    import cupy
    import cupyx.scipy.ndimage
    
    def multiresolution_transformation(points: np.ndarray, maximum_resolution: int, cycle_count: int, kernel_radius: int) -> np.ndarray:
        points = cupy.asarray(points, dtype=cupy.float32)

        for _ in tqdm(range(cycle_count)):

            resolution = maximum_resolution
            while resolution >= 2:

                # Compute density
                density, _, _ = cupy.histogram2d(points[:, 0], points[:, 1], bins=resolution, range=[[0, 1], [0, 1]])
                density = cupyx.scipy.ndimage.gaussian_filter(density, sigma=(2 * kernel_radius + 1) / 6.0, mode="reflect")
                density = cupy.pad(density, pad_width=1, mode="edge")

                # Compute deformation
                D00 = density[:-1, :-1]
                D01 = density[:-1, 1:]
                D10 = density[1:, :-1]
                D11 = density[1:, 1:]

                denominator = 4.0 * (D00 + D01 + D10 + D11 + 1e-8)
                deformation = cupy.stack([
                    D00 + D01 - D10 - D11,
                    D00 - D01 + D10 - D11
                ], axis=-1) / denominator[:, :, None] / resolution

                # Perform bilinear interpolation
                xy = points * resolution
                ixy = cupy.minimum(cupy.floor(xy).astype(cupy.int32), resolution - 1)

                uv = xy - ixy
                u = uv[:, 0]
                v = uv[:, 1]

                d00 = deformation[ixy[:, 0] + 0, ixy[:, 1] + 0]
                d01 = deformation[ixy[:, 0] + 0, ixy[:, 1] + 1]
                d10 = deformation[ixy[:, 0] + 1, ixy[:, 1] + 0]
                d11 = deformation[ixy[:, 0] + 1, ixy[:, 1] + 1]

                w00 = (1 - u) * (1 - v)
                w01 = (1 - u) * v
                w10 = u * (1 - v)
                w11 = u * v
            
                points += w00[:, None] * d00 + w01[:, None] * d01 + w10[:, None] * d10 + w11[:, None] * d11
                points = cupy.clip(points, 0.0, 1.0)

                resolution //= 2

        return cupy.asnumpy(points)

except ImportError:
    def multiresolution_transformation(points: np.ndarray, maximum_resolution: int, cycle_count: int, kernel_radius: int) -> np.ndarray:
        for _ in tqdm(range(cycle_count)):

            resolution = maximum_resolution
            while resolution >= 2:

                # Compute density
                density, _, _ = np.histogram2d(points[:, 0], points[:, 1], bins=resolution, range=[[0.0, 1.0], [0.0, 1.0]])
                density = scipy.ndimage.gaussian_filter(density, sigma=(2.0 * kernel_radius + 1.0) / 6.0, mode="reflect")
                density = np.pad(density, pad_width=1, mode="edge")

                # Compute deformation
                D00 = density[:-1, :-1]
                D01 = density[:-1, 1:]
                D10 = density[1:, :-1]
                D11 = density[1:, 1:]

                denominator = 4.0 * (D00 + D01 + D10 + D11 + 1e-8)
                deformation = np.stack([
                    D00 + D01 - D10 - D11,
                    D00 - D01 + D10 - D11
                ], axis=-1) / denominator[:, :, None] / resolution

                # Perform bilinear interpolation
                xy = points * resolution
                ixy = np.minimum(np.floor(xy).astype(np.int32), resolution - 1)

                uv = xy - ixy
                u = uv[:, 0]
                v = uv[:, 1]

                d00 = deformation[ixy[:, 0] + 0, ixy[:, 1] + 0]
                d01 = deformation[ixy[:, 0] + 0, ixy[:, 1] + 1]
                d10 = deformation[ixy[:, 0] + 1, ixy[:, 1] + 0]
                d11 = deformation[ixy[:, 0] + 1, ixy[:, 1] + 1]

                w00 = (1 - u) * (1 - v)
                w01 = (1 - u) * v
                w10 = u * (1 - v)
                w11 = u * v
            
                points += w00[:, None] * d00 + w01[:, None] * d01 + w10[:, None] * d10 + w11[:, None] * d11
                points = np.clip(points, 0.0, 1.0)

                resolution //= 2

        return points

# ===== Utility functions

def normalize_to_unit_square(points: np.ndarray) -> np.ndarray:
    minimum = points.min(axis=0)
    maximum = points.max(axis=0)

    center = (minimum + maximum) / 2.0
    extent = (maximum - minimum).max()

    points = (points - center) / extent + 0.5
    return np.clip(points, 0.0, 1.0)



# ===== Prepare datasets
datasets = []

points, labels = sklearn.datasets.make_circles(n_samples=2000, noise=0.05, factor=0.5, random_state=42)
points = normalize_to_unit_square(points)
datasets.append(("Circles", points, labels, 10.0))

points, labels = sklearn.datasets.make_moons(n_samples=10000, noise=0.05, random_state=42)
points = normalize_to_unit_square(points)
datasets.append(("Moons", points, labels, 1.0))

np.random.seed(42)
points_a = np.random.normal(loc=0.0, scale=0.05, size=(2500, 2)) + np.array([0.3, 0.3])
points_b = np.random.normal(loc=0.0, scale=0.05, size=(5000, 2)) + np.array([0.4, 0.75])
points_c = np.random.normal(loc=0.0, scale=0.05, size=(2500, 2)) + np.array([0.75, 0.55])
points = normalize_to_unit_square(np.vstack([points_a, points_b, points_c]))
labels = np.array([0] * len(points_a) + [1] * len(points_b) + [2] * len(points_c))
datasets.append(("Clusters", points, labels, 1.0))

colors = np.array(["#6A3D9A", "#FF7F00",  "#33A02B"], dtype=object)

# ===== Prepare algorithms

algorithms = [
    ("Original", lambda points: points),
    ("Multiresolution", lambda points: multiresolution_transformation(points, maximum_resolution=1024, cycle_count=50, kernel_radius=2))
]

# ===== Generate figures

def generate_teaser():
    points, labels = sklearn.datasets.make_moons(n_samples=300, noise=0.05, random_state=42)
    points = normalize_to_unit_square(points)

    points_transformed = multiresolution_transformation(points, maximum_resolution=1024, cycle_count=50, kernel_radius=2)

    t = points[:, 0].flatten()
    t = 3 * t**2 - 2 * t**3

    points_interpolated = t[:, None] * points_transformed + (1 - t[:, None]) * points

    figure, axis = plt.subplots(nrows=1, ncols=1, figsize=(1, 1))
    axis.scatter(points_interpolated[:, 0], points_interpolated[:, 1], c=colors[labels], s=1.0, clip_on=False)
    axis.set_xlim(0.0, 1.0)
    axis.set_ylim(0.0, 1.0)
    axis.axis("off")

    figure.subplots_adjust(left=0.02, right=0.98, bottom=0.02, top=0.98)
    figure.savefig("teaser.png", dpi=250)

def generate_example():
    nrows = len(algorithms)
    ncols = len(datasets)
    figure, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(ncols * 4, nrows * 4))

    for row, (algorithm_name, algorithm) in enumerate(algorithms):
        for col, (dataset_name, points, labels, point_size) in enumerate(datasets):
            axis = axes[row, col]

            transformed_points = algorithm(points)
            axis.scatter(transformed_points[:, 0], transformed_points[:, 1], c=colors[labels], s=point_size)

            axis.set_xlim(0.0, 1.0)
            axis.set_ylim(0.0, 1.0)
            axis.set_xticks([])
            axis.set_yticks([])

            if row == 0:
                axis.set_title(dataset_name, fontsize=20, fontweight="bold", pad=10)
            if col == 0:
                axis.set_ylabel(algorithm_name, fontsize=20, fontweight="bold", labelpad=10)

    figure.tight_layout()
    figure.savefig("example.png", dpi=300, bbox_inches="tight")
    plt.show()

if __name__ == "__main__":
    # generate_teaser()
    generate_example()

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API