How to resolve the algorithm QR decomposition step by step in the jq programming language

Published on 12 May 2024 09:40 PM
#Jq

How to resolve the algorithm QR decomposition step by step in the jq programming language

Table of Contents

Problem Statement

Any rectangular

m × n

{\displaystyle m\times n}

matrix

A

{\displaystyle {\mathit {A}}}

can be decomposed to a product of an orthogonal matrix

Q

{\displaystyle {\mathit {Q}}}

and an upper (right) triangular matrix

R

{\displaystyle {\mathit {R}}}

, as described in QR decomposition. Task Demonstrate the QR decomposition on the example matrix from the Wikipedia article: and the usage for linear least squares problems on the example from Polynomial regression. The method of Householder reflections should be used: Method Multiplying a given vector

a

{\displaystyle {\mathit {a}}}

, for example the first column of matrix

A

{\displaystyle {\mathit {A}}}

, with the Householder matrix

H

{\displaystyle {\mathit {H}}}

, which is given as reflects

a

{\displaystyle {\mathit {a}}}

about a plane given by its normal vector

u

{\displaystyle {\mathit {u}}}

. When the normal vector of the plane

u

{\displaystyle {\mathit {u}}}

is given as then the transformation reflects

a

{\displaystyle {\mathit {a}}}

onto the first standard basis vector which means that all entries but the first become zero. To avoid numerical cancellation errors, we should take the opposite sign of

a

1

{\displaystyle a_{1}}

: and normalize with respect to the first element: The equation for

H

{\displaystyle H}

thus becomes: or, in another form with Applying

H

{\displaystyle {\mathit {H}}}

on

a

{\displaystyle {\mathit {a}}}

then gives and applying

H

{\displaystyle {\mathit {H}}}

on the matrix

A

{\displaystyle {\mathit {A}}}

zeroes all subdiagonal elements of the first column: In the second step, the second column of

A

{\displaystyle {\mathit {A}}}

, we want to zero all elements but the first two, which means that we have to calculate

H

{\displaystyle {\mathit {H}}}

with the first column of the submatrix (denoted *), not on the whole second column of

A

{\displaystyle {\mathit {A}}}

. To get

H

2

{\displaystyle H_{2}}

, we then embed the new

H

{\displaystyle {\mathit {H}}}

into an

m × n

{\displaystyle m\times n}

identity: This is how we can, column by column, remove all subdiagonal elements of

A

{\displaystyle {\mathit {A}}}

and thus transform it into

R

{\displaystyle {\mathit {R}}}

. The product of all the Householder matrices

H

{\displaystyle {\mathit {H}}}

, for every column, in reverse order, will then yield the orthogonal matrix

Q

{\displaystyle {\mathit {Q}}}

. The QR decomposition should then be used to solve linear least squares (Multiple regression) problems

A

x

b

{\displaystyle {\mathit {A}}x=b}

by solving When

R

{\displaystyle {\mathit {R}}}

is not square, i.e.

m

n

{\displaystyle m>n}

we have to cut off the

m

− n

{\displaystyle {\mathit {m}}-n}

zero padded bottom rows. and the same for the RHS: Finally, solve the square upper triangular system by back substitution:

Let's start with the solution:

Step by Step solution about How to resolve the algorithm QR decomposition step by step in the jq programming language

Source code in the jq programming language

def sum(s): reduce s as $_ (0; . + $_);

# Sum of squares
def ss(s): sum(s|.*.);

# Create an m x n matrix
def matrix(m; n; init):
  if m == 0 then []
  elif m == 1 then [range(0;n) | init]
  elif m > 0 then
    matrix(1;n;init) as $row
    | [range(0;m) | $row ]
  else error("matrix\(m);_;_) invalid")
  end;

def dot_product(a; b):
  reduce range(0;a|length) as $i (0; . + (a[$i] * b[$i]) );

# A and B should both be numeric matrices, A being m by n, and B being n by p.
def multiply($A; $B):
  ($B[0]|length) as $p
  | ($B|transpose) as $BT
  | reduce range(0; $A|length) as $i
       ([];
       reduce range(0; $p) as $j 
         (.;
          .[$i][$j] = dot_product( $A[$i]; $BT[$j] ) ));

