Skip to content

Conversation

@mhauru
Copy link
Member

@mhauru mhauru commented Dec 15, 2025

This PR adds a wrapper type ArrayLikeBlock, that PartialArray uses to store any elements that have non-zero size but are not AbstractArrays, when such a value is being set.

Other improvements to VNT are also included, that I happened to need and implement while working on this.

This PR is just completing the work in #1150, but I keep it separate to make reviewing easier.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 15, 2025

Benchmark Report

  • this PR's head: 51b399aeb1f3c4ee29e1029215668b47847e0a15
  • base branch: 44be19dcb0ed4e61aaf834527a48ddf16e3c279f

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 × AMD EPYC 7763 64-Core Processor
  WORD_SIZE: 64
  LLVM: libLLVM-16.0.6 (ORCJIT, znver3)
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 │   337.08 │   336.10 │    1.00 │   10.87 │   10.12 │    1.07 │     3664.22 │   3402.78 │    1.08 │
│                   LDA │    12 │ reversediff │             typed │   true │  2417.20 │  2589.76 │    0.93 │    5.08 │    5.86 │    0.87 │    12277.62 │  15163.22 │    0.81 │
│   Loop univariate 10k │ 10000 │    mooncake │             typed │   true │ 52985.36 │ 53140.12 │    1.00 │    6.21 │    6.55 │    0.95 │   328885.10 │ 348077.21 │    0.94 │
├───────────────────────┼───────┼─────────────┼───────────────────┼────────┼──────────┼──────────┼─────────┼─────────┼─────────┼─────────┼─────────────┼───────────┼─────────┤
│    Loop univariate 1k │  1000 │    mooncake │             typed │   true │  5333.29 │  5295.54 │    1.01 │    5.86 │    5.98 │    0.98 │    31263.29 │  31668.51 │    0.99 │
│      Multivariate 10k │ 10000 │    mooncake │             typed │   true │ 32385.77 │ 69338.17 │    0.47 │ 1602.55 │    4.86 │  330.04 │ 51899861.47 │ 336677.39 │  154.15 │
│       Multivariate 1k │  1000 │    mooncake │             typed │   true │  3314.84 │  3234.69 │    1.02 │    9.35 │    9.46 │    0.99 │    30980.55 │  30588.84 │    1.01 │
├───────────────────────┼───────┼─────────────┼───────────────────┼────────┼──────────┼──────────┼─────────┼─────────┼─────────┼─────────┼─────────────┼───────────┼─────────┤
│ Simple assume observe │     1 │ forwarddiff │             typed │  false │     2.39 │     2.39 │    1.00 │    3.85 │    3.93 │    0.98 │        9.23 │      9.39 │    0.98 │
│           Smorgasbord │   201 │ forwarddiff │             typed │  false │  1009.59 │  1002.51 │    1.01 │  136.41 │  135.18 │    1.01 │   137716.48 │ 135522.81 │    1.02 │
│           Smorgasbord │   201 │ forwarddiff │       simple_dict │   true │      err │      err │     err │     err │     err │     err │         err │       err │     err │
├───────────────────────┼───────┼─────────────┼───────────────────┼────────┼──────────┼──────────┼─────────┼─────────┼─────────┼─────────┼─────────────┼───────────┼─────────┤
│           Smorgasbord │   201 │ forwarddiff │ simple_namedtuple │   true │      err │      err │     err │     err │     err │     err │         err │       err │     err │
│           Smorgasbord │   201 │      enzyme │             typed │   true │  1389.40 │  1376.74 │    1.01 │    6.54 │    6.75 │    0.97 │     9088.99 │   9296.74 │    0.98 │
│           Smorgasbord │   201 │    mooncake │             typed │   true │  1903.94 │  1410.33 │    1.35 │    4.26 │    5.49 │    0.78 │     8120.29 │   7748.32 │    1.05 │
├───────────────────────┼───────┼─────────────┼───────────────────┼────────┼──────────┼──────────┼─────────┼─────────┼─────────┼─────────┼─────────────┼───────────┼─────────┤
│           Smorgasbord │   201 │ reversediff │             typed │   true │  1450.52 │  1383.54 │    1.05 │   96.52 │  100.14 │    0.96 │   140008.34 │ 138553.25 │    1.01 │
│           Smorgasbord │   201 │ forwarddiff │      typed_vector │   true │  1366.91 │  1378.24 │    0.99 │   64.71 │   70.65 │    0.92 │    88450.71 │  97376.37 │    0.91 │
│           Smorgasbord │   201 │ forwarddiff │           untyped │   true │  1382.40 │  1367.10 │    1.01 │   62.94 │   64.56 │    0.97 │    87001.41 │  88258.57 │    0.99 │
├───────────────────────┼───────┼─────────────┼───────────────────┼────────┼──────────┼──────────┼─────────┼─────────┼─────────┼─────────┼─────────────┼───────────┼─────────┤
│           Smorgasbord │   201 │ forwarddiff │    untyped_vector │   true │  1386.26 │  1369.99 │    1.01 │   62.33 │   61.61 │    1.01 │    86406.20 │  84402.89 │    1.02 │
│              Submodel │     1 │    mooncake │             typed │   true │     3.03 │     2.92 │    1.04 │   11.58 │   11.31 │    1.02 │       35.06 │     33.08 │    1.06 │
└───────────────────────┴───────┴─────────────┴───────────────────┴────────┴──────────┴──────────┴─────────┴─────────┴─────────┴─────────┴─────────────┴───────────┴─────────┘

