Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
b3fad62
update to pretrain object plotting functions
smribet Apr 19, 2026
24f2e3d
fixing type hints
arthurmccray Apr 22, 2026
2cd6d12
adding final activation
smribet Apr 23, 2026
e7b2ce0
position correction plotting and functionality for ptycholite
smribet May 6, 2026
0c5fe6e
small bug fix
smribet May 6, 2026
b504b76
one more tiny bug fix
smribet May 6, 2026
5c539ba
multiprocessing working for ptycho multi gpu single node, linter erro…
arthurmccray May 19, 2026
d047283
Merge branch 'electronmicroscopy:dev' into multi_gpu_ptycho
arthurmccray May 19, 2026
beb1dd9
return fig options
smribet May 20, 2026
0e7396a
getting working for DGP
arthurmccray May 21, 2026
f5c0c73
cleaning up multi GPU DGP fixing bugs in lr and opt persistence
arthurmccray May 22, 2026
34a09ce
consistent devices
arthurmccray May 22, 2026
f1417fe
fixing linter errors
arthurmccray May 22, 2026
578ace1
fix for optimize hyperparameters
smribet May 22, 2026
71840e0
converting ptycho to dataloader while maintaining jupyter notebook mu…
arthurmccray May 23, 2026
caa5c39
move _build_dataloaders to ptychography_base
arthurmccray May 23, 2026
5e36706
bug fix
smribet May 23, 2026
2ef8f9a
cleaning up ptycho_opt
arthurmccray May 26, 2026
107ccd3
adding tests
arthurmccray May 26, 2026
6412263
Merge branch 'dev' into diffractive_imaging
arthurmccray May 26, 2026
55d7d40
Merge branch 'diffractive_imaging' into multi_gpu_ptycho
arthurmccray May 26, 2026
07d739b
adding iterative ptycho constraint params
arthurmccray May 27, 2026
e5207c8
removing additional constraint flags from reconstruct
arthurmccray May 27, 2026
10d3538
improving constraint params docstrings, parse_dict
arthurmccray May 27, 2026
e3d15ae
changing pure_phase to be unwrapped real values
arthurmccray May 28, 2026
85dfa3f
bugfix of initializing to first devices even if not specified
arthurmccray May 28, 2026
74f52ea
adding TODO for amp/phase tv weight splitting
arthurmccray May 28, 2026
b7d084b
moving hard constraints outside of computational graph
arthurmccray May 29, 2026
72efce9
workingish ptycho inr
arthurmccray May 29, 2026
0c55030
fixing linter errors
arthurmccray May 29, 2026
ae503f7
Merge branch 'optmixin_pplr' into diffractive_imaging
arthurmccray Jun 1, 2026
3807f6e
Merge branch 'optmixin_pplr' into diffractive_imaging
arthurmccray Jun 1, 2026
5488cd9
adding loss criterion organization, testing inr with s3im and smoothl1
arthurmccray Jun 1, 2026
990430a
bufix of saving criterion, potential INR to softplus final activation…
arthurmccray Jun 1, 2026
f8e300f
comment removal
arthurmccray Jun 1, 2026
e977ba4
Merge branch 'dev' into diffractive_imaging
arthurmccray Jun 1, 2026
0b15305
Merge branch 'diffractive_imaging' into multi_gpu_ptycho
arthurmccray Jun 1, 2026
73c9aee
Merge branch 'multi_gpu_ptycho' into ptycho_constraints
arthurmccray Jun 1, 2026
4a6478b
Merge branch 'ptycho_constraints' into ptycho_inr
arthurmccray Jun 1, 2026
586c1d3
fixing linter error
arthurmccray Jun 2, 2026
7b7566d
Merge pull request #234 from arthurmccray/multi_gpu_ptycho
arthurmccray Jun 2, 2026
c26c6bf
initial workingish kplanes
arthurmccray Jun 3, 2026
7400459
Merge pull request #241 from arthurmccray/ptycho_constraints
arthurmccray Jun 3, 2026
732aac7
Merge branch 'dev' into diffractive_imaging
arthurmccray Jun 3, 2026
00938eb
Merge branch 'diffractive_imaging' into ptycho_inr
arthurmccray Jun 3, 2026
8c6ea7e
bug fix
smribet Jun 3, 2026
d906a10
device and lr changes. all ptycholite.
smribet Jun 3, 2026
e067d6b
fixing linter errors kplanes
arthurmccray Jun 3, 2026
dcfb122
Merge branch 'diffractive_imaging' into ptycho_inr
arthurmccray Jun 3, 2026
c2bf1bf
adding hard constraints for INR positivity
arthurmccray Jun 4, 2026
6e111d5
adding show probe function
smribet Jun 4, 2026
c71ad00
adding alternative upsampling method to CNNs
arthurmccray Jun 10, 2026
9b1aec9
fixing linter errors in ptycho viz
arthurmccray Jun 10, 2026
68ac07b
Merge branch 'diffractive_imaging' into ptycho_inr
arthurmccray Jun 10, 2026
e2f084d
fixing bug of constraints not being passed to multiple gpus
arthurmccray Jun 10, 2026
5027a72
fixing bug in poisson loss scaling for batch sizes
arthurmccray Jun 10, 2026
4045da2
cleaning up some type hints
arthurmccray Jun 10, 2026
a8d7a53
descan_shifts_zero name change, and adding shrinkage vs clamp option …
arthurmccray Jun 11, 2026
50f4948
fixing docstring
arthurmccray Jun 11, 2026
c2edbd6
Merge pull request #247 from arthurmccray/ptycho_inr
arthurmccray Jun 11, 2026
80a7b19
converting to degrees
smribet Jun 11, 2026
61ca852
correcting stig rotation
smribet Jun 12, 2026
44481bc
basic plotting
smribet Jun 12, 2026
c837502
Merge remote-tracking branch 'origin/diffractive_imaging' into direct…
smribet Jun 12, 2026
5fa1c61
fixing bug in com determination when DP sum = 0
arthurmccray Jun 12, 2026
b79ab8d
Revert "correcting stig rotation"
smribet Jun 12, 2026
e7cd652
adding position masking to iterative ptycho
arthurmccray Jun 12, 2026
2a4972e
small vis change
smribet Jun 13, 2026
6e15f34
clean up
gvarnavi Jun 15, 2026
c9b4f1e
Merge pull request #249 from smribet/direct_ptycho_refactor
smribet Jun 15, 2026
54618f9
fixing device handling coverage in models, passing kwaargs in show_fo…
arthurmccray Jun 17, 2026
63e06bc
fixing bug combining large aspect ratio scans with large com_rotations
arthurmccray Jul 2, 2026
27ba927
reducing memory for preprocessing and free port bugfix
arthurmccray Jul 7, 2026
571f4f6
memory optimizations for preprocessing
arthurmccray Jul 22, 2026
19f85de
big cleanup of iterative ptycho code, simplifications and bugfixes
arthurmccray Jul 31, 2026
50b6ce3
some code simplifications and dedup
arthurmccray Aug 2, 2026
507acb0
fixing seed for INR tests
arthurmccray Aug 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 126 additions & 37 deletions src/quantem/core/ml/blocks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Callable
from typing import Callable, Literal

import numpy as np
import torch
Expand Down Expand Up @@ -141,7 +141,17 @@ def forward(self, x: torch.Tensor) -> torch.Tensor:


class Upsample2dBlock(nn.Module):
"""Upsampling block using transposed convolution or interpolation followed by convolution."""
"""Upsampling block using transposed convolution or interpolation followed by convolution.

Two upsampling methods are available:

- ``"transpose"`` (default): transposed convolution (for ``scale_factor==2``,
otherwise interpolation) followed by a 1x1 convolution. This is the
original behavior.
- ``"resize"``: interpolation followed by a 3x3 convolution. Decoupling the
upsampling from the learned weights avoids the uneven kernel overlap that
produces checkerboard artifacts with transposed convolutions.
"""

def __init__(
self,
Expand All @@ -151,6 +161,7 @@ def __init__(
dtype: "torch.dtype" = torch.float32,
scale_factor: int = 2,
mode: str = "bilinear",
method: Literal["transpose", "resize"] = "transpose",
):
"""Initialize Upsample2dBlock.

Expand All @@ -168,41 +179,74 @@ def __init__(
Factor by which to scale the input, by default 2
mode : str, optional
Interpolation mode, either "bilinear" or "nearest", by default "bilinear"
method : str, optional
Upsampling method, either "transpose" (transposed convolution) or
"resize" (interpolation followed by convolution). The "resize"
method reduces checkerboard artifacts. By default "transpose".
"""
super().__init__()
assert mode in ["bilinear", "nearest"], "Mode must be 'bilinear' or 'nearest'."
assert method in ("transpose", "resize"), "method must be 'transpose' or 'resize'."
self.scale_factor = scale_factor
self.mode = mode
self.use_batchnorm = use_batchnorm
self.dtype = dtype
self.upsample2x = nn.ConvTranspose2d(
input_channels,
input_channels,
kernel_size=3,
stride=2,
padding=(1, 1),
output_padding=(1, 1),
dtype=self.dtype,
)
self.conv = nn.Conv2d(
input_channels,
output_channels,
kernel_size=1,
stride=1,
padding=0,
dtype=self.dtype,
padding_mode="circular",
)
self.method = method

if method == "transpose":
self.upsample2x = nn.ConvTranspose2d(
input_channels,
input_channels,
kernel_size=3,
stride=2,
padding=(1, 1),
output_padding=(1, 1),
dtype=self.dtype,
)
self.conv = nn.Conv2d(
input_channels,
output_channels,
kernel_size=1,
stride=1,
padding=0,
dtype=self.dtype,
padding_mode="circular",
)
else:
# Resize-conv: interpolate first, then a 3x3 conv smooths the result.
self.upsample2x = None
self.conv = nn.Conv2d(
input_channels,
output_channels,
kernel_size=3,
stride=1,
padding=1,
dtype=self.dtype,
padding_mode="circular",
)

if self.dtype.is_complex:
self.bn = ComplexBatchNorm2D(output_channels)
else:
self.bn = nn.BatchNorm2d(output_channels)

def _interpolate(self, x: torch.Tensor) -> torch.Tensor:
# F.interpolate does not support complex tensors, so handle parts separately.
if x.is_complex():
real = F.interpolate(x.real, scale_factor=self.scale_factor, mode=self.mode)
imag = F.interpolate(x.imag, scale_factor=self.scale_factor, mode=self.mode)
return torch.complex(real, imag)
return F.interpolate(x, scale_factor=self.scale_factor, mode=self.mode)

def forward(self, x: torch.Tensor) -> torch.Tensor:
if self.scale_factor == 2:
x = self.upsample2x(x)
if getattr(self, "method", "transpose") == "transpose":
if self.scale_factor == 2:
assert self.upsample2x is not None
x = self.upsample2x(x)
else:
x = self._interpolate(x)
else:
x = F.interpolate(x, scale_factor=self.scale_factor, mode=self.mode)
x = self._interpolate(x)

x = self.conv(x)
if self.use_batchnorm:
Expand Down Expand Up @@ -399,7 +443,17 @@ def forward(self, x: torch.Tensor) -> torch.Tensor:


class Upsample3dBlock(nn.Module):
"""3D upsampling block using transposed convolution followed by 1x1x1 convolution."""
"""3D upsampling block.

Two upsampling methods are available:

- ``"transpose"`` (default): transposed convolution followed by a 1x1x1
convolution. This is the original behavior.
- ``"resize"``: nearest/trilinear interpolation followed by a 3x3x3
convolution. Decoupling the upsampling from the learned weights avoids
the uneven kernel overlap that produces checkerboard artifacts with
transposed convolutions.
"""

def __init__(
self,
Expand All @@ -408,7 +462,8 @@ def __init__(
use_batchnorm: bool = False,
dtype: torch.dtype = torch.float32,
scale_factor: int = 2,
mode: str = "trilinear",
mode: str = "nearest",
method: Literal["transpose", "resize"] = "transpose",
) -> None:
"""Initialize Upsample3dBlock.

Expand All @@ -425,29 +480,63 @@ def __init__(
scale_factor : int, optional
Factor by which to scale the input, by default 2
mode : str, optional
Interpolation mode, by default "trilinear"
Interpolation mode used when ``method="resize"``, by default "trilinear"
method : str, optional
Upsampling method, either "transpose" (transposed convolution) or
"resize" (interpolation followed by convolution). The "resize"
method reduces checkerboard artifacts. By default "transpose".
"""
super().__init__()
assert method in ("transpose", "resize"), "method must be 'transpose' or 'resize'."
self.dtype = dtype
self.use_batchnorm = use_batchnorm
self.upsample = nn.ConvTranspose3d(
input_channels,
input_channels,
kernel_size=3,
stride=2,
padding=1,
output_padding=1,
dtype=dtype,
)
self.conv = nn.Conv3d(input_channels, output_channels, kernel_size=1, dtype=dtype)
self.method = method
self.scale_factor = scale_factor
self.mode = mode

if method == "transpose":
self.upsample = nn.ConvTranspose3d(
input_channels,
input_channels,
kernel_size=3,
stride=2,
padding=1,
output_padding=1,
dtype=dtype,
)
self.conv = nn.Conv3d(input_channels, output_channels, kernel_size=1, dtype=dtype)
else:
# Resize-conv: interpolate first, then a 3x3x3 conv smooths the result.
self.upsample = None
self.conv = nn.Conv3d(
input_channels,
output_channels,
kernel_size=3,
padding=1,
dtype=dtype,
padding_mode="circular",
)

self.bn = (
ComplexBatchNorm3D(output_channels)
if dtype.is_complex
else nn.BatchNorm3d(output_channels)
)

def _interpolate(self, x: torch.Tensor) -> torch.Tensor:
# F.interpolate does not support complex tensors, so handle parts separately.
if x.is_complex():
real = F.interpolate(x.real, scale_factor=self.scale_factor, mode=self.mode)
imag = F.interpolate(x.imag, scale_factor=self.scale_factor, mode=self.mode)
return torch.complex(real, imag)
return F.interpolate(x, scale_factor=self.scale_factor, mode=self.mode)

def forward(self, x: torch.Tensor) -> torch.Tensor:
x = self.upsample(x)
if getattr(self, "method", "transpose") == "transpose":
assert self.upsample is not None
x = self.upsample(x)
else:
x = self._interpolate(x)
x = self.conv(x)
if self.use_batchnorm:
x = self.bn(x)
Expand Down
Loading