[ index.php ] [ docs.php ] [ package.php ] [ test.php ] [ example.php ] [ download.php ]

Magic Square Example

The Jama distribution comes with a magic square example that is used to test and benchmark the LU, QR, SVD and symmetric Eig decompositions. The example outputs a multi-column table with these column headings:

n Order of magic square.
trace Diagonal sum, should be the magic sum, (n^3 + n)/2.
max_eig Maximum eigenvalue of (A + A')/2, should equal trace.
rank Linear algebraic rank, should equal n if n is odd, be less than n if n is even.
cond L_2 condition number, ratio of singular values.
lu_res test of LU factorization, norm1(L*U-A(p,:))/(n*eps).
qr_res test of QR factorization, norm1(Q*R-A)/(n*eps).

Running the Java-based version of the matix square example produces these results:

n trace max_eig rank cond lu_res qr_res
31515.00034.3300.00011.333
43434.0003 Inf0.00013.500
56565.00055.4620.00014.400
6111111.0005 Inf5.33316.000
7175175.00077.1112.28637.714
8260260.0003 Inf0.00059.000
9369369.00099.1027.11153.333
10505505.0007 Inf3.200159.200
11671671.0001111.1022.909215.273
12870870.0003 Inf0.000185.333
1311051105.0001313.0604.923313.846
1413791379.0009 Inf4.571540.571
1516951695.0001515.0624.267242.133
1620562056.0003 Inf0.000488.500
1724652465.0001717.0427.529267.294
1829252925.00011 Inf7.111520.889
1934393439.0001919.04816.842387.368
2040104010.0003 Inf14.400584.800
2146414641.0002121.0356.0951158.095
2253355335.00013 Inf6.5451132.364
2360956095.0002323.03711.1301268.870
2469246924.0003 Inf10.667827.500
2578257825.0002525.02935.8401190.400
2688018801.00015 Inf4.9231859.077
2798559855.0002727.03237.9261365.333
281099010990.0003 Inf34.2861365.714
291220912209.0002929.02530.8971647.448
301351513515.00017 Inf8.5332571.733
311491114911.0003131.02733.0321426.581
321640016400.0003 Inf0.0001600.125
Elapsed Time = 0.710 seconds

The magic square example does not fare well when run as a PHP script. For a 32x32 matrix array it takes around a second to complete just the last row of computations in the above table. Hopefully this result will spur PHP developers to find optimizations and better attuned algorithms to speed things up. Matrix algebra is a great testing ground for ideas about time and memory performance optimation. Keep in perspective that PHP JAMA scripts are still plenty fast for use as a tool for learning about matrix algebra and quickly extending your knowledge with new scripts to apply knowledge.

To learn more about the subject of magic squares you can visit the Drexel Math Forum on Magic Squares. You can also learn more by carefully examining the MagicSquareExample.php source code below.

<?php
/**
* @package JAMA
*/

require_once "../Matrix.php";

/**
* Example of use of Matrix Class, featuring magic squares.
*/
class MagicSquareExample {

  
/**
  * Generate magic square test matrix.
  * @param int n dimension of matrix
  */
  
function magic($n) {

    
// Odd order

    
if (($n 2) == 1) {
      
$a = ($n+1)/2;
      
$b = ($n+1);
      for (
$j 0$j $n; ++$j)
        for (
$i 0$i $n; ++$i)
          
$M[$i][$j] = $n*(($i+$j+$a) % $n) + (($i+2*$j+$b) % $n) + 1;

    
// Doubly Even Order

    
} else if (($n 4) == 0) {
      for (
$j 0$j $n; ++$j) {
        for (
$i 0$i $n; ++$i) {
          if (((
$i+1)/2)%== (($j+1)/2)%2)
            
$M[$i][$j] = $n*$n-$n*$i-$j;
          else
            
$M[$i][$j] = $n*$i+$j+1;
        }
      }

    
// Singly Even Order

    
} else {

      
$p $n/2;
      
$k = ($n-2)/4;
      
$A $this->magic($p);
      
$M = array();
      for (
$j 0$j $p; ++$j) {
        for (
$i 0$i $p; ++$i) {
          
$aij $A->get($i,$j);
          
$M[$i][$j]       = $aij;
          
$M[$i][$j+$p]    = $aij 2*$p*$p;
          
$M[$i+$p][$j]    = $aij 3*$p*$p;
          
$M[$i+$p][$j+$p] = $aij $p*$p;
        }
      }

      for (
$i 0$i $p; ++$i) {
        for (
$j 0$j $k; ++$j) {
          
$t $M[$i][$j];
          
$M[$i][$j] = $M[$i+$p][$j];
          
$M[$i+$p][$j] = $t;
        }
        for (
$j $n-$k+1$j $n; ++$j) {
          
$t $M[$i][$j];
          
$M[$i][$j] = $M[$i+$p][$j];
          
$M[$i+$p][$j] = $t;
        }
      }

      
$t $M[$k][0];  $M[$k][0]  = $M[$k+$p][0];  $M[$k+$p][0]  = $t;
      
$t $M[$k][$k]; $M[$k][$k] = $M[$k+$p][$k]; $M[$k+$p][$k] = $t;

    }

    return new 
Matrix($M);

  }

  
/**
  * Simple function to replicate PHP 5 behaviour
  */
  
function microtime_float() {
    list(
$usec$sec) = explode(" "microtime());
    return ((float)
$usec + (float)$sec);
  }

  
/**
  * Tests LU, QR, SVD and symmetric Eig decompositions.
  *
  *   n       = order of magic square.
  *   trace   = diagonal sum, should be the magic sum, (n^3 + n)/2.
  *   max_eig = maximum eigenvalue of (A + A')/2, should equal trace.
  *   rank    = linear algebraic rank, should equal n if n is odd,
  *             be less than n if n is even.
  *   cond    = L_2 condition number, ratio of singular values.
  *   lu_res  = test of LU factorization, norm1(L*U-A(p,:))/(n*eps).
  *   qr_res  = test of QR factorization, norm1(Q*R-A)/(n*eps).
  */
  
function main() {
    
?>
    <p>Test of Matrix Class, using magic squares.</p>
    <p>See MagicSquareExample.main() for an explanation.</p>
    <table border='1' cellspacing='0' cellpadding='4'>
      <tr>
        <th>n</th>
        <th>trace</th>
        <th>max_eig</th>
        <th>rank</th>
        <th>cond</th>
        <th>lu_res</th>
        <th>qr_res</th>
      </tr>
      <?php
      $start_time 
$this->microtime_float();
      
$eps pow(2.0,-52.0);
      for (
$n 3$n <= 6; ++$n) {
        echo 
"<tr>";

        echo 
"<td align='right'>$n</td>";

        
$M $this->magic($n);
        
$t = (int) $M->trace();

        echo 
"<td align='right'>$t</td>";

        
$O $M->plus($M->transpose());
        
$E = new EigenvalueDecomposition($O->times(0.5));
        
$d $E->getRealEigenvalues();

        echo 
"<td align='right'>".$d[$n-1]."</td>";

        
$r $M->rank();

        echo 
"<td align='right'>".$r."</td>";

        
$c $M->cond();

        if (
$c 1/$eps)
          echo 
"<td align='right'>".sprintf("%.3f",$c)."</td>";
        else
          echo 
"<td align='right'>Inf</td>";

        
$LU = new LUDecomposition($M);
        
$L $LU->getL();
        
$U $LU->getU();
        
$p $LU->getPivot();
        
// Java version: R = L.times(U).minus(M.getMatrix(p,0,n-1));
        
$S $L->times($U);
        
$R $S->minus($M->getMatrix($p,0,$n-1));
        
$res $R->norm1()/($n*$eps);

        echo 
"<td align='right'>".sprintf("%.3f",$res)."</td>";

        
$QR = new QRDecomposition($M);
        
$Q $QR->getQ();
        
$R $QR->getR();
        
$S $Q->times($R);
        
$R $S->minus($M);
        
$res $R->norm1()/($n*$eps);

        echo 
"<td align='right'>".sprintf("%.3f",$res)."</td>";

        echo 
"</tr>";

     }
     echo 
"<table>";
     echo 
"<br />";

     
$stop_time $this->microtime_float();
     
$etime $stop_time $start_time;

     echo 
"<p>Elapsed time is "sprintf("%.4f",$etime) ." seconds.</p>";

  }

}

$magic = new MagicSquareExample();
$magic->main();

?>