Skip to main content

Conditional Push-Forward Neural Network estimator

Project description

CPFN — Conditional Push-Forward Neural Network

Compact, importable implementation of a Conditional Push-Forward Neural Network (CPFN) estimator.

Paper: https://arxiv.org/pdf/2511.14455

Goals

  • Provide a lightweight CPFN class for estimating conditional generators.
  • Expose a simple API for training and sampling.

Install

From PyPI

pip install cpfn

Quick Usage

import random
import numpy as np
import torch
from cpfn import CPFN

# matplotlib is not a dependency of cpfn — install separately if needed:
#   pip install matplotlib
import matplotlib.pyplot as plt

# ---------------------------------------------------------------------------
# 1. Setup
# ---------------------------------------------------------------------------

SEED = 42
random.seed(SEED)
np.random.seed(SEED)
torch.manual_seed(SEED)

if torch.cuda.is_available():
    device = torch.device("cuda")
elif torch.backends.mps.is_available():
    device = torch.device("mps")
else:
    device = torch.device("cpu")

print(f"Using device: {device}")

# ---------------------------------------------------------------------------
# 2. Synthetic Data — Branching Distribution
# ---------------------------------------------------------------------------
# For x < 0.5: single Gaussian branch (mu1).
# For x >= 0.5: equal-weight mixture of two Gaussian branches (mu1, mu2).

def mu1(x):
    return 10 * x * (x - 0.5) * (1.5 - x)

def mu2(x):
    return 10 * x * (x - 0.5) * (0.8 - x)

def noise_std(x):
    return 0.3 * (1.3 - x)

def sample_y(x):
    z = np.random.randn()
    if x < 0.5 or np.random.rand() < 0.5:
        return mu1(x) + z * noise_std(x)
    else:
        return mu2(x) + z * noise_std(x)

def true_conditional_pdf(y, x):
    """Analytic conditional density p(y | x)."""
    s = noise_std(x)
    def gauss(y, m): 
        return np.exp(-0.5 * ((y - m) / s) ** 2) / (np.sqrt(2 * np.pi) * s)
    if x < 0.5:
        return gauss(y, mu1(x))
    return 0.5 * gauss(y, mu1(x)) + 0.5 * gauss(y, mu2(x))


N_TRAIN = 1000
xs = np.random.rand(N_TRAIN)
ys = np.array([sample_y(x) for x in xs])

# ---------------------------------------------------------------------------
# 3. Model Training
# ---------------------------------------------------------------------------

model = CPFN(d=1, q=1, r=20, width=50, hidden_layers=3, delta=1e-15)
model.to(device)

model.fit(xs, ys, epochs=3000, lr=1e-3, m=30, h0=5e-2)
model.freeze()

# ---------------------------------------------------------------------------
# 4. Sample Comparison Plot
# ---------------------------------------------------------------------------

ys_gen = model.sample_conditional(xs, num_samples=1).flatten()

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5), sharey=True)

ax1.scatter(xs, ys, alpha=0.6, s=15, color="steelblue")
ax1.set_title("Ground Truth Samples")
ax1.set_xlabel("x")
ax1.set_ylabel("y")

ax2.scatter(xs, ys_gen, alpha=0.6, s=15, color="darkorange")
ax2.set_title("CPFN Generated Samples")
ax2.set_xlabel("x")

fig.suptitle("Training Data vs. CPFN Samples", fontsize=13, fontweight="bold")
plt.tight_layout()
plt.show()

# ---------------------------------------------------------------------------
# 5. Conditional Density Comparison
# ---------------------------------------------------------------------------

ygrid = np.linspace(-1.5, 3.0, 1000)
x_evals = [0.3, 0.7]

fig, axes = plt.subplots(1, len(x_evals), figsize=(5 * len(x_evals), 4), sharey=True)

for ax, x0 in zip(axes, x_evals):
    model_density = np.exp(model.logdensity(x0, ygrid, m=100_000))
    true_density  = true_conditional_pdf(ygrid, x0)

    ax.plot(ygrid, model_density, label="CPFN", color="darkorange", linewidth=1.8)
    ax.fill_between(ygrid, 0, model_density, alpha=0.20, color="darkorange")

    ax.plot(ygrid, true_density, label="True", color="steelblue",
            linestyle="--", linewidth=1.8)
    ax.fill_between(ygrid, 0, true_density, alpha=0.12, color="steelblue")

    ax.set_title(f"p(y | x = {x0:.1f})")
    ax.set_xlabel("y")
    ax.legend()

axes[0].set_ylabel("Density")
fig.suptitle("Conditional Density: CPFN vs. True", fontsize=13, fontweight="bold")
plt.tight_layout()
plt.show()

Results

Samples: Training Data vs. CPFN Sample Comparison

Conditional Density: CPFN vs. True Conditional Density

Tests

Run the included pytest smoke test:

pytest -q

Development

  • Source: src/cpfn/
  • Tests: tests/

License

See LICENCE in the repository root.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cpfn-1.0.4.tar.gz (10.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cpfn-1.0.4-py3-none-any.whl (8.3 kB view details)

Uploaded Python 3

File details

Details for the file cpfn-1.0.4.tar.gz.

File metadata

  • Download URL: cpfn-1.0.4.tar.gz
  • Upload date:
  • Size: 10.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for cpfn-1.0.4.tar.gz
Algorithm Hash digest
SHA256 192e06099b2a9a4f39665dc1a3e35fc680590ef6c033aa82ab252c2d223113e6
MD5 822c73bfbbd6e909fc441ab37386caa4
BLAKE2b-256 ba84bc7212665e1e11472a871041b98b942644d5216bb4c01a7e5abf4dac4d1a

See more details on using hashes here.

File details

Details for the file cpfn-1.0.4-py3-none-any.whl.

File metadata

  • Download URL: cpfn-1.0.4-py3-none-any.whl
  • Upload date:
  • Size: 8.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for cpfn-1.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 9ab2fedd8a9f7513acb3da7fce20aa411ed30367ed6f9553117126696514392c
MD5 7e4ab8adccd6d7535ae60c261a468b93
BLAKE2b-256 c5187494a2ac0f4ee1a3c0379e0445494c2a4c8f907f869f18411c418cde89e9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page