Repository navigation
Conversation
BlockLinearOperator._getitem had a fast path for slices: if all slice bounds are multiples of num_blocks, it divided them by num_blocks and took those rows and columns from every block. That is right for BlockInterleavedLinearOperator, where row i of the matrix is row i // num_blocks of block i % num_blocks, but BlockDiagLinearOperator inherited it too. For example, with two 4 x 4 blocks, op[..., 0:4, 0:4] returned the top-left 2 x 2 entries of both blocks instead of the first block, and op[..., 0:4, 4:8] returned non-zero entries. If the divided slices were not square, it raised "base_linear_op must be a batch of square matrices" instead (cornellius-gp/gpytorch#2747). Move the fast path to BlockInterleavedLinearOperator. BlockLinearOperator keeps the batch-only fast path, so BlockDiagLinearOperator now uses the generic path for slices of the matrix dimensions. Add tests that compare slices of block-diagonal and interleaved operators against their dense matrices, including slices whose bounds are multiples of num_blocks or of the block size. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes cornellius-gp/gpytorch#2747. Alternative to #130 (see below).
Problem
BlockLinearOperator._getitemhas a fast path for slices: if all slice bounds are multiples ofnum_blocks, it divides them bynum_blocksand takes those rows and columns from every block. That is right forBlockInterleavedLinearOperator, where rowiof the matrix is rowi // num_blocksof blocki % num_blocks, butBlockDiagLinearOperatorinherits it too:op[0:4, 0:4]returns the top-left 2 x 2 entries of both blocks instead of the first block, with the correct shape, so the result is silently wrong. If the divided slices are not square, it raisesbase_linear_op must be a batch of square matricesinstead (cornellius-gp/gpytorch#2747, e.g. inExactGPprediction). Before #115 (released in 0.6.1), these slices raised errors; since then the square ones return wrong values. In BoTorch, this made the joint posterior covariance of aModelListGPwith batched multi-output sub-models wrong wheneverqwas divisible by the number of outputs (meta-pytorch/botorch#3409).Change
Move the fast path to
BlockInterleavedLinearOperator.BlockLinearOperator._getitemkeeps the batch-only fast path, soBlockDiagLinearOperatornow uses the generic path for slices of the matrix dimensions (as it already did for slices whose bounds are not multiples ofnum_blocks).#130 changes the shared base-class fast path to block-diagonal semantics instead. That fixes
BlockDiagLinearOperatorbut breaksBlockInterleavedLinearOperator: for two 4 x 4 interleaved blocks,op[0:4, 0:4]then differs from the dense slice by 2.04. The existing tests do not catch this, because none of the generictest_getitemslices have bounds that are multiples ofnum_blocksor the block size.A structure-preserving fast path for slices that select whole diagonal blocks is possible, but
base_linear_op[..., i:j, :, :]currently returns the wrong blocks for aCatLinearOperatorbase whenjis the number of blocks (#137), which is how GPyTorch'sMultitaskMultivariateNormal.from_independent_mvnsbuilds its covariance. So I left it out of this fix.Tests
test_getitem_slicescompares 100 row x column slice pairs (block-aligned,num_blocks-aligned, straddling, off-diagonal, negative, with a step), with and without batch indices, against the dense matrix for all block-diagonal and interleaved test classes, plus a block-diagonal operator with aCatLinearOperatorbase.main: 16 of the 100 slice pairs fail for each of the 5 block-diagonal test classes (wrong values or errors); the interleaved ones pass.CatLinearOperatorbase (4 slice pairs).test_psd_safe_cholesky_psd, which also fails onmainlocally (torch 2.13 on Windows, unrelated).flake8 passes. Tested with Python 3.13, PyTorch 2.13 (CPU).
🤖 Generated with Claude Code