Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cf24b92
remove scikit radon warning by adjusting radial mask for test image t…
j-c-c Aug 29, 2024
fd96cc0
tox
j-c-c Aug 29, 2024
2d8b2a2
resolve scipy cg warning by adjusting version in wrapper to 1.12.0
j-c-c Aug 29, 2024
7886c46
Resolve Matplotlib warning by explicilty closing plots in testing.
j-c-c Aug 29, 2024
ba58b43
explicitly close figures in matplotlib test wrapper.
j-c-c Sep 3, 2024
80274fe
Resolve FRC legend warning by adding default label for single correla…
j-c-c Sep 3, 2024
8011e10
resolve numpy.product warning
j-c-c Sep 3, 2024
358afa6
resolve Conversion of an array wit
j-c-c Sep 3, 2024
dd65d1c
Resolve expected BlkDiagMatrix truncation warning.
j-c-c Sep 3, 2024
194434c
Resolve test_micrograph_source warnings by using zeros instead of emp…
j-c-c Sep 4, 2024
23e3a07
Resolve test_image.py warning. Remove Ray xfail and use context to ch…
j-c-c Sep 9, 2024
bb76e21
resolve test_FLEbasis2D.py warnings.
j-c-c Sep 9, 2024
1f292cc
resolve numpy multiply overflow warning by using ones for test image …
j-c-c Sep 9, 2024
6ef824c
resolve test_diag_matrix.py invalid cast value warning, by replacing …
j-c-c Sep 9, 2024
a641921
catch np.exp overflow warnings
j-c-c Sep 10, 2024
0f9e9b5
Add comments explaining changes.
j-c-c Sep 10, 2024
6ed0643
Add pytest --strict-warnings to CI workflows.
j-c-c Sep 10, 2024
0366a10
Use PYTHONWARNINGS=error when python runs pytest.
j-c-c Sep 10, 2024
41e8c57
enforce C order on signal for cufinufft transform.
j-c-c Sep 10, 2024
3fc856b
Skip jobs on CI that will error on cached warnings. ampere, osx, long…
j-c-c Sep 11, 2024
b350ea0
Only check warnings on ampere_gpu job.
j-c-c Sep 11, 2024
00a02c4
remove symmetry warning pytest skips.
j-c-c Sep 11, 2024
a36dfa4
remove unnecesary plt.close()
j-c-c Sep 11, 2024
673323a
unused import
j-c-c Sep 11, 2024
9a0afa9
Remove useless test case.
j-c-c Sep 11, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ jobs:
run: |
ASPIREDIR=${{ env.WORK_DIR }} python -c \
"import aspire; print(aspire.config['ray']['temp_dir'])"
ASPIREDIR=${{ env.WORK_DIR }} python -m pytest --durations=50
ASPIREDIR=${{ env.WORK_DIR }} PYTHONWARNINGS=error python -m pytest --durations=50
- name: Cache Data
run: |
ASPIREDIR=${{ env.WORK_DIR }} python -c \
Expand Down
8 changes: 7 additions & 1 deletion src/aspire/abinitio/commonline_c3_c4.py
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,13 @@ def cl_angles_to_ind(cl_angles, n_theta):
thetas = np.mod(thetas, 2 * np.pi)

# linear scale from [0,2*pi) to [0,n_theta).
return np.mod(np.round(thetas / (2 * np.pi) * n_theta), n_theta).astype(int)
ind = np.mod(np.round(thetas / (2 * np.pi) * n_theta), n_theta).astype(int)

# Return scalar for single value.
if ind.size == 1:
ind = ind.flat[0]

return ind

@staticmethod
def g_sync(rots, order, rots_gt):
Expand Down
9 changes: 8 additions & 1 deletion src/aspire/abinitio/commonline_sync3n.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
import os.path
import warnings

import numpy as np
from numpy.linalg import norm
Expand Down Expand Up @@ -727,7 +728,13 @@ def fun(x, B, P, b, x0, A=A, a=a):

# Calculate probabilities
ln_f_ind, ln_f_arb = self._pairs_probabilities(Rijs, P**2, A, a, B, b, x0)
Pij = 1 / (1 + (1 - P) / P * np.exp(ln_f_arb - ln_f_ind))

with warnings.catch_warnings():
# For large values of (ln_f_arb - ln_f_ind), numpy exponential will overflow. We still
# get the intended result of Pij = 0, so we capture and ignore the overflow warning.
warnings.filterwarnings("ignore", r".*overflow encountered in exp.*")

