Implementation:Pyro ppl Pyro SineSkewed
| Attribute | Value |
|---|---|
| Sources | pyro/distributions/sine_skewed.py |
| Domains | Probabilistic Programming, Directional Statistics, Protein Bioinformatics, Torus Distributions |
| Last Updated | 2026-02-09 |
Overview
Description
The SineSkewed distribution implements the Sine Skewing procedure from Ameijeiras-Alonso and Ley (2019) for breaking the pointwise symmetry of distributions defined on a torus. A torus distribution has support on products of circles, i.e., the Cartesian product of d copies of S^1 where S^1 = [-pi, pi). The 1-torus is a circle, the 2-torus is commonly associated with the donut shape, and higher-dimensional toruses generalize this structure.
Given a symmetric base distribution on a d-dimensional torus, the Sine Skewed distribution introduces an asymmetry controlled by a skewness parameter vector of dimension d. The key mathematical property is that the skewness parameters must satisfy the constraint that the sum of their absolute values is at most 1 (i.e., |skew_1| + |skew_2| + ... + |skew_d| <= 1). This constraint ensures that the sine skewing does not alter the normalization constant of the base distribution.
The distribution is particularly useful in protein bioinformatics, where torus-valued data arises from the dihedral angle pairs (phi, psi) of amino acid residues in the Ramachandran plot.
This class extends TorchDistribution and supports sampling, log probability computation, batch expansion, and works with Pyro's inference engines including HMC, NUTS, and SVI.
Usage
SineSkewed is designed to be wrapped around a symmetric torus distribution such as VonMises, SineBivariateVonMises, ProjectedNormal (1D), or Uniform(-pi, pi). It is used as a likelihood in SVI, and can be used with HMC/NUTS for inference over skewness parameters. For 2-torus and higher, using it as a latent variable in SVI leads to slow inference because the base distribution cannot be reparameterized.
Code Reference
Source Location
| Property | Value |
|---|---|
| File | pyro/distributions/sine_skewed.py
|
| Module | pyro.distributions.sine_skewed
|
| Repository | pyro-ppl/pyro |
Signature
class SineSkewed(TorchDistribution):
arg_constraints = {
"skewness": constraints.independent(constraints.interval(-1.0, 1.0), 1)
}
support = constraints.independent(constraints.real, 1)
def __init__(self, base_dist: TorchDistribution, skewness, validate_args=None):
...
def sample(self, sample_shape=torch.Size()):
...
def log_prob(self, value):
...
def expand(self, batch_shape, _instance=None):
...
Import
from pyro.distributions import SineSkewed
# Or from the module directly:
from pyro.distributions.sine_skewed import SineSkewed
I/O Contract
Constructor Parameters
| Parameter | Type | Description |
|---|---|---|
base_dist |
TorchDistribution |
A symmetric base distribution on a d-dimensional torus. Supported: 1D VonMises, SineBivariateVonMises, 1D ProjectedNormal, Uniform(-pi, pi). |
skewness |
torch.Tensor |
Skewness parameter with shape (..., d). Each element must be in [-1, 1], and the sum of absolute values along the last dimension must be <= 1.
|
validate_args |
bool or None |
Whether to validate input arguments. Default: None.
|
Constraints
| Constraint | Details |
|---|---|
| arg_constraints | skewness: each element in [-1, 1] (independent constraint over last dimension)
|
| support | Independent real-valued constraint over the event dimension (values on the torus in [-pi, pi)) |
| Normalization constraint | sum(abs(skewness), dim=-1) <= 1 -- a warning is emitted if violated
|
| Shape constraint | base_dist.event_shape == skewness.shape[-1:] -- skewness must have one weight per torus dimension
|
Methods
| Method | Return Type | Description |
|---|---|---|
sample(sample_shape) |
torch.Tensor |
Draws samples using the acceptance method from Section 2.3 of the reference. Returns values in [-pi, pi). |
log_prob(value) |
torch.Tensor |
Computes log probability as base_dist.log_prob(value) + log(1 + sum(skewness * sin(value - base_dist.mean))) per Eq. 2.1 in the reference.
|
expand(batch_shape) |
SineSkewed |
Returns a new SineSkewed instance with expanded batch dimensions. |
Usage Examples
Basic SineSkewed VonMises (1-Torus)
import torch
import pyro.distributions as dist
# 1D Von Mises base distribution on a circle
base = dist.VonMises(loc=torch.tensor(0.0), concentration=torch.tensor(5.0))
# Apply sine skewing with a single skewness parameter
skewness = torch.tensor([0.3])
ss_dist = dist.SineSkewed(base, skewness)
# Sample and compute log probability
sample = ss_dist.sample(torch.Size([100]))
log_p = ss_dist.log_prob(sample)
print(sample.shape) # torch.Size([100, 1])
print(log_p.shape) # torch.Size([100])
SineSkewed SineBivariateVonMises (2-Torus) for Protein Bioinformatics
import torch
import pyro
import pyro.distributions as dist
from pyro.distributions import SineBivariateVonMises, SineSkewed
def model(obs):
# Priors for the bivariate von Mises parameters
phi_loc = pyro.sample('phi_loc', dist.VonMises(torch.tensor(3.14), torch.tensor(2.0)))
psi_loc = pyro.sample('psi_loc', dist.VonMises(torch.tensor(-1.57), torch.tensor(2.0)))
phi_conc = pyro.sample('phi_conc', dist.Beta(torch.tensor(2.0), torch.tensor(5.0)))
psi_conc = pyro.sample('psi_conc', dist.Beta(torch.tensor(2.0), torch.tensor(5.0)))
corr_scale = pyro.sample('corr_scale', dist.Beta(torch.tensor(2.0), torch.tensor(5.0)))
# Skewness priors respecting the L1 constraint
skew_phi = pyro.sample('skew_phi', dist.Uniform(-1.0, 1.0))
psi_bound = 1 - skew_phi.abs()
skew_psi = pyro.sample('skew_psi', dist.Uniform(-1.0, 1.0))
skewness = torch.stack((skew_phi, psi_bound * skew_psi), dim=-1)
with pyro.plate('obs_plate'):
base = SineBivariateVonMises(
phi_loc=phi_loc, psi_loc=psi_loc,
phi_concentration=1000 * phi_conc,
psi_concentration=1000 * psi_conc,
weighted_correlation=corr_scale,
)
return pyro.sample('phi_psi', SineSkewed(base, skewness), obs=obs)
Computing Log Probabilities
import torch
import pyro.distributions as dist
# Create a skewed distribution on the 1-torus
base = dist.VonMises(loc=torch.tensor(0.0), concentration=torch.tensor(3.0))
skewness = torch.tensor([-0.5])
ss = dist.SineSkewed(base, skewness)
# Evaluate log probability at specific angles
values = torch.linspace(-3.14, 3.14, 50).unsqueeze(-1)
log_probs = ss.log_prob(values)
print(log_probs.shape) # torch.Size([50])
Related Pages
- SineBivariateVonMises -- A common base distribution for 2-torus data used with SineSkewed
- VonMises -- Circular distribution commonly used as a 1-torus base distribution
- ProjectedNormal -- Another option for 1-torus base distributions
- Distributions_Init -- Central registry of all Pyro distributions
- Unit -- Trivial distribution used for factor statements in Pyro models