Skip to content

Simplify internals to prep for 1.0 release #601

Description

@jbogaardt

On the path towards 1.0 there are several refactors worth considering that would simplify the code base and bring it more into software development best practices.

Backend support

To migrate to an arrow backend, we should minimize existing backend support to just numpy/sparse.

cupy and dask were always elective and not well maintained. They add unnecessary complexity to the project and should be removed.

Clarify Triangle public API

The Estimators occasionally manipulate the private internals of the Triangle class. This makes the Estimator code heavily coupled to the internals of the Triangle making refactors of Triangle internals difficult. We need to force etimators to only use the public API. This may include adding additional support properties and methods to the Triangle class that the estimators can call on as opposed to direct manipulation of the underlying triangle structure.

kdims, vdims, odims, ddims

These are internal aliases for index, columns, origin, development. They exist to bypass some of the consistency checks on these axes. These certainly shouldn't be in the public API, but even internal to a Triangle, they are difficult to manage. They should be refactored out of the Triangle with no changes to the public API.

None of these should change any of the exiting public API of the package and all test should continue to pass.

Activity

  1. self-assigned this
    on Sep 25, 2025
  2. henrydingliu commented on Oct 29, 2025

    @henrydingliu
    Member

    @jbogaardt apologies if you've discussed this at meeting that I missed/spaced out on. by arrow backend, do you mean we'll entirely abandon the 5D numpy structure into a 2D columnar structure?

  3. jbogaardt commented on Oct 29, 2025

    @jbogaardt
    ContributorAuthor

    Yes, that was the initial thought - the idea being arrow has a much more active community than numpy/sparse. That said, there are some aspects of the models that are much easier to compute using arrays. Bootstrap resampling comes to mind, but a lot of triangle work is vector/array arithmetic which is better with arrays. So arrow is not a perfect fit and I think it requires more thought. It would be nice if there was a modern array library with dense and sparse support beyond 2D that capitalizes on some of the latest trends in compression/serialization. I think pydata/sparse remains the best option at this point.

  4. henrydingliu commented on Oct 29, 2025

    @henrydingliu
    Member

    yo, I think 2D is the solution to all our problems. I'm so sick of all the grain issues. i'm one more grain issue away from rewriting the triangle internals to force storing at OMDM, and only regraining for estimation and display. In 2D, we can keep claim-level accident date and dates of transactions. then only map them to o and d grain at run time and aggregate for estimation or display. basically 2D enables sparse (if I'm understanding the purpose of sparse correctly) support by default.

  5. jbogaardt commented on Oct 30, 2025

    @jbogaardt
    ContributorAuthor

    That's a more lazy (as opposed to eager) approach and has merit. It would certainly simplify the internals. It could also improve or degrade performance issues on larger datasets. The package was written to manage all data in RAM and sparse is used to minimize the RAM footprint. Consider this example:

    import chainladder as cl
    prism_sparse = cl.load_sample('prism')
    prism_dense = prism_sparse.set_backend('numpy')
    print('Sparse Array Mb Usage:', prism_sparse.values.nbytes)
    print('Dense Array Mb Usage:', prism_dense.values.nbytes)
    print('Size Ratio:', prism_sparse.values.nbytes / prism_dense.values.nbytes)
    Sparse Array Mb Usage: 2908272
    Dense Array Mb Usage: 15779635200
    Size Ratio: 0.0001843054014328544

    Both numpy and sparse give the same API for matrix arithmetic (extremely useful for developing actuarial methods), but when throwing around in-memory data at very a granular level, a sparse representation is far more scalable. The competing forces we have are:

    • Data management - I believe Arrow is superior and your lazy evaluation will keep data sparse
    • Reserving Algorithms - Array-based math is superior. I think developing actuarial techniques using dataframe-style arithmetic is much less elegant.

    There are a ton of ways to architect a better solution including hybrid approaches - I'm just not sure we fully explored the merits of different approaches holistically enough.

  6. henrydingliu commented on Oct 30, 2025

    @henrydingliu
    Member

    We can use caching to improve lazy evaluation speed.

    On your comment regarding array math, maybe we can think of it as an opportunity for stricter API adherence. If I'm understanding your point correctly, you are talking about how easy it is to just plop down a tri.values[,,,1:] / tri.values[,,,:-1] versus clumsy window functions in columnar representation. But we can work around that with a dedicated get_sub_triangle() function in the API. then tri.get_sub_triangle(development, [1:]) / tri.get_sub_triangle(development, [:-1]) can be just as convenient as array math yet also allow an arrow implementation.

  7. jbogaardt commented on Oct 30, 2025

    @jbogaardt
    ContributorAuthor

    For array math, I was thinking more from the internals . Things like:

    These are just a couple of higher complexity examples, but linear algebra operations on triangles are abundant in the internals and in research papers. Will DataFrame-style arithmetic be performant? Will we be trading lower code complexity in the Triangle class for higher code complexity in our estimators - and is this trade-off worth it?

    I don't really know. The Triangle class is not perfect, but its relatively mature and mostly there. Being built on arrays seems to set us up for easier expansion of our list of estimators. I have a nagging feeling that chainladder should aspire to be more of a suite of actuarial techniques than a data management tools.

  8. henrydingliu commented on Oct 30, 2025

    @henrydingliu
    Member

    Alas we don't have to choose between actuarial techniques or data management. Here is an excerpt from Clark 2003, which we reference in the package but is also on the exam 7 syllabus currently. According to him, Arrow is integral to more techniques.

    Image

    Let me think about how we can do some performance testing next.

  9. added this to the 1.0.0 milestone on May 18, 2026
  10. genedan commented on May 24, 2026

    @genedan
    Member

    I need close out a bunch of browser windows full of ideas so I'm recording them here:

    Have any of you explored JAX? It looks like an all-in-one library with CPU, GPU, parallel processing, and sparse support, with a numpy-compatible API, which I thought could cut down on trying to maintain 4 separate backends. The main downside I see is that their sparse support is still listed as experimental. I think we can keep an eye on this one to see where it goes.

    Since Polars is a dataframe and not a multidimensional array library, I suppose it's off the table as a backend option. I think there might be some performance improvements in select areas, such as I/O like when data are ingested into a DataFrame during the Triangle __init__, or when they are output as DataFrames.

  11. 26 remaining items

  12. henrydingliu commented on Oct 3, 2026

    @henrydingliu
    Member

    @genedan @priyam0k thanks for your sustained efforts on this issue. i just wanted to check in on the deprecation plan. will vdims etc. go away immediately upon #1303 landing?

  13. priyam0k commented on Oct 3, 2026

    @priyam0k
    Contributor

    will vdims etc. go away immediately upon #1303 landing?

    In the current version of #1303, yes: vdims and kdims are removed directly from Triangle (raising AttributeError), with setstate migrating legacy pickled instances into _axes and emitting a UserWarning. The initial draft had FutureWarning property shims, but we dropped them during review since they were internal implementation details rather than intended public API.

    In #1304, odims and ddims currently keep FutureWarning property shims pointing to origin and development. If the team prefers having consistent FutureWarning shims for vdims and kdims across 0.11 leading up to 1.0 rather than dropping them immediately, restoring those property shims on Triangle is straightforward. Open to whichever deprecation path you prefer.

  14. henrydingliu commented on Oct 3, 2026

    @henrydingliu
    Member

    i see. i would prefer a consistent approach. but it sounds like you guys came across implementation challenges that resulted diverging approaches.

    how much are you gonna be putting in to the TriangleAxis API? as in, how much are we expecting users change how they work with the existing public axis properties? my default mental model would be

    • if users don't need to change how they currently work with index, development etc., we can just put a more descriptive error message into all the xdims shims
    • if users need to change how they currently work, we probably should offer working shims with warnings as a crutch for a release or two.
  15. genedan commented on Oct 3, 2026

    @genedan
    Member

    They shouldn't need to change how they currently work, as in, we're keeping the public API as-is (we are keeping index -> DataFrame, for example). If you try to even search for an xdim term in the docs you turn up with nothing, so I think justifiably we can just drop them, they were never meant to be part of the public API and not naming them with a preceding underscore was a mistake.

  16. added theissue type on Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

Fields

Priority

High

Effort

High

Scope

Codebase

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions