From f9393344bb673ac9d545fffece6778ccd8af6d76 Mon Sep 17 00:00:00 2001 From: Nicolas Boullis Date: Tue, 13 May 2025 10:11:37 +0200 Subject: [PATCH 01/12] Add an assertion to ensure that the random value drawn for SMS movement is compatible with the sum of the probablities of the neighbour cells. --- Individual.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Individual.cpp b/Individual.cpp index 345e827..4629888 100644 --- a/Individual.cpp +++ b/Individual.cpp @@ -1473,6 +1473,7 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, do { do { double rnd = pRandom->Random(); + assert(rnd < cumulative[8]); j = 0; for (y2 = 0; y2 < 3; y2++) { for (x2 = 0; x2 < 3; x2++) { From 1c22a855cee51868a339c754d3e20af7a54e9dca Mon Sep 17 00:00:00 2001 From: Nicolas Boullis Date: Tue, 13 May 2025 10:43:20 +0200 Subject: [PATCH 02/12] =?UTF-8?q?[SMS]=20Fix=20the=20calculation=20of=20th?= =?UTF-8?q?e=20cells=E2=80=99=20probabilities=20to=20ensure=20they=20reall?= =?UTF-8?q?y=20sum=20up=20to=201.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Individual.cpp | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/Individual.cpp b/Individual.cpp index 4629888..503d11a 100644 --- a/Individual.cpp +++ b/Individual.cpp @@ -1427,7 +1427,6 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, // set any cells beyond the current landscape limits and any no-data cells // to have zero probability - // increment total for re-scaling to sum to unity for (y2 = 2; y2 > -1; y2--) { for (x2 = 0; x2 < 3; x2++) { @@ -1441,31 +1440,28 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, if (pCell == 0) nbr.cell[x2][y2] = 0.0; // no-data cell } } - - sum_nbrs += nbr.cell[x2][y2]; - } - } - - // scale effective costs as probabilities summing to 1 - if (sum_nbrs > 0.0) { // should always be the case, but safest to check... - for (y2 = 2; y2 > -1; y2--) { - for (x2 = 0; x2 < 3; x2++) { - nbr.cell[x2][y2] = nbr.cell[x2][y2] / (float)sum_nbrs; - } } } - // set up cell selection probabilities + // sum up cell weights double cumulative[9]; int j = 0; - cumulative[0] = nbr.cell[0][0]; for (y2 = 0; y2 < 3; y2++) { for (x2 = 0; x2 < 3; x2++) { - if (j != 0) cumulative[j] = cumulative[j - 1] + nbr.cell[x2][y2]; + sum_nbrs += nbr.cell[x2][y2]; + cumulative[j] = sum_nbrs; j++; } } + // scale cumulative weights to convert them to cumulative probabilities + if (sum_nbrs > 0.0) { // should always be the case, but safest to check... + for (j = 0; j < 9; j++) { + cumulative[j] /= sum_nbrs; + } + assert(cumulative[8] == 1.); + } + // select direction at random based on cell selection probabilities // landscape boundaries and no-data cells may be reflective or absorbing cellcost = pCurrCell->getCost(); From ec311e27a49161895d34f56c5c376cfbc5ab553b Mon Sep 17 00:00:00 2001 From: Nicolas Boullis Date: Tue, 13 May 2025 11:06:25 +0200 Subject: [PATCH 03/12] [SMS] Add an assertion to ensure all cell weights are non-negative. --- Individual.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Individual.cpp b/Individual.cpp index 503d11a..18d2d50 100644 --- a/Individual.cpp +++ b/Individual.cpp @@ -1448,6 +1448,7 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, int j = 0; for (y2 = 0; y2 < 3; y2++) { for (x2 = 0; x2 < 3; x2++) { + assert(nbr.cell[x2][y2] >= 0); sum_nbrs += nbr.cell[x2][y2]; cumulative[j] = sum_nbrs; j++; From f578ca1ad83074fd9c3430abb84d8ec48e057b74 Mon Sep 17 00:00:00 2001 From: Nicolas Boullis Date: Tue, 13 May 2025 11:13:13 +0200 Subject: [PATCH 04/12] [SMS] If no neighbour cell is available to move, avoid to try 1000 times before deciding to flag the individual to die. --- Individual.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Individual.cpp b/Individual.cpp index 18d2d50..cd2ec5e 100644 --- a/Individual.cpp +++ b/Individual.cpp @@ -1462,6 +1462,13 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, } assert(cumulative[8] == 1.); } + else { + // unable to make a move + // flag individual to die + move.dist = -123.0; + pCurrCell = 0; + return move; + } // select direction at random based on cell selection probabilities // landscape boundaries and no-data cells may be reflective or absorbing From fbb36184b988b4c190a5afca79a8d49469c264b5 Mon Sep 17 00:00:00 2001 From: Nicolas Boullis Date: Tue, 13 May 2025 11:22:45 +0200 Subject: [PATCH 05/12] [SMS] Add an assertion to ensure a random neighbour cell was successfully chosen. --- Individual.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Individual.cpp b/Individual.cpp index cd2ec5e..2421a28 100644 --- a/Individual.cpp +++ b/Individual.cpp @@ -1491,6 +1491,7 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, j++; } } + assert((y2 == 1000) && (x2 == 1000)); loopsteps++; } while (loopsteps < 1000 && (!absorbing && (newX < land.minX || newX > land.maxX From 24f79c9c60b26c4e4e19e93bc50b8b17388650b6 Mon Sep 17 00:00:00 2001 From: Nicolas Boullis Date: Tue, 13 May 2025 11:30:34 +0200 Subject: [PATCH 06/12] [SMS] Add an assertion to ensure the chosen random neighbout is not outside reflective landscape boundaries. --- Individual.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Individual.cpp b/Individual.cpp index 2421a28..d398680 100644 --- a/Individual.cpp +++ b/Individual.cpp @@ -1492,6 +1492,8 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, } } assert((y2 == 1000) && (x2 == 1000)); + assert(absorbing || (newX >= land.minX && newX <= land.maxX + && newY >= land.minY && newY <= land.maxY)); loopsteps++; } while (loopsteps < 1000 && (!absorbing && (newX < land.minX || newX > land.maxX From c6764b9175cddab70102f949e8ab39c4095398b8 Mon Sep 17 00:00:00 2001 From: Nicolas Boullis Date: Tue, 13 May 2025 11:54:10 +0200 Subject: [PATCH 07/12] [SMS] Kill the inner loop, proven to be useless by the added assertions. --- Individual.cpp | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/Individual.cpp b/Individual.cpp index d398680..40e16e3 100644 --- a/Individual.cpp +++ b/Individual.cpp @@ -1475,29 +1475,25 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, cellcost = pCurrCell->getCost(); int loopsteps = 0; // new counter to prevent infinite loop added 14/8/15 do { - do { - double rnd = pRandom->Random(); - assert(rnd < cumulative[8]); - j = 0; - for (y2 = 0; y2 < 3; y2++) { - for (x2 = 0; x2 < 3; x2++) { - if (rnd < cumulative[j]) { - newX = current.x + x2 - 1; - newY = current.y + y2 - 1; - if (x2 == 1 || y2 == 1) move.dist = (float)(land.resol); - else move.dist = (float)(land.resol) * (float)SQRT2; - y2 = 999; x2 = 999; //to break out of x2 and y2 loops. - } - j++; + double rnd = pRandom->Random(); + assert(rnd < cumulative[8]); + j = 0; + for (y2 = 0; y2 < 3; y2++) { + for (x2 = 0; x2 < 3; x2++) { + if (rnd < cumulative[j]) { + newX = current.x + x2 - 1; + newY = current.y + y2 - 1; + if (x2 == 1 || y2 == 1) move.dist = (float)(land.resol); + else move.dist = (float)(land.resol) * (float)SQRT2; + y2 = 999; x2 = 999; //to break out of x2 and y2 loops. } + j++; } - assert((y2 == 1000) && (x2 == 1000)); - assert(absorbing || (newX >= land.minX && newX <= land.maxX - && newY >= land.minY && newY <= land.maxY)); - loopsteps++; - } while (loopsteps < 1000 - && (!absorbing && (newX < land.minX || newX > land.maxX - || newY < land.minY || newY > land.maxY))); + } + assert((y2 == 1000) && (x2 == 1000)); + assert(absorbing || (newX >= land.minX && newX <= land.maxX + && newY >= land.minY && newY <= land.maxY)); + loopsteps++; if (loopsteps >= 1000) pNewCell = 0; else { if (newX < land.minX || newX > land.maxX From ee4af45dd210bbb3e90d4f38cd2572c411e8c81b Mon Sep 17 00:00:00 2001 From: Nicolas Boullis Date: Tue, 13 May 2025 12:18:40 +0200 Subject: [PATCH 08/12] [SMS] Add an assertion to ensure the chosen random neighbour is not a reflective no-data cell. --- Individual.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Individual.cpp b/Individual.cpp index 40e16e3..7b3856f 100644 --- a/Individual.cpp +++ b/Individual.cpp @@ -1494,15 +1494,14 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, assert(absorbing || (newX >= land.minX && newX <= land.maxX && newY >= land.minY && newY <= land.maxY)); loopsteps++; - if (loopsteps >= 1000) pNewCell = 0; - else { - if (newX < land.minX || newX > land.maxX - || newY < land.minY || newY > land.maxY) { - pNewCell = 0; - } else{ - pNewCell = pLand->findCell(newX, newY); // would also return 0 if outside boundary - } + if (newX < land.minX || newX > land.maxX + || newY < land.minY || newY > land.maxY) { + pNewCell = 0; + } else { + pNewCell = pLand->findCell(newX, newY); // would also return 0 if outside boundary } + assert(absorbing || (pNewCell != 0)); + if (loopsteps >= 1000) pNewCell = 0; } while (!absorbing && pNewCell == 0 && loopsteps < 1000); // no-data cell if (loopsteps >= 1000 || pNewCell == 0 || (newX == -9 || newY== -9)) { // if no cell was found // unable to make a move or crossed absorbing boundary From ef5285f9c7b54cd4081426cd4ef78ea9dcf6705e Mon Sep 17 00:00:00 2001 From: Nicolas Boullis Date: Tue, 13 May 2025 12:23:11 +0200 Subject: [PATCH 09/12] [SMS] Kill the outer loop, proven to be useless by the added assertions. --- Individual.cpp | 56 ++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/Individual.cpp b/Individual.cpp index 7b3856f..3d3eeed 100644 --- a/Individual.cpp +++ b/Individual.cpp @@ -1474,35 +1474,33 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, // landscape boundaries and no-data cells may be reflective or absorbing cellcost = pCurrCell->getCost(); int loopsteps = 0; // new counter to prevent infinite loop added 14/8/15 - do { - double rnd = pRandom->Random(); - assert(rnd < cumulative[8]); - j = 0; - for (y2 = 0; y2 < 3; y2++) { - for (x2 = 0; x2 < 3; x2++) { - if (rnd < cumulative[j]) { - newX = current.x + x2 - 1; - newY = current.y + y2 - 1; - if (x2 == 1 || y2 == 1) move.dist = (float)(land.resol); - else move.dist = (float)(land.resol) * (float)SQRT2; - y2 = 999; x2 = 999; //to break out of x2 and y2 loops. - } - j++; - } - } - assert((y2 == 1000) && (x2 == 1000)); - assert(absorbing || (newX >= land.minX && newX <= land.maxX - && newY >= land.minY && newY <= land.maxY)); - loopsteps++; - if (newX < land.minX || newX > land.maxX - || newY < land.minY || newY > land.maxY) { - pNewCell = 0; - } else { - pNewCell = pLand->findCell(newX, newY); // would also return 0 if outside boundary - } - assert(absorbing || (pNewCell != 0)); - if (loopsteps >= 1000) pNewCell = 0; - } while (!absorbing && pNewCell == 0 && loopsteps < 1000); // no-data cell + double rnd = pRandom->Random(); + assert(rnd < cumulative[8]); + j = 0; + for (y2 = 0; y2 < 3; y2++) { + for (x2 = 0; x2 < 3; x2++) { + if (rnd < cumulative[j]) { + newX = current.x + x2 - 1; + newY = current.y + y2 - 1; + if (x2 == 1 || y2 == 1) move.dist = (float)(land.resol); + else move.dist = (float)(land.resol) * (float)SQRT2; + y2 = 999; x2 = 999; //to break out of x2 and y2 loops. + } + j++; + } + } + assert((y2 == 1000) && (x2 == 1000)); + assert(absorbing || (newX >= land.minX && newX <= land.maxX + && newY >= land.minY && newY <= land.maxY)); + loopsteps++; + if (newX < land.minX || newX > land.maxX + || newY < land.minY || newY > land.maxY) { + pNewCell = 0; + } else { + pNewCell = pLand->findCell(newX, newY); // would also return 0 if outside boundary + } + assert(absorbing || (pNewCell != 0)); + if (loopsteps >= 1000) pNewCell = 0; if (loopsteps >= 1000 || pNewCell == 0 || (newX == -9 || newY== -9)) { // if no cell was found // unable to make a move or crossed absorbing boundary // flag individual to die From 664a0bd59a4ec4f0609fdceb4e5eab09796284de Mon Sep 17 00:00:00 2001 From: Nicolas Boullis Date: Tue, 13 May 2025 12:26:54 +0200 Subject: [PATCH 10/12] [SMS] Kill the now-useless loopsteps variable. --- Individual.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Individual.cpp b/Individual.cpp index 3d3eeed..43b928c 100644 --- a/Individual.cpp +++ b/Individual.cpp @@ -1473,7 +1473,6 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, // select direction at random based on cell selection probabilities // landscape boundaries and no-data cells may be reflective or absorbing cellcost = pCurrCell->getCost(); - int loopsteps = 0; // new counter to prevent infinite loop added 14/8/15 double rnd = pRandom->Random(); assert(rnd < cumulative[8]); j = 0; @@ -1492,7 +1491,6 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, assert((y2 == 1000) && (x2 == 1000)); assert(absorbing || (newX >= land.minX && newX <= land.maxX && newY >= land.minY && newY <= land.maxY)); - loopsteps++; if (newX < land.minX || newX > land.maxX || newY < land.minY || newY > land.maxY) { pNewCell = 0; @@ -1500,8 +1498,7 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, pNewCell = pLand->findCell(newX, newY); // would also return 0 if outside boundary } assert(absorbing || (pNewCell != 0)); - if (loopsteps >= 1000) pNewCell = 0; - if (loopsteps >= 1000 || pNewCell == 0 || (newX == -9 || newY== -9)) { // if no cell was found + if (pNewCell == 0 || (newX == -9 || newY== -9)) { // if no cell was found // unable to make a move or crossed absorbing boundary // flag individual to die move.dist = -123.0; From f63750bc46c79fc34d6149e38cba146ee6e74d53 Mon Sep 17 00:00:00 2001 From: Nicolas Boullis Date: Tue, 13 May 2025 12:29:29 +0200 Subject: [PATCH 11/12] [SMS] Remove now-useless conditions. --- Individual.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Individual.cpp b/Individual.cpp index 43b928c..6d20f1a 100644 --- a/Individual.cpp +++ b/Individual.cpp @@ -1498,11 +1498,11 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, pNewCell = pLand->findCell(newX, newY); // would also return 0 if outside boundary } assert(absorbing || (pNewCell != 0)); - if (pNewCell == 0 || (newX == -9 || newY== -9)) { // if no cell was found + if (pNewCell == 0) { // if no cell was found // unable to make a move or crossed absorbing boundary // flag individual to die move.dist = -123.0; - if (pNewCell == 0) pCurrCell = pNewCell; + pCurrCell = pNewCell; } else { newcellcost = pNewCell->getCost(); From 47fed3869543334c985f8e3ebf58a3016138f03e Mon Sep 17 00:00:00 2001 From: Nicolas Boullis Date: Tue, 13 May 2025 13:52:15 +0200 Subject: [PATCH 12/12] =?UTF-8?q?Move=20the=20common=20=E2=80=9CpCurrCell?= =?UTF-8?q?=20=3D=20pNewCell;=E2=80=9D=20instruction=20outside=20the=20if/?= =?UTF-8?q?else=20structure.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Individual.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Individual.cpp b/Individual.cpp index 6d20f1a..fa395e0 100644 --- a/Individual.cpp +++ b/Individual.cpp @@ -1502,7 +1502,6 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, // unable to make a move or crossed absorbing boundary // flag individual to die move.dist = -123.0; - pCurrCell = pNewCell; } else { newcellcost = pNewCell->getCost(); @@ -1512,8 +1511,8 @@ movedata Individual::smsMove(Landscape* pLand, Species* pSpecies, memory.pop(); // remove oldest memory element } memory.push(current); // record previous location in memory - pCurrCell = pNewCell; } + pCurrCell = pNewCell; return move; }