Skip to content

Commit 541d759

Browse files
authored
Merge pull request #7 from alextsfox/dev
Dev
2 parents 7374e87 + b2183e8 commit 541d759

7 files changed

Lines changed: 56 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,7 @@
1616
- fixed a bug preventing early exit in some situations during optimization when the convergence criterion is met
1717

1818
# 1.3.20251010
19-
- fixed critical bug preventing export to xarray datasets
19+
- fixed critical bug preventing export to xarray datasets
20+
21+
# 1.3.20251011
22+
- fixed a bug where optimization did not work for single-outlet networks

PyOCN/c_src/flowgrid.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,10 @@ FlowGrid *fg_copy(FlowGrid *G){
204204

205205
Status fg_destroy(FlowGrid *G){
206206
if (G != NULL){
207-
if (G->vertices != NULL) free(G->vertices); G->vertices = NULL;
207+
if (G->vertices != NULL){
208+
free(G->vertices);
209+
G->vertices = NULL;
210+
}
208211
free(G);
209212
}
210213
return SUCCESS;

PyOCN/c_src/ocn.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@
2222
* @param temperature The current temperature.
2323
* @return true if the new state is accepted, false otherwise.
2424
*/
25-
static inline bool simulate_annealing(double energy_new, double energy_old, double inv_temperature, rng_state_t *rng){
25+
static inline bool simulate_annealing(double energy_new, double energy_old, double temperature, rng_state_t *rng){
26+
const double accpt_prob = rng_uniformdouble(rng);
2627
const double delta_energy = energy_new - energy_old;
2728
if (delta_energy <= 0.0) return true; // Always accept improvements
28-
const double p = exp(-delta_energy * inv_temperature);
29-
return rng_uniformdouble(rng) < p;
29+
const double p = exp(-delta_energy / temperature);
30+
return accpt_prob < p;
3031
}
3132

3233
/**
@@ -97,8 +98,6 @@ Status ocn_single_erosion_event(FlowGrid *G, double gamma, double temperature){
9798
CartPair dims = G->dims;
9899
linidx_t nverts = (linidx_t)dims.row * (linidx_t)dims.col;
99100

100-
double inv_temperature = 1.0 / temperature;
101-
102101
double energy_old, energy_new;
103102
energy_old = G->energy;
104103

@@ -172,7 +171,7 @@ Status ocn_single_erosion_event(FlowGrid *G, double gamma, double temperature){
172171
update_drained_area(G, -da_inc, a_down_old); // remove drainage from old path
173172
update_drained_area(G, da_inc, a_down_new); // add drainage to new path
174173
energy_new = ocn_compute_energy(G, gamma); // recompute energy from scratch
175-
if (simulate_annealing(energy_new, energy_old, inv_temperature, &G->rng)){
174+
if (simulate_annealing(energy_new, energy_old, temperature, &G->rng)){
176175
G->energy = energy_new;
177176
return SUCCESS;
178177
}

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
project = 'PyOCN'
1515
copyright = '2025, Alexander S. Fox'
1616
author = 'Alexander S. Fox'
17-
release = '1.3.20251010'
17+
release = '1.3.20251011'
1818

1919
# -- General configuration ---------------------------------------------------
2020
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "PyOCN"
7-
version = "1.3.20251010"
7+
version = "1.3.20251011"
88
description = "Optimal Channel Networks (OCN) in Python with a C core"
99
readme = "readme.md"
1010
requires-python = ">=3.10"

sandbox.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
1-
from timeit import timeit
21

2+
import matplotlib.pyplot as plt
33
import PyOCN as po
4+
import numpy as np
45

56
ocn = po.OCN.from_net_type(
6-
net_type="V",
7-
dims=(74, 74),
8-
random_state=8472,
7+
net_type="E",
8+
dims=(16, 16),
9+
wrap=True,
10+
random_state=84712,
911
)
10-
print(timeit(ocn.fit, number=10)/10)
12+
ocn.fit_custom_cooling(lambda t: np.ones_like(t)*1e-1, pbar=True, n_iterations=16*16*100, max_iterations_per_loop=1)
13+
ocn.fit(max_iterations_per_loop=1)
14+
15+
16+
17+
# print(ocn.history.shape)
18+
# print(np.max(np.diff(ocn.history[:, 1])))
19+
# print(np.quantile(np.diff(ocn.history[:, 1]), 0.999))
20+
21+
plt.plot(ocn.history[:, 0], ocn.history[:, 1])
22+
plt.plot(ocn.history[:, 0], ocn.history[:, 2])
23+
# plt.xscale("log")
24+
# plt.yscale("log")
25+
plt.show()

tests/test_basic.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,27 @@ def test_error_handling(self):
252252
with self.assertRaises((ValueError, TypeError)):
253253
po.OCN.from_net_type("V", dims=(5,)) # Should need 2D dims
254254

255+
def test_greedy_optimization(self):
256+
ocn = po.OCN.from_net_type(
257+
net_type="V",
258+
dims=(16, 16),
259+
wrap=True,
260+
random_state=8472,
261+
)
262+
# use a temperature of 1e-7 since we were bitten before by accidentally using 1/temperature.
263+
# Using temperature=0 could lead to infinities, which might behave unexpectedly.
264+
# this way, if our mass is wrong and blows up somewhere, we know that we will catch it.
265+
ocn.fit_custom_cooling(lambda t: np.ones_like(t)*1e-7, pbar=False, n_iterations=16**2*100, max_iterations_per_loop=1)
266+
self.assertLessEqual(np.quantile(np.diff(ocn.history[:, 1]), 0.999) - 1e-7, 0, "Energy did not decrease monotonically.")
267+
268+
ocn = po.OCN.from_net_type(
269+
net_type="E",
270+
dims=(16, 16),
271+
wrap=True,
272+
random_state=8472,
273+
)
274+
ocn.fit_custom_cooling(lambda t: np.ones_like(t)*1e-7, pbar=False, n_iterations=16**2*100, max_iterations_per_loop=1)
275+
self.assertLessEqual(np.quantile(np.diff(ocn.history[:, 1]), 0.999) - 1e-7, 0, "Energy did not decrease monotonically.")
255276

256277
if __name__ == "__main__":
257278
unittest.main()

0 commit comments

Comments
 (0)