Add reproducible random sampling and many new pmag.py and ipmag.py tests#826
Conversation
… (2023) functions np.matrix is deprecated and broken in modern numpy, causing failures in common_mean_bootstrap_H23, mean_bootstrap_confidence, and reversal_test_bootstrap_H23. Changes in pmag.py: - form_Mhat, form_Ghat, form_Q, find_CMDT_CR, find_T, find_CR: replace np.matrix() with np.asarray(), .getT() with .T, .getH() with .conj().T, and matrix * with @ operator - find_T: add condition number guard for singular covariance matrices that arise from degenerate bootstrap samples Changes in ipmag.py: - common_mean_bootstrap_H23: replace .getH()* with .conj().T @ - mean_bootstrap_confidence: filter degenerate bootstrap samples before computing quantile - common_mean_watson: fix '%.1f' % array formatting (use angle[0]) - lat_from_pole: fix float() on 1-element array (use paleo_lat[0])
* Plot map fix (#810) * develop some more tests * update plot_map to return axes * implement axes fix to show two continents * new tests removed from this plot_map_fix branch * use elope_path instead of rewriting Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix orthographic typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * edits to PR --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: apivarunas <apiv93@gmail.com> * change from predetermined index for percentiles to direct percentile calculation in find_ei and find_ei_kent (#795) After reviewing this PR and concurring that it fixes the issue, merging. * remove duplication, fix docs, readability (#807) * simple style fixes via ruff (#812) * Working through errors / warnings in ipmag (#813) * slowly fixing errors and warnings * additional warning fixes * additional Exception clarity * clearing bare excepts * addressing review comments * adding ipmag tests * add to same file * added watson conglomerate tests * renamed file --------- Co-authored-by: Nick Swanson-Hysell <nicks-h@umn.edu> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ag.py and ipmag.py
There was a problem hiding this comment.
Pull request overview
This PR addresses issue #447 by adding an optional random_seed parameter (using the modern numpy.random.default_rng() API) to all stochastic functions in pmag.py, ipmag.py, and rockmag.py, and adds a comprehensive 369-test pytest suite covering core paleomagnetic functions. A _resolve_rng() helper is introduced in pmag.py to standardize seed handling across call chains.
Changes:
- Add
_resolve_rng()helper topmag.pyand convert 11 functions inpmag.py, 14 inipmag.py, and 1 inrockmag.pyto use it for reproducible random sampling - Add 369 tests across 24 new test files covering coordinate transforms, statistics, VGP, field models, E/I corrections, bootstrap tests, anisotropy, and data I/O
- Remove the old
test_fisher_sample.py(superseded bytest_ipmag_random.py)
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
pmagpy/pmag.py |
Adds _resolve_rng() and converts fshdev, kentdev, gaussdev, get_unf, pseudosample, apseudo, mktk03, pseudo, di_boot, dir_df_boot, s_boot to use it |
pmagpy/ipmag.py |
Converts fishrot, fisher_mean_resample, kentrot, tk03, bootstrap_fold_test, mean_bootstrap_confidence, common_mean_bootstrap, common_mean_bootstrap_H23, common_mean_watson, reversal_test_bootstrap, reversal_test_bootstrap_H23, find_svei_kent, plate_rate_mc, MADcrit (signature only) to accept random_seed |
pmagpy/rockmag.py |
Converts backfield_MaxUnmix bootstrap loop to use seeded RNG |
pytest.ini |
Configures test discovery and warning filters |
pmagpy/test/conftest.py |
Session-scoped fixture to force Agg matplotlib backend |
pmagpy/test/test_fisher_sample.py |
Removed (superseded by test_ipmag_random.py) |
pmagpy/test/test_pmag_*.py (10 files) |
New tests for pmag.py functions |
pmagpy/test/test_ipmag_*.py (10 files) |
New tests for ipmag.py functions |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… TypeError on None
…py and ipmag.py Add random_seed parameter to scalc_vgp_df, find_ei, find_ei_kent, find_compilation_kent, reversal_test_MM1990, simul_correlation_prob, and rand_correlation_prob. Thread random_seed through internal calls to pseudo, fishrot, fisher_mean_resample, and common_mean_watson. Also fix verbose parameter not being forwarded in reversal_test_bootstrap and correct 'indicies' typos.
|
These four commits address the Copilot review feedback and extend
Remaining unseeded stochastic functions in |
|
@apivarunas The tests you wrote in that previous 5 tests file are now spread across the new tests files. |
|
@Swanson-Hysell I've given this PR a once-over. Nice comprehensive random_seed implementation and test coverage! The tests ran in about 14.82 seconds on my machine. I made 2 commits to it:
If that's okay with you, I think you can go ahead and merge. Having this test coverage will be nice. |
|
Thanks for the review and commits on this @apivarunas! It looks like 8861e12 removed the legacy seed option from the docstring for the |
Summary
random_seedparameter to all functions inpmag.py,ipmag.py, androckmag.pythat use random number generation, using the modernnumpy.random.default_rng()API. Closes include the ability to set a random.seed() in each function that uses random or np.random() #447.Reproducible random sampling
All functions that use random number generation now accept an optional
random_seedparameter (approach 1 from #447 — default behavior unchanged, but reproducibility is available when needed). This will close #447 :Implementation uses a
_resolve_rng()helper inpmag.pythat acceptsNone(fresh entropy),int(seeded Generator), or aGeneratorinstance (passed through). All legacynp.random.*andrandom.*calls within converted functions are replaced withrng.*calls on the local Generator.Converted functions
pmag.py (11 functions):
fshdev,kentdev,gaussdev,get_unf,pseudosample,apseudo,mktk03,pseudo,di_boot,dir_df_boot,s_bootipmag.py (14 functions):
fishrot,fisher_mean_resample,kentrot,tk03,bootstrap_fold_test,mean_bootstrap_confidence,common_mean_bootstrap,common_mean_bootstrap_H23,common_mean_watson,reversal_test_bootstrap,reversal_test_bootstrap_H23,find_svei_kent,plate_rate_mc,MADcritrockmag.py (1 function):
backfield_MaxUnmixOut of scope (cosmetic/plotting random, not scientific)
Site.eq_plot/Site.eq_plot_everything(random RGB colors for plot styling)Test suite
369 tests across 24 test files, all passing. Organized by scientific topic with one pytest class per function.
test_pmag_directions.py,test_pmag_transforms.py,test_pmag_projection.pydir2cart,cart2dir,angle,dotilt,dogeo,doflip,flip,dimaptest_pmag_statistics.py,test_ipmag_statistics.py,test_pmag_kent_bingham.pyfisher_mean,doprinc,Tmatrix,tauV,dokent,dobingham,bingham_mean,kent_meantest_pmag_domean.pydomean(DE-FM, DE-BFL, DE-BFL-A, DE-BFP, DE-BFL-O)test_pmag_random.py,test_ipmag_random.pyfshdev,fishrot,fisher_mean_resample,kentrot,tk03test_pmag_vgp.py,test_ipmag_vgp.pypinc,plat,dia_vgp,vgp_di,b_vdm,vdm_b,vgp_calc,sb_vgp_calc,lat_from_inctest_igrf.py,test_pmag_field_models.pyipmag.igrf(IGRF14 reference values),pmag.doigrf(property-based + paleo models)test_ipmag_ei.pysquish,unsquish,f_factor_calc,find_f,find_eitest_ipmag_field_tests.py,test_ipmag_common_mean.pybootstrap_fold_test,conglomerate_test_Watson, reversal tests, common mean teststest_pmag_anisotropy.pys2a,a2s,doseigs,dosgeo,dostilttest_pmag_data_handling.py,test_pmag_io.py,test_pmag_utilities.py,test_ipmag_data_handling.pymagic_read,magic_write,get_dictitem,fillkeys,parse_site,make_di_blocktest_ipmag_plotting.pyplot_net,plot_di,fishqqtest_ipmag_correlation.pysimul_correlation_prob,rand_correlation_probTest design highlights
lat_from_incvsplat,vgp_calcvsdia_vgp)random_seedparameter rather than globalnp.random.seed()conftest.pywith Agg backend fixture,pytest.iniconfiguration