# $ndec decimal places
def round($ndec):
  def rpad: tostring | ($ndec - length) as $l | . + ("0" * $l);
  def abs: if . < 0 then -. else . end;
  pow(10; $ndec) as $p
  | round as $round
  | if $p * ((. - $round)|abs) < 0.1
    then ($round|tostring)  + "." + ($ndec * "0")
    else  . * $p | round / $p
    | tostring
    | capture("(?<left>[^.]*)[.](?<right>.*)")
    | .left + "." + (.right|rpad)
    end;

# pretty-print a 2-d matrix
def pp($ndec; $width):
  def pad(n): tostring | (n - length) * " " + .;
  def row: map(round($ndec) | pad($width)) | join(" ");
  reduce .[] as $row (""; . + "\n\($row|row)");

def minor($x; $d):
   ($x|length) as $nr
   | ($x[0]|length) as $nc
   | reduce range(0; $d) as $i (matrix($nr;$nc;0); .[$i][$i] = 1)
   | reduce range($d; $nr) as $i (.;
       reduce range($d;$nc) as $j (.; .[$i][$j] = $x[$i][$j] ) );

def vmadd($a; $b; $s):
  reduce range (0; $a|length) as $i ([];
    .[$i] = $a[$i] + $s * $b[$i] );

def vmul($v):
  ($v|length) as $n
  | reduce range(0;$n) as $i (null;
      reduce range(0;$n) as $j (.; .[$i][$j] = -2 * $v[$i] * $v[$j] ))
  | reduce range(0;$n) as $i (.; .[$i][$i] += 1 );

def vnorm($x):
  sum($x[] | .*.) | sqrt;

def vdiv($x; $d):
  [range (0;$x|length) | $x[.] / $d];

def mcol($m; $c):
  [range (0;$m|length) | $m[.][$c]];

def householder($m):
    ($m|length) as $nr
    | ($m[0]|length) as $nc
    | { q: [], # $nr
        z: $m,
        k: 0 }
    | until( .k >= $nc or .k >= $nr-1;
        .z = minor(.z; .k)
        | .x = mcol(.z; .k)
        | .a = vnorm(.x)
        | if ($m[.k][.k] > 0) then .a = -.a else . end
        | .e = [range (0; $nr) as $i | if ($i == .k) then 1 else 0 end]
        | .e = vmadd(.x; .e; .a)
        | .e = vdiv(.e; vnorm(.e))
        | .q[.k] = vmul(.e)
        | .z = multiply(.q[.k]; .z)
        | .k += 1 )
    | .Q = .q[0]
    | .R = multiply(.q[0]; $m)
    | .i = 1
    | until (.i >= $nc or .i >= $nr-1;
        .Q = multiply(.q[.i]; .Q)
        | .i += 1 )
    | .R = multiply(.Q; $m)
    | .Q |= transpose
    | [.Q, .R] ;

def x: [
  [12, -51,   4],
  [ 6, 167, -68],
  [-4,  24, -41],
  [-1,   1,   0],
  [ 2,   0,   3]
];

def task:
  def pp: pp(3;8);

  # Assume $a and $b are conformal
  def ssd($a; $b):
    [$a[][]] as $a
    | [$b[][]] as $b
    | ss( range(0;$a|length) | $a[.] - $b[.] );

  householder(x) as [$Q, $R]
  | multiply($Q; $R) as $m
  | "Q:",      ($Q|pp),
   "\nR:",     ($R|pp),
   "\nQ * R:", ($m|pp),
   "\nSum of squared discrepancies: \(ssd(x; $m))" 
;

task

  

You may also check:How to resolve the algorithm Sum of a series step by step in the Unicon programming language
You may also check:How to resolve the algorithm MD5 step by step in the Slate programming language
You may also check:How to resolve the algorithm Stack step by step in the Action! programming language
You may also check:How to resolve the algorithm Check that file exists step by step in the Elena programming language
You may also check:How to resolve the algorithm Execute a system command step by step in the FreeBASIC programming language