Pij = 1 / (1 + (1 - P) / P * np.exp(ln_f_arb - ln_f_ind))

# Fix singular output
num_nan = np.sum(np.isnan(Pij))
Expand Down
3 changes: 2 additions & 1 deletion src/aspire/nufft/cufinufft.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ def transform(self, signal):
" In the future this will be an error."
)

signal = cp.asarray(signal, dtype=self.complex_dtype)
# Note, if not C order, cuFINUFFT will copy-cast anyway.
signal = cp.asarray(signal, order="C", dtype=self.complex_dtype)

sig_shape = signal.shape
res_shape = self.num_pts
Expand Down
7 changes: 4 additions & 3 deletions src/aspire/numeric/scipy.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@

def cg(*args, **kwargs):
"""
Supports scipy cg before and after 1.14.0.
Supports scipy cg before and after 1.12.0.
"""

# older scipy cg interface uses `tol` instead of `rtol`
if Version(scipy.__version__) < Version("1.14.0"):
# older (<1.12.0) scipy cg interface uses `tol` instead of `rtol`.
# `tol` will be removed in scipy 1.14.0.
if Version(scipy.__version__) < Version("1.12.0"):
kwargs["tol"] = kwargs.pop("rtol", None)
return scipy.sparse.linalg.cg(*args, **kwargs)
2 changes: 1 addition & 1 deletion src/aspire/sinogram/sinogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def __init__(self, data, dtype=None):
self.shape = self._data.shape
self.stack_shape = self._data.shape[:-2]
self.stack_n_dim = self._data.ndim - 2
self.n = np.product(self.stack_shape)
self.n = np.prod(self.stack_shape)
self.n_angles = self._data.shape[-2]
self.n_radial_points = self._data.shape[-1]

Expand Down
3 changes: 2 additions & 1 deletion src/aspire/utils/resolution_estimation.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,8 @@ def plot(self, cutoff=None, save_to_file=False, labels=None):
plt.ylabel("Correlation")
plt.ylim([0, 1.1])
for i, line in enumerate(self.correlations):
_label = None
# Set default label for single correlation (required by plt.legend() below).
_label = "correlation"
if len(self.correlations) > 1:
_label = f"{i}"
if labels is not None:
Expand Down
4 changes: 4 additions & 0 deletions src/aspire/utils/rotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,10 @@ def angle_dist(r1, r2, dtype=None):
theta = (tr_r[non_zero_dist_ind] - 1) / 2
theta = np.maximum(np.minimum(theta, 1), -1) # Clamp theta in [-1,1]
dist[non_zero_dist_ind] = np.arccos(theta, dtype=dtype)

# Return scalar for single value.
if dist.size == 1:
dist = dist.flat[0]
return dist

@staticmethod
Expand Down
7 changes: 4 additions & 3 deletions tests/test_FLEbasis2D.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,22 +230,23 @@ def testLowPass():
def testRadialConvolution():
# test ability to accurately convolve with a radial
# (e.g. CTF) function via FLE coefficients

L = 32
basis = FLEBasis2D(L, match_fb=False)

# load test radial function
x = np.load(os.path.join(DATA_DIR, "fle_radial_fn_32x32.npy")).reshape(1, 32, 32)
x = x / np.max(np.abs(x.flatten()))

# get sample images
ims = create_images(L, 10)

# convolve using coefficients
basis = FLEBasis2D(L, match_fb=False, dtype=ims.dtype)
coefs = basis.evaluate_t(ims)
coefs_convolved = basis.radial_convolve(coefs, x)
imgs_convolved_fle = basis.evaluate(coefs_convolved).asnumpy()

# convolve using FFT
x = basis.evaluate(basis.evaluate_t(x)).asnumpy()
x = basis.evaluate(basis.evaluate_t(Image(x))).asnumpy()
ims = basis.evaluate(coefs).asnumpy()

imgs_convolved_slow = np.zeros((10, L, L))
Expand Down
20 changes: 18 additions & 2 deletions tests/test_covar2d_denoiser.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,28 @@
RadialCTFFilter(5, 200, defocus=d, Cs=2.0, alpha=0.1)
for d in np.linspace(1.5e4, 2.5e4, 7)
]

# For (F)PSWFBasis2D we get off-block entries which are truncated
# when converting to block-diagonal. We filter these warnings.
BASIS = [
pytest.param(FBBasis2D, marks=pytest.mark.expensive),
FFBBasis2D,
FLEBasis2D,
pytest.param(PSWFBasis2D, marks=pytest.mark.expensive),
FPSWFBasis2D,
pytest.param(
PSWFBasis2D,
marks=[
pytest.mark.expensive,
pytest.mark.filterwarnings(
"ignore:BlkDiagMatrix.from_dense truncating values*"
),
],
),
pytest.param(
FPSWFBasis2D,
marks=pytest.mark.filterwarnings(
"ignore:BlkDiagMatrix.from_dense truncating values*"
),
),
]


Expand Down
32 changes: 16 additions & 16 deletions tests/test_diag_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def test_repr():
Test accessing the `repr` does not crash.
"""

d = DiagMatrix(np.empty((10, 8)))
d = DiagMatrix(np.ones((10, 8)))
assert repr(d).startswith("DiagMatrix(")


Expand All @@ -86,7 +86,7 @@ def test_str():
Test accessing the `str` does not crash.
"""

d = DiagMatrix(np.empty((10, 8)))
d = DiagMatrix(np.ones((10, 8)))
assert str(d).startswith("DiagMatrix(")


Expand All @@ -104,13 +104,13 @@ def test_len():
"""
Test the `len`.
"""
d = DiagMatrix(np.empty((10, 8)))
d = DiagMatrix(np.ones((10, 8)))

assert d.size == 10
assert d.count == 8
assert len(d) == 10

d = DiagMatrix(np.empty((2, 5, 8)))
d = DiagMatrix(np.ones((2, 5, 8)))

assert d.size == 10
assert d.count == 8
Expand All @@ -121,8 +121,8 @@ def test_size_mismatch():
"""
Test we raise operating on `DiagMatrix` having different counts.
"""
d1 = DiagMatrix(np.empty((10, 8)))
d2 = DiagMatrix(np.empty((10, 7)))
d1 = DiagMatrix(np.ones((10, 8)))
d2 = DiagMatrix(np.ones((10, 7)))

with pytest.raises(RuntimeError, match=r".*not same dimension.*"):
_ = d1 + d2
Expand All @@ -132,8 +132,8 @@ def test_dtype_mismatch():
"""
Test we raise operating on `DiagMatrix` having different dtypes.
"""
d1 = DiagMatrix(np.empty((10, 8)), dtype=np.float32)
d2 = DiagMatrix(np.empty((10, 8)), dtype=np.float64)
d1 = DiagMatrix(np.ones((10, 8)), dtype=np.float32)
d2 = DiagMatrix(np.ones((10, 8)), dtype=np.float64)

with pytest.raises(RuntimeError, match=r".*received different types.*"):
_ = d1 + d2
Expand All @@ -144,7 +144,7 @@ def test_dtype_passthrough():
Test that the datatype is inferred correctly.
"""
for dtype in (int, np.float32, np.float64, np.complex64, np.complex128):
d_np = np.empty(42, dtype=dtype)
d_np = np.ones(42, dtype=dtype)
d = DiagMatrix(d_np)
assert d.dtype == dtype

Expand All @@ -154,7 +154,7 @@ def test_dtype_cast():
Test that a datatype is cast when overridden.
"""
for dtype in (int, np.float32, np.float64, np.complex64, np.complex128):
d_np = np.empty(42, dtype=np.float16)
d_np = np.ones(42, dtype=np.float16)
d = DiagMatrix(d_np, dtype)
assert d.dtype == dtype

Expand Down Expand Up @@ -444,7 +444,7 @@ def test_diag_badtype_matmul():
"""
Test matrix multiply of `DiagMatrix` with incompatible type raises.
"""
d1 = DiagMatrix(np.empty(8))
d1 = DiagMatrix(np.ones(8))

# matmul
with pytest.raises(RuntimeError, match=r".*not implemented for.*"):
Expand Down Expand Up @@ -576,7 +576,7 @@ def test_bad_as_blk_diag(matrix_size, blk_diag):
"""
with pytest.raises(RuntimeError, match=r".*only implemented for singletons.*"):
# Construct via Numpy.
d_np = np.empty((2, matrix_size), dtype=blk_diag.dtype)
d_np = np.ones((2, matrix_size), dtype=blk_diag.dtype)

# Create DiagMatrix then convert to BlkDiagMatrix
d = DiagMatrix(d_np)
Expand Down Expand Up @@ -654,7 +654,7 @@ def test_diag_blk_mul():
"""
Test mixing `BlkDiagMatrix` with `DiagMatrix` element-wise multiplication raises.
"""
d = DiagMatrix(np.empty(8))
d = DiagMatrix(np.ones(8))

partition = [(4, 4), (4, 4)]
b = BlkDiagMatrix.ones(partition, dtype=d.dtype)
Expand All @@ -672,7 +672,7 @@ def test_non_square_as_blk_diag():
"""
Test non square partition blocks raise an error in as_blk_diag.
"""
d = DiagMatrix(np.empty(8))
d = DiagMatrix(np.ones(8))

partition = [(4, 5), (4, 3)]
with pytest.raises(RuntimeError, match=r".*not square.*"):
Expand All @@ -683,8 +683,8 @@ def test_bad_broadcast():
"""
Test incompatible stack shapes raise appropriate error.
"""
d1 = DiagMatrix(np.empty((2, 3, 8)))
d2 = DiagMatrix(np.empty((2, 2, 8)))
d1 = DiagMatrix(np.ones((2, 3, 8)))
d2 = DiagMatrix(np.ones((2, 2, 8)))

with pytest.raises(ValueError, match=r".*incompatible shapes.*"):
_ = d1 + d2
18 changes: 9 additions & 9 deletions tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,34 +353,34 @@ def test_asnumpy_readonly():
vw[0, 0, 0] = 123


@pytest.mark.xfail(reason="Ray logging issue ray#37711", strict=False)
def test_corrupt_mrc_load(caplog):
"""
Test that corrupt mrc files are logged as expected.
"""

caplog.set_level(logging.WARNING)

# Create a tmp dir for this test output
with tempfile.TemporaryDirectory() as tmpdir_name:
# tmp filename
mrc_path = os.path.join(tmpdir_name, "bad.mrc")

# Create and save image
Image(np.empty((1, 8, 8), dtype=np.float32)).save(mrc_path)
Image(np.ones((1, 8, 8), dtype=np.float32)).save(mrc_path)

# Open mrc file and soft corrupt it
with mrcfile.open(mrc_path, "r+") as fh:
fh.header.map = -1

# Check that we get a WARNING
_ = Image.load(mrc_path)
with caplog.at_level(logging.WARNING):
_ = Image.load(mrc_path)

# Check the message prefix
assert f"Image.load of {mrc_path} reporting 1 corruptions" in caplog.text

# Check the message prefix
assert f"Image.load of {mrc_path} reporting 1 corruptions" in caplog.text
# Check the message contains the file path
assert mrc_path in caplog.text

# Check the message contains the file path
assert mrc_path in caplog.text
caplog.clear()


def test_load_bad_ext():
Expand Down
2 changes: 1 addition & 1 deletion tests/test_micrograph_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def test_rectangular_micrograph_source_files():
"""

# Test inconsistent mrc files
imgs = [np.empty((7, 7)), np.empty((8, 8))]
imgs = [np.zeros((7, 7)), np.zeros((8, 8))]
with tempfile.TemporaryDirectory() as tmp_output_dir:
# Save the files
for i, img in enumerate(imgs):
Expand Down
3 changes: 3 additions & 0 deletions tests/test_rotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ def test_angle_dist(dtype):
with pytest.raises(ValueError, match=r"r1 and r2 are not broadcastable*"):
_ = Rotation.angle_dist(rots[:3], rots[:5])

# Test that single value returns as 0-dim.
assert Rotation.angle_dist(rots[0], rots[1], dtype).ndim == 0


def test_mean_angular_distance(dtype):
rots_z = Rotation.about_axis("z", [0, np.pi / 4, np.pi / 2], dtype=dtype).matrices
Expand Down
2 changes: 1 addition & 1 deletion tests/test_sinogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def test_project_multidim(num_ang):

# Generate a mask
g = grid_2d(L, normalized=True, shifted=True)
mask = g["r"] < 1
mask = g["r"] < 0.99

# Generate images
imgs = Image(np.random.random((m, n, L, L))) * mask
Expand Down
3 changes: 3 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,9 @@ def matplotlib_no_gui():

yield

# Explicitly close all figures before making backend changes.
matplotlib.pyplot.close("all")

# Restore backend
matplotlib.use(backend)

Expand Down
6 changes: 0 additions & 6 deletions tests/test_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -846,12 +846,6 @@ def test_aglebraic_ops_symmetry_warnings(symmetric_vols):
# Should have 4 warnings on record.
assert len(record) == 4

# Check that warning occurs only once per line.
with warnings.catch_warnings(record=True) as record:
for _ in range(5):
vol_c3 + vol_c4
assert len(record) == 1


def test_volume_load_with_symmetry():
# Check we can load a Volume with symmetry_group.
Expand Down