class_docstr: Auxiliary variable reparameterizer for arbitrary
  :class:`~pyro.distributions.Stable` random variables.
  
  This is useful in inference of non-symmetric
  :class:`~pyro.distributions.Stable` variables because the
  :meth:`~pyro.distributions.Stable.log_prob` is not implemented.
  
  This reparameterizes a :class:`~pyro.distributions.Stable` random variable
  as sum of two other stable random variables, one symmetric and the other
  totally skewed (applying Property 2.3.a of [1]). The totally skewed
  variable is sampled as in :class:`LatentStableReparam` , and the symmetric
  variable is decomposed as in :class:`SymmetricStableReparam` .
  
  [1] V. M. Zolotarev (1986)
      "One-dimensional stable distributions"
class_docstr_tok: ["Auxiliary","variable","reparameterizer","for","arbitrary",":","class",":","`","~pyro.distributions.Stable","`","random","variables",".","This","is","useful","in","inference","of","non-symmetric",":","class",":","`","~pyro.distributions.Stable","`","variables","because","the",":","meth",":","`","~pyro.distributions.Stable.log_prob","`","is","not","implemented",".","This","reparameterizes","a",":","class",":","`","~pyro.distributions.Stable","`","random","variable","as","sum","of","two","other","stable","random","variables",",","one","symmetric","and","the","other","totally","skewed","(","applying","Property","2.3.a","of","[","1","]",")",".","The","totally","skewed","variable","is","sampled","as","in",":","class",":","`","LatentStableReparam","`",",","and","the","symmetric","variable","is","decomposed","as","in",":","class",":","`","SymmetricStableReparam","`",".","[","1","]","V.","M.","Zolotarev","(","1986",")","``","One-dimensional","stable","distributions","''"]
code: def apply(self, msg):
      name = msg["name"]
      fn = msg["fn"]
      value = msg["value"]
      is_observed = msg["is_observed"]
  
      fn, event_dim = self._unwrap(fn)
      assert isinstance(fn, dist.Stable) and fn.coords == "S0"
  
      # Strategy: Let X ~ S0(a,b,s,m) be the stable variable of interest.
      # 1. WLOG scale and shift so s=1 and m=0, additionally shifting to convert
      #    from Zolotarev's S parameterization to Nolan's S0 parameterization.
      # 2. Decompose X = S + T, where
      #    S ~ S(a,0,...,0) is symmetric and
      #    T ~ S(a,sgn(b),...,0) is totally skewed.
      # 3. Decompose S = G * sqrt(Z) via the symmetric strategy, where
      #    Z ~ S(a/2,1,...,0) is totally-skewed and
      #    G ~ Normal(0,1) is Gaussian.
      # 4. Defer the totally-skewed Z and T to the Chambers-Mallows-Stuck
      #    strategy: Z = f(Unif,Exp), T = f(Unif,Exp).
      #
      # To derive the parameters of S and T, we solve the equations
      #
      #   T.stability = a            S.stability = a
      #   T.skew = sgn(b)            S.skew = 0
      #   T.loc = 0                  S.loc = 0
      #
      #   s = (S.scale**a + T.scale**a)**(1/a) = 1       # by step 1.
      #
      #       S.skew * S.scale**a + T.skew * T.scale**a
      #   b = ----------------------------------------- = sgn(b) * T.scale**a
      #                S.scale**a + T.scale**a
      # yielding
      #
      #   T.scale = |b| ** (1/a)     S.scale = (1 - |b|) ** (1/a)
  
      # Draw parameter-free noise.
      proto = fn.stability
      half_pi = proto.new_tensor(math.pi / 2)
      one = proto.new_ones(proto.shape)
      zu = pyro.sample(
          "{}_z_uniform".format(name),
          self._wrap(
              dist.Uniform(-half_pi, half_pi).expand(proto.shape),
              event_dim,
          ),
      )
      ze = pyro.sample(
          "{}_z_exponential".format(name),
          self._wrap(dist.Exponential(one), event_dim),
      )
      tu = pyro.sample(
          "{}_t_uniform".format(name),
          self._wrap(
              dist.Uniform(-half_pi, half_pi).expand(proto.shape),
              event_dim,
          ),
      )
      te = pyro.sample(
          "{}_t_exponential".format(name),
          self._wrap(dist.Exponential(one), event_dim),
      )
  
      # Differentiably transform.
      a = fn.stability
      z = _unsafe_standard_stable(a / 2, 1, zu, ze, coords="S")
      t = _standard_stable(a, one, tu, te, coords="S0")
      a_inv = a.reciprocal()
      eps = torch.finfo(a.dtype).eps
      skew_abs = fn.skew.abs().clamp(min=eps, max=1 - eps)
      t_scale = skew_abs.pow(a_inv)
      s_scale = (1 - skew_abs).pow(a_inv)
      shift = _safe_shift(a, fn.skew, t_scale, skew_abs)
      loc = fn.loc + fn.scale * (fn.skew.sign() * t_scale * t + shift)
      scale = (
          fn.scale
          * s_scale
          * z.sqrt()
          * (math.pi / 4 * a).cos().pow(a_inv)
      )
      scale = scale.clamp(min=torch.finfo(scale.dtype).tiny)
  
      # Construct a scaled Gaussian, using Stable(2,0,s,m) == Normal(m,s*sqrt(2)).
      new_fn = self._wrap(
          dist.Normal(loc, scale * (2**0.5)), event_dim
      )
      return {"fn": new_fn, "value": value, "is_observed": is_observed}
  
file_path: pyro/pyro/infer/reparam/stable.py
imports: import math
  import torch
  import pyro
  import pyro.distributions
  from pyro.distributions.stable import _standard_stable, _unsafe_standard_stable
  from pyro.infer.util import is_validation_enabled
  from .reparam import Reparam
label_desc: Use image node_id 1 for calling the StableReparam obj's underlying member method code with example usage: obj.apply(msg) and returns: dict