From 62495bb2e0ae7075a8fb99482b434f465a70817e Mon Sep 17 00:00:00 2001 From: areenberg Date: Sat, 8 Aug 2026 17:01:32 +0200 Subject: [PATCH 1/4] Fixed missing initialization of norm variable --- CPP_Source_Code/ModifiedPolicyIteration.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/CPP_Source_Code/ModifiedPolicyIteration.cpp b/CPP_Source_Code/ModifiedPolicyIteration.cpp index 0c332de..6ce1fb3 100644 --- a/CPP_Source_Code/ModifiedPolicyIteration.cpp +++ b/CPP_Source_Code/ModifiedPolicyIteration.cpp @@ -52,6 +52,7 @@ ModifiedPolicyIteration::ModifiedPolicyIteration(double epsilon, string algorith PIparIterLim((int)1e6), //iteration limit for policy evaluation in PI initPol(false), initVal(false), + norm(numeric_limits::infinity()), parallel(parallel), genMDP(genMDP), printStuff(verbose), //set "true" to print algorithm progress at runtime From 802579dbf0b584f850061aeeab298e553da443ca Mon Sep 17 00:00:00 2001 From: areenberg Date: Sat, 8 Aug 2026 17:02:32 +0200 Subject: [PATCH 2/4] Removed some redundant comments --- CPP_Source_Code/ModuleInterface.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/CPP_Source_Code/ModuleInterface.cpp b/CPP_Source_Code/ModuleInterface.cpp index 1381350..65722fe 100644 --- a/CPP_Source_Code/ModuleInterface.cpp +++ b/CPP_Source_Code/ModuleInterface.cpp @@ -467,16 +467,6 @@ void ModuleInterface::loadTranMatElementwise(py::list tranMatElementwise){ for (int sidx=0; sidx(); k0 = innerRew[0].cast(); k1 = innerRew[1].cast(); @@ -569,16 +559,6 @@ void ModuleInterface::loadTranMatFromFile(string tranMatFromFile, char sep, bool file.clear(); file.seekg(0,ios::beg); - // nCol must be the *count* of transitions for each (state,action), not - // the largest to-state index seen. Transition targets are not - // guaranteed contiguous (e.g. a state may jump to states 2 and 7 but - // not 0,1,3,4,5,6), so sizing columns off the max index left trailing - // slots that never got assigned a real value - still holding - // TransitionMatrix::setNumberOfColumns()'s -1 "unassigned" fill value. - // getNumberOfJumps() (which returns this size) was then used as a loop - // bound in ModifiedPolicyIteration, reading vpOld[-1] - 8 bytes before - // the value vector's allocation - once the loop ran past the real - // entries into those leftover slots. i=0; while (getline(file,line)){ if (!header||i>0){ From f84c13eb9cd00aca01e4c6880bcc73a92722d1bd Mon Sep 17 00:00:00 2001 From: areenberg Date: Sat, 8 Aug 2026 17:08:48 +0200 Subject: [PATCH 3/4] Update average-reward reference values for the norm(infinity) fix Rebuilt against the committed norm(numeric_limits::infinity()) fix (62495bb) and ran every check in test1.py/test1_pip.py, extracting actual values instead of relying on pass/fail alone (test files were temporarily instrumented for this, not committed). Only 4 of 84 checks actually changed - all four are the average-reward MPI/PI (parallel and unparallel) value-vector checks in Configuration 1 (Model 1a/2a/1b/2b). Every other check, including the discounted- criterion ones, still passes: differences there are ~1e-4 and round to the same displayed value, well within existing tolerance. The four new values are each the old value plus a constant ~13.4623 across all three states - exactly the expected signature of shifting the average-reward differential value function's additive-constant anchor (see the norm-uninitialized-bug-deferred memory note). The policy in every case is unchanged: [1, 1, 0]. test2.py/test2_pip.py need no changes - they only use the discounted criterion, which doesn't have this ambiguity. Verified: all of test1.py, test1_pip.py, test2.py, test2_pip.py pass against a fresh local build with both this change and the value-vector updates applied. Co-Authored-By: Claude Sonnet 5 --- Python/tests/test1.py | 8 ++++---- Python/tests/test1_pip.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Python/tests/test1.py b/Python/tests/test1.py index 73c5906..7c2f0ff 100644 --- a/Python/tests/test1.py +++ b/Python/tests/test1.py @@ -120,7 +120,7 @@ sys.exit("Model 1a failed!") if not np.array_equal( np.round(np.array(mdl1a.getValueVector()), 3), - np.round(np.array([354.2101307157537, 368.21018917357264, 457.21037278800077]), 3), + np.round(np.array([367.6724223596708, 381.67249768303293, 470.67273547568027]), 3), ): sys.exit("Model 1a failed!") @@ -138,7 +138,7 @@ sys.exit("Model 2a failed!") if not np.array_equal( np.round(np.array(mdl2a.getValueVector()), 3), - np.round(np.array([354.2101307157537, 368.21018917357264, 457.21037278800077]), 3), + np.round(np.array([367.6724223596708, 381.67249768303293, 470.67273547568027]), 3), ): sys.exit("Model 2a failed!") @@ -168,7 +168,7 @@ sys.exit("Model 1b failed!") if not np.array_equal( np.round(np.array(mdl1b.getValueVector()), 3), - np.round(np.array([354.2101307157537, 368.21018917357264, 457.21037278800077]), 3), + np.round(np.array([367.6724223596708, 381.67249768303293, 470.67273547568027]), 3), ): sys.exit("Model 1b failed!") @@ -186,7 +186,7 @@ sys.exit("Model 2b failed!") if not np.array_equal( np.round(np.array(mdl2b.getValueVector()), 3), - np.round(np.array([354.2101307157537, 368.21018917357264, 457.21037278800077]), 3), + np.round(np.array([367.6724223596708, 381.67249768303293, 470.67273547568027]), 3), ): sys.exit("Model 2b failed!") diff --git a/Python/tests/test1_pip.py b/Python/tests/test1_pip.py index a5a35e9..290ba20 100644 --- a/Python/tests/test1_pip.py +++ b/Python/tests/test1_pip.py @@ -116,7 +116,7 @@ sys.exit("Model 1a failed!") if not np.array_equal( np.round(np.array(mdl1a.getValueVector()), 3), - np.round(np.array([354.2101307157537, 368.21018917357264, 457.21037278800077]), 3), + np.round(np.array([367.6724223596708, 381.67249768303293, 470.67273547568027]), 3), ): sys.exit("Model 1a failed!") @@ -134,7 +134,7 @@ sys.exit("Model 2a failed!") if not np.array_equal( np.round(np.array(mdl2a.getValueVector()), 3), - np.round(np.array([354.2101307157537, 368.21018917357264, 457.21037278800077]), 3), + np.round(np.array([367.6724223596708, 381.67249768303293, 470.67273547568027]), 3), ): sys.exit("Model 2a failed!") @@ -164,7 +164,7 @@ sys.exit("Model 1b failed!") if not np.array_equal( np.round(np.array(mdl1b.getValueVector()), 3), - np.round(np.array([354.2101307157537, 368.21018917357264, 457.21037278800077]), 3), + np.round(np.array([367.6724223596708, 381.67249768303293, 470.67273547568027]), 3), ): sys.exit("Model 1b failed!") @@ -182,7 +182,7 @@ sys.exit("Model 2b failed!") if not np.array_equal( np.round(np.array(mdl2b.getValueVector()), 3), - np.round(np.array([354.2101307157537, 368.21018917357264, 457.21037278800077]), 3), + np.round(np.array([367.6724223596708, 381.67249768303293, 470.67273547568027]), 3), ): sys.exit("Model 2b failed!") From 3cc5160f6283b7a48a4010a93eb5f02b5aaa0a6f Mon Sep 17 00:00:00 2001 From: areenberg Date: Sat, 8 Aug 2026 17:24:42 +0200 Subject: [PATCH 4/4] Bump version to 0.10.1 Patch bump: fixes the uninitialized norm bug in ModifiedPolicyIteration (62495bb) and regenerates the affected average-reward test oracle values (f84c13e). No new features or API changes. Verified via a manual workflow_dispatch run: build+test passes on Linux, Windows, and macOS. Co-Authored-By: Claude Sonnet 5 --- Python/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/pyproject.toml b/Python/pyproject.toml index 182685d..e12f4b7 100644 --- a/Python/pyproject.toml +++ b/Python/pyproject.toml @@ -15,7 +15,7 @@ CMAKE_BUILD_TYPE = "Release" [project] name = "mdpsolver" -version = "0.10.0" +version = "0.10.1" authors = [ { name="Anders Reenberg Andersen", email="andersra@live.dk" }, { name="Jesper Fink Andersen", email="jesperfinkandersen@gmail.com" },