"unexpected behaviour" in loop lazarus / 62-General ---------------------------------------------------------------------- ebuxbaum · 2020-11-28 07:12 Hi, for a program that performs k-means clustering, I used the following procedure: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure CalculateNewCentroids (const Data      : MatrixTyp;                                     k         : word;                                 var   Group     : GroupTyp;                                 var   Centroids : MatrixTyp                                ); var i, j, l, n, p, nk  : word;    Sums               : array [1..kNNMax] of float;  begin  n := MatrixRows(Data);  p := MatrixColumns(Data);  for l := 1 to k do    begin      for j := 1 to k do Sums[j] := 0.0;      nk := 0;      for i := 1 to n do        if Group[i] = l          then            begin              for j := 1 to p do                begin                Sums[j] := Sums[j] + GetMatrixElement(Data, i, j);                writeln(j:5, ' ', n:5, ' ', p:5, ' ', nk:5);              end;              inc(nk);            end;      for j := 1 to p do      SetMatrixElement(Centroids, k, j, Sums[j]/nk);    end; // for lend; // CalculateNewCentroids    which is supposed to calculate the averages of all features for each group. Unexpectedly, the inner loop changes values of counters for the outer loop, the output of the writeln-statement is:        1   101    23     0        2   101    23     0        3   101    23     0        4   101    23     0        5   101    23     0        6   101    23     0        7   101    23     0        8   101    23     0        9   101    23     0       10   101    23     0       11   101    23     0       12   101    23     0       13   101    23     0       14   101    23     0       15   101    23     0       16   101    23  6291       17 34078 60293  6291    65108 34078 60293  6291 ---------------------------------------------------------------------- lucamar · 2020-11-28 07:23 I'd start by checking what GetMatrixElement() is doing and how it's declared; it might somehow be changing i and/or j (and getting away with it!). Otherwise, check for unexpected overflows or run-away pointers. Dfficult to say more without seeing all the related code. ---------------------------------------------------------------------- ebuxbaum · 2020-11-28 07:44 GetMatrixElement is defined as Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  MatrixStruc = record    Columns, Rows: word;    Data: array[1..MaxVector] of float;  end;  MatrixTyp = ^MatrixStruc;    function GetMatrixElement(const A: MatrixTyp; Row, Column: word): float; var n, p : word; begin  n := MatrixRows(A);  p := MatrixColumns(A);  if (Row <= n) and (Column <= p)    then      Result := A^.Data[pred(Row)*p + Column]    else      ch := WriteErrorMessage(' Attempt to read a non-existent matrix element');end; and therefore should not change the counters (although n and p exist there too, they are different variables because of locality). In addition, a proposed side-effect would start with the first call, not the 16th. Btw, the error occurs both under Win10 and Linux. ---------------------------------------------------------------------- bytebites · 2020-11-28 08:20 Still not compiöable code. Maybe kNNMax too small. Good luck for guessing ---------------------------------------------------------------------- lucamar · 2020-11-28 08:31 Quote from: ebuxbaum on November 28, 2020, 08:44:10 amGetMatrixElement is defined as [...] and therefore should not change the counters (although n and p exist there too, they are different variables because of locality). In addition, a proposed side-effect would start with the first call, not the 16th. Yes, of course. But then, we couldn't know that without seeing it, would we? One new thing is that all your accesses to the matrix seems to be through a pointer; check then whether the "runaway pointer" theory migth hold some water. It doesn't seems probable since the structure itself shouldn't be anywhere near the stack--where all the locals are kept--and you don't seem to be doing anthing "strange" with the pointer, but one never knows ... One strange thing is that the first to fail seems to be nk (or it might be all of them at once); it looks as if somehow the index of Sums[] is becoming negative and thrashing the vars declared before it, but I can't see how, sorry ---------------------------------------------------------------------- MarkMLl · 2020-11-28 09:51 I very much dislike criticising code on account of well-intentioned indentation etc., but cutting out some of the cruft: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure CalculateNewCentroids (const Data      : MatrixTyp;                                     k         : word;                                 var   Group     : GroupTyp;                                 var   Centroids : MatrixTyp                                );     var i, j, l, n, p, nk  : word;    Sums               : array [1..kNNMax] of float;          begin  n := MatrixRows(Data);  p := MatrixColumns(Data);   for l := 1 to k do begin    for j := 1 to k do      Sums[j] := 0.0;    nk := 0;     for i := 1 to n do      if Group[i] = l then begin            for j := 1 to p do begin              Sums[j] := Sums[j] + GetMatrixElement(Data, i, j);                writeln(j:5, ' ', n:5, ' ', p:5, ' ', nk:5)            end;          inc(nk)      end;     for j := 1 to p do      SetMatrixElement(Centroids, k, j, Sums[j]/nk)  end // for l end; // CalculateNewCentroids  Take a careful look at what's in the group array, and put i at the front of that WriteLn(). MarkMLl ---------------------------------------------------------------------- ebuxbaum · 2020-11-28 10:53 Ok, done. i stays 1, and Group is 1 (I checked), as higher groups will be handled only in further iterations of the outer loop. k is unaffected, too. The values returned by GetMatrixElement are correct. ---------------------------------------------------------------------- MarkMLl · 2020-11-28 11:45 Allowing that I'm working on something else... does control reach the statement with the divide-by-zero in it? MarkMLl ---------------------------------------------------------------------- ebuxbaum · 2020-11-28 13:07 I am not sure what you mean by that. The only division in this procedure is the calculation of averages (Sums[j]/nk), and this is never reached. But you raise an important point, the situation nk = 0 is possible and needs to be taken care of (then Sums[j] would also be 0). ---------------------------------------------------------------------- Jonas Maebe · 2020-11-29 13:56 Try enabling range checking. ---------------------------------------------------------------------- jamie · 2020-11-29 22:58 looks like your n and p could be exceeding your kNNMax which would cause a clobbering of the stack there by messing up your counter variables, which are also on the stack  looking at the use of DATA which is a pointer, most likely returning bad data or out of range data. ---------------------------------------------------------------------- ebuxbaum · 2020-12-01 09:20 Actually no: KNNMax is the maximal number of clusters, into which the data can be sorted. Following the conventional nomenclature in multivariate statistics, n is the number of items (say, persons participating in a study), p the number of variables. Thus, problems would occur only if k (actual number of clusters)  is larger than KNNmax, but that is prevented by the calling routine. Indeed, KNNmax is a global constant and set to 15, k is set by the calling routine to 2 for the time being.  I have now modified the inner loop as follows: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i for j := 1 to p do begin   x := GetMatrixElement(Data, i, j);   write(k:5, ' ', i:5, ' ', j:5, ' ', n:5, ' ', p:5, ' ', nk:5, ' ', Group[i]:5, ' ', x:3:6, ' ', Sums[j]:3:3, ' ');   Sums[j] := Sums[j] + x;   writeln(p:5, ' ', Sums[j]:3:3);end; and got the following result:      k     i     j    n     p     nk  Group   x        Sums[j]  p  Sums[j]     2     1     1   101    23     0        1   32.000000 0.000    23 32.000      2     1     2   101    23     0        1   -0.307000 0.000    23 -0.307      2     1     3   101    23     0        1   -0.081000 0.000    23 -0.081      2     1     4   101    23     0        1   -0.466000 0.000    23 -0.466      2     1     5   101    23     0        1    1.170000 0.000    23  1.170      2     1     6   101    23     0        1    1.894000 0.000    23  1.894      2     1     7   101    23     0        1   -0.913000 0.000    23 -0.913      2     1     8   101    23     0        1    0.628000 0.000    23  0.628      2     1     9   101    23     0        1   -0.904000 0.000    23 -0.904      2     1    10   101    23     0        1    0.458000 0.000    23  0.458      2     1    11   101    23     0        1   -1.160000 0.000    23 -1.160      2     1    12   101    23     0        1    0.360000 0.000    23  0.360      2     1    13   101    23     0        1    1.507000 0.000    23  1.507      2     1    14   101    23     0        1   -0.505000 0.000    23 -0.505      2     1    15   101    23     0        1    0.454000 0.000    23  0.454      2     1    16   101    23     0        1    0.753000 0.000  6291  0.753      2     1    17   101  6291 27263        1    0.305000 0.000  6291  0.305      2     1    18 60293  6291 27263        1   -0.453000 0.000  6291  0.000        In other words, the guilty line is Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Sums[j] := Sums[j] + x;, because when j = 16, p is correct before and wrong afterwards. ---------------------------------------------------------------------- MathMan · 2020-12-01 11:58 Quote from: ebuxbaum on December 01, 2020, 10:20:22 amActually no: KNNMax is the maximal number of clusters, into which the data can be sorted. Following the conventional nomenclature in multivariate statistics, n is the number of items (say, persons participating in a study), p the number of variables. Thus, problems would occur only if k (actual number of clusters)  is larger than KNNmax, but that is prevented by the calling routine. Indeed, KNNmax is a global constant and set to 15, k is set by the calling routine to 2 for the time being. Hm, looking at your initial post your problems seem to start with j=16 and KNNMax = 15 as per your above statement. I'd say this smells like exceeding the array bounds of "Sums". I can only advise like what has already been advised by Jonas - compile with range checks on and see what happens when the function is executed. MathMan ---------------------------------------------------------------------- dseligo · 2020-12-01 13:31 Quote from: ebuxbaum on December 01, 2020, 10:20:22 amIn other words, the guilty line is Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Sums[j] := Sums[j] + x;, because when j = 16, p is correct before and wrong afterwards. j shouldn't be 16, because of this declaration:     Sums               : array [1..kNNMax] of float; and you said that kNNMax is set to 15. So you have:     Sums:array [1..15] of float; And you then do this: Sums[16]:=Sums[16]+x; ---------------------------------------------------------------------- ebuxbaum · 2020-12-02 08:40 Yes, that should have been pMax, not KNNmax. Thanks everybody for your help. I only wonder why this didn't cause a runtime error. In TP, it would have. ---------------------------------------------------------------------- Bart · 2020-12-02 09:51 By default range checking is off in fpc, that's why. Bart ---------------------------------------------------------------------- PascalDragon · 2020-12-02 13:46 Well, Jonas Maebe did suggest to enable range checking... Quote from: Jonas Maebe on November 29, 2020, 02:56:18 pmTry enabling range checking.