TweetFollow Us on Twitter

Linear Equations
Volume Number:1
Issue Number:11
Column Tag:Forth Forum

Solving Systems of Linear Equations

By Jörg Langowski, Chemical Engineer, Fed. Rep. of Germany, MacTutor Editorial Board

This is the first of a series of columns that will deal with the general problem of doing numerical calculations in MacForth. Forth's philosophy is to use integer arithmetic in many cases that would be handled with floating point in other languages. The reason for this has to be seen historically in the development of Forth, which first was used almost exclusively as a language to do process control. It was desirable not to have the ballast of a floating point package in implementations that used 8-bit processors with a limited amount of memory, and there is, of course, a great speed advantage in using integer arithmetic.

When used in 'custom-designed' routines for one particular problem, integer arithmetic can do as well as floating point. However, one has to scale all the numbers involved so that they fit into the range that is given by the 4 bytes of the Mac's integer arithmetic (or the 2 bytes of some other system). On the other hand, numbers shouldn't get too small, either, because accuracy is lost very quickly. The constant need to haul scaling factors around between parts of the program then makes the code rather hard to read and bug-prone.

Again this is the old tradeoff between speed and low memory requirement on one side and flexibility and readability on the other. If we want to write a set of mathematical routines in Forth that will be useful no matter what the particular problem is (whether distances are in nanometers or lightyears, weights in tons or micrograms) the easiest way to do this is to use floating point arithmetic. This is especially true on the Macintosh, since we have an excellent floating point package with 80-bit accuracy built in.

