ReactiveBayes / RxInfer.jl

Julia package for automated Bayesian inference on a factor graph with reactive message passing
MIT License
270 stars 23 forks source link

Brief emphasis on the use of aliases for Distributions #63

Closed alstat closed 1 year ago

alstat commented 1 year ago

Hi again, I followed some of the examples in the documentation, and initially thought that RxInfer.jl has its own implementation of probability distributions, for example for MVGaussian, NormalMeanVariance, etc. But upon inspecting the source code, I found out that these are just aliases to the probability distributions defined in Distributions.jl (please confirm). If this is the case, maybe we can emphasize in the doc that RxInfer.jl uses Distributions.jl but with extended aliases. The idea here is to avoid misleading impression of learning new APIs for probability distributions from new users who are already familiar with the Distributions.jl.

JOSS#5161

bartvanerp commented 1 year ago

Hi @alstat!

Thanks for the feedback! Your observation is partly correct. Indeed for certain distributions we rely in ReactiveMP.jl on the implementation from Distributions.jl. These cases include for example the Beta and Wishart distributions. However, specifically for the Gaussian/Normal case we have custom implementations that yield a higher computational efficiency and improved stability. Our aliases for these distributions therefore do not correspond to the implementations from Distributions.jl. This is mainly because of the following 3 reasons:

  1. Distributions.jl constructs normal distributions by saving the corresponding covariance matrices in a PDMat object from PDMats.jl. This construction always computes the Cholesky decompositions of the covariance matrices, which is very convenient for sampling-based procedures. However, in RxInfer.jl we mostly base our computations on analytical expressions which do not always need to compute the Cholesky decomposition. In order to reduce the overhead that Distributions.jl introduces, we therefore have custom implementations.
  2. Depending on the update rules, we might favor different parameterizations of the normal distributions. ReactiveMP.jl has quite a variety in parameterizations that allow us to efficient computations where we convert between parameterizations as little as possible.
  3. In certain situations we value stability a lot, especially when inverting matrices. PDMats.jl, and hence Distributions.jl, is not capable to fulfill all needs that we have here. Therefore we use PositiveFactorizations.jl to cope with the corner-cases.

Because of this partial dependency, we have added functionality in line with what Distributions.jl implements to make it convenient for Distributions.jl users. I agree that this might not be as well represented in our documentation though. For this reason I will update the documentation accordingly to reflect your feedback. I will leave this issue open as a reminder and close it once the documentation is updated.

bvdmitri commented 1 year ago

@alstat @bartvanerp Note, however, that it has been documented already in the doc-strings for the @model macro.


help?> @model
 @model function model_name(model_arguments...; model_keyword_arguments...)
   # model description
 end

 @model macro generates a function that returns an equivalent graph-representation of the given probabilistic model description.

 Supported alias in the model specification
 ============================================

  • a || b: alias for OR(a, b) node (operator precedence between ||, &&, -> and ! is the same as in Julia).

  • a && b: alias for AND(a, b) node (operator precedence ||, &&, -> and ! is the same as in Julia).

  • a -> b: alias for IMPLY(a, b) node (operator precedence ||, &&, -> and ! is the same as in Julia).

  • ¬a and !a: alias for NOT(a) node (Unicode \neg, operator precedence ||, &&, -> and ! is the same as in Julia).

  • a + b + c: alias for (a + b) + c

  • a * b * c: alias for (a * b) * c

  • Normal(μ|m|mean = ..., σ²|τ⁻¹|v|var|variance = ...) alias for NormalMeanVariance(..., ...) node. Gaussian could be used instead Normal too.

  • Normal(μ|m|mean = ..., τ|γ|σ⁻²|w|p|prec|precision = ...) alias for NormalMeanPrecision(..., ...) node. Gaussian could be used instead Normal too.

  • MvNormal(μ|m|mean = ..., Σ|V|Λ⁻¹|cov|covariance = ...) alias for MvNormalMeanCovariance(..., ...) node. MvGaussian could be used instead MvNormal too.

  • MvNormal(μ|m|mean = ..., Λ|W|Σ⁻¹|prec|precision = ...) alias for MvNormalMeanPrecision(..., ...) node. MvGaussian could be used instead MvNormal too.

  • MvNormal(μ|m|mean = ..., τ|γ|σ⁻²|scale_diag_prec|scale_diag_precision = ...) alias for MvNormalMeanScalePrecision(..., ...) node. MvGaussian could be used instead MvNormal too.

  • Gamma(α|a|shape = ..., θ|β⁻¹|scale = ...) alias for GammaShapeScale(..., ...) node.

  • Gamma(α|a|shape = ..., β|θ⁻¹|rate = ...) alias for GammaShapeRate(..., ...) node.

EDIT: Sorry, I misread your question. The RxInfer sometimes reuses distributions from the Distributions.jl package but in general uses its own implementations. The aliases are working the other way around, we change from something that users are familiar from Distributions to internal RxInfer implementations.

bvdmitri commented 1 year ago

@bartvanerp I think your explanation is very descriptive, we should indeed put that in our documentation