<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>http://e6.ijs.si/medusa/wiki/api.php?action=feedcontributions&amp;user=Mdepolli&amp;feedformat=atom</id>
		<title>Medusa: Coordinate Free Mehless Method implementation - User contributions [en]</title>
		<link rel="self" type="application/atom+xml" href="http://e6.ijs.si/medusa/wiki/api.php?action=feedcontributions&amp;user=Mdepolli&amp;feedformat=atom"/>
		<link rel="alternate" type="text/html" href="http://e6.ijs.si/medusa/wiki/index.php/Special:Contributions/Mdepolli"/>
		<updated>2026-04-24T08:15:20Z</updated>
		<subtitle>User contributions</subtitle>
		<generator>MediaWiki 1.27.1</generator>

	<entry>
		<id>http://e6.ijs.si/medusa/wiki/index.php?title=Pardiso&amp;diff=1223</id>
		<title>Pardiso</title>
		<link rel="alternate" type="text/html" href="http://e6.ijs.si/medusa/wiki/index.php?title=Pardiso&amp;diff=1223"/>
				<updated>2017-07-19T13:19:31Z</updated>
		
		<summary type="html">&lt;p&gt;Mdepolli: /* Pardiso library */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Pardiso library=&lt;br /&gt;
&lt;br /&gt;
Pardiso library can be downloaded from [http://www.pardiso-project.org/]. There are also examples of use, the manual, etc. Note that the source of the library is not available. There are only pre-compiled objects available on download after one registers on the site.&lt;br /&gt;
* The package PARDISO is a thread-safe, high-performance, robust, memory efficient and easy to use software for solving large sparse symmetric and unsymmetric linear systems of equations on shared-memory and distributed-memory multiprocessors. The solver uses a combination of left- and right-looking Level-3 BLAS supernode techniques&amp;quot;. &lt;br /&gt;
* Pardiso is proprietary software but is available for academic use; by registering a yearly license can be obtained.&lt;br /&gt;
* Parallelization is implemented using both OpenMP and MPI.&lt;br /&gt;
* MPI-based numerical factorization and parallel forward/backward substitution on distributed-memory architectures is implemented for '''symmetric indefinite matrices'''.&lt;br /&gt;
&lt;br /&gt;
==Example of use==&lt;br /&gt;
&lt;br /&gt;
This minimal example makes use of the OpenMP parallelization to run Pardiso solver on a sample matrix. &lt;br /&gt;
&lt;br /&gt;
test.cpp:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
#include &amp;lt;sstream&amp;gt;&lt;br /&gt;
#include &amp;lt;vector&amp;gt;&lt;br /&gt;
#include &amp;lt;cassert&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// PARDISO prototype.&lt;br /&gt;
// There is no pardiso.h, therefore function prototypes have to be defined within the user's code&lt;br /&gt;
extern &amp;quot;C&amp;quot; void pardisoinit (void   *, int    *,   int *, int *, double *, int *);&lt;br /&gt;
extern &amp;quot;C&amp;quot; void pardiso     (void   *, int    *,   int *, int *,    int *, int *, &lt;br /&gt;
                  double *, int    *,    int *, int *,   int *, int *,&lt;br /&gt;
                     int *, double *, double *, int *, double *);&lt;br /&gt;
extern &amp;quot;C&amp;quot; void pardiso_chkmatrix  (int *, int *, double *, int *, int *, int *);&lt;br /&gt;
extern &amp;quot;C&amp;quot; void pardiso_chkvec     (int *, int *, double *, int *);&lt;br /&gt;
extern &amp;quot;C&amp;quot; void pardiso_printstats (int *, int *, double *, int *, int *, int *, double *, int *);&lt;br /&gt;
&lt;br /&gt;
struct TestCase {&lt;br /&gt;
    // the dimension of the problem, Matrix A will have n*n elements, while vectors b and x will have n elements&lt;br /&gt;
    int n;&lt;br /&gt;
    // indices of individual rows in compact matrix definition: row[0] contains non zero elements at column indices ia[0] .. (ia[1]-1)&lt;br /&gt;
    std::vector&amp;lt;int&amp;gt; ia;&lt;br /&gt;
    // column indices of non-zero elements&lt;br /&gt;
    std::vector&amp;lt;int&amp;gt; ja;&lt;br /&gt;
    // values of non-zero elements&lt;br /&gt;
    std::vector&amp;lt;double&amp;gt; A;&lt;br /&gt;
    // right hand side&lt;br /&gt;
    std::vector&amp;lt;double&amp;gt; b;&lt;br /&gt;
    // solution vector&lt;br /&gt;
    std::vector&amp;lt;double&amp;gt; x;&lt;br /&gt;
&lt;br /&gt;
    // Pardiso workspace variable (using void* as the type ensures enough space is allocated on 32 and 64-bit systems)&lt;br /&gt;
    void*    pt[64];&lt;br /&gt;
    // Pardiso solver parameter variables&lt;br /&gt;
    int      iparm[64];&lt;br /&gt;
    double   dparm[64];&lt;br /&gt;
    &lt;br /&gt;
    void createTestMatrix(int n) {&lt;br /&gt;
        this-&amp;gt;n = n;&lt;br /&gt;
        &lt;br /&gt;
        // -2 on the diagonal (except topmost and bottommost, which are 1), 1 on the +1 and -1 diagonals (except topmost and bottommost, which are 0)&lt;br /&gt;
        ia.reserve(n*2);&lt;br /&gt;
        ja.reserve(n*3);&lt;br /&gt;
        A.reserve(n*3);  // 3 per row&lt;br /&gt;
        &lt;br /&gt;
        // 1st row (only 1 element)&lt;br /&gt;
        ia.push_back(0);&lt;br /&gt;
        A.push_back(1);&lt;br /&gt;
        ja.push_back(0);&lt;br /&gt;
&lt;br /&gt;
        for (int i = 1; i &amp;lt; n-1; ++i) {&lt;br /&gt;
            // new row is stating&lt;br /&gt;
            ia.push_back(A.size());&lt;br /&gt;
            &lt;br /&gt;
            // the 3 diagonal elements&lt;br /&gt;
            A.push_back(1);&lt;br /&gt;
            ja.push_back(i-1);&lt;br /&gt;
            &lt;br /&gt;
            A.push_back(-2);&lt;br /&gt;
            ja.push_back(i);&lt;br /&gt;
            &lt;br /&gt;
            A.push_back(1);&lt;br /&gt;
            ja.push_back(i+1);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // last row (only 1 element)&lt;br /&gt;
        ia.push_back(A.size());&lt;br /&gt;
        A.push_back(1);&lt;br /&gt;
        ja.push_back(n-1);&lt;br /&gt;
        &lt;br /&gt;
        // the last element points at the end of the matrix (n+1 row)&lt;br /&gt;
        ia.push_back(A.size());&lt;br /&gt;
        &lt;br /&gt;
        // Convert matrix from 0-based to 1-based notation (Fortran...)&lt;br /&gt;
        for (auto i = 0; i &amp;lt; ia.size(); i++) &lt;br /&gt;
            ia[i] += 1;&lt;br /&gt;
        for (auto i = 0; i &amp;lt; ja.size(); i++) &lt;br /&gt;
            ja[i] += 1;&lt;br /&gt;
            &lt;br /&gt;
        // set b to 1 (topmost and bottommost elements) and 1/n² (the other elements)&lt;br /&gt;
        b.resize(n, 1);&lt;br /&gt;
        for (auto i = 0; i &amp;lt; b.size(); i++) &lt;br /&gt;
            b[i] /= (n*n);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    void solve(int numThreads) {&lt;br /&gt;
        // the solution placeholder&lt;br /&gt;
        x.resize(n, 0);&lt;br /&gt;
        &lt;br /&gt;
        int error = 0;&lt;br /&gt;
        int solver = 0;     // use sparse direct solver&lt;br /&gt;
        int maxfct, mnum, phase, msglvl;&lt;br /&gt;
        int mtype = 11;     // Real unsymmetric matrix&lt;br /&gt;
        double ddum;        // double dummy variable&lt;br /&gt;
        int idum;           // integer dummy variable&lt;br /&gt;
        int nrhs = 1;       // number of right hand sides&lt;br /&gt;
        maxfct = 1;         // Maximum number of numerical factorizations.&lt;br /&gt;
        mnum   = 1;         // Which factorization to use.&lt;br /&gt;
        msglvl = 1;         // Print statistical information&lt;br /&gt;
        error  = 0;         // Initialize error flag to no-error&lt;br /&gt;
        iparm[2] = numThreads;  // initialize the number of threads &lt;br /&gt;
        &lt;br /&gt;
        pardisoinit (pt,  &amp;amp;mtype, &amp;amp;solver, iparm, dparm, &amp;amp;error);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        pardiso_chkmatrix  (&amp;amp;mtype, &amp;amp;n, A.data(), ia.data(), ja.data(), &amp;amp;error);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        pardiso_chkvec (&amp;amp;n, &amp;amp;nrhs, b.data(), &amp;amp;error);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        pardiso_printstats (&amp;amp;mtype, &amp;amp;n, A.data(), ia.data(), ja.data(), &amp;amp;nrhs, b.data(), &amp;amp;error);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   &lt;br /&gt;
        phase = 11;     // reordering &amp;amp; symbolic factorization&lt;br /&gt;
        pardiso (pt, &amp;amp;maxfct, &amp;amp;mnum, &amp;amp;mtype, &amp;amp;phase, &amp;amp;n, A.data(), ia.data(), ja.data(), &amp;amp;idum, &amp;amp;nrhs, iparm, &amp;amp;msglvl, &amp;amp;ddum, &amp;amp;ddum, &amp;amp;error, dparm); &lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        phase = 22;     // numerical factorization&lt;br /&gt;
        pardiso (pt, &amp;amp;maxfct, &amp;amp;mnum, &amp;amp;mtype, &amp;amp;phase, &amp;amp;n, A.data(), ia.data(), ja.data(), &amp;amp;idum, &amp;amp;nrhs, iparm, &amp;amp;msglvl, &amp;amp;ddum, &amp;amp;ddum, &amp;amp;error, dparm);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        phase = 33;     // back substitution and refinement&lt;br /&gt;
        iparm[7] = 10;  // number of refinement steps&lt;br /&gt;
        pardiso (pt, &amp;amp;maxfct, &amp;amp;mnum, &amp;amp;mtype, &amp;amp;phase, &amp;amp;n, A.data(), ia.data(), ja.data(), &amp;amp;idum, &amp;amp;nrhs, iparm, &amp;amp;msglvl, b.data(), x.data(), &amp;amp;error, dparm);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        phase = -1;     // finalization and memory release&lt;br /&gt;
        pardiso (pt, &amp;amp;maxfct, &amp;amp;mnum, &amp;amp;mtype, &amp;amp;phase, &amp;amp;n, A.data(), ia.data(), ja.data(), &amp;amp;idum, &amp;amp;nrhs, iparm, &amp;amp;msglvl, b.data(), x.data(), &amp;amp;error, dparm);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    assert(argc == 2 &amp;amp;&amp;amp; &amp;quot;Second argument is size of the system.&amp;quot;);&lt;br /&gt;
    int n, nThreads;&lt;br /&gt;
    std::stringstream ss(argv[1]);&lt;br /&gt;
    ss &amp;gt;&amp;gt; n;&lt;br /&gt;
    auto omp_num_threads = getenv(&amp;quot;OMP_NUM_THREADS&amp;quot;);&lt;br /&gt;
    if (omp_num_threads != nullptr) {&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;OMP_NUM_THREADS = &amp;quot; &amp;lt;&amp;lt; omp_num_threads &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        nThreads = atoi(omp_num_threads);      // number of threads&lt;br /&gt;
    }&lt;br /&gt;
    std::cout &amp;lt;&amp;lt; &amp;quot;n = &amp;quot; &amp;lt;&amp;lt; n &amp;lt;&amp;lt; &amp;quot;, num threads = &amp;quot; &amp;lt;&amp;lt; nThreads &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
    &lt;br /&gt;
    TestCase testcase;&lt;br /&gt;
    testcase.createTestMatrix(n);&lt;br /&gt;
    testcase.solve(nThreads);&lt;br /&gt;
    return 0;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code is compiled with GCC using the following command line: &lt;br /&gt;
&amp;lt;code&amp;gt;g++ test.cpp -o test -O3 -L./ -lpardiso500-GNU481-X86-64 -lgfortran -fopenmp -lpthread -lm -llapack -lblas&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then it is executed:&lt;br /&gt;
&amp;lt;code&amp;gt;OMP_NUM_THREADS=8 ./test 1000000&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This takes quite some time on the experimental machine (Intel i7 870 @ 2.93 GHz), which is dilligently reported by Pardiso itself:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Summary PARDISO 5.0.0: ( reorder to reorder )&lt;br /&gt;
    Time fulladj: 0.054887 s&lt;br /&gt;
    Time reorder: 2.192858 s&lt;br /&gt;
    Time symbfct: 0.189367 s&lt;br /&gt;
    Time parlist: 0.059304 s&lt;br /&gt;
    Time malloc : 0.122711 s&lt;br /&gt;
    Time total  : 2.871472 s total - sum: 0.252344 s&lt;br /&gt;
Summary PARDISO 5.0.0: ( factorize to factorize )&lt;br /&gt;
    Time A to LU: 0.000000 s&lt;br /&gt;
    Time numfct : 0.196008 s&lt;br /&gt;
    Time malloc : -0.000004 s&lt;br /&gt;
    Time total  : 0.196070 s total - sum: 0.000066 s&lt;br /&gt;
Summary PARDISO 5.0.0: ( solve to solve )&lt;br /&gt;
    Time solve  : 0.033254 s&lt;br /&gt;
    Time total  : 0.200364 s total - sum: 0.167110 s&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mdepolli</name></author>	</entry>

	<entry>
		<id>http://e6.ijs.si/medusa/wiki/index.php?title=Pardiso&amp;diff=1222</id>
		<title>Pardiso</title>
		<link rel="alternate" type="text/html" href="http://e6.ijs.si/medusa/wiki/index.php?title=Pardiso&amp;diff=1222"/>
				<updated>2017-07-19T11:21:00Z</updated>
		
		<summary type="html">&lt;p&gt;Mdepolli: Created page with &amp;quot;=Pardiso library=  Pardiso library can be downloaded from [http://www.pardiso-project.org/]. There are also examples of use, the manual, etc. Note that the source of the libra...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Pardiso library=&lt;br /&gt;
&lt;br /&gt;
Pardiso library can be downloaded from [http://www.pardiso-project.org/]. There are also examples of use, the manual, etc. Note that the source of the library is not available. There are only pre-compiled objects available on download after one registers on the site.&lt;br /&gt;
* The package PARDISO is a thread-safe, high-performance, robust, memory efficient and easy to use software for solving large sparse symmetric and unsymmetric linear systems of equations on shared-memory and distributed-memory multiprocessors. The solver uses a combination of left- and right-looking Level-3 BLAS supernode techniques&amp;quot;. &lt;br /&gt;
* Pardiso is proprietary software but is available for academic use; by registering a yearly license can be obtained.&lt;br /&gt;
* Parallelization is implemented using both OpenMP and MPI.&lt;br /&gt;
&lt;br /&gt;
==Example of use==&lt;br /&gt;
&lt;br /&gt;
This minimal example makes use of the OpenMP parallelization to run Pardiso solver on a sample matrix. &lt;br /&gt;
&lt;br /&gt;
test.cpp:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
#include &amp;lt;sstream&amp;gt;&lt;br /&gt;
#include &amp;lt;vector&amp;gt;&lt;br /&gt;
#include &amp;lt;cassert&amp;gt;&lt;br /&gt;
#include &amp;lt;math.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// PARDISO prototype.&lt;br /&gt;
// There is no pardiso.h, therefore function prototypes have to be defined within the user's code&lt;br /&gt;
extern &amp;quot;C&amp;quot; void pardisoinit (void   *, int    *,   int *, int *, double *, int *);&lt;br /&gt;
extern &amp;quot;C&amp;quot; void pardiso     (void   *, int    *,   int *, int *,    int *, int *, &lt;br /&gt;
                  double *, int    *,    int *, int *,   int *, int *,&lt;br /&gt;
                     int *, double *, double *, int *, double *);&lt;br /&gt;
extern &amp;quot;C&amp;quot; void pardiso_chkmatrix  (int *, int *, double *, int *, int *, int *);&lt;br /&gt;
extern &amp;quot;C&amp;quot; void pardiso_chkvec     (int *, int *, double *, int *);&lt;br /&gt;
extern &amp;quot;C&amp;quot; void pardiso_printstats (int *, int *, double *, int *, int *, int *, double *, int *);&lt;br /&gt;
&lt;br /&gt;
struct TestCase {&lt;br /&gt;
    // the dimension of the problem, Matrix A will have n*n elements, while vectors b and x will have n elements&lt;br /&gt;
    int n;&lt;br /&gt;
    // indices of individual rows in compact matrix definition: row[0] contains non zero elements at column indices ia[0] .. (ia[1]-1)&lt;br /&gt;
    std::vector&amp;lt;int&amp;gt; ia;&lt;br /&gt;
    // column indices of non-zero elements&lt;br /&gt;
    std::vector&amp;lt;int&amp;gt; ja;&lt;br /&gt;
    // values of non-zero elements&lt;br /&gt;
    std::vector&amp;lt;double&amp;gt; A;&lt;br /&gt;
    // right hand side&lt;br /&gt;
    std::vector&amp;lt;double&amp;gt; b;&lt;br /&gt;
    // solution vector&lt;br /&gt;
    std::vector&amp;lt;double&amp;gt; x;&lt;br /&gt;
&lt;br /&gt;
    // Pardiso workspace variable (using void* as the type ensures enough space is allocated on 32 and 64-bit systems)&lt;br /&gt;
    void*    pt[64];&lt;br /&gt;
    // Pardiso solver parameter variables&lt;br /&gt;
    int      iparm[64];&lt;br /&gt;
    double   dparm[64];&lt;br /&gt;
    &lt;br /&gt;
    void createTestMatrix(int n) {&lt;br /&gt;
        this-&amp;gt;n = n;&lt;br /&gt;
        &lt;br /&gt;
        // -2 on the diagonal (except topmost and bottommost, which are 1), 1 on the +1 and -1 diagonals (except topmost and bottommost, which are 0)&lt;br /&gt;
        ia.reserve(n*2);&lt;br /&gt;
        ja.reserve(n*3);&lt;br /&gt;
        A.reserve(n*3);  // 3 per row&lt;br /&gt;
        &lt;br /&gt;
        // 1st row (only 1 element)&lt;br /&gt;
        ia.push_back(0);&lt;br /&gt;
        A.push_back(1);&lt;br /&gt;
        ja.push_back(0);&lt;br /&gt;
&lt;br /&gt;
        for (int i = 1; i &amp;lt; n-1; ++i) {&lt;br /&gt;
            // new row is stating&lt;br /&gt;
            ia.push_back(A.size());&lt;br /&gt;
            &lt;br /&gt;
            // the 3 diagonal elements&lt;br /&gt;
            A.push_back(1);&lt;br /&gt;
            ja.push_back(i-1);&lt;br /&gt;
            &lt;br /&gt;
            A.push_back(-2);&lt;br /&gt;
            ja.push_back(i);&lt;br /&gt;
            &lt;br /&gt;
            A.push_back(1);&lt;br /&gt;
            ja.push_back(i+1);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // last row (only 1 element)&lt;br /&gt;
        ia.push_back(A.size());&lt;br /&gt;
        A.push_back(1);&lt;br /&gt;
        ja.push_back(n-1);&lt;br /&gt;
        &lt;br /&gt;
        // the last element points at the end of the matrix (n+1 row)&lt;br /&gt;
        ia.push_back(A.size());&lt;br /&gt;
        &lt;br /&gt;
        // Convert matrix from 0-based to 1-based notation (Fortran...)&lt;br /&gt;
        for (auto i = 0; i &amp;lt; ia.size(); i++) &lt;br /&gt;
            ia[i] += 1;&lt;br /&gt;
        for (auto i = 0; i &amp;lt; ja.size(); i++) &lt;br /&gt;
            ja[i] += 1;&lt;br /&gt;
            &lt;br /&gt;
        // set b to 1 (topmost and bottommost elements) and 1/n² (the other elements)&lt;br /&gt;
        b.resize(n, 1);&lt;br /&gt;
        for (auto i = 0; i &amp;lt; b.size(); i++) &lt;br /&gt;
            b[i] /= (n*n);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    void solve(int numThreads) {&lt;br /&gt;
        // the solution placeholder&lt;br /&gt;
        x.resize(n, 0);&lt;br /&gt;
        &lt;br /&gt;
        int error = 0;&lt;br /&gt;
        int solver = 0;     // use sparse direct solver&lt;br /&gt;
        int maxfct, mnum, phase, msglvl;&lt;br /&gt;
        int mtype = 11;     // Real unsymmetric matrix&lt;br /&gt;
        double ddum;        // double dummy variable&lt;br /&gt;
        int idum;           // integer dummy variable&lt;br /&gt;
        int nrhs = 1;       // number of right hand sides&lt;br /&gt;
        maxfct = 1;         // Maximum number of numerical factorizations.&lt;br /&gt;
        mnum   = 1;         // Which factorization to use.&lt;br /&gt;
        msglvl = 1;         // Print statistical information&lt;br /&gt;
        error  = 0;         // Initialize error flag to no-error&lt;br /&gt;
        iparm[2] = numThreads;  // initialize the number of threads &lt;br /&gt;
        &lt;br /&gt;
        pardisoinit (pt,  &amp;amp;mtype, &amp;amp;solver, iparm, dparm, &amp;amp;error);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        pardiso_chkmatrix  (&amp;amp;mtype, &amp;amp;n, A.data(), ia.data(), ja.data(), &amp;amp;error);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        pardiso_chkvec (&amp;amp;n, &amp;amp;nrhs, b.data(), &amp;amp;error);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        pardiso_printstats (&amp;amp;mtype, &amp;amp;n, A.data(), ia.data(), ja.data(), &amp;amp;nrhs, b.data(), &amp;amp;error);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   &lt;br /&gt;
        phase = 11;     // reordering &amp;amp; symbolic factorization&lt;br /&gt;
        pardiso (pt, &amp;amp;maxfct, &amp;amp;mnum, &amp;amp;mtype, &amp;amp;phase, &amp;amp;n, A.data(), ia.data(), ja.data(), &amp;amp;idum, &amp;amp;nrhs, iparm, &amp;amp;msglvl, &amp;amp;ddum, &amp;amp;ddum, &amp;amp;error, dparm); &lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        phase = 22;     // numerical factorization&lt;br /&gt;
        pardiso (pt, &amp;amp;maxfct, &amp;amp;mnum, &amp;amp;mtype, &amp;amp;phase, &amp;amp;n, A.data(), ia.data(), ja.data(), &amp;amp;idum, &amp;amp;nrhs, iparm, &amp;amp;msglvl, &amp;amp;ddum, &amp;amp;ddum, &amp;amp;error, dparm);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        phase = 33;     // back substitution and refinement&lt;br /&gt;
        iparm[7] = 10;  // number of refinement steps&lt;br /&gt;
        pardiso (pt, &amp;amp;maxfct, &amp;amp;mnum, &amp;amp;mtype, &amp;amp;phase, &amp;amp;n, A.data(), ia.data(), ja.data(), &amp;amp;idum, &amp;amp;nrhs, iparm, &amp;amp;msglvl, b.data(), x.data(), &amp;amp;error, dparm);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        phase = -1;     // finalization and memory release&lt;br /&gt;
        pardiso (pt, &amp;amp;maxfct, &amp;amp;mnum, &amp;amp;mtype, &amp;amp;phase, &amp;amp;n, A.data(), ia.data(), ja.data(), &amp;amp;idum, &amp;amp;nrhs, iparm, &amp;amp;msglvl, b.data(), x.data(), &amp;amp;error, dparm);&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;error = &amp;quot; &amp;lt;&amp;lt; error &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    assert(argc == 2 &amp;amp;&amp;amp; &amp;quot;Second argument is size of the system.&amp;quot;);&lt;br /&gt;
    int n, nThreads;&lt;br /&gt;
    std::stringstream ss(argv[1]);&lt;br /&gt;
    ss &amp;gt;&amp;gt; n;&lt;br /&gt;
    auto omp_num_threads = getenv(&amp;quot;OMP_NUM_THREADS&amp;quot;);&lt;br /&gt;
    if (omp_num_threads != nullptr) {&lt;br /&gt;
        std::cerr &amp;lt;&amp;lt; &amp;quot;OMP_NUM_THREADS = &amp;quot; &amp;lt;&amp;lt; omp_num_threads &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
        nThreads = atoi(omp_num_threads);      // number of threads&lt;br /&gt;
    }&lt;br /&gt;
    std::cout &amp;lt;&amp;lt; &amp;quot;n = &amp;quot; &amp;lt;&amp;lt; n &amp;lt;&amp;lt; &amp;quot;, num threads = &amp;quot; &amp;lt;&amp;lt; nThreads &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
    &lt;br /&gt;
    TestCase testcase;&lt;br /&gt;
    testcase.createTestMatrix(n);&lt;br /&gt;
    testcase.solve(nThreads);&lt;br /&gt;
    return 0;&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code is compiled with GCC using the following command line: &lt;br /&gt;
&amp;lt;code&amp;gt;g++ test.cpp -o test -O3 -L./ -lpardiso500-GNU481-X86-64 -lgfortran -fopenmp -lpthread -lm -llapack -lblas&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then it is executed:&lt;br /&gt;
&amp;lt;code&amp;gt;OMP_NUM_THREADS=8 ./test 1000000&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This takes quite some time on the experimental machine (Intel i7 870 @ 2.93 GHz), which is dilligently reported by Pardiso itself:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Summary PARDISO 5.0.0: ( reorder to reorder )&lt;br /&gt;
    Time fulladj: 0.054887 s&lt;br /&gt;
    Time reorder: 2.192858 s&lt;br /&gt;
    Time symbfct: 0.189367 s&lt;br /&gt;
    Time parlist: 0.059304 s&lt;br /&gt;
    Time malloc : 0.122711 s&lt;br /&gt;
    Time total  : 2.871472 s total - sum: 0.252344 s&lt;br /&gt;
Summary PARDISO 5.0.0: ( factorize to factorize )&lt;br /&gt;
    Time A to LU: 0.000000 s&lt;br /&gt;
    Time numfct : 0.196008 s&lt;br /&gt;
    Time malloc : -0.000004 s&lt;br /&gt;
    Time total  : 0.196070 s total - sum: 0.000066 s&lt;br /&gt;
Summary PARDISO 5.0.0: ( solve to solve )&lt;br /&gt;
    Time solve  : 0.033254 s&lt;br /&gt;
    Time total  : 0.200364 s total - sum: 0.167110 s&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mdepolli</name></author>	</entry>

	<entry>
		<id>http://e6.ijs.si/medusa/wiki/index.php?title=Solving_sparse_systems&amp;diff=1221</id>
		<title>Solving sparse systems</title>
		<link rel="alternate" type="text/html" href="http://e6.ijs.si/medusa/wiki/index.php?title=Solving_sparse_systems&amp;diff=1221"/>
				<updated>2017-07-19T10:57:19Z</updated>
		
		<summary type="html">&lt;p&gt;Mdepolli: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;There are many methods available for solving sparse systems. We compare some of them here.&lt;br /&gt;
&amp;lt;figure id=&amp;quot;fig:matrix1&amp;quot;&amp;gt;&lt;br /&gt;
[[File:matrix.png|300px|thumb|upright=2|alt=Matrix of the discretized PDE.|&amp;lt;caption&amp;gt;Matrix of the discretized PDE. &amp;lt;/caption&amp;gt;]]&lt;br /&gt;
&amp;lt;/figure&amp;gt;&lt;br /&gt;
Mathematica has the following methods available (https://reference.wolfram.com/language/ref/LinearSolve.html#DetailsAndOptions)&lt;br /&gt;
* direct: banded, cholesky, multifrontal (direct sparse LU)&lt;br /&gt;
* iterative: Krylov&lt;br /&gt;
&lt;br /&gt;
Matlab has the following methods:&lt;br /&gt;
* direct: https://www.mathworks.com/help/matlab/ref/mldivide.html#bt42omx_head&lt;br /&gt;
* iterative: https://www.mathworks.com/help/matlab/math/systems-of-linear-equations.html#brzoiix, including bicgstab, gmres &lt;br /&gt;
&lt;br /&gt;
Eigen has the following methods: (https://eigen.tuxfamily.org/dox-devel/group__TopicSparseSystems.html)&lt;br /&gt;
* direct: sparse LU&lt;br /&gt;
* iterative: bicgstab, cg&lt;br /&gt;
&lt;br /&gt;
Solving a simple sparse system $A x = b$ for steady space of heat equation in 1d with $n$ nodes, results in a matrix shown in &amp;lt;xr id=&amp;quot;fig:matrix1&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following timings of solvers are given in seconds:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! $n = 10^6$&lt;br /&gt;
! Matlab&lt;br /&gt;
! Mathematica&lt;br /&gt;
! Eigen&lt;br /&gt;
|-&lt;br /&gt;
! Banded&lt;br /&gt;
| 0.16&lt;br /&gt;
| 0.28&lt;br /&gt;
| 0.04&lt;br /&gt;
|-&lt;br /&gt;
! SparseLU&lt;br /&gt;
| /&lt;br /&gt;
| 1.73&lt;br /&gt;
| 0.82&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Incomplete LU preconditioner was used for BICGStab.&lt;br /&gt;
Without the preconditioner BICGStab does not converge.&lt;br /&gt;
&lt;br /&gt;
==Parallel execution ==&lt;br /&gt;
BICGStab can be run in parallel, as explain in the general parallelism: https://eigen.tuxfamily.org/dox/TopicMultiThreading.html, and specifically&lt;br /&gt;
&lt;br /&gt;
'''&amp;quot;When using sparse matrices, best performance is achied for a row-major sparse matrix format.&amp;lt;br&amp;gt;Moreover, in this case multi-threading can be exploited if the user code is compiled with OpenMP enabled&amp;quot;.'''&lt;br /&gt;
&lt;br /&gt;
Eigen uses number of threads specified my OopenMP, unless &amp;lt;code&amp;gt;Eigen::setNbThreads(n);&amp;lt;/code&amp;gt; was called.&lt;br /&gt;
Minimal working example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;figure id=&amp;quot;fig:eigen_par&amp;quot;&amp;gt;&lt;br /&gt;
[[File:eigen_parallel_2.png|1200px|thumb|upright=2|alt=Matrix of the discretized PDE.|&amp;lt;caption&amp;gt;Memory and CPU usage. C stands for construction of the system, L stands for the calculation of ILUT preconditioner and S for BICGStab iteration.&amp;lt;/caption&amp;gt;]]&lt;br /&gt;
&amp;lt;/figure&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
#include &amp;lt;vector&amp;gt;&lt;br /&gt;
#include &amp;quot;Eigen/Sparse&amp;quot;&lt;br /&gt;
#include &amp;quot;Eigen/IterativeLinearSolvers&amp;quot;&lt;br /&gt;
&lt;br /&gt;
using namespace std;&lt;br /&gt;
using namespace Eigen;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
&lt;br /&gt;
    assert(argc == 2 &amp;amp;&amp;amp; &amp;quot;Second argument is size of the system.&amp;quot;);&lt;br /&gt;
    stringstream ss(argv[1]);&lt;br /&gt;
    int n;&lt;br /&gt;
    ss &amp;gt;&amp;gt; n;&lt;br /&gt;
    cout &amp;lt;&amp;lt; &amp;quot;n = &amp;quot; &amp;lt;&amp;lt; n &amp;lt;&amp;lt; endl;&lt;br /&gt;
&lt;br /&gt;
    // Fill the matrix&lt;br /&gt;
    VectorXd b = VectorXd::Ones(n) / n / n;&lt;br /&gt;
    b(0) = b(n-1) = 1;&lt;br /&gt;
    SparseMatrix&amp;lt;double, RowMajor&amp;gt; A(n, n);&lt;br /&gt;
    A.reserve(vector&amp;lt;int&amp;gt;(n, 3));  // 3 per row&lt;br /&gt;
    for (int i = 0; i &amp;lt; n-1; ++i) {&lt;br /&gt;
        A.insert(i, i) = -2;&lt;br /&gt;
        A.insert(i, i+1) = 1;&lt;br /&gt;
        A.insert(i+1, i) = 1;&lt;br /&gt;
    }&lt;br /&gt;
    A.coeffRef(0, 0) = 1;&lt;br /&gt;
    A.coeffRef(0, 1) = 0;&lt;br /&gt;
    A.coeffRef(n-1, n-2) = 0;&lt;br /&gt;
    A.coeffRef(n-1, n-1) = 1;&lt;br /&gt;
&lt;br /&gt;
    // Solve the system&lt;br /&gt;
    BiCGSTAB&amp;lt;SparseMatrix&amp;lt;double, RowMajor&amp;gt;, IncompleteLUT&amp;lt;double&amp;gt;&amp;gt; solver;&lt;br /&gt;
    solver.setTolerance(1e-10);&lt;br /&gt;
    solver.setMaxIterations(1000);&lt;br /&gt;
    solver.compute(A);&lt;br /&gt;
    VectorXd x = solver.solve(b);&lt;br /&gt;
    cout &amp;lt;&amp;lt; &amp;quot;#iterations:     &amp;quot; &amp;lt;&amp;lt; solver.iterations() &amp;lt;&amp;lt; endl;&lt;br /&gt;
    cout &amp;lt;&amp;lt; &amp;quot;estimated error: &amp;quot; &amp;lt;&amp;lt; solver.error()      &amp;lt;&amp;lt; endl;&lt;br /&gt;
    cout &amp;lt;&amp;lt; &amp;quot;sol: &amp;quot; &amp;lt;&amp;lt; x.head(6).transpose() &amp;lt;&amp;lt; endl;&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
was compiled with &amp;lt;code&amp;gt;g++ -o parallel_solve -O3 -fopenmp solver_test_parallel.cpp&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;xr id=&amp;quot;fig:eigen_par&amp;quot;/&amp;gt; was produced when the program above was run as &amp;lt;code&amp;gt;./parallel_solve 10000000&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==ILUT factorization==&lt;br /&gt;
This is a method to compute a general algebraic preconditioner which has the ability to control (to some degree) the memory and time complexity of the solution.&lt;br /&gt;
It is described in a paper by  &lt;br /&gt;
&lt;br /&gt;
  Saad, Yousef. &amp;quot;ILUT: A dual threshold incomplete LU factorization.&amp;quot; Numerical linear algebra with applications 1.4 (1994): 387-402.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It has two parameters, tolerance $\tau$ and fill factor $f$. Elements in $L$ and $U$ factors that would be below relative tolerance $\tau$ (say, to 2-norm of this row) are dropped. &lt;br /&gt;
Then, only $f$ elements per row in $L$ and $U$ are allowed and the maximum $f$ (if they exist) are taken. The diagonal elements are always kept. This means that the number of non-zero&lt;br /&gt;
elements in the rows of the preconditioner can not exceed $2f+1$. &lt;br /&gt;
&lt;br /&gt;
Greater parameter $f$ means slover but better $ILUT$ factorization. If $\tau$ is small, having a very large $f$ means getting a complete $LU$ factorization. Typical values are $5, 10, 20, 50$.&lt;br /&gt;
Lower $\tau$ values mean that move small elements are kept, resulting in a more precise, but longer computation. With a large $f$, the factorization approaches the direct $LU$ factorization as $\tau \to 0$. Typical values are from 1e-2 &lt;br /&gt;
to 1e-6.&lt;br /&gt;
&lt;br /&gt;
Whether the method converges or diverges for given parameters is very sudden and should be determined for each apllication separately. An example of &lt;br /&gt;
such a convergence graph for the diffusion equation is presented in figures below:&lt;br /&gt;
&lt;br /&gt;
[[File:bicgstab_err.png|400px]][[File:bicgstab_conv.png|400px]][[File:bicgstab_iter.png|400px]]&lt;br /&gt;
&lt;br /&gt;
We can see that low BiCGSTAB error estimate coincides with the range of actual small error compared to the analytical solution as well as with the range of lesser number of iterations.&lt;br /&gt;
&lt;br /&gt;
==Pardiso library==&lt;br /&gt;
[[Pardiso]]&lt;br /&gt;
&lt;br /&gt;
==Herzian contact ==&lt;br /&gt;
The matrix of the [[Hertzian contact]] problem is shown below:&lt;br /&gt;
 [[File:matrix_hertzian.png|500px]]&lt;br /&gt;
&lt;br /&gt;
The condition number iz arther large and grows superlinearly with size in the example below.&lt;br /&gt;
 [[File:cond.png|600px]]&lt;/div&gt;</summary>
		<author><name>Mdepolli</name></author>	</entry>

	<entry>
		<id>http://e6.ijs.si/medusa/wiki/index.php?title=Weighted_Least_Squares_(WLS)&amp;diff=1170</id>
		<title>Weighted Least Squares (WLS)</title>
		<link rel="alternate" type="text/html" href="http://e6.ijs.si/medusa/wiki/index.php?title=Weighted_Least_Squares_(WLS)&amp;diff=1170"/>
				<updated>2017-05-19T13:44:53Z</updated>
		
		<summary type="html">&lt;p&gt;Mdepolli: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;One of the most important building blocks of the meshless methods is the Moving Least Squares approximation, which is implemented in the [http://www-e6.ijs.si/ParallelAndDistributedSystems/MeshlessMachine/technical_docs/html/classEngineMLS.html EngineMLS class]. Check [https://gitlab.com/e62Lab/e62numcodes/blob/master/test/mls_test.cpp EngineMLS unit tests] for examples.&lt;br /&gt;
&lt;br /&gt;
= Notation Cheat sheet =&lt;br /&gt;
\begin{align*}&lt;br /&gt;
  m \in \N                  &amp;amp; \dots \text{number of basis functions} \\&lt;br /&gt;
  n \geq m \in \N           &amp;amp; \dots \text{number of points in support domain} \\&lt;br /&gt;
  k \in \mathbb{N}          &amp;amp; \dots \text{dimensionality of vector space} \\&lt;br /&gt;
  \vec s_j \in \R^k         &amp;amp; \dots \text{point in support domain } \quad j=1,\dots,n \\&lt;br /&gt;
  u_j \in \R                &amp;amp; \dots \text{value of function to approximate in }\vec{s}_j \quad j=1,\dots,n \\&lt;br /&gt;
  \vec p \in \R^k           &amp;amp; \dots \text{center point of approximation} \\&lt;br /&gt;
  b_i\colon \R^k \to \R     &amp;amp; \dots \text{basis functions } \quad i=1,\dots,m \\&lt;br /&gt;
  B_{j, i} \in \R           &amp;amp; \dots \text{value of basis functions in support points } b_i(s_j-p) \quad j=1,\dots,n, \quad i=1,\dots,m\\&lt;br /&gt;
  \omega \colon \R^k \to \R &amp;amp; \dots \text{weight function} \\&lt;br /&gt;
  w_j \in \R                &amp;amp; \dots \text{weights } \omega(\vec{s}_j-\vec{p})  \quad j=1,\dots,n \\&lt;br /&gt;
  \alpha_i \in \R           &amp;amp; \dots \text{expansion coefficients around point } \vec{p} \quad i=1,\dots,m \\&lt;br /&gt;
  \hat u\colon \R^k \to \R  &amp;amp; \dots \text{approximation function (best fit)} \\&lt;br /&gt;
  \chi_j \in \R          &amp;amp; \dots \text{shape coefficient for point }\vec{p} \quad j=1,\dots,n \\&lt;br /&gt;
\end{align*}&lt;br /&gt;
&lt;br /&gt;
We will also use \(\b{s}, \b{u}, \b{b}, \b{\alpha}, \b{\chi} \) to annotate a column of corresponding values,&lt;br /&gt;
$W$ as a $n\times n$ diagonal matrix filled with $w_j$ on the diagonal and $B$ as a $n\times m$ matrix filled with $B_{j, i}$.&lt;br /&gt;
&lt;br /&gt;
= Definition of local approximation =&lt;br /&gt;
&amp;lt;figure id=&amp;quot;fig:1DWLS&amp;quot;&amp;gt;&lt;br /&gt;
[[File:image_1avhdsfej1b9cao01029m1e13o69.png|600px|thumb|upright=2|alt=1D MLS example|&amp;lt;caption&amp;gt;Example of 1D WLS approximation &amp;lt;/caption&amp;gt;]]&lt;br /&gt;
&amp;lt;/figure&amp;gt;&lt;br /&gt;
Our wish is to approximate an unknown function $u\colon \R^k \to \R$ while knowing $n$ values $u(\vec{s}_j) := u_j$.&lt;br /&gt;
The vector of known values will be denoted by $\b{u}$ and the vector of coordinates where those values were achieved by $\b{s}$.&lt;br /&gt;
Note that $\b{s}$ is not a vector in the usual sense since its components $\vec{s}_j$ are elements of $\R^k$, but we will call it vector anyway.&lt;br /&gt;
The values of $\b{s}$ are called ''nodes'' or ''support nodes'' or ''support''. The known values $\b{u}$ are also called ''support values''.&lt;br /&gt;
&lt;br /&gt;
In general, an approximation function around point $\vec{p}\in\R^k$ can be&lt;br /&gt;
written as \[\hat{u} (\vec{x}) = \sum_{i=1}^m \alpha_i b_i(\vec{x}) = \b{b}(\vec{x})^\T \b{\alpha} \]&lt;br /&gt;
where $\b{b} = (b_i)_{i=1}^m$ is a set of ''basis functions'', $b_i\colon \R^k \to\R$, and $\b{\alpha} = (\alpha_i)_{i=1}^m$ are the unknown coefficients.&lt;br /&gt;
&lt;br /&gt;
In MLS the goal is to minimize the error of approximation in given values, $\b{e} = \hat u(\b{s}) - \b{u}$&lt;br /&gt;
between the approximation function and target function in the known points $\b{x}$. The error can also be written as $B\b{\alpha} - \b{u}$,&lt;br /&gt;
where $B$ is rectangular matrix of dimensions $n \times m$ with rows containing basis function evaluated in points $\vec{s}_j$.&lt;br /&gt;
\[ B =&lt;br /&gt;
\begin{bmatrix}&lt;br /&gt;
b_1(\vec{s}_1) &amp;amp; \ldots &amp;amp; b_m(\vec{s}_1) \\&lt;br /&gt;
\vdots &amp;amp; \ddots &amp;amp; \vdots \\&lt;br /&gt;
b_1(\vec{s}_n) &amp;amp; \ldots &amp;amp; b_m(\vec{s}_n)&lt;br /&gt;
\end{bmatrix} =&lt;br /&gt;
 [b_i(\vec{s}_j)]_{j=1,i=1}^{n,m} = [\b{b}(\vec{s}_j)^\T]_{j=1}^n. \]&lt;br /&gt;
&lt;br /&gt;
We can choose to minimize any norm of the error vector $e$&lt;br /&gt;
and usually choose to minimize the $2$-norm or square norm \[ \|\b{e}\| = \|\b{e}\|_2 = \sqrt{\sum_{j=1}^n e_j^2}. \]&lt;br /&gt;
Commonly, we also choose to minimize a weighted norm&lt;br /&gt;
&amp;lt;ref&amp;gt;Note that our definition is a bit unusual, usually weights are not&lt;br /&gt;
 squared with the values. However, we do this to avoid computing square&lt;br /&gt;
 roots when doing MLS. If you are used to the usual definition,&lt;br /&gt;
consider the weight to be $\omega^2$.&amp;lt;/ref&amp;gt;&lt;br /&gt;
instead \[ \|\b{e}\|_{2,w} = \|\b{e}\|_w = \sqrt{\sum_{j=1}^n (w_j e_j)^2}. \]&lt;br /&gt;
The ''weights'' $w_i$ are assumed to be non negative and are assembled in a vector $\b{w}$ or a matrix $W = \operatorname{diag}(\b{w})$ and usually obtained from a weight function.&lt;br /&gt;
A ''weight function'' is a function $\omega\colon \R^k \to[0,\infty)$. We calculate $w_j$ as $w_i := \omega(\vec{p}-\vec{s}_j)$, so&lt;br /&gt;
good choices for $\omega$ are function which have higher values close to $0$ (making closer nodes more important), like the normal distribution.&lt;br /&gt;
If we choose $\omega \equiv 1$, we get the unweighted version.&lt;br /&gt;
&lt;br /&gt;
A choice of minimizing the square norm gave this method its name - Least Squares approximation. If we use the weighted version, we get the Weighted Least Squares or WLS.&lt;br /&gt;
In the most general case we wish to minimize&lt;br /&gt;
\[ \|\b{e}\|_{2,w}^2 = \b{e}^\T W^2 \b{e} = (B\b{\alpha} - \b{u})^\T W^2(B\b{\alpha} - \b{u}) =  \sum_j^n w_j^2 (\hat{u}(\vec{s}_j) - u_j)^2  \]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The problem of finding the coefficients $\b{\alpha}$ that minimize the error $\b{e}$ can be solved with at least three approaches:&lt;br /&gt;
* Normal equations (fastest, less accurate) - using Cholesky decomposition of $B$ (requires full rank and $m \leq n$)&lt;br /&gt;
* QR decomposition of $B$ (requires full rank and $m \leq n$, more precise)&lt;br /&gt;
* SVD decomposition of $B$ (more expensive, even more reliable, no rank demand)&lt;br /&gt;
&lt;br /&gt;
In MM we use SVD with regularization described below.&lt;br /&gt;
&lt;br /&gt;
= Computing approximation coefficients =&lt;br /&gt;
&lt;br /&gt;
== Normal equations ==&lt;br /&gt;
We seek the minimum of&lt;br /&gt;
\[ \|\b{e}\|_2^2 = (B\b{\alpha} - \b{u})^\T(B\b{\alpha} - \b{u}) \]&lt;br /&gt;
By seeking the zero gradient in terms of coefficients $\alpha_i$&lt;br /&gt;
\[\frac{\partial}{\partial \alpha_i} (B\b{\alpha} - \b{u})^\T(B\b{\alpha} - \b{u})  = 0\]&lt;br /&gt;
resulting in&lt;br /&gt;
\[ B^\T B\b{\alpha} = B^\T \b{u}. \]&lt;br /&gt;
The coefficient matrix $B^\T B$ is symmetric and positive definite. However, solving above problem directly is&lt;br /&gt;
poorly behaved with respect to round-off errors since the condition number $\kappa(B^\T B)$ is the square&lt;br /&gt;
of $\kappa(B)$.&lt;br /&gt;
&lt;br /&gt;
In case of WLS the equations become&lt;br /&gt;
\[ (WB)^\T WB \b{\alpha} = WB^\T \b{u}. \]&lt;br /&gt;
&lt;br /&gt;
Complexity of Cholesky decomposition is $\frac{n^3}{3}$ and complexity of matrix multiplication $nm^2$. To preform the Cholesky decomposition the rank of $WB$ must be full.&lt;br /&gt;
&lt;br /&gt;
'''Pros:'''&lt;br /&gt;
* simple to implement&lt;br /&gt;
* low computational complexity&lt;br /&gt;
&lt;br /&gt;
'''Cons:'''&lt;br /&gt;
* numerically unstable&lt;br /&gt;
* full rank requirement&lt;br /&gt;
&lt;br /&gt;
== [https://en.wikipedia.org/wiki/QR_decomposition $QR$ Decomposition] ==&lt;br /&gt;
\[{\bf{B}} = {\bf{QR}} = \left[ {{{\bf{Q}}_1},{{\bf{Q}}_2}} \right]\left[ {\begin{array}{*{20}{c}}&lt;br /&gt;
{{{\bf{R}}_1}}\\&lt;br /&gt;
0&lt;br /&gt;
\end{array}} \right]\]&lt;br /&gt;
\[{\bf{B}} = {{\bf{Q}}_1}{{\bf{R}}_1}\]&lt;br /&gt;
$\bf{Q}$ is unitary matrix ($\bf{Q}^{-1}=\bf{Q}^T$). Useful property of a unitary matrices is that multiplying with them does not alter the (Euclidean) norm of a vector, i.e.,&lt;br /&gt;
\[\left\| {{\bf{Qx}}} \right\|{\bf{ = }}\left\| {\bf{x}} \right\|\]&lt;br /&gt;
And $\bf{R}$ is upper diagonal matrix&lt;br /&gt;
\[{\bf{R = (}}{{\bf{R}}_{\bf{1}}}{\bf{,}}0{\bf{)}}\]&lt;br /&gt;
therefore we can say&lt;br /&gt;
\[\begin{array}{l}&lt;br /&gt;
\left\| {{\bf{B\alpha }} - {\bf{u}}} \right\| = \left\| {{{\bf{Q}}^{\rm{T}}}\left( {{\bf{B\alpha }} - {\bf{u}}} \right)} \right\| = \left\| {{{\bf{Q}}^{\rm{T}}}{\bf{B\alpha }} - {{\bf{Q}}^{\rm{T}}}{\bf{u}}} \right\|\\&lt;br /&gt;
 = \left\| {{{\bf{Q}}^{\rm{T}}}\left( {{\bf{QR}}} \right){\bf{\alpha }} - {{\bf{Q}}^{\rm{T}}}{\bf{u}}} \right\| = \left\| {\left( {{{\bf{R}}_1},0} \right){\bf{\alpha }} - {{\left( {{{\bf{Q}}_1},{{\bf{Q}}_{\bf{2}}}} \right)}^{\rm{T}}}{\bf{u}}} \right\|\\&lt;br /&gt;
 = \left\| {{{\bf{R}}_{\bf{1}}}{\bf{\alpha }} - {{\bf{Q}}_{\bf{1}}}{\bf{u}}} \right\| + \left\| {{\bf{Q}}_2^{\rm{T}}{\bf{u}}} \right\|&lt;br /&gt;
\end{array}\]&lt;br /&gt;
Of the two terms on the right we have no control over the second, and we can render the first one&lt;br /&gt;
zero by solving&lt;br /&gt;
\[{{\bf{R}}_{\bf{1}}}{\bf{\alpha }} = {\bf{Q}}_{_{\bf{1}}}^{\rm{T}}{\bf{u}}\]&lt;br /&gt;
Which results in a minimum. We could also compute it with pseudo inverse&lt;br /&gt;
	\[\mathbf{\alpha }={{\mathbf{B}}^{-1}}\mathbf{u}\]&lt;br /&gt;
Where pseudo inverse is simply \[{{\mathbf{B}}^{-1}}=\mathbf{R}_{\text{1}}^{\text{-1}}{{\mathbf{Q}}^{\text{T}}}\] (once again, $R$ is upper diagonal matrix, and $Q$ is unitary matrix).&lt;br /&gt;
And for weighted case&lt;br /&gt;
	\[\mathbf{\alpha }={{\left( {{\mathbf{W}}^{0.5}}\mathbf{B} \right)}^{-1}}\left( {{\mathbf{W}}^{0.5}}\mathbf{u} \right)\]&lt;br /&gt;
&lt;br /&gt;
Complexity of $QR$ decomposition \[\frac{2}{3}m{{n}^{2}}+{{n}^{2}}+\frac{1}{3}n-2=O({{n}^{3}})\]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;Pros:&amp;lt;/strong&amp;gt; better stability in comparison with normal equations &amp;lt;strong&amp;gt; cons: &amp;lt;/strong&amp;gt;higher complexity&lt;br /&gt;
&lt;br /&gt;
== [https://en.wikipedia.org/wiki/Singular_value_decomposition SVD decomposition] ==&lt;br /&gt;
In linear algebra, the [https://en.wikipedia.org/wiki/Singular_value_decomposition singular value decomposition (SVD)]&lt;br /&gt;
is a factorization of a real or complex matrix. It has many useful&lt;br /&gt;
applications in signal processing and statistics.&lt;br /&gt;
&lt;br /&gt;
Formally, the singular value decomposition of an $m \times n$ real or complex&lt;br /&gt;
matrix $\bf{B}$ is a factorization of the form $\bf{B}= \bf{U\Sigma V^\T}$, where&lt;br /&gt;
$\bf{U}$ is an $m \times m$ real or complex unitary matrix, $\bf{\Sigma}$ is an $m \times n$&lt;br /&gt;
rectangular diagonal matrix with non-negative real numbers on the diagonal, and&lt;br /&gt;
$\bf{V}^\T$  is an $n \times n$ real or complex unitary matrix. The diagonal entries&lt;br /&gt;
$\Sigma_{ii}$ are known as the singular values of $\bf{B}$. The $m$ columns of&lt;br /&gt;
$\bf{U}$ and the $n$ columns of $\bf{V}$ are called the left-singular vectors and&lt;br /&gt;
right-singular vectors of $\bf{B}$, respectively.&lt;br /&gt;
&lt;br /&gt;
The singular value decomposition and the eigen decomposition are closely&lt;br /&gt;
related. Namely:&lt;br /&gt;
&lt;br /&gt;
* The left-singular vectors of $\bf{B}$ are eigen vectors of $\bf{BB}^\T$.&lt;br /&gt;
* The right-singular vectors of $\bf{B}$ are eigen vectors of $\bf{B}^\T{B}$.&lt;br /&gt;
* The non-zero singular values of $\bf{B}$ (found on the diagonal entries of $\bf{\Sigma}$) are the square roots of the non-zero eigenvalues of both $\bf{B}^\T\bf{B}$ and $\bf{B}^\T \bf{B}$.&lt;br /&gt;
&lt;br /&gt;
with SVD we can write $\bf{B}$ as \[\bf{B}=\bf{U\Sigma{{V}^{\T}}}\] where are $\bf{U}$ and $\bf{V}$ again unitary matrices and $\bf{\Sigma}$&lt;br /&gt;
stands for diagonal matrix of singular values.&lt;br /&gt;
&lt;br /&gt;
Again we can solve either the system or compute pseudo inverse as&lt;br /&gt;
&lt;br /&gt;
\[ \bf{B}^{-1} = \left( \bf{U\Sigma V}^\T\right)^{-1} = \bf{V}\bf{\Sigma^{-1}U}^\T \]&lt;br /&gt;
where $\bf{\Sigma}^{-1}$ is trivial, just replace every non-zero diagonal entry by&lt;br /&gt;
its reciprocal and transpose the resulting matrix. The stability gain is&lt;br /&gt;
exactly here, one can now set threshold below which the singular value is&lt;br /&gt;
considered as $0$, basically truncate all singular values below some value and&lt;br /&gt;
thus stabilize the inverse.&lt;br /&gt;
&lt;br /&gt;
SVD decomposition complexity \[ 2mn^2+2n^3 = O(n^3) \]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;Pros:&amp;lt;/strong&amp;gt; stable &amp;lt;strong&amp;gt; cons: &amp;lt;/strong&amp;gt;high complexity&lt;br /&gt;
&lt;br /&gt;
Method used in MLMS is SVD with regularization.&lt;br /&gt;
&lt;br /&gt;
= Weighted Least Squares =&lt;br /&gt;
Weighted least squares approximation is the simplest version of the procedure described above. Given support $\b{s}$, values $\b{u}$&lt;br /&gt;
and an anchor point $\vec{p}$, we calculate the coefficients $\b{\alpha}$ using one of the above methods.&lt;br /&gt;
Then, to approximate a function in the neighbourhood of $\vec p$ we use the formula&lt;br /&gt;
\[&lt;br /&gt;
\hat{u}(\vec x) = \b{b}(\vec x)^\T \b{\alpha} = \sum_{i=1}^m \alpha_i b_i(\vec x).&lt;br /&gt;
\]&lt;br /&gt;
&lt;br /&gt;
To approximate the derivative $\frac{\partial u}{\partial x_i}$, or any linear partial differential operator $\mathcal L$ on $u$, we&lt;br /&gt;
simply take the same linear combination of transformed basis functions $\mathcal L b_i$. We have considered coefficients $\alpha_i$ to be&lt;br /&gt;
constant and applied the linearity.&lt;br /&gt;
\[&lt;br /&gt;
 \widehat{\mathcal L u}(\vec x) = \sum_{i=1}^m \alpha_i (\mathcal L b_i)(\vec x).&lt;br /&gt;
\]&lt;br /&gt;
&lt;br /&gt;
= WLS at fixed point with fixed support and unknown function values =&lt;br /&gt;
Suppose now we are given support $\b{s}$ and a point $\b{p}$ and want to construct the function approximation from values $\b{u}$.&lt;br /&gt;
We proceed as usual, solving the overdetermined system $WB \b{\alpha} = W\b{u}$ for coefficients $\b{\alpha}$ using the pseudoinverse&lt;br /&gt;
\[ \b{\alpha} = (WB)^+W\b{u}, \]&lt;br /&gt;
where $A^+$ denotes the Moore-Penrose pseudoinverse that can be calculated using SVD.&lt;br /&gt;
&lt;br /&gt;
Writing down the approximation function $\hat{u}$ we get&lt;br /&gt;
\[&lt;br /&gt;
\hat u (\vec{p}) = \b{b}(\vec{p})^\T \b{\alpha} = \b{b}(\vec{p})^\T (WB)^+W\b{u} = \b{\chi}(\vec{p}) \b{u}.&lt;br /&gt;
\]&lt;br /&gt;
&lt;br /&gt;
We have defined $\b{\chi}$ to be&lt;br /&gt;
\[ \b{\chi}(\vec{p}) = \b{b}(\vec{p})^\T (WB)^+W. \]&lt;br /&gt;
Vector $\b{\chi}$  is a row vector, also called a ''shape function''. The name comes from being able to take all the information&lt;br /&gt;
about shape of the domain and choice of approximation and store it in a single row vector, being able to approximate&lt;br /&gt;
a function value from given support values $\b{u}$ with a single dot product. For any values $\b{u}$, value $\b{\chi}(p) \b{u}$&lt;br /&gt;
gives us the approximation $\hat{u}(\vec{p})$ of $u$ in point $\vec{p}$.&lt;br /&gt;
Mathematically speaking, $\b{\chi}(\vec{p})$ is a functional, $\b{\chi}(\vec{p})\colon \R^n \to \R$, mapping $n$-tuples of known function values to&lt;br /&gt;
their approximations in point $\vec{p}$.&lt;br /&gt;
&lt;br /&gt;
The same approach works for any linear operator $\mathcal L$ applied to $u$, just replace every $b_i$ in definition of $\b{\chi}$ with $\mathcal Lb_i$.&lt;br /&gt;
For example, take a $1$-dimensional case for approximation of derivatives with weight equal to $1$ and $n=m=3$, with equally spaced support values at distances $h$.&lt;br /&gt;
We wish to approximate $u''$ in the middle support point, just by making a weighted sum of the values, something like the finite difference&lt;br /&gt;
\[ u'' \approx \frac{u_1 - 2u_2 + u_3}{h^2}. \]&lt;br /&gt;
This is exactly the same formula as we would have come to by computing $\b{\chi}$, except that our approach is a lot more general. But one should think about&lt;br /&gt;
$\b{\chi}$ as one would about the finite difference scheme, it is a rule, telling us how to compute the derivative.&lt;br /&gt;
\[ u''(s_2) \approx \underbrace{\begin{bmatrix} \frac{1}{h^2} &amp;amp; \frac{-2}{h^2} &amp;amp; \frac{1}{h^2} \end{bmatrix}}_{\b{\chi}} \begin{bmatrix}u_1 \\ u_2 \\ u_3 \end{bmatrix}  \]&lt;br /&gt;
&lt;br /&gt;
The fact that $\b{\chi}$ is independent of the function values $\b{u}$ but depend only on domain geometry means that&lt;br /&gt;
'''we can just compute the shape functions $\b{\chi}$ for points of interest and then approximate any linear operator&lt;br /&gt;
of any function, given its values, very fast, using only a single dot product.'''&lt;br /&gt;
&lt;br /&gt;
= MLS =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;figure id=&amp;quot;fig:comparisonMLSandWLS&amp;quot;&amp;gt;&lt;br /&gt;
[[File:mlswls.svg|thumb|upright=2|&amp;lt;caption&amp;gt;Comparison of WLS and MLS approximation&amp;lt;/caption&amp;gt;]]&lt;br /&gt;
&amp;lt;/figure&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When using WLS the approximation gets worse as we move away from the central point $\vec{p}$.&lt;br /&gt;
This is partially due to not being in the center of the support any more and partially due to weight&lt;br /&gt;
being distributed in such a way to assign more importance to nodes closer to $\vec{p}$.&lt;br /&gt;
&lt;br /&gt;
We can battle this problem in two ways: when we wish to approximate in a new point that is sufficiently far&lt;br /&gt;
away from $\vec{p}$ we can compute new support, recompute the new coefficients $\b{\alpha}$ and approximate again.&lt;br /&gt;
This is very costly and we would like to avoid that. A partial fix is to keep support the same, but only&lt;br /&gt;
recompute the weight vector $\b{w}$, that will now assign weight values to nodes close to the new point.&lt;br /&gt;
We still need to recompute the coefficients $\b{\alpha}$, however we avoid the cost of setting up new support&lt;br /&gt;
and function values and recomputing $B$. This approach is called Moving Least Squares due to recomputing&lt;br /&gt;
the weighted least squares problem whenever we move the point of approximation.&lt;br /&gt;
&lt;br /&gt;
Note that if out weight is constant or if $n = m$, when approximation reduces to interpolation, the weights do not play&lt;br /&gt;
any role and this method is redundant. In fact, its benefits arise when supports are rather large.&lt;br /&gt;
&lt;br /&gt;
See &amp;lt;xr id=&amp;quot;fig:comparisonMLSandWLS&amp;quot;/&amp;gt; for comparison between MLS and WLS approximations. MLS approximation remains close to&lt;br /&gt;
actual function while still inside the support domain, while WLS approximation becomes bad when&lt;br /&gt;
we come out of the reach of the weight function.&lt;br /&gt;
&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Mdepolli</name></author>	</entry>

	<entry>
		<id>http://e6.ijs.si/medusa/wiki/index.php?title=Weighted_Least_Squares_(WLS)&amp;diff=1169</id>
		<title>Weighted Least Squares (WLS)</title>
		<link rel="alternate" type="text/html" href="http://e6.ijs.si/medusa/wiki/index.php?title=Weighted_Least_Squares_(WLS)&amp;diff=1169"/>
				<updated>2017-05-19T12:49:47Z</updated>
		
		<summary type="html">&lt;p&gt;Mdepolli: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;One of the most important building blocks of the meshless methods is the Moving Least Squares approximation, which is implemented in the [http://www-e6.ijs.si/ParallelAndDistributedSystems/MeshlessMachine/technical_docs/html/classEngineMLS.html EngineMLS class]. Check [https://gitlab.com/e62Lab/e62numcodes/blob/master/test/mls_test.cpp EngineMLS unit tests] for examples.&lt;br /&gt;
&lt;br /&gt;
= Notation Cheat sheet =&lt;br /&gt;
\begin{align*}&lt;br /&gt;
  m \in \N                  &amp;amp; \dots \text{number of basis functions} \\&lt;br /&gt;
  n \geq m \in \N           &amp;amp; \dots \text{number of points in support domain} \\&lt;br /&gt;
  k \in \mathbb{N}          &amp;amp; \dots \text{dimensionality of vector space} \\&lt;br /&gt;
  \vec s_j \in \R^k         &amp;amp; \dots \text{point in support domain } \quad j=1,\dots,n \\&lt;br /&gt;
  u_j \in \R                &amp;amp; \dots \text{value of function to approximate in }\vec{s}_j \quad j=1,\dots,n \\&lt;br /&gt;
  \vec p \in \R^k           &amp;amp; \dots \text{center point of approximation} \\&lt;br /&gt;
  b_i\colon \R^k \to \R     &amp;amp; \dots \text{basis functions } \quad i=1,\dots,m \\&lt;br /&gt;
  B_{j, i} \in \R           &amp;amp; \dots \text{value of basis functions in support points } b_i(s_j-p) \quad j=1,\dots,n, \quad i=1,\dots,m\\&lt;br /&gt;
  \omega \colon \R^k \to \R &amp;amp; \dots \text{weight function} \\&lt;br /&gt;
  w_j \in \R                &amp;amp; \dots \text{weights } \omega(\vec{s}_j-\vec{p})  \quad j=1,\dots,n \\&lt;br /&gt;
  \alpha_i \in \R           &amp;amp; \dots \text{expansion coefficients around point } \vec{p} \quad i=1,\dots,m \\&lt;br /&gt;
  \hat u\colon \R^k \to \R  &amp;amp; \dots \text{approximation function (best fit)} \\&lt;br /&gt;
  \chi_j \in \R          &amp;amp; \dots \text{shape coefficient for point }\vec{p} \quad j=1,\dots,n \\&lt;br /&gt;
\end{align*}&lt;br /&gt;
&lt;br /&gt;
We will also use \(\b{s}, \b{u}, \b{b}, \b{\alpha}, \b{\chi} \) to annotate a column of corresponding values,&lt;br /&gt;
$W$ as a $n\times n$ diagonal matrix filled with $w_j$ on the diagonal and $B$ as a $n\times m$ matrix filled with $B_{j, i}$.&lt;br /&gt;
&lt;br /&gt;
= Definition of local aproximation =&lt;br /&gt;
&amp;lt;figure id=&amp;quot;fig:1DWLS&amp;quot;&amp;gt;&lt;br /&gt;
[[File:image_1avhdsfej1b9cao01029m1e13o69.png|600px|thumb|upright=2|alt=1D MLS example|&amp;lt;caption&amp;gt;Example of 1D WLS approximation &amp;lt;/caption&amp;gt;]]&lt;br /&gt;
&amp;lt;/figure&amp;gt;&lt;br /&gt;
Our wish is to approximate an unknown function $u\colon \R^k \to \R$ while knowing $n$ values $u(\vec{s}_j) := u_j$.&lt;br /&gt;
The vector of known values will be denoted by $\b{u}$ and the vector of coordinates where those values were achieved by $\b{s}$.&lt;br /&gt;
Note that $\b{s}$ is not a vector in the usual sense since its components $\vec{s}_j$ are elements of $\R^k$, but we will call it vector anyway.&lt;br /&gt;
The values of $\b{s}$ are called ''nodes'' or ''support nodes'' or ''support''. The known values $\b{u}$ are also called ''support values''.&lt;br /&gt;
&lt;br /&gt;
In general, an approximation function around point $\vec{p}\in\R^k$ can be&lt;br /&gt;
written as \[\hat{u} (\vec{x}) = \sum_{i=1}^m \alpha_i b_i(\vec{x}) = \b{b}(\vec{x})^\T \b{\alpha} \]&lt;br /&gt;
where $\b{b} = (b_i)_{i=1}^m$ is a set of ''basis functions'', $b_i\colon \R^k \to\R$, and $\b{\alpha} = (\alpha_i)_{i=1}^m$ are the unknown coefficients.&lt;br /&gt;
&lt;br /&gt;
In MLS the goal is to minimize the error of approximation in given values, $\b{e} = \hat u(\b{s}) - \b{u}$&lt;br /&gt;
between the approximation function and target function in the known points $\b{x}$. The error can also be written as $B\b{\alpha} - \b{u}$,&lt;br /&gt;
where $B$ is rectangular matrix of dimensions $n \times m$ with rows containing basis function evaluated in points $\vec{s}_j$.&lt;br /&gt;
\[ B =&lt;br /&gt;
\begin{bmatrix}&lt;br /&gt;
b_1(\vec{s}_1) &amp;amp; \ldots &amp;amp; b_m(\vec{s}_1) \\&lt;br /&gt;
\vdots &amp;amp; \ddots &amp;amp; \vdots \\&lt;br /&gt;
b_1(\vec{s}_n) &amp;amp; \ldots &amp;amp; b_m(\vec{s}_n)&lt;br /&gt;
\end{bmatrix} =&lt;br /&gt;
 [b_i(\vec{s}_j)]_{j=1,i=1}^{n,m} = [\b{b}(\vec{s}_j)^\T]_{j=1}^n. \]&lt;br /&gt;
&lt;br /&gt;
We can choose to minimize any norm of the error vector $e$&lt;br /&gt;
and usually choose to minimize the $2$-norm or square norm \[ \|\b{e}\| = \|\b{e}\|_2 = \sqrt{\sum_{j=1}^n e_j^2}. \]&lt;br /&gt;
Commonly, we also choose to minimize a weighted norm&lt;br /&gt;
&amp;lt;ref&amp;gt;Note that our definition is a bit unusual, usually weights are not&lt;br /&gt;
 squared with the values. However, we do this to avoid computing square&lt;br /&gt;
 roots when doing MLS. If you are used to the usual definition,&lt;br /&gt;
consider the weight to be $\omega^2$.&amp;lt;/ref&amp;gt;&lt;br /&gt;
instead \[ \|\b{e}\|_{2,w} = \|\b{e}\|_w = \sqrt{\sum_{j=1}^n (w_j e_j)^2}. \]&lt;br /&gt;
The ''weights'' $w_i$ are assumed to be non negative and are assembled in a vector $\b{w}$ or a matrix $W = \operatorname{diag}(\b{w})$ and usually obtained from a weight function.&lt;br /&gt;
A ''weight function'' is a function $\omega\colon \R^k \to[0,\infty)$. We calculate $w_j$ as $w_i := \omega(\vec{p}-\vec{s}_j)$, so&lt;br /&gt;
good choices for $\omega$ are function which have higher values close to $0$ (making closer nodes more important), like the normal distribution.&lt;br /&gt;
If we choose $\omega \equiv 1$, we get the unweighted version.&lt;br /&gt;
&lt;br /&gt;
A choice of minimizing the square norm gave this method its name - Least Squares appoximation. If we use the weighted version, we get the Weighted Least Squares or WLS.&lt;br /&gt;
In the most general case we wish to minimize&lt;br /&gt;
\[ \|\b{e}\|_{2,w}^2 = \b{e}^\T W^2 \b{e} = (B\b{\alpha} - \b{u})^\T W^2(B\b{\alpha} - \b{u}) =  \sum_j^n w_j^2 (\hat{u}(\vec{s}_j) - u_j)^2  \]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The problem of finding the coefficients $\b{\alpha}$ that minimize the error $\b{e}$ can be solved with at least three approaches:&lt;br /&gt;
* Normal equations (fastest, less accurate) - using Cholesky decomposition of $B$ (requires full rank and $m \leq n$)&lt;br /&gt;
* QR decomposition of $B$ (requires full rank and $m \leq n$, more precise)&lt;br /&gt;
* SVD decomposition of $B$ (more expensive, even more reliable, no rank demand)&lt;br /&gt;
&lt;br /&gt;
In MM we use SVD with regularization described below.&lt;br /&gt;
&lt;br /&gt;
= Computing approximation coefficients =&lt;br /&gt;
&lt;br /&gt;
== Normal equations ==&lt;br /&gt;
We seek the minimum of&lt;br /&gt;
\[ \|\b{e}\|_2^2 = (B\b{\alpha} - \b{u})^\T(B\b{\alpha} - \b{u}) \]&lt;br /&gt;
By seeking the zero gradient in terms of coefficients $\alpha_i$&lt;br /&gt;
\[\frac{\partial}{\partial \alpha_i} (B\b{\alpha} - \b{u})^\T(B\b{\alpha} - \b{u})  = 0\]&lt;br /&gt;
resulting in&lt;br /&gt;
\[ B^\T B\b{\alpha} = B^\T \b{u}. \]&lt;br /&gt;
The coefficient matrix $B^\T B$ is symmetric and positive definite. However, solving above problem directly is&lt;br /&gt;
poorly behaved with respect to round-off errors since the condition number $\kappa(B^\T B)$ is the square&lt;br /&gt;
of $\kappa(B)$.&lt;br /&gt;
&lt;br /&gt;
In case of WLS the equations become&lt;br /&gt;
\[ (WB)^\T WB \b{\alpha} = WB^\T \b{u}. \]&lt;br /&gt;
&lt;br /&gt;
Complexity of Cholesky decomposition is $\frac{n^3}{3}$ and complexity of matrix multiplication $nm^2$. To preform the Cholesky decomposition the rank of $WB$ must be full.&lt;br /&gt;
&lt;br /&gt;
'''Pros:'''&lt;br /&gt;
* simple to implement&lt;br /&gt;
* low computational complexity&lt;br /&gt;
&lt;br /&gt;
'''Cons:'''&lt;br /&gt;
* numerically unstable&lt;br /&gt;
* full rank requirement&lt;br /&gt;
&lt;br /&gt;
== [https://en.wikipedia.org/wiki/QR_decomposition $QR$ Decomposition] ==&lt;br /&gt;
\[{\bf{B}} = {\bf{QR}} = \left[ {{{\bf{Q}}_1},{{\bf{Q}}_2}} \right]\left[ {\begin{array}{*{20}{c}}&lt;br /&gt;
{{{\bf{R}}_1}}\\&lt;br /&gt;
0&lt;br /&gt;
\end{array}} \right]\]&lt;br /&gt;
\[{\bf{B}} = {{\bf{Q}}_1}{{\bf{R}}_1}\]&lt;br /&gt;
$\bf{Q}$ is unitary matrix ($\bf{Q}^{-1}=\bf{Q}^T$). Useful property of a unitary matrices is that multiplying with them does not alter the (Euclidean) norm of a vector, i.e.,&lt;br /&gt;
\[\left\| {{\bf{Qx}}} \right\|{\bf{ = }}\left\| {\bf{x}} \right\|\]&lt;br /&gt;
And $\bf{R}$ is upper diagonal matrix&lt;br /&gt;
\[{\bf{R = (}}{{\bf{R}}_{\bf{1}}}{\bf{,}}0{\bf{)}}\]&lt;br /&gt;
therefore we can say&lt;br /&gt;
\[\begin{array}{l}&lt;br /&gt;
\left\| {{\bf{B\alpha }} - {\bf{u}}} \right\| = \left\| {{{\bf{Q}}^{\rm{T}}}\left( {{\bf{B\alpha }} - {\bf{u}}} \right)} \right\| = \left\| {{{\bf{Q}}^{\rm{T}}}{\bf{B\alpha }} - {{\bf{Q}}^{\rm{T}}}{\bf{u}}} \right\|\\&lt;br /&gt;
 = \left\| {{{\bf{Q}}^{\rm{T}}}\left( {{\bf{QR}}} \right){\bf{\alpha }} - {{\bf{Q}}^{\rm{T}}}{\bf{u}}} \right\| = \left\| {\left( {{{\bf{R}}_1},0} \right){\bf{\alpha }} - {{\left( {{{\bf{Q}}_1},{{\bf{Q}}_{\bf{2}}}} \right)}^{\rm{T}}}{\bf{u}}} \right\|\\&lt;br /&gt;
 = \left\| {{{\bf{R}}_{\bf{1}}}{\bf{\alpha }} - {{\bf{Q}}_{\bf{1}}}{\bf{u}}} \right\| + \left\| {{\bf{Q}}_2^{\rm{T}}{\bf{u}}} \right\|&lt;br /&gt;
\end{array}\]&lt;br /&gt;
Of the two terms on the right we have no control over the second, and we can render the first one&lt;br /&gt;
zero by solving&lt;br /&gt;
\[{{\bf{R}}_{\bf{1}}}{\bf{\alpha }} = {\bf{Q}}_{_{\bf{1}}}^{\rm{T}}{\bf{u}}\]&lt;br /&gt;
Which results in a minimum. We could also compute it with pseudo inverse&lt;br /&gt;
	\[\mathbf{\alpha }={{\mathbf{B}}^{-1}}\mathbf{u}\]&lt;br /&gt;
Where pseudo inverse is simply \[{{\mathbf{B}}^{-1}}=\mathbf{R}_{\text{1}}^{\text{-1}}{{\mathbf{Q}}^{\text{T}}}\] (once again, $R$ is upper diagonal matrix, and $Q$ is unitary matrix).&lt;br /&gt;
And for weighted case&lt;br /&gt;
	\[\mathbf{\alpha }={{\left( {{\mathbf{W}}^{0.5}}\mathbf{B} \right)}^{-1}}\left( {{\mathbf{W}}^{0.5}}\mathbf{u} \right)\]&lt;br /&gt;
&lt;br /&gt;
Complexity of $QR$ decomposition \[\frac{2}{3}m{{n}^{2}}+{{n}^{2}}+\frac{1}{3}n-2=O({{n}^{3}})\]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;Pros:&amp;lt;/strong&amp;gt; better stability in comparison with normal equations &amp;lt;strong&amp;gt; cons: &amp;lt;/strong&amp;gt;higher complexity&lt;br /&gt;
&lt;br /&gt;
== [https://en.wikipedia.org/wiki/Singular_value_decomposition SVD decomposition] ==&lt;br /&gt;
In linear algebra, the [https://en.wikipedia.org/wiki/Singular_value_decomposition singular value decomposition (SVD)]&lt;br /&gt;
is a factorization of a real or complex matrix. It has many useful&lt;br /&gt;
applications in signal processing and statistics.&lt;br /&gt;
&lt;br /&gt;
Formally, the singular value decomposition of an $m \times n$ real or complex&lt;br /&gt;
matrix $\bf{B}$ is a factorization of the form $\bf{B}= \bf{U\Sigma V^\T}$, where&lt;br /&gt;
$\bf{U}$ is an $m \times m$ real or complex unitary matrix, $\bf{\Sigma}$ is an $m \times n$&lt;br /&gt;
rectangular diagonal matrix with non-negative real numbers on the diagonal, and&lt;br /&gt;
$\bf{V}^\T$  is an $n \times n$ real or complex unitary matrix. The diagonal entries&lt;br /&gt;
$\Sigma_{ii}$ are known as the singular values of $\bf{B}$. The $m$ columns of&lt;br /&gt;
$\bf{U}$ and the $n$ columns of $\bf{V}$ are called the left-singular vectors and&lt;br /&gt;
right-singular vectors of $\bf{B}$, respectively.&lt;br /&gt;
&lt;br /&gt;
The singular value decomposition and the eigen decomposition are closely&lt;br /&gt;
related. Namely:&lt;br /&gt;
&lt;br /&gt;
* The left-singular vectors of $\bf{B}$ are eigen vectors of $\bf{BB}^\T$.&lt;br /&gt;
* The right-singular vectors of $\bf{B}$ are eigen vectors of $\bf{B}^\T{B}$.&lt;br /&gt;
* The non-zero singular values of $\bf{B}$ (found on the diagonal entries of $\bf{\Sigma}$) are the square roots of the non-zero eigenvalues of both $\bf{B}^\T\bf{B}$ and $\bf{B}^\T \bf{B}$.&lt;br /&gt;
&lt;br /&gt;
with SVD we can write $\bf{B}$ as \[\bf{B}=\bf{U\Sigma{{V}^{\T}}}\] where are $\bf{U}$ and $\bf{V}$ again unitary matrices and $\bf{\Sigma}$&lt;br /&gt;
stands for diagonal matrix of singular values.&lt;br /&gt;
&lt;br /&gt;
Again we can solve either the system or compute pseudo inverse as&lt;br /&gt;
&lt;br /&gt;
\[ \bf{B}^{-1} = \left( \bf{U\Sigma V}^\T\right)^{-1} = \bf{V}\bf{\Sigma^{-1}U}^\T \]&lt;br /&gt;
where $\bf{\Sigma}^{-1}$ is trivial, just replace every non-zero diagonal entry by&lt;br /&gt;
its reciprocal and transpose the resulting matrix. The stability gain is&lt;br /&gt;
exactly here, one can now set threshold below which the singular value is&lt;br /&gt;
considered as $0$, basically truncate all singular values below some value and&lt;br /&gt;
thus stabilize the inverse.&lt;br /&gt;
&lt;br /&gt;
SVD decomposition complexity \[ 2mn^2+2n^3 = O(n^3) \]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;Pros:&amp;lt;/strong&amp;gt; stable &amp;lt;strong&amp;gt; cons: &amp;lt;/strong&amp;gt;high complexity&lt;br /&gt;
&lt;br /&gt;
Method used in MLMS is SVD with regularization.&lt;br /&gt;
&lt;br /&gt;
= Weighted Least Squares =&lt;br /&gt;
Weighted least squares approximation is the simplest version of the procedure described above. Given supoprt $\b{s}$, values $\b{u}$&lt;br /&gt;
and an anchor point $\vec{p}$, we calculate the coefficients $\b{\alpha}$ using one of the above methods.&lt;br /&gt;
Then, to approximate a function in the neighbourhood of $\vec p$ we use the formula&lt;br /&gt;
\[&lt;br /&gt;
\hat{u}(\vec x) = \b{b}(\vec x)^\T \b{\alpha} = \sum_{i=1}^m \alpha_i b_i(\vec x).&lt;br /&gt;
\]&lt;br /&gt;
&lt;br /&gt;
To approximate the derivative $\frac{\partial u}{\partial x_i}$, or any linear partial differential operator $\mathcal L$ on $u$, we&lt;br /&gt;
simply take the same linear combination of transformed basis functions $\mathcal L b_i$. We have considered coefficients $\alpha_i$ to be&lt;br /&gt;
constant and applied the linearity.&lt;br /&gt;
\[&lt;br /&gt;
 \widehat{\mathcal L u}(\vec x) = \sum_{i=1}^m \alpha_i (\mathcal L b_i)(\vec x).&lt;br /&gt;
\]&lt;br /&gt;
&lt;br /&gt;
= WLS at fixed point with fixed support and unknown function values =&lt;br /&gt;
Suppose now we are given support $\b{s}$ and a point $\b{p}$ and want to construct the function approximation from values $\b{u}$.&lt;br /&gt;
We proceed as usual, solving the overdetemined system $WB \b{\alpha} = W\b{u}$ for coefficients $\b{\alpha}$ using the pseudoinverse&lt;br /&gt;
\[ \b{\alpha} = (WB)^+W\b{u}, \]&lt;br /&gt;
where $A^+$ denotes the Moore-Penrose pseudoinverse that can be calculated using SVD.&lt;br /&gt;
&lt;br /&gt;
Writing down the appoximation function $\hat{u}$ we get&lt;br /&gt;
\[&lt;br /&gt;
\hat u (\vec{p}) = \b{b}(\vec{p})^\T \b{\alpha} = \b{b}(\vec{p})^\T (WB)^+W\b{u} = \b{\chi}(\vec{p}) \b{u}.&lt;br /&gt;
\]&lt;br /&gt;
&lt;br /&gt;
We have defined $\b{\chi}$ to be&lt;br /&gt;
\[ \b{\chi}(\vec{p}) = \b{b}(\vec{p})^\T (WB)^+W. \]&lt;br /&gt;
Vector $\b{\chi}$  is a row vector, also called a ''shape function''. The name comes from being able to take all the information&lt;br /&gt;
about shape of the domain and choice of approximation and store it in a single row vector, being able to approximate&lt;br /&gt;
a function value from givven supoprt values $\b{u}$ with a single dot product. For any values $\b{u}$, value $\b{\chi}(p) \b{u}$&lt;br /&gt;
gives us the appoximation $\hat{u}(\vec{p})$ of $u$ in point $\vec{p}$.&lt;br /&gt;
Mathematically speaking, $\b{\chi}(\vec{p})$ is a functional, $\b{\chi}(\vec{p})\colon \R^n \to \R$, mapping $n$-tuples of known function values to&lt;br /&gt;
their approximations in point $\vec{p}$.&lt;br /&gt;
&lt;br /&gt;
The same approach works for any linear operator $\mathcal L$ applied to $u$, just replace every $b_i$ in definition of $\b{\chi}$ with $\mathcal Lb_i$.&lt;br /&gt;
For example, take a $1$-dimensional case for approximation of derivatives with weight equal to $1$ and $n=m=3$, with equally spaced support values at distances $h$.&lt;br /&gt;
We wish to approximate $u''$ in the middle support point, just by making a weighted sum of the values, something like the finite difference&lt;br /&gt;
\[ u'' \approx \frac{u_1 - 2u_2 + u_3}{h^2}. \]&lt;br /&gt;
This is exactly the same formula as we would have come to by computing $\b{\chi}$, except that our approach is a lot more general. But one should think about&lt;br /&gt;
$\b{\chi}$ as one would about the finite difference scheme, it is a rule, telling us how to compute the derivative.&lt;br /&gt;
\[ u''(s_2) \approx \underbrace{\begin{bmatrix} \frac{1}{h^2} &amp;amp; \frac{-2}{h^2} &amp;amp; \frac{1}{h^2} \end{bmatrix}}_{\b{\chi}} \begin{bmatrix}u_1 \\ u_2 \\ u_3 \end{bmatrix}  \]&lt;br /&gt;
&lt;br /&gt;
The fact that $\b{\chi}$ is independant of the function values $\b{u}$ but depend only on domain geometry means that&lt;br /&gt;
'''we can just compute the shape functions $\b{\chi}$ for points of interest and then approximate any linear operator&lt;br /&gt;
of any function, given its values, very fast, using only a single dot product.'''&lt;br /&gt;
&lt;br /&gt;
= MLS =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;figure id=&amp;quot;fig:comparisonMLSandWLS&amp;quot;&amp;gt;&lt;br /&gt;
[[File:mlswls.svg|thumb|upright=2|&amp;lt;caption&amp;gt;Comparison of WLS and MLS approximation&amp;lt;/caption&amp;gt;]]&lt;br /&gt;
&amp;lt;/figure&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When using WLS the approximation gets worse as we move away from the central point $\vec{p}$.&lt;br /&gt;
This is partially due to not being in the center of the support any more and partially due to weight&lt;br /&gt;
being distribute in such a way to assign more importance to nodes closer to $\vec{p}$.&lt;br /&gt;
&lt;br /&gt;
We can battle this problem in two ways: when we wish to approximate in a new point that is sufficiently far&lt;br /&gt;
away from $\vec{p}$ we can compute new support, recompute the new coefficients $\b{\alpha}$ and approximate again.&lt;br /&gt;
This is very costly and we would like to avoid that. A partial fix is to keep support the same, but only&lt;br /&gt;
recompute the weight vector $\b{w}$, that will now assign hight values to nodes close to the new point.&lt;br /&gt;
We still need to recompute the coefficients $\b{\alpha}$, hoever we avoid the cost of setting up new support&lt;br /&gt;
and function values and recomputing $B$. This approach is called Moving Least Squares due to recomputing&lt;br /&gt;
the weighted least squares problem whenever we move the point of approximation.&lt;br /&gt;
&lt;br /&gt;
Note that is out weight is constant or if $n = m$, when approximation reduces to interpolation, the weights do not play&lt;br /&gt;
any role and this method is redundant. In fact, its benefits arise when supports are rather large.&lt;br /&gt;
&lt;br /&gt;
See &amp;lt;xr id=&amp;quot;fig:comparisonMLSandWLS&amp;quot;/&amp;gt; for comparison between MLS and WLS approximations. MLS approximation remains close to&lt;br /&gt;
actual function while still inside the support domain, while WLS approximation becomes bad when&lt;br /&gt;
we come out of the reach of the weight function.&lt;br /&gt;
&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Mdepolli</name></author>	</entry>

	</feed>