Skip to content

Conversation

@penelopeysm
Copy link
Member

@penelopeysm penelopeysm commented Nov 13, 2025

There are two commits in this PR:

  1. Replace LogDensityFunction with FastLDF (that's not controversial);

  2. Remove SimpleVarInfo (which might be controversial).

SVI

I would argue that the main purpose of SVI was for performance when evaluating a model with NamedTuple or Dict parameters:

retval, svi = DynamicPPL.evaluate!!(model, SimpleVarInfo(params)) # using DefaultContext

However, exactly the same thing can be accomplished now with the combination of InitFromParams + OnlyAccsVarInfo:

# it's a one-liner now:
retval, oavi = DynamicPPL.fast_evaluate!!(model, InitFromParams(params, nothing), DynamicPPL.default_accumulators())

# Which expands to this:
ctx = InitContext(InitFromParams(params), nothing))
oavi = OnlyAccsVarInfo(DynamicPPL.default_accumulators())
model = setleafcontext(model, ctx)
retval, oavi = DynamicPPL.evaluate!!(model, oavi)

Indeed, this strategy is on par with SVI for the NamedTuple case and faster than SVI for the Dict case (see benchmarks on #1125; performance characteristics on this PR are the same).

The other context in which SVI was useful was evaluation with vector parameters, but that's handled by FastLDF. Thus, my conclusion is that SVI is no longer needed.

Furthermore, because SVI is gone, I believe DynamicTransformation, StaticTransformation, and maybe_invlink_before_eval!! can also go. However, we'll leave that for another time.

I looked through GitHub for usage of SimpleVarInfo, and it's only in DPPL, Turing, Mooncake benchmarks (trivially fixed once FastLDF is released), and https://github.com/epimap/Epimap.jl-public and https://github.com/marius311/MuseInference.jl, which haven't been updated in a while.

Miscellany

I'm lumping these into a single PR, but also happy to split them up. The two commits are completely standalone though. The main purpose of lumping them is so that I have a single branch to test Turing CI against.

@penelopeysm penelopeysm marked this pull request as draft November 13, 2025 22:17
@github-actions
Copy link
Contributor

github-actions bot commented Nov 13, 2025

Benchmark Report

  • this PR's head: b1368dde6848ffc4399a62cb1803bca887d3dd8c
  • base branch: 3cd8d3431e14ebc581266c1323d1db8a5bd4c0eb

Computer Information

Julia Version 1.11.7
Commit f2b3dbda30a (2025-09-08 12:10 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 4 × Intel(R) Xeon(R) Platinum 8370C CPU @ 2.80GHz
  WORD_SIZE: 64
  LLVM: libLLVM-16.0.6 (ORCJIT, icelake-server)
Threads: 1 default, 0 interactive, 1 GC (on 4 virtual cores)

Benchmark Results

┌───────────────────────┬───────┬─────────────┬───────────────────┬────────┬─────────────────────────────────┬───────────────────────────┬─────────────────────────────────┐
│                       │       │             │                   │        │        t(eval) / t(ref)         │     t(grad) / t(eval)     │        t(grad) / t(ref)         │
│                       │       │             │                   │        │ ──────────┬───────────┬──────── │ ──────┬─────────┬──────── │ ──────────┬───────────┬──────── │
│                 Model │   Dim │  AD Backend │           VarInfo │ Linked │      base │   this PR │ speedup │  base │ this PR │ speedup │      base │   this PR │ speedup │
├───────────────────────┼───────┼─────────────┼───────────────────┼────────┼───────────┼───────────┼─────────┼───────┼─────────┼─────────┼───────────┼───────────┼─────────┤
│               Dynamic │    10 │    mooncake │             typed │   true │    422.27 │    357.12 │    1.18 │ 11.91 │   14.87 │    0.80 │   5027.66 │   5312.18 │    0.95 │
│                   LDA │    12 │ reversediff │             typed │   true │   2778.63 │   3044.29 │    0.91 │  2.15 │    4.52 │    0.47 │   5964.78 │  13758.56 │    0.43 │
│   Loop univariate 10k │ 10000 │    mooncake │             typed │   true │ 148886.43 │ 104304.91 │    1.43 │  5.77 │    4.28 │    1.35 │ 859246.35 │ 446092.40 │    1.93 │
├───────────────────────┼───────┼─────────────┼───────────────────┼────────┼───────────┼───────────┼─────────┼───────┼─────────┼─────────┼───────────┼───────────┼─────────┤
│    Loop univariate 1k │  1000 │    mooncake │             typed │   true │  15212.10 │   8636.76 │    1.76 │  5.57 │    4.31 │    1.29 │  84681.98 │  37202.96 │    2.28 │
│      Multivariate 10k │ 10000 │    mooncake │             typed │   true │  32309.10 │  30994.68 │    1.04 │  9.87 │   10.15 │    0.97 │ 318868.95 │ 314713.37 │    1.01 │
│       Multivariate 1k │  1000 │    mooncake │             typed │   true │   3709.74 │   3719.56 │    1.00 │  8.54 │    8.85 │    0.96 │  31675.75 │  32912.40 │    0.96 │
├───────────────────────┼───────┼─────────────┼───────────────────┼────────┼───────────┼───────────┼─────────┼───────┼─────────┼─────────┼───────────┼───────────┼─────────┤
│ Simple assume observe │     1 │ forwarddiff │             typed │  false │     17.74 │      3.31 │    5.36 │  1.89 │    2.90 │    0.65 │     33.58 │      9.60 │    3.50 │
│           Smorgasbord │   201 │ forwarddiff │             typed │  false │   2626.67 │   1227.61 │    2.14 │ 44.32 │  121.36 │    0.37 │ 116404.27 │ 148981.78 │    0.78 │
│           Smorgasbord │   201 │ forwarddiff │       simple_dict │   true │  24123.23 │       err │     err │ 25.35 │     err │     err │ 611510.25 │       err │     err │
├───────────────────────┼───────┼─────────────┼───────────────────┼────────┼───────────┼───────────┼─────────┼───────┼─────────┼─────────┼───────────┼───────────┼─────────┤
│           Smorgasbord │   201 │ forwarddiff │ simple_namedtuple │   true │   1078.98 │       err │     err │ 74.98 │     err │     err │  80904.52 │       err │     err │
│           Smorgasbord │   201 │      enzyme │             typed │   true │   2596.64 │       err │     err │  5.18 │     err │     err │  13451.98 │       err │     err │
│           Smorgasbord │   201 │    mooncake │             typed │   true │   2631.17 │   1709.53 │    1.54 │  5.76 │    5.70 │    1.01 │  15160.90 │   9740.00 │    1.56 │
├───────────────────────┼───────┼─────────────┼───────────────────┼────────┼───────────┼───────────┼─────────┼───────┼─────────┼─────────┼───────────┼───────────┼─────────┤
│           Smorgasbord │   201 │ reversediff │             typed │   true │   2545.29 │   1740.95 │    1.46 │ 54.72 │   83.77 │    0.65 │ 139274.38 │ 145839.79 │    0.95 │
│           Smorgasbord │   201 │ forwarddiff │      typed_vector │   true │   2691.31 │   1680.21 │    1.60 │ 43.29 │   56.40 │    0.77 │ 116517.19 │  94762.10 │    1.23 │
│           Smorgasbord │   201 │ forwarddiff │           untyped │   true │   2363.30 │   1690.01 │    1.40 │ 44.75 │   58.67 │    0.76 │ 105756.45 │  99160.00 │    1.07 │
├───────────────────────┼───────┼─────────────┼───────────────────┼────────┼───────────┼───────────┼─────────┼───────┼─────────┼─────────┼───────────┼───────────┼─────────┤
│           Smorgasbord │   201 │ forwarddiff │    untyped_vector │   true │   2372.46 │   1674.30 │    1.42 │ 44.50 │   58.07 │    0.77 │ 105585.72 │  97231.51 │    1.09 │
│              Submodel │     1 │    mooncake │             typed │   true │     27.02 │      7.50 │    3.60 │  5.08 │    4.99 │    1.02 │    137.27 │     37.41 │    3.67 │
└───────────────────────┴───────┴─────────────┴───────────────────┴────────┴───────────┴───────────┴─────────┴───────┴─────────┴─────────┴───────────┴───────────┴─────────┘

@github-actions
Copy link
Contributor

DynamicPPL.jl documentation for PR #1139 is available at:
https://TuringLang.github.io/DynamicPPL.jl/previews/PR1139/

@codecov
Copy link

codecov bot commented Nov 13, 2025

Codecov Report

❌ Patch coverage is 90.69767% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.21%. Comparing base (3cd8d34) to head (b1368dd).

Files with missing lines Patch % Lines
src/fasteval.jl 86.36% 3 Missing ⚠️
src/test_utils/model_interface.jl 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           breaking    #1139      +/-   ##
============================================
- Coverage     81.60%   77.21%   -4.40%     
============================================
  Files            42       40       -2     
  Lines          3925     3708     -217     
============================================
- Hits           3203     2863     -340     
- Misses          722      845     +123     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@penelopeysm penelopeysm force-pushed the py/not-experimental branch 2 times, most recently from c4f93fe to c1ee6cc Compare November 14, 2025 00:37
@penelopeysm penelopeysm force-pushed the py/not-experimental branch 2 times, most recently from d86ff22 to be27184 Compare November 14, 2025 01:11
@penelopeysm penelopeysm force-pushed the py/not-experimental branch 2 times, most recently from 0cc9278 to 6f5df1a Compare November 14, 2025 02:07
@penelopeysm penelopeysm force-pushed the py/not-experimental branch 2 times, most recently from 177656b to 9310ec0 Compare November 15, 2025 20:44
@penelopeysm penelopeysm requested a review from mhauru November 17, 2025 16:08
@penelopeysm
Copy link
Member Author

@mhauru Feel free to not do a full review at this stage, right now I'm more curious as to your thoughts about SVI. Happy to either keep it in for now, or remove it outright. Regardless of which way we go, I can rebase this against breaking so that this can actually be reviewed.

@mhauru
Copy link
Member

mhauru commented Nov 18, 2025

Trying to think of reasons to keep SVI, and the only one I can think of would be if we, or someone else, used it with one of the methods that don't use LDF. It hasn't traditionally worked with particle samplers because it didn't have num_produce. It should now work, since num_produce is an accumulator. I doubt anyone has started using it. I guess we could try to start using it, see what happens. I can't recall how it does with Gibbs, but I doubt it'll work in cases where you mix samplers that want or do not want linking, since it can't link individual variables.

Its days are definitely numbered. The only reason I can think of for not doing it now would be to investigate using it with particle samplers, but I doubt we'll get to that. So I'd lean towards getting the code simplification straight-away, make it easier to work with VarInfo as we keep refactoring.

@penelopeysm penelopeysm changed the base branch from py/params-from-ldf to breaking November 18, 2025 11:07
@penelopeysm penelopeysm force-pushed the py/not-experimental branch 3 times, most recently from 7fa0986 to 6849bc2 Compare November 18, 2025 15:48
@penelopeysm
Copy link
Member Author

penelopeysm commented Nov 18, 2025

I think CloudFlare is responsible for the remaining ordinary test suite failures. The perf benchmarks are pretty much where I expected them to be.

The gradient evaluation benchmarks are not amazing, but I think it's mainly because any perf gains for these models are in primal evaluation, and so if the gradient doesn't also get sped up, it looks bad.

Arguably there should be a third column which is t(grad) / t(ref).

@penelopeysm penelopeysm marked this pull request as ready for review November 18, 2025 16:12

#### SimpleVarInfo

`SimpleVarInfo` has been removed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SimpleVarInfo has cleaner code than VarInfo. Doesn't it make more sense to remove VarInfo and replace it with SimpleVarInfo?

Copy link
Member Author

@penelopeysm penelopeysm Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Markus can probably say more, but I don't think the choice is between SVI and VI. The structs are actually the same except that SVI carries an extra field to store a transformation (which would typically be part of VI's metadata field).

struct SimpleVarInfo{NT,Accs<:AccumulatorTuple where {N},C<:AbstractTransformation} <:
AbstractVarInfo
"underlying representation of the realization represented"
values::NT
"tuple of accumulators for things like log prior and log likelihood"
accs::Accs
"represents whether it assumes variables to be transformed"
transformation::C
end

struct VarInfo{Tmeta,Accs<:AccumulatorTuple} <: AbstractVarInfo
metadata::Tmeta
accs::Accs
end

I think the complexity lies in what kinds of metadata they carry. SVI code looks simpler because src/simple_varinfo.jl only handles NamedTuple or Dict metadata. VarInfo code looks terrible because src/varinfo.jl mostly deals with Metadata metadata (hence all the generated functions).

So instead of saying removing SVI, maybe it would have been more accurate to say that this is removing NamedTuple and Dict as supported forms of metadata. As far as I can tell it's mostly a coincidence that this code is associated with SVI.

If the aim is to clean up src/varinfo.jl, then what really needs to happen is to replace Metadata with VNV (although VNV has 1.7k lines of code to itself, so IMO that's not exactly clean; but it probably feels better because it's in its own file, more documented, etc.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(#1105)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, the plan is to use SimpleVarInfo with VNV and VNT as the unifying tracing data type, largely because SimpleVarInfo has a cleaner API and better documentation. I agree that the differences between SVI and VI aren’t all that significant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants