Skip to content

Commit 45c5f5c

Browse files
authored
Merge pull request #1 from USACE-RMC/code-review-fixes-and-enhancements
Code review fixes + launch readiness for public release
2 parents 462147a + a0ddf5e commit 45c5f5c

27 files changed

Lines changed: 3182 additions & 2201 deletions

.gitignore

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,12 +398,23 @@ FodyWeavers.xsd
398398

399399
# VS Code files for those working on multiple tools
400400
.vscode/*
401-
!.vscode/settings.json
402401
!.vscode/tasks.json
403402
!.vscode/launch.json
404-
!.vscode/extensions.json
405403
!.vscode/*.code-snippets
406404

405+
# Claude Code session state
406+
.claude/
407+
408+
# Local NuGet package install location (populated by `nuget install -OutputDirectory packages`)
409+
packages/
410+
411+
# Python virtual environments
412+
.venv/
413+
venv/
414+
415+
# Jupyter checkpoint snapshots
416+
**/.ipynb_checkpoints/
417+
407418
# Local History for Visual Studio Code
408419
.history/
409420

.vscode/extensions.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

.vscode/settings.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

CITATION.cff

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ cff-version: 1.2.0
22
message: "If you use this software, please cite it using the information below."
33
type: software
44
title: "Numerics-Python-Examples"
5-
version: 0.0.0
6-
repository-code:
7-
date-released: "2026-03-30"
5+
version: "1.0.0"
6+
repository-code: "https://github.com/USACE-RMC/Numerics-Python-Examples"
7+
date-released: "2026-04-23"
88
license: BSD-3-Clause
99
url: "https://github.com/USACE-RMC/Numerics-Python-Examples.git"
1010
abstract: >-
@@ -26,9 +26,10 @@ authors:
2626
affiliation: "U.S. Army Corps of Engineers, Risk Management Center"
2727
orcid: "https://orcid.org/0009-0008-8588-4816"
2828
- family-names: "Gonzalez"
29-
given-names: "Tiki"
29+
given-names: "Julian"
3030
email: "julian.t.gonzalez@usace.army.mil"
3131
affiliation: "U.S. Army Corps of Engineers, Risk Management Center"
32+
orcid: "https://orcid.org/0009-0009-9058-7653"
3233
- family-names: "Smith"
3334
given-names: "C. Haden"
3435
email: "cole.h.smith@usace.army.mil"

CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ Given the life-safety applications of Numerics, which this demo is an extension
3737
Pull requests may take several weeks or longer to review. Before submitting code:
3838

3939
1. **Open an issue first** to discuss the proposed change
40-
2. **Follow the coding standards**, including XML documentation on all public types and members
41-
3. **Include unit tests** that validate against known results (R, SciPy, Mathematica, or published tables)
42-
5. **Ensure a clean build** with zero errors and zero warnings
40+
2. **Follow PEP 8** and add docstrings to new functions and modules; clear notebook outputs before submitting (`jupyter nbconvert --clear-output --inplace notebooks/*.ipynb`)
41+
3. **Include validation** against known results (R, SciPy, Mathematica, or published tables) where applicable
42+
4. **Ensure notebooks run end to end** without errors in a fresh virtual environment
4343

4444
## Developer Certificate of Origin
4545

README.md

Lines changed: 62 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Numerics Python Examples
1+
# Numerics Python Examples
22

33
This repository contains Python notebooks that demonstrate the Numerics .NET library through pythonnet. The notebooks provide practical, reproducible examples of Numerics applications, including distribution fitting, MCMC, optimization, statistical analysis, time series analysis, machine learning, and linear model fitting.
44

@@ -22,60 +22,86 @@ This repository contains Python notebooks that demonstrate the Numerics .NET lib
2222
- `12_linear_models.ipynb` Linear/GLM workflows
2323

2424
## Prerequisites
25-
- Windows with .NET installed (or .NET 6+ runtime on Linux/macOS)
26-
- Python with [pythonnet](https://github.com/pythonnet/pythonnet)
27-
- Numerics built and compiled locally. The resulting DLL path must match the path referenced in the notebooks — see Quick Start and/or notebook 00 for setup instructions.
25+
- Python **3.10–3.13** recommended. pythonnet does not yet support Python 3.14.
26+
- .NET 6+ runtime (or .NET Framework 4.8.1 on Windows). Install the [.NET SDK](https://dotnet.microsoft.com/download) if you don't already have it.
27+
- The [RMC.Numerics](https://www.nuget.org/packages/RMC.Numerics) NuGet package (see Quick Start).
2828

2929
## Quick Start
30-
The quick start will step through the creation of an active python environment, installing the notebook requirements & RMC-Numerics, and getting the methods ready to be used. For a more in depth walk through see notebook 00.
31-
**NOTE:** This set up is geared towards Windows users. For other operating systems see notebook 00.
30+
The quick start will walk you through creating a virtual Python environment, installing the notebook requirements, and pulling in the `RMC.Numerics` NuGet package. For a more in-depth walkthrough see notebook [`00_getting_started.ipynb`](notebooks/00_getting_started.ipynb).
31+
**NOTE:** The commands below assume Windows. See notebook `00` for macOS/Linux equivalents.
3232

3333
1. Create and activate a virtual Python environment
34-
```bash
35-
python -m venv .venv
36-
.venv\Scripts\Activate.ps1
37-
pip install ipykernel
38-
python -m ipykernel install --user --name=.venv --display-name "Python (.venv)"
39-
```
4034

41-
2. Install notebook requirements
42-
```bash
43-
pip install -r notebook-requirements.txt
44-
```
35+
```bash
36+
python -m venv .venv
37+
.venv\Scripts\Activate.ps1
38+
pip install ipykernel
39+
python -m ipykernel install --user --name=.venv --display-name "Python (.venv)"
40+
```
41+
42+
2. Install the Python requirements
43+
44+
```bash
45+
pip install -r notebook-requirements.txt
46+
```
47+
48+
3. Install the `RMC.Numerics` NuGet package
49+
50+
```bash
51+
# Option A — global NuGet cache (recommended; requires the .NET SDK):
52+
dotnet add package RMC.Numerics
53+
54+
# Option B — local packages/ folder (requires nuget.exe on PATH):
55+
nuget install RMC.Numerics -OutputDirectory packages
56+
```
57+
58+
Both commands pull the **latest** published version by default. This demo was built against version `2.0.1`; to pin that version, append `--version 2.0.1` (Option A) or `-Version 2.0.1` (Option B).
59+
60+
The notebooks auto-discover the DLL in either location via `resolve_numerics_dll()` in [`notebooks/helper_functions.py`](notebooks/helper_functions.py).
61+
62+
4. Load Numerics in a notebook or script
63+
64+
```python
65+
import pythonnet
66+
pythonnet.load("coreclr")
67+
68+
import clr
69+
from helper_functions import resolve_numerics_dll
70+
clr.AddReference(str(resolve_numerics_dll()))
71+
```
72+
73+
5. Create a Normal distribution
74+
75+
```python
76+
from Numerics.Distributions import Normal
77+
dist = Normal(100, 15)
78+
```
79+
80+
## Using a local Numerics build instead of NuGet
81+
82+
If you prefer to build Numerics from source — for example, to develop against the latest `main` branch — clone the [Numerics](https://github.com/USACE-RMC/Numerics) repo and build it:
4583

46-
3. Install and build Numerics
4784
```bash
4885
git clone https://github.com/USACE-RMC/Numerics.git
4986
cd Numerics
5087
dotnet build Numerics.sln --configuration Release
5188
```
5289

53-
4. Load and confirm the DLL path
54-
```bash
55-
import pythonnet
56-
pythonnet.load("coreclr")
90+
Then point the notebooks at your build by setting the `NUMERICS_DLL` environment variable before launching Jupyter:
5791

58-
import clr
59-
from pathlib import Path
92+
```powershell
93+
# PowerShell
94+
$env:NUMERICS_DLL = "C:\path\to\Numerics\Numerics\bin\Release\net8.0\Numerics.dll"
6095
61-
# Path to your Numerics.dll
62-
# MODIFY THIS PATH to make your installation
63-
dll_path = Path(r"C:\GIT\Numerics\Numerics\bin\Debug\net8.0\Numerics.dll")
64-
clr.AddReference(str(dll_path))
96+
# bash / zsh
97+
export NUMERICS_DLL=/path/to/Numerics/Numerics/bin/Release/net8.0/Numerics.dll
6598
```
6699

67-
5. Create a Normal Distribution
68-
```bash
69-
from Numerics.Distributions import Normal
70-
dist = Normal(100,15)
71-
```
100+
`resolve_numerics_dll()` uses this variable first, then falls back to the NuGet cache and finally a local `packages/` folder.
72101

73102
## Notes
74103
- These notebooks compare Numerics to common Python libraries where relevant. When comparing MCMC chains, align warmup/thinning settings.
75104
- Many examples use synthetic data to keep results consistent and easy to interpret.
76105

77-
## How To Update DLL Path
78-
If your Numerics build output is in a different location, update the DLL path in the setup cell of each notebook. Search for `Numerics.dll` and replace the path with your local build output.
79-
80106
## License
81-
See the Numerics project license for usage and distribution terms.
107+
This project is released under the [USACE-RMC license](LICENSE). It is compatible with the parent [Numerics project license](https://github.com/USACE-RMC/Numerics/blob/main/LICENSE).

examples/bayesian_regression.py

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
and tables will be output to the terminal.
44
"""
55

6+
import os
67
from pathlib import Path
78

89
import matplotlib.pyplot as plt
@@ -14,12 +15,29 @@
1415
import clr
1516

1617

18+
def _resolve_numerics_dll():
19+
"""Resolve Numerics.dll from NUMERICS_DLL env var, the NuGet cache, or a local packages/ folder."""
20+
env = os.environ.get("NUMERICS_DLL")
21+
if env:
22+
return Path(env)
23+
cache = Path.home() / ".nuget" / "packages" / "rmc.numerics"
24+
if cache.exists():
25+
hits = sorted(cache.glob("*/lib/net8.0/Numerics.dll"), reverse=True)
26+
if hits:
27+
return hits[0]
28+
for root in (Path.cwd(), Path(__file__).parent.parent):
29+
local = sorted((root / "packages").glob("RMC.Numerics.*/lib/net8.0/Numerics.dll"), reverse=True)
30+
if local:
31+
return local[0]
32+
raise FileNotFoundError(
33+
"Numerics DLL not found. Install via `dotnet add package RMC.Numerics` "
34+
"(pulls latest; append `--version 2.0.1` to pin) or set the NUMERICS_DLL "
35+
"environment variable."
36+
)
37+
38+
1739
def load_numerics():
18-
dll_path = Path(r"C:\GIT\Numerics\Numerics\bin\Debug\net8.0\Numerics.dll")
19-
if not dll_path.exists():
20-
raise FileNotFoundError(
21-
f"Numerics DLL not found at {dll_path}. Update `dll_path` in this script."
22-
)
40+
dll_path = _resolve_numerics_dll()
2341
clr.AddReference(str(dll_path))
2442

2543

@@ -29,21 +47,13 @@ def main(seed=123):
2947
from Numerics.Distributions import IUnivariateDistribution, Normal, Uniform
3048
from Numerics.Sampling.MCMC import DEMCzs, LogLikelihood, MCMCResults
3149
from System.Collections.Generic import List
32-
from System.Threading import ThreadPool
33-
import os
34-
35-
''' NOTE FOR DEMO USERS:
36-
When calling Numerics MCMC samplers from Python via pythonnet, the samplers'
37-
internal parallel chains (Parallel.For) contend for Python's Global Interpreter
38-
Lock (GIL). This makes parallel execution slower than sequential. Setting
39-
max thread pool workers to 1 forces sequential execution and removes that
40-
overhead. In pure C#, you can remove this line and parallelism will work as
41-
intended.
42-
43-
This is also why we set sampler.ParallelizeChains = False for every example.
44-
It defaults to True, which works well in C#, but it slows the sampler down
45-
in Python'''
46-
ThreadPool.SetMaxThreads(1, 1) # (workerThreads, completionPortThreads)
50+
51+
# NOTE FOR DEMO USERS:
52+
# When calling Numerics MCMC samplers from Python via pythonnet, the samplers'
53+
# internal parallel chains (Parallel.For) contend for Python's Global Interpreter
54+
# Lock (GIL). This makes parallel execution slower than sequential. This is why
55+
# we set sampler.ParallelizeChains = False below. It defaults to True, which
56+
# works well in C#, but it slows the sampler down when driven from Python.
4757

4858
x = np.linspace(0, 10, 80)
4959
true_a, true_b, true_sigma = 2.0, 1.4, 1.2

examples/flood_frequency_analysis.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
and tables will be output to the terminal.
33
"""
44

5+
import os
56
from pathlib import Path
67

78
import matplotlib.pyplot as plt
@@ -13,12 +14,29 @@
1314
import clr
1415

1516

17+
def _resolve_numerics_dll():
18+
"""Resolve Numerics.dll from NUMERICS_DLL env var, the NuGet cache, or a local packages/ folder."""
19+
env = os.environ.get("NUMERICS_DLL")
20+
if env:
21+
return Path(env)
22+
cache = Path.home() / ".nuget" / "packages" / "rmc.numerics"
23+
if cache.exists():
24+
hits = sorted(cache.glob("*/lib/net8.0/Numerics.dll"), reverse=True)
25+
if hits:
26+
return hits[0]
27+
for root in (Path.cwd(), Path(__file__).parent.parent):
28+
local = sorted((root / "packages").glob("RMC.Numerics.*/lib/net8.0/Numerics.dll"), reverse=True)
29+
if local:
30+
return local[0]
31+
raise FileNotFoundError(
32+
"Numerics DLL not found. Install via `dotnet add package RMC.Numerics` "
33+
"(pulls latest; append `--version 2.0.1` to pin) or set the NUMERICS_DLL "
34+
"environment variable."
35+
)
36+
37+
1638
def load_numerics():
17-
dll_path = Path(r"C:\GIT\Numerics\Numerics\bin\Debug\net8.0\Numerics.dll")
18-
if not dll_path.exists():
19-
raise FileNotFoundError(
20-
f"Numerics DLL not found at {dll_path}. Update `dll_path` in this script."
21-
)
39+
dll_path = _resolve_numerics_dll()
2240
clr.AddReference(str(dll_path))
2341

2442

examples/reliability_analysis.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
"""Reliability analysis demo using Monte Carlo simulation. Once this file runs you will have a popup window of graphs
2-
and tables will be ouput to the terminal."""
1+
"""Reliability analysis demo using Monte Carlo simulation. Once this file runs you will have a popup window of graphs
2+
and tables will be output to the terminal."""
33

44
import math
5+
import os
56
from pathlib import Path
67

78
import matplotlib.pyplot as plt
@@ -14,12 +15,29 @@
1415
import clr
1516

1617

18+
def _resolve_numerics_dll():
19+
"""Resolve Numerics.dll from NUMERICS_DLL env var, the NuGet cache, or a local packages/ folder."""
20+
env = os.environ.get("NUMERICS_DLL")
21+
if env:
22+
return Path(env)
23+
cache = Path.home() / ".nuget" / "packages" / "rmc.numerics"
24+
if cache.exists():
25+
hits = sorted(cache.glob("*/lib/net8.0/Numerics.dll"), reverse=True)
26+
if hits:
27+
return hits[0]
28+
for root in (Path.cwd(), Path(__file__).parent.parent):
29+
local = sorted((root / "packages").glob("RMC.Numerics.*/lib/net8.0/Numerics.dll"), reverse=True)
30+
if local:
31+
return local[0]
32+
raise FileNotFoundError(
33+
"Numerics DLL not found. Install via `dotnet add package RMC.Numerics` "
34+
"(pulls latest; append `--version 2.0.1` to pin) or set the NUMERICS_DLL "
35+
"environment variable."
36+
)
37+
38+
1739
def load_numerics():
18-
dll_path = Path(r"C:\GIT\Numerics\Numerics\bin\Debug\net8.0\Numerics.dll")
19-
if not dll_path.exists():
20-
raise FileNotFoundError(
21-
f"Numerics DLL not found at {dll_path}. Update `dll_path` in this script."
22-
)
40+
dll_path = _resolve_numerics_dll()
2341
clr.AddReference(str(dll_path))
2442

2543

0 commit comments

Comments
 (0)