This FP package, also called SANE (Standard Apple Numeric Environment) conforms to the proposed IEEE standard on floating point arithmetic (see my article in MacTutor V1#1). MacForth 2.0 offers Forth code for a very slick interface to the SANE routines, using its own floating point stack and even modifying the interpreter so that real numbers are accepted as input. There are two problems with this code, though: First, we cannot print it here for obvious reasons and therefore our program would run only under MacForth 2.0, which would be a little too restricted for this Forum. Second, according to my own tests the floating point interface adds so much overhead that actual calculations are slowed down by a factor of 2 to 3.

The code that we write here uses a more 'direct' approach to FP arithmetic, which is variable-oriented rather than stack-oriented (see V1#1). It looks a little more clumsy and is definitely harder to read, but since we want to generate a set of Forth words for general use which should be fast more than anything, this is justified.

Definition of the problem - fitting experimental data to a theoretical equation

Enough of the preliminaries, I should tell you now what exactly we want to do. One of the bread-and-butter problems in experimental science is to extract theoretical parameters from a set of experimental data points, given a theoretical equation that can predict those data points from the parameters.

Example: You measure the time response of a physical system, for instance the voltage across a capacitor C as it is discharged through a resistor R. The time behavior of the voltage versus time looks like:

U(t) = Uo exp(-t/RC)

or, if the voltage does not drop all the way down to zero (e.g. some bias applied),

U(t) = Uo exp(-t/RC) + U1 .

In practice, we may have measured a series of points Ui at times ti. Our problem is to get Uo, U1 and RC from that data. Fig. 1 shows how the data and the 'exact' theoretical curve might look like.

Fig. 1: Fitting a theoretical curve to experimental data

Of course, for all U(t) curves with different U0, U1 and RC, there is only one that fits the data points best. The quality of the fit is usually checked by summing the squared differences (the 'residuals') between the data points and the theoretical curve. We have to vary the parameters U0, U1 and RC in such a way that this sum-of-squares becomes a minimum.

Iterative least-squares fitting

Let's state the problem in a more general way. We have a function y = f(t,a1,a2,a3...am) that, given certain values for the parameters a1,a2,a3...am, tells us the time dependence of some quantity y that can be measured. Furthermore we have a set of n data points (ti,yi), the y-values that are actually measured at times ti. The residual for data point i is then

There exists a variety of techniques that one can use to minimize the sum of the squared residuals in such a case. All of them require that one first estimates initial values for the parameters that are not too far away from reality; this is usually possible. From these initial values one can then compute a better estimate of the parameters, and iterate this process until the fit does not improve anymore.

One rather simple algorithm that solves the fitting problem in such an interative way is given by T.R.McCalla in his book 'Introduction to Numerical Methods and FORTRAN Programming' (Wiley & Sons, New York 1967). I won't give the details here, since we are mainly interested in how to program such an algorithm in Forth. The only thing we need to know is the final result: a set of linear equations whose solution gives correction terms ak. These ak have to be added to the initial ak to get the new estimate.

The linear equations that one gets look like this:

Fig.2: System of linear equations

where the cij are coefficients that one calculates from the set of n data points (ti,yi) and the derivatives of the function f(ti, a1,a2,a3...am ) at each data point with respect to the parameters ak. The bi contain the residuals.

So the first problem that we have to solve - and this will be plenty for this column - is to solve a system of linear equations like the one given above. In later columns we will build on the basics of floating-point arithmetic that we develop here and end up with a functional curve-fitting program.

The Gauss Algorithm

A linear equation system like the one above is often solved using the Gauss algorithm. One starts writing the coefficients on the left and right hand sides of the equations as a m*m+1 matrix:

(3 by 4 in this example).

The algorithm then converts this matrix into a triangular matrix:

where the bottom left 'triangle' is equal to zero: multiples of the first row are subtracted from the rows below it until the first column is all zeroes except for the first row, then multiples of the second row are subtracted from the rows below it until the second column is all zeroes except for the first two rows, and so on.

After that procedure is completed, the bottom row has become a simple equation of one variable:

from which a3 can easily be calculated. a3 is then substituted into the equation above it and a2 obtained, and from a3 and a2 finally a1. This procedure can, of course, be expanded to be used on any number of equations.

The Gauss algorithm is given as a Pascal program (to improve readability) in Listing 1. To code it in Forth we first have to give the problem of data representation a little thought, namely: how are we going to store a matrix?

Data representation for arrays of floating point numbers

The SANE routines work on 80-bit numbers. This is ideal for accurate calculations, but a little expensive as far as storage goes; a 100 * 100 matrix would already occupy 80K. If high precision is not needed, large arrays may be stored as lower precision FP numbers. Single precision uses only 32 bits, less than half of the standard SANE length. Therefore we are going to store matrices as two-dimensional arrays of 32-bit long words that contain single precision real numbers. The MATRIX definition (in the example program in listing 2) is modified from the example released by Creative Solution on the Forth disk. We have separated the DOES> part that calculates the address of a matrix element from its indices and defined it as a separate word, CALC.OFFSET. This was done so that our routine works with any matrix variable whose address is passed on the stack.

You define a matrix with r rows and c columns by

r c MATRIX X    .

When you later execute

i  j  X   ,

the address of the element in row i and column j of matrix x will be on the stack. When you execute 0 0 X (all rows and columns start with 0), the address of the first element in the matrix will be on the stack. If we want to write a Gauss algorithm routine that works with any matrix of any size, we have to be able to calculate the offset into the matrix from the row and column indices just as the DOES> part of the MATRIX defining word does. In our definitions, i j addr CALC.OFFSET leaves on the stack the address of the element at row i and column j of the matrix whose first element is at addr.

The solution of the linear equation system will be stored in an array z. For this array we do not need a DOES> part because it is one-dimensional, no need to keep track of row and column lengths here.

Strategy for floating point calculations using the SANE package

The SANE routines expect addresses of floating point numbers on the stack as their parameters (see V1#1). All arithmetic operators are two-address operators, where the first parameter is added to, subtracted from, divided or multiplied into the second parameter. The second parameter is always 80-bit extended precision, while the first one may be any precision. So for any calculation we will transfer the numbers out of the 32-bit variables into 80-bit variables (or add them in etc., if it is convenient), then do all intermediate calculations in 80-bit precision and at the end store the 80-bit result into a 32-bit single precision variable again.

The Gauss Algorithm Routine

Listing 2 shows the example program containing the GAUSS routine for solution of linear equation systems of any size. The routine expects on the stack, from bottom to top: the address of a solution vector z, which for n unknowns has n 32-bit words allocated; the address of the n (rows) by n+1 (columns) matrix X that contains the coefficients of the linear equation system; and n, the number of equations (or unknowns, respectively). The routine first converts the X matrix into its triangular form (so X is changed upon exit), then proceeds to calculate the values of the unknowns, starting in the bottom row of the matrix and working its way up.

The K function: extracting the loop index 2 levels up

The first part of the algorithm has DO..LOOP constructs nested 3 levels deep. The inner loop needs the outermost loop index, and there is no standard word in MacForth that handles this. Therefore we define : k rp@ 20 + @ ; which does this job. (There is also a k defined in machine code; see V1#9).

The example program

Our example calculates the solution of the system of equations

The solution is x1 = 1.2308, x2 = -1.0769, x3 = -0.1538. The word gbm calculates and prints this solution (it actually calculates n times, with n on top of the stack, for benchmark purposes).

Listing 1: Gaussian algorithm - Pascal example

program LinEqu;
 type  matrix = array[1..10, 1..11] of real;
          column = array[1..10] of real;
 var  x : matrix;    z : column;   n, i : integer;

 procedure gaussalg (var x : matrix;
         var z : column;  n : integer);
  var   dg, fk, ee : real;   i, j, k : integer;
 begin
  for i := 1 to n - 1 do
   begin  dg := x[i, i];
    for j := i + 1 to n do
     begin  fk := x[j, i] / dg;
      for k := i to n + 1 do
       x[j, k] := x[j, k] - fk * x[i, k]
     end
   end;
  for i := 1 to n do   z[i] := x[i, n + 1];
  for i := n downto 2 do
   begin   dg := x[i, i];    ee := z[i];
    for j := i - 1 downto 1 do  
        z[j] := z[j] - ee * x[j, i] / dg
   end;
  for i := 1 to n do   z[i] := z[i] / x[i, i]
 end;

begin  n := 3;
 x[1, 1] := 1; x[1, 2] := 1; x[1, 3] := 1; x[1, 4] := 0;
 x[2, 1] := 1; x[2, 2] := -1; x[2, 3] := 2; x[2, 4] := 2;
 x[3, 1] := 4; x[3, 2] := 1; x[3, 3] := -1; x[3, 4] := 4;

 gaussalg(x, z, n);
 for i := 1 to 3 do  writeln('z[', i : 1, ']= ', z[i] : 7 : 4)
end.
Listing 2: Gaussian algorithm, FORTH example

( Floating point primitives )
( This is part of the SANE interface given in MT V1#1; not all of it 
is needed here)
hex a9eb w>mt fp68k     ( package 4 )
    a9ec w>mt elems68k  ( package 5 )
( extended precision operations )
: f+ 0 fp68k ; : f- 2 fp68k ; : f* 4 fp68k ; : f/ 6 fp68k ;
: x2x e fp68k ;  : fneg d fp68k ;
( single to extended operations )
: s+ 1000 fp68k ; : s- 1002 fp68k ; : s2x 100e fp68k ;
: s* 1004 fp68k ; : s/ 1006 fp68k ; : x2s 1010 fp68k ;
( long integer to extended operations )
: in+ 2800 fp68k ; : in- 2802 fp68k ; 
: in2x 280e fp68k ; : in* 2804 fp68k ; 
: in/ 2806 fp68k ; : x2in 2810 fp68k ;
: d2b 9 fp68k ; : b2d b fp68k ;
   ( decimal <--> binary conversions )
: float create 10 allot ; : integer create 4 allot ;
: wvar create 2 allot ;    ( type declarations )
( floating point i/o )
decimal
: numstring create 24 allot ;  ( decimal display string )
hex 1000000 constant fixdec decimal 
( format style control )
variable zzformat 
( internal format for conversion routine )
numstring zzs1 ( internal conversion string )
: dec. ( float\format# -- )
       zzformat ! zzformat swap zzs1 b2d
       zzs1 dup w@ 255 > if ." -" else ."  " then
       dup 4+ count type ( mantissa )
       2+ w@ ( get exponent )
            1 w* ( convert to 32 bit integer )
            ." E" . ;

( floating point initialization )
: fclear 0 over ! 0 over 4+ ! 0 over 8+ w! drop ;
: sclear 0 swap ! ;

( Matrix Operators )                               
: calc.offset  ( row\col\addr -- addr )
           dup>r  4+ @  ( #cols)  4*        ( 32-bit )
           rot *  ( offset to row)  swap 4*    ( 32-bit )
           +  ( offset to element ) r> 8+  + ( add base addr) ;

: matrix  ( #rows\#cols -- )
    create over ,  ( #rows )  dup ,  ( #cols )
            *  4* allot  ( allot the space for the matrix )
    does>  calc.offset ;

( Gauss algorithm for linear equations, definitions)
: k rp@ 20 + @ ;
variable nv   variable coeff variable solution
( addresses for storing actual parameters)
float one  float -one  float zero  float two  float four
1 one !  -1 -one !  0 zero !  2 two !  4 four !
one one in2x  two two in2x  -one -one in2x  
zero zero in2x four four in2x
float fa1   float fa2   float fa3   float fa4
( define some floating accumulators)
float dg    float fk    float ee
create z 12 allot   3 4 matrix x
: ztest 
      3 0 do i 4* solution @ + fa1 s2x fa1 5 dec. loop cr ;
( setup coefficient matrix for example)
one 0 0 x x2s  one 0 1 x x2s  one 0 2 x x2s  
                                                           zero 0 3 x 
x2s
one 1 0 x x2s -one 1 1 x x2s  two 1 2 x x2s   
                                                           two 1 3 x 
x2s
four 2 0 x x2s  one 2 1 x x2s -one 2 2 x x2s  
                                                           four 2 3 x 
x2s
( Gauss algorithm for linear equations) 
: gauss ( z\x\n | --)  nv !  8- coeff !  solution !
  nv @ 1- 0 do  ( i-loop)
     i dup coeff @ calc.offset dg s2x ( diag elem)
     nv @ i 1+ do  ( j-loop)
        i j coeff @ calc.offset fk s2x   dg fk f/
        nv @ 1+ j do  ( k-loop)
            k i coeff @ calc.offset fa1 s2x
                      fk fa1 f*  fa1 fneg  ( -fk*x[i,k])
            j i coeff @ calc.offset dup fa1 s+
                      fa1 swap x2s
                  loop
              loop
           loop
nv @ dup 0 do i over coeff @ calc.offset  fa1 s2x
                       fa1 solution @ i 4* + x2s loop
1 nv @ 1- do
     i dup coeff @ calc.offset dg s2x
     solution @ i 4* + ee s2x  dg ee f/
     0 i 1- do i j coeff @ calc.offset fa1 s2x
                         ee fa1 f* fa1 fneg
               solution @ i 4* + dup fa1 s+ fa1 swap x2s
            -1 +loop
       -1 +loop
nv @ 0 do  solution @ i 4* +  fa1 s2x
           i dup coeff @ calc.offset  fa1 s/
           fa1 solution @ i 4* + x2s
       loop ;

: soln ." The solution is: " ztest ; 

: gbm 0 do z 0 0 x 3 gauss loop soln ;
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

BusyCal 3.7.2 - Powerful calendar app wi...
BusyCal is the powerful, flexible, reliable calendar app for macOS. It's packed with time-saving features and compatible with all leading cloud services including iCloud, Google, Exchange and more.... Read more
BusyContacts 1.4.2 - Fast, efficient con...
BusyContacts is a contact manager for OS X that makes creating, finding, and managing contacts faster and more efficient. It brings to contact management the same power, flexibility, and sharing... Read more
iShowU Instant 1.3.0 - Full-featured scr...
iShowU Instant gives you real-time screen recording like you've never seen before! It is the fastest, most feature-filled real-time screen capture tool from shinywhitebox yet. All of the features you... Read more
Capo 3.8 - Slow down and learn to play y...
Capo lets you slow down your favorite songs so you can hear the notes and learn how they are played. With Capo, you can quickly tab out your songs atop a highly-detailed OpenCL-powered spectrogram... Read more
Viber 11.7.0 - Send messages and make fr...
Viber lets you send free messages and make free calls to other Viber users, on any device and network, in any country! Viber syncs your contacts, messages and call history with your mobile device, so... Read more
Drive Genius 5.3.1 - $79.00
Drive Genius features a comprehensive Malware Scan. Automate your malware protection. Protect your investment from any threat. The Malware Scan is part of the automated DrivePulse utility. DrivePulse... Read more
CorelDRAW 21.2.0.708 - Graphic design so...
CorelDRAW - professional graphic design software for vector illustration, layout, and so much more. Get started quickly and easily with a wealth of intuitive tools, built-in learning materials,... Read more
Navicat Premium Essentials 12.1.27 - Pro...
Navicat Premium Essentials is a compact version of Navicat which provides basic and necessary features you will need to perform simple administration on a database. It supports the latest features... Read more
OmniFocus 3.4.3 - GTD task manager with...
OmniFocus is an organizer app. It uses projects to organize tasks naturally, and then add tags to organize across projects. Easily enter tasks when you’re on the go, and process them when you have... Read more
Microsoft OneNote 16.30 - Free digital n...
OneNote is your very own digital notebook. With OneNote, you can capture that flash of genius, that moment of inspiration, or that list of errands that's too important to forget. Whether you're at... Read more

Latest Forum Discussions

See All

Upcoming mobile MMO TeeTINY Online’s sec...
TeeTINY Online is an upcoming anime-themed MMO which boasts striking visuals and an ambitious open world to explore. Following a successful CBT (closed beta test) on Android, developer TEENY.studio has now released a follow-up test which adds an... | Read more »
Breakout: Dark Prison is a fast-paced ac...
Breakout: Dark Prison is an action RPG from LaterSoft. Set in the aftermath of a deadly virus outbreak your daughter has been taken from you because she has an immunity to the illness in her DNA. Not being a fan of experimentation on children –... | Read more »
Apple Arcade in review
This weekend, Apple Arcade will officially be one month old. That means anyone who signed up for the free trial on day one has a decision to make: Stick with the service and shell out $5 a month, or cancel and go about your merry way. | Read more »
Alluris is a choose-your-own adventure g...
Alluris is an RPG that the developer's are calling a swipe-your-own adventure game. This is because the game incorporates a Reigns-style - swiping left or right - selection mechanic to make all the decisions you'd usually expect to make across... | Read more »
Hello Hero All Stars receives update wit...
The first Hello Hero game hit global platforms in 2013 and proved a huge success, with developer Fincon adding two more entries to this popular series of casual RPG games since. Released in June this year, Hello Hero All Stars brings many of the... | Read more »
Zombieland: Double Tapper, a cartoon idl...
Zombieland: Double Tapper is the idle RPG tie-in to the upcoming Zombieland: Double Tap. Oddly, it's one of two different Zombieland games launching today, with the other being the Switch title Zombieland: Double Tap - Road Trip. [Read more] | Read more »
Rusty Lake's The White Door launche...
Rusty Lake and Second Maze's intriguing point-and-click adventure game, The White Door, is now up for pre-order on the App Store. This one sees you playing as Robert Hill, a mental health patient who is suffering from severe memory loss. The game... | Read more »
Hellrule is an auto-runner inspired by G...
Hellrule is an upcoming auto-runner game from independent developer Pedrocorp where players will take control of a dapperly dressed gentlemen who comes equipped with a razor-sharp umbrella for slicing up his foes. The game will be available for... | Read more »
Grobo is a gravity bending puzzle platfo...
Grobo is a 2D puzzle platformer that marks the first release from developers Hot Chocolate Games. You'll find yourself manipulating gravity as you make your through this title that's available now for iOS and Android. [Read more] | Read more »
Adrenaline, Compulsive Entertainment’s h...
Compulsive Entertainment’s high-octane arcade racer, Adrenaline, has now made its way to the App Store following a successful launch on Google Play. It’s a ton of challenging, fast-paced fun, boasting easy-to-learn controls and a varied selection... | Read more »

Price Scanner via MacPrices.net

11″ WiFi iPad Pros on sale today for up to $2...
Amazon has new 2018 Apple 11″ WiFi iPad Pros in stock today and on sale for up to $250 off Apple’s MSRP. These are the same iPad Pros sold by Apple in its retail and online stores. Be sure to select... Read more
Apple offers clearance 2018 15″ 6-Core MacBoo...
Apple has clearance 2018 15″ 6-Core Touch Bar MacBook Pros, Certified Refurbished, available starting at only $1999. Each model features a new outer case, shipping is free, and an Apple 1-year... Read more
Apple continues to offer refurbished 2019 21″...
Apple has Certified Refurbished 2019 21″ & 27″ iMacs available starting at $929 and up to $350 off the cost of new models. Apple’s one-year warranty is standard, shipping is free, and each iMac... Read more
Verizon offers free iPhone 7 for customers wh...
Verizon is offering a free 32GB iPhone 7 for new or existing customers who open a new line of service, no trade-in required. Cost of the phone is credited to your account monthly for 24 months. The... Read more
13″ 2.4GHz 4-Core MacBook Pros remain availab...
Apple has a full line of Certified Refurbished 2019 13″ 2.4GHz 4-Core Touch Bar MacBook Pros available starting at $1529 and up to $300 off MSRP. Apple’s one-year warranty is included, shipping is... Read more
Lease one iPhone Xs, Xr, or X at Sprint and g...
Purchase an Apple iPhone X, Xr, Xs, or Xs Max at Sprint, and get a 64GB iPhone Xr for free. Requires 2 new lines or 1 upgrade-eligible line and 1 new line. The fine print: “iPhone Xs (64 GB) $37.50/... Read more
How to use your Apple Education discount to s...
Purchase a new MacBook Pro or MacBook Air using Apple’s Education discount, and take up to $200 off MSRP. All teachers, students, and staff of any educational institution with a .edu email address... Read more
Base 2019 13″ 1.4GHz 4-Core MacBook Pro on sa...
Amazon has the new 2019 13″ 1.4GHz/128GB 4-Core Space Gray Touch Bar MacBook Pro on sale for $100 off Apple’s MSRP. This is the same MacBook Pro sold by Apple in its retail and online stores: – 2019... Read more
Save up to $30 on Apple’s AirPods at these re...
Amazon is offering discounts on new 2019 Apple AirPods ranging up to $30 off MSRP. Shipping is free: – AirPods with Charging Case: $144 $15 off MSRP – AirPods with Wireless Charging Case: $169 $30... Read more
Save $15 on Apple Watch Series 5 models on Wa...
Walmart has new Apple Watch Series 5 models on sale for $15 off Apple’s MSRP on their online store. Choose free shipping or free local store pickup (if available). Sale prices for online orders only... Read more

Jobs Board

Best Buy *Apple* Computing Master - Best Bu...
**734688BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Store Associates **Location Number:** 000297-Reston-Store **Job Description:** The Core Read more
Best Buy *Apple* Computing Master - Best Bu...
**738529BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Location Number:** 000258-Lewisville-Store **Job Description:** **What does a Read more
*Apple* Mobility Pro - Best Buy (United Stat...
**741888BR** **Job Title:** Apple Mobility Pro **Job Category:** Store Associates **Location Number:** 000870-N Scottsdale-Store **Job Description:** At Best Buy, Read more
*Apple* Mobile App Developer - eiWorkflow So...
…eiWorkflow Solutions, LLC is currently looking for a consultant for the following role. Apple Mobile App Developer Tasks the role will be performing: ? Mobile App Read more
Essbase Developer - *Apple* - Theorem, LLC...
Job Summary Apple is seeking an experienced, detail-minded Essbase developer to join our worldwide business development and strategy team. If you are someone who Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.