pymc-devs / pymc-devs/pytensor
local_useless_unbatched_blockwise emits squeeze(expand_dims(x)) that nothing collapses, so factorizations stop merging
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 644
- Forks
- 208
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 16
Description
A Blockwise whose batch dims are all broadcastable is unbatched by local_useless_unbatched_blockwise into core_op(squeeze(expand_dims(x))), and the compiled graph keeps that Squeeze/ExpandDims pair. The rewrite is registered at optdb position 60, after every DimShuffle-collapsing pass, so merge3 at position 100 sees Cholesky(A) and Cholesky(squeeze(expand_dims(A))) as different nodes and a positive-definite matrix used both batched and unbatched is factored twice.
import pytensor
import pytensor.tensor as pt
A = pt.matrix("A", shape=(5, 5))
b = pt.tensor("b", shape=(3, 5, 1))
logdet = 2 * pt.log(pt.diagonal(pt.linalg.cholesky(A))).sum()
quad = (b * pt.linalg.solve(A, b, assume_a="pos", b_ndim=2)).sum()
pytensor.dprint(pytensor.function([A, b], logdet + quad))
Composite{((2.0 * i1) + i0)} [id A] 9
├─ FusedElemwise{Mul, reduce[add@(0, 1, 2)]} [id B] 8
│ ├─ b [id C]
│ └─ [Blockwise{CholeskySolve{lower=True, b_ndim=2, overwrite_b=False}, (m,m),(m,n)->(m,n)}] [id D] 7
│ ├─ ExpandDims{axis=0} [id E] 6
│ │ └─ Cholesky{lower=True, overwrite_a=False} [id F] 5
│ │ └─ Squeeze{axis=0} [id G] 4
│ │ └─ ExpandDims{axis=0} [id H] 3
│ │ └─ A [id I]
│ ├─ b [id C]
│ └─ [5 1] [id J]
└─ FusedElemwise{Log, reduce[add@(0,)]} [id K] 2
└─ ExtractDiag{offset=0, axis1=0, axis2=1, view=True} [id L] 1
└─ Cholesky{lower=True, overwrite_a=False} [id M] 0
└─ A [id I]
The same pair with no solve involved:
pytensor.dprint(pytensor.function([A], [pt.linalg.cholesky(A), pt.linalg.cholesky(A[None])]))
Cholesky{lower=True, overwrite_a=False} [id A] 4
└─ A [id B]
ExpandDims{axis=0} [id C] 3
└─ Cholesky{lower=True, overwrite_a=False} [id D] 2
└─ Squeeze{axis=0} [id E] 1
└─ ExpandDims{axis=0} [id F] 0
└─ A [id B]
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with local_useless_unbatched_blockwise and the optimization ordering around positions 60 and 100; reproduce both examples with pytensor.dprint. Done means the redundant Squeeze/ExpandDims pair no longer prevents equivalent Cholesky operations from sharing one factorization.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100