@codecov
Copy link

codecov bot commented Dec 15, 2025

Codecov Report

❌ Patch coverage is 90.99099% with 10 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (mhauru/vnt-for-fastldf@44be19d). Learn more about missing BASE report.

Files with missing lines Patch % Lines
src/varnamedtuple.jl 89.89% 10 Missing ⚠️
Additional details and impacted files
@@                    Coverage Diff                    @@
##             mhauru/vnt-for-fastldf    #1180   +/-   ##
=========================================================
  Coverage                          ?   80.13%           
=========================================================
  Files                             ?       42           
  Lines                             ?     4298           
  Branches                          ?        0           
=========================================================
  Hits                              ?     3444           
  Misses                            ?      854           
  Partials                          ?        0           

☔ 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.

@github-actions
Copy link
Contributor

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

Copy link
Member Author

@mhauru mhauru left a comment

Choose a reason for hiding this comment

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

Tests pass, and I think this is ready for review. The only thing I think is missing is the support for (concretised) Colons, but I'll put that in a separate PR.

# whether it's linked
is_linked::Bool
# original size of the variable before vectorisation
original_size::T
Copy link
Member Author

Choose a reason for hiding this comment

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

It's a bit unfortunate to have to create this field, because now you may end up with abstractly typed RangeAndLinked, which obscures the fact that the two fields you really care about, namely range and is_linked, are still concrete. The reason this is needed is that VNT needs to know how much "space" in a PartialArray an instance of RangeAndLinked takes. An alternative to this could be something like giving setindex!!(::VarNamedTuple, ...) an extra kwarg of something like ignore_size_checks.

# vr.varname_ranges[vn] may infer to have type `Any`. In this case it is helpful to
# assert that it is a RangeAndLinked, because even though it remains non-concrete,
# it'll allow the compiler to infer the types of `range` and `is_linked`.
return vr.varname_ranges[vn]::RangeAndLinked
Copy link
Member Author

Choose a reason for hiding this comment

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

Without this assertion, this model:

@model function demo_one_variable_multiple_constraints(
    ::Type{TV}=Vector{Float64}
) where {TV}
    x = TV(undef, 5)
    x[1] ~ Normal()
    x[2] ~ InverseGamma(2, 3)
    x[3] ~ truncated(Normal(), -5, 20)
    x[4:5] ~ Dirichlet([1.0, 2.0])
    return (x=x,)
end

fails the test that checks that the return type of LogDensityProblems.logdensity(ldf, x) can be inferred. It infers as Any. This happens because the VNT gets a PartialArray where three elements are RangeAndLinked{Tuple{}} and one is a ArrayLikeBlock{RangeAndLinked{Tuple{Int}}, Tuple{Int}}. That makes the element type Any, and _get_range_and_linked's return type infers as Any, and all is lost.

Now, I'm fine saying that a model that mixes x[i] and x[j:l] for the same x is pretty type unstable (note that the priors have to mix univariate and multivariate). However, I don't think that should be licence for even the logdensity return type to infer as Any, and it didn't before this PR. Hence this extra assertion, to have the type instability not be so egregious.

Note that this problem is independent of the above comment of introducing a type parameter to RangeAndLinked: Even without the type parameter, the wrapping in ArrayLikeBlock would force the PartialArray to have element type Any.

_has_colon(::T) where {T<:Tuple} = any(x <: Colon for x in T.parameters)
function _is_multiindex(::T) where {T<:Tuple}
return any(x <: UnitRange || x <: Colon for x in T.parameters)
# The non-generated function implementations of these would be
Copy link
Member Author

Choose a reason for hiding this comment

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

Without this, getindex(vnt, @varname(y.z[2:3, 3, 2:3])) was type stable but getindex(vnt, @varname(y.z[2, 2:3, 3, 2:3, 4])) was not.

@mhauru mhauru marked this pull request as ready for review December 15, 2025 17:34
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.

2 participants