RxInfer.jl

repository·main·Indexed 19 days ago

https://github.com/reactivebayes/rxinfer.jl

A Julia package for automatic Bayesian inference on factor graphs using reactive message passing. Optimized for speed and accuracy in models with conjugate likelihood-prior pairings, it supports both exact and approximate inference. RxInfer uses the @model macro from GraphPPL.jl to convert Julia functions into factor graphs, enabling efficient state estimation, smoothing, and recursive filtering for real-time applications.

Tokens
46K
Snippets
138
Records
182
Agent score
63%

What's inside RxInfer.jl

  1. What is RxInfer.jl?

    main

    RxInfer.jl is a Julia package designed for real-time, variational Bayesian inference on infinite asynchronous data streams. It uses a reactive message passing paradigm on a factor graph representation of probabilistic models.

    Key capabilities include:

    • Hybrid Inference Engine: Combines different message passing methods (e.g., belief propagation, expectation propagation, mean-field variational message passing) at different locations in the graph to trade off accuracy for speed.
    • Model Specification: Uses Julia macros to transform textual model descriptions into factor graphs.
    • Scalability: Supports both real-time stream processing and batch processing of large datasets with hundreds of thousands of latent variables.
    • Extensibility: Provides a public API to add custom nodes and message update rules.
    • Auto-differentiation: Compatible with ForwardDiff.jl and ReverseDiff.jl.
  2. Overview of RxInfer.jl

    main

    RxInfer.jl is a Julia package designed for automatic Bayesian inference on factor graphs using reactive message passing. It is optimized for real-time applications (such as audio processing, self-driving vehicles, and weather forecasting) that require continuous inference in large state-space models with many latent variables.

    Key capabilities include:

    • Efficient Inference: Uses message-passing algorithms that scale better than Monte Carlo methods for large models.
    • Hybrid Models: Supports models combining both discrete and continuous latent variables.
    • Extensibility: Allows for custom factor nodes and message passing update rules.
    • Reactive Engine: Powered by ReactiveMP.jl, enabling high-performance, schedule-free, and differentiable inference procedures.
  3. Compare RxInfer.jl with other probabilistic programming toolboxes

    main

    RxInfer.jl is a Julia-based probabilistic programming library primarily focused on message-passing inference. It is designed for high efficiency and modularity, allowing users to compose complex models from smaller ones.

    Key differentiators include:

    • Inference Engine: Uses reactive message passing, which supports real-time updates, parallelization, and interruptibility.
    • Modularity: Unlike many other toolboxes (e.g., Turing.jl, PyMC, Stan), RxInfer.jl allows for the fusion of models by integrating smaller models into larger ones.
    • Universality: Highly effective for models derived from the exponential family of distributions (e.g., Gaussian, Bernoulli, Autoregressive models, Gamma Mixture models) and deterministic transformations of these variables. For models outside the exponential family, users must define custom nodes and rules.
    • Expressiveness: Uses Julia macros to mirror probabilistic notation. Note that RxInfer uses := for deterministic relationships to enable its message-passing capabilities.
  4. Overview of the RxInfer ecosystem

    main

    RxInfer.jl is a Julia package for Bayesian Inference on Factor Graphs by Message Passing, supporting both exact and variational inference algorithms. It operates as an ecosystem around three core packages:

    • ReactiveMP.jl: The underlying message passing-based inference engine.
    • GraphPPL.jl: Used for model and constraints specification.
    • Rocket.jl: Provides reactive extensions for Julia.
  5. The RxInfer inference workflow

    main

    The RxInfer approach to solving inference problems follows three distinct phases:

    1. Model specification: Uses the GraphPPL package to define your probabilistic model using a domain-specific language (DSL).
    2. Inference specification: Uses the ReactiveMP engine to define how inference should be performed. This is compatible with both static datasets and asynchronous infinite data streams.
    3. Inference execution: Using the RxInfer API to pass data to the backend and run the actual inference process.
  6. Distinguish between `=` and `:=` in model specification

    main

    When defining models in RxInfer (typically via GraphPPL), it is critical to use the correct assignment operator to ensure variables are treated as part of the probabilistic factor graph:

    • Use = for regular Julia assignment. This is for standard Julia variables that are not part of the probabilistic model structure.
    • Use := to create a deterministic node. Use this operator to define deterministic relationships between latent variables within your model.

    Using = instead of := for model relationships is a common error that prevents variables from being correctly included in the inference process.

  7. Constraints on control flow and latent variables

    main

    While you can use standard Julia control flow (for, while, if) within an @model function, there is a critical restriction: you cannot use latent variables within control flow statements or indexing brackets.

    This is because the structure of the factor graph must be statically known and fixed before inference begins.

    Invalid usage:

    c ~ Categorical([ 1/2, 1/2 ])
    if c > 1  # ERROR: c is a latent variable and cannot be evaluated here
        # ...
    end

    Indexing restriction: Do not use latent variables inside square brackets (e.g., x[c]) or within the conditions of if/for loops.

  8. Store and access data via Model Metadata

    main

    Callbacks can store arbitrary information in the ProbabilisticModel's metadata dictionary (Dict{Any, Any}). This is useful for tracking history (like marginal updates) across iterations. The stored data is accessible from the inference result via result.model.metadata.

    struct MarginalHistoryCollector end
    
    ReactiveMP.handle_event(::MarginalHistoryCollector, ::ReactiveMP.Event) = nothing
    
    function ReactiveMP.handle_event(::MarginalHistoryCollector, event::OnMarginalUpdateEvent)
        # Access or initialize the metadata dictionary
        history = get!(() -> [], event.model.metadata, :marginal_history)
        push!(history, (iteration_variable = event.variable_name, value = event.update))
    end
    
    result = infer(
        model = my_model(),
        data  = my_data,
        callbacks = MarginalHistoryCollector(),
        # ... other params
    )
    
    # Access the data later
    history = result.model.metadata[:marginal_history]
  9. Core Concepts of RxInfer

    main

    To use RxInfer effectively, you should understand three foundational concepts:

    1. Factor Graphs: The graphical representation of probabilistic models, consisting of variable nodes and factor nodes.
    2. Message Passing: The algorithms (such as VMP/BP) used to perform Bayesian inference by passing messages between nodes in the factor graph.
    3. Reactive Programming Model: A mental model based on data streams that enables real-time, continuous inference as new observations arrive.
  10. Handle data that is not available at model creation time

    main

    For scenarios like reactive inference where data is not known upfront, use RxInfer.DeferredDataHandler(). This allows you to specify that certain parameters will be provided as data later during the inference process, rather than as static hyperparameters.

    # Use DeferredDataHandler for variables provided during inference
    conditioned_with_deferred_data = coin_model() | (
        y = [ true, false, true ], 
        a = RxInfer.DeferredDataHandler(), 
        b = RxInfer.DeferredDataHandler()
    )
    
    model_with_deferred_data = RxInfer.create_model(conditioned_with_deferred_data)
  11. Compare Static, Streaming, and Batched Inference approaches

    main

    RxInfer provides three primary inference paradigms depending on your data availability and latency requirements:

    Static Inference

    Processes a complete dataset all at once. It is best for offline analysis and batch processing where all data is available upfront.

    • Return Type: InferenceResult (contains final posteriors).
    • Pros: Simple to implement; best for exploration.
    • Cons: High memory usage (scales with dataset); no real-time updates; high latency (must wait for all data).

    Streaming (Online) Inference

    Processes data points sequentially as they arrive. Designed for real-time applications and online learning.

    • Return Type: RxInferenceEngine (provides reactive streams).
    • Pros: Low latency; real-time belief updates; controlled memory usage via history buffers.
    • Cons: Requires managing state updates (autoupdates).

    Batched Inference

    A hybrid approach that processes data in configurable chunks. It balances memory usage and update frequency.

    • Return Type: RxInferenceEngine.
    • Pros: More efficient than streaming for large datasets; allows for batch-level autoupdates; medium memory footprint.
    • Cons: Latency is dependent on batch size.
    AspectStaticStreamingBatched
    Data ProcessingAll at onceOne at a timeIn chunks
    Memory UsageHighLow (controlled)Medium
    Update FrequencyOnceReal-timeBatch-level
    LatencyHighLowMedium
    Return TypeInferenceResultRxInferenceEngineRxInferenceEngine
    AutoupdatesNoYesYes
    History TrackingNoYesYes