Started just max_unpooling check, but then realized there are tons of duplicated logic across numerous pooling checks headers:
- Move all pooling ops helper functions into PoolingChecks.h
- Delete duplicate `max_unpooling3d_shape_check` from MaxUnpooling.cu
- Refactor inlined shapecheck into `max_unpool2d_shape_check` used by CPU, CUDA and MPS
- Make `gradOutput` a trailing `std::optional<Tensor>`, so forward callers don't have to pass a dummy undefined tensor.
- Extend the existing `error_inputs_max_unpool` OpInfo with the dtype, rank and zero-stride cases rather than adding a device-specific test.
Authored with the assistance of Claude Code.
Fixes https://github.com/pytorch/pytorch/issues/195849
Pull Request resolved: https://github.com/pytorch/pytorch/pull/196230
Approved by: https://github.com/kurtamohler, https://github.com/Skylion007