<?xml version="1.0" encoding="UTF-8"?>
<!--
 * Scilab ( https://www.scilab.org/ ) - This file is part of Scilab
 * Copyright (C) 2013 - Paul Bignier
 * Copyright (C) 2008 - Rainer von Seggern
 * Copyright (C) 2008 - Bruno Pincon
 * Copyright (C) 2009 - INRIA - Michael Baudin
 * Copyright (C) 2010-2011 - DIGITEO - Michael Baudin
 *
 * Copyright (C) 2012 - 2016 - Scilab Enterprises
 *
 * This file is hereby licensed under the terms of the GNU GPL v2.0,
 * pursuant to article 5.3.4 of the CeCILL v.2.1.
 * This file was originally licensed under the terms of the CeCILL v2.1,
 * and continues to be available under such terms.
 * For more information, see the COPYING file which you should have received
 * along with this program.
 *
 -->
<refentry version="5.0-subset Scilab"
          xml:id="numderivative"
          xml:lang="fr"
          xmlns="http://docbook.org/ns/docbook"
          xmlns:xlink="http://www.w3.org/1999/xlink"
          xmlns:svg="http://www.w3.org/2000/svg"
          xmlns:ns4="http://www.w3.org/1999/xhtml"
          xmlns:mml="http://www.w3.org/1998/Math/MathML"
          xmlns:db="http://docbook.org/ns/docbook">
    <refnamediv>
        <refname>numderivative</refname>
        <refpurpose>approximation des dérivées d'une fonction (matrices jacobienne ou hessienne)</refpurpose>
    </refnamediv>
    <refsynopsisdiv>
        <title>Séquence d'appel</title>
        <synopsis>
            J = numderivative(f, x)
            J = numderivative(f, x, h)
            J = numderivative(f, x, h, order)
            J = numderivative(f, x, h, order, H_form)
            J = numderivative(f, x, h, order, H_form, Q)
            [J, H] = numderivative(...)
        </synopsis>
    </refsynopsisdiv>
    <refsection>
        <title>Paramètres</title>
        <variablelist>
            <varlistentry>
                <term>f</term>
                <listitem>
                    <para>
                        une fonction ou une liste, la fonction à différencier.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>x</term>
                <listitem>
                    <para>
                        un vecteur de réels n-par-1 ou 1-par-n, le point où calculer la dérivée.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>h</term>
                <listitem>
                    <para>
                        un vecteur de réels 1-par-1 ou n-par-1, le pas utilisé dans les approximations
                        aux différences finies.
                        Si <literal>h</literal> n'est pas fourni, alors le pas par défaut est calculé
                        suivant <literal>x</literal> et l'ordre, <literal>order</literal>.
                        Si <literal>h</literal> est une matrice 1-par-1, elle est étendue
                        à la même taille que <literal>x</literal>.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>order</term>
                <listitem>
                    <para>
                        une matrice d'entiers positifs 1-par-1, l'ordre de la formule de différences
                        finies (par défaut, <literal>order=2</literal>).
                        Les valeurs possibles de <literal>order</literal> sont 1, 2 et 4.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>H_form</term>
                <listitem>
                    <para>
                        une chaîne de caractères, la forme sous laquelle la matrice hessienne est souhaitée
                        en retour (par défaut, <literal>H_form="default"</literal>).
                    </para>
                    <para>
                        Les valeurs possibles sont "default", "blockmat" et "hypermat".
                        Voir la section "Forme de la matrice hessienne" ci-dessous pour des détails sur cette option.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>Q</term>
                <listitem>
                    <para>
                        une matrice de réels, modifie la direction de différenciation (par défaut, <literal>Q=eye(n,n)</literal>).
                        La matrice <literal>Q</literal> doit être orthogonale. Q fournit la possibilité de retirer
                        l'arbitrarité de l'utilisation de la base canonique pour approximer les dérivées de la fonction.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>J</term>
                <listitem>
                    <para>
                        une matrice réelle m-par-n, l'approximation de la matrice jacobienne.
                        La ligne <literal>J(k, :)</literal> approche le gradient de <literal>fk</literal>,
                        pour <literal>k = 1, 2, ..., m</literal>.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>H</term>
                <listitem>
                    <para>
                        une matrice réelle, l'approximation de la matrice hessienne.
                        Les éléments de <literal>H</literal> approchent les dérivées partielles de second ordre de <literal>f</literal>.
                    </para>
                </listitem>
            </varlistentry>
        </variablelist>
    </refsection>
    <refsection>
        <title>Description</title>
        <para>
            Calcule une approximation des matrices jacobienne et hessienne d'une fonction par différences finies.
        </para>
        <para>
            La fonction <literal>f</literal> prend comme paramètre d'entrée
            <literal>x</literal>, un vecteur <literal>n-par-1</literal>,
            et retourne <literal>y</literal>, un vecteur <literal>m-par-1</literal>.
            Dans la suite, la k-ème composante de <literal>f</literal>
            est notée <literal>fk</literal> et la matrice hessienne de
            <literal>fk</literal> est notée <literal>Hk</literal>,
            pour <literal>k = 1, 2, ..., m</literal>.
        </para>
        <para>
            Tout paramètre égal à la matrice vide <literal>[]</literal>
            sera remplacé par sa valeur par défaut.
        </para>
        <para>
            En général, la formule découlant de <literal>order=1</literal>
            fournit une erreur maximale (la moins précise), la formule de <literal>order = 2</literal> fournit une erreur
            moyenne et la formule <literal>order=4</literal> la plus faible erreur (la plus précise).
            Cela est faux dans certains cas : voir la section "Problèmes de précision" ci-dessous
            pour plus de détails.
        </para>
        <para>
            Les dérivées secondes sont calculées par composition des dérivées du premier ordre.
        </para>
    </refsection>
    <refsection>
        <title>La fonction</title>
        <para>
            La fonction <literal>f</literal> doit avoir le prototype suivant :
        </para>
        <screen>
            y = f (x)
        </screen>
        <para>où</para>
        <variablelist>
            <varlistentry>
                <term>x</term>
                <listitem>
                    <para>un vecteur de réels n-par-1, le point actuel</para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>y</term>
                <listitem>
                    <para>un vecteur de réels m-par-1, la valeur de la fonction</para>
                </listitem>
            </varlistentry>
        </variablelist>
        <para>
            Il se peut que la fonction requière des paramètres additionnels pour être évaluée.
            Dans ce cas, on peut utiliser la fonctionnalité suivante.
            Le paramètre <literal>f</literal> peut aussi être une liste <literal>(myfun, a1, a2, ...)</literal>.
            Dans ce cas, <literal>myfun</literal>, le premier élément de la liste, doit être une fonction au prototype :
            <screen>
                y = myfun (x, a1, a2, ...)
            </screen>
            où les paramètres d'entrée <literal>a1, a2, ...</literal>
            sont concaténés automatiquement à la fin de la séquence d'appel.
        </para>
    </refsection>
    <refsection>
        <title>Forme de la matrice hessienne</title>
        <para>
            L'option <literal>H_form</literal> change les dimensions du paramètre de sortie
            <literal>H</literal>.
            Elle gère le cas général où <literal>m</literal> est différent de <literal>1</literal>,
            c'est-à-dire quand il y a plusieurs fonctions à différencier en même temps.
        </para>
        <para>
            Les valeurs possibles de <literal>H_form</literal> sont :
        </para>
        <variablelist>
            <varlistentry>
                <term>H_form = "default" :</term>
                <listitem>
                    <para>
                        H est une matrice <literal>m-par-(n^2)</literal>; sous cette forme,
                        la ligne <literal>H(k, :)</literal> contient <literal>Hk</literal>:
                    </para>
                    <screen>
                        H(k, :) == Hk(:)'
                    </screen>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>H_form = "blockmat" :</term>
                <listitem>
                    <para>
                        H est une matrice <literal>(mn)-par-n</literal> : les matrices hessiennes <literal>n-par-n</literal>
                        de chaque composante de <literal>f</literal> sont stockées ligne par ligne, c'est-à-dire :
                    </para>
                    <screen>
                        H == [H1 ; H2 ; ... ; Hm]
                    </screen>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>H_form = "hypermat" :</term>
                <listitem>
                    <para>
                        H est une matrice n-par-n pour <literal>m=1</literal>, et une hypermatrice n-par-n-par-m sinon.
                        La matrice <literal>H(:, :, k)</literal> est la matrice hessienne de la k-ème
                        composante de <literal>f</literal>, i.e.
                    </para>
                    <screen>
                        H(:, :, k) == Hk
                    </screen>
                </listitem>
            </varlistentry>
        </variablelist>
    </refsection>
    <refsection>
        <title>Performances</title>
        <para>
            Si le problème est correctement dimensionné, augmenter l'ordre de la formule de différences finies peut réduire
            l'erreur d'approximation mais requiert plus d'évaluations de la fonction.
            La liste suivante présente le nombre d'évaluations nécéssaires au calcul du
            Jacobien suivant l'ordre de la formule et la dimension de <literal>x</literal>,
            notée <literal>n</literal> :
        </para>
        <itemizedlist>
            <listitem>
                <para>
                    <literal>order = 1</literal>, <literal>n+1</literal> evaluations,
                </para>
            </listitem>
            <listitem>
                <para>
                    <literal>order = 2</literal>, <literal>2n</literal> évaluations,
                </para>
            </listitem>
            <listitem>
                <para>
                    <literal>order = 4</literal>, <literal>4n</literal> évaluations.
                </para>
            </listitem>
        </itemizedlist>
        <para>
            Calculer la matrice hessienne requiert le carré du nombre d'évaluations,
            comme détaillé ci-après :
        </para>
        <itemizedlist>
            <listitem>
                <para>
                    <literal>order = 1</literal>, <literal>(n+1)^2</literal> évaluations (au total <literal>(n+1)^2+n+1</literal>),
                </para>
            </listitem>
            <listitem>
                <para>
                    <literal>order = 2</literal>, <literal>4n^2</literal> évaluations (au total <literal>4n^2+2n</literal>),
                </para>
            </listitem>
            <listitem>
                <para>
                    <literal>order = 4</literal>, <literal>16n^2</literal> évaluations (au total <literal>16n^2+4n</literal>).
                </para>
            </listitem>
        </itemizedlist>
    </refsection>
    <refsection>
        <title>Exemples</title>
        <programlisting role="example">
            <![CDATA[
// La fonction à différencier
function y = f(x)
  f1 = sin(x(1)*x(2)) + exp(x(2)*x(3)+x(1))
  f2 = sum(x.^3)
  y = [f1; f2]
endfunction
// Le gradient exact
function [g1, g2] = exactg(x)
  g1(1) = cos(x(1)*x(2))*x(2) + exp(x(2)*x(3)+x(1))
  g1(2) = cos(x(1)*x(2))*x(1) + exp(x(2)*x(3)+x(1))*x(3)
  g1(3) = exp(x(2)*x(3) + x(1))*x(2)
  g2(1) = 3*x(1)^2
  g2(2) = 3*x(2)^2
  g2(3) = 3*x(3)^2
endfunction
// La hessienne exacte
function [H1, H2] = exactH(x)
  H1(1, 1) = -sin(x(1)*x(2))*x(2)^2 + exp(x(2)*x(3)+x(1))
  H1(1, 2) = cos(x(1)*x(2)) - sin(x(1)*x(2))*x(2)*x(1) + exp(x(2)*x(3)+x(1))*x(3)
  H1(1, 3) = exp(x(2)*x(3)+x(1))*x(2)
  H1(2, 1) = H1(1, 2)
  H1(2, 2) = -sin(x(1)*x(2))*x(1)^2 + exp(x(2)*x(3)+x(1))*x(3)^2
  H1(2, 3) = exp(x(2)*x(3)+x(1)) + exp(x(2)*x(3)+x(1))*x(3)*x(2)
  H1(3, 1) = H1(1, 3)
  H1(3, 2) = H1(2, 3)
  H1(3, 3) = exp(x(2)*x(3)+x(1))*x(2)^2
  //
  H2(1, 1) = 6*x(1)
  H2(1, 2) = 0
  H2(1, 3) = 0
  H2(2, 1) = H2(1, 2)
  H2(2, 2) = 6*x(2)
  H2(2, 3) = 0
  H2(3, 1) = H2(1, 3)
  H2(3, 2) = H2(2, 3)
  H2(3, 3) = 6*x(3)
endfunction

// Calcul du jacobien et de la hessienne
x = [1; 2; 3];
J = numderivative(f, x)
[J, H] = numderivative(f, x)

// Comparaison avec les valeurs exactes
[g1, g2] = exactg(x);
Jexact = [g1'; g2']
[H1, H2] = exactH(x);
Hexact = [H1(:)'; H2(:)']

// Configuration du pas
J = numderivative(f, x, 1.e-1)

// Configuration de l'ordre
J = numderivative(f, x, [], 4)

// Configuration de la forme de la hessienne
[J, H] = numderivative(f, x, [], [], "blockmat")
[J, H] = numderivative(f, x, [], [], "hypermat")

// Configuration de Q
n = 3;
Q = qr(rand(n, n))
J = numderivative(f, x, [], [], [], Q)

// Test des formules d'ordre 1, 2 et 4.
// Pour la formule d'ordre 4, certaines entrées de H
// sont calculées comme non-nulles.
[g1, g2] = exactg(x);
[H1, H2] = exactH(x);
Jexact = [g1'; g2'];
Hexact = [H1(:)'; H2(:)'];
for i = [1, 2, 4]
  [J, H] = numderivative(f, x, [], i);
  dJ = floor(min(assert_computedigits(J, Jexact)));
  dH = floor(min(assert_computedigits(H, Hexact)));
  mprintf("ordre = %d, dJ = %d, dH = %d \n", i, dJ, dH);
end
]]>
        </programlisting>
    </refsection>
    <refsection>
        <title>Paramètres additionnels</title>
        <para>
            Dans l'exemple suivant, on utilise une fonction qui requiert le paramètre additionnel
            <literal>p</literal>.
            Soit, on utilise le fait que <literal>f</literal> puisse être une liste,
            où le premier paramètre de la fonction
            <literal>G</literal> et les éléments restants sont automatiquement passés
            à la fonction.
        </para>
        <programlisting role="example">
            <![CDATA[
function y = G(x, p)
  f1 = sin(x(1)*x(2)*p) + exp(x(2)*x(3)+x(1))
  f2 = sum(x.^3)
  y = [f1; f2]
endfunction

p = 1;
h = 1e-3;
[J, H] = numderivative(list(G, p), x)
]]>
        </programlisting>
    </refsection>
    <refsection>
        <title>La formule de Taylor</title>
        <para>
            Si <literal>H</literal> est donnée sous sa forme par défaut, alors les séries de Taylor de
            <literal>f(x)</literal> jusqu'aux termes de second ordre sont donnés par :
        </para>
        <para>
            <latex>
                <![CDATA[
f(x+h)\approx f(x)+J(x) h + \frac{1}{2} h^T H(x) h
]]>
            </latex>
        </para>
        <para>
            Dans le script suivant, on vérifié que cette formule à l'aide de différences finies.
        </para>
        <programlisting role="example">
            <![CDATA[
// La fonction à différencier
function y = f(x)
  f1 = sin(x(1)*x(2)) + exp(x(2)*x(3)+x(1))
  f2 = sum(x.^3)
  y = [f1; f2]
endfunction
x = [1; 2; 3];
dx = 1e-3*[1; 1; -1];
[J, H] = numderivative(f, x);
f(x+dx)
f(x+dx)-f(x)
f(x+dx)-f(x)-J*dx
f(x+dx)-f(x)-J*dx-1/2*H*(dx .*. dx)
]]>
        </programlisting>
        <para>
            Dans l'exemple suivant, on utilise une fonction qui requiert trois paramètres additionnnels
            <literal>A</literal>, <literal>p</literal> et <literal>w</literal>
        </para>
        <programlisting role="example">
            <![CDATA[
// Un exemple trivial
function y = f(x, A, p, w)
  y = x'*A*x + p'*x + w;
endfunction
// avec les matrices jacobienne et hessienne données par
// J(x) = x'*(A+A')+p' et H(x) = A+A'.
A = rand(3, 3);
p = rand(3, 1);
w = 1;
x = rand(3, 1);
h = 1;
[J, H] = numderivative(list(f, A, p, w), x, h, [], "blockmat")
]]>
        </programlisting>
    </refsection>
    <refsection>
        <title>Problèmes de précision</title>
        <para>
            Quand le pas <literal>h</literal> n'est pas fourni, la fonction <literal>numderivative</literal>
            tente de calculer un pas qui donne une précision suffisante.
            Ce pas dépend du degré des dérivées (jacobienne ou hessienne),
            l'ordre de la formule et le point <literal>x</literal>.
            Plus précisément, si <literal>x</literal> est non-nul,
            alors le pas par défaut <literal>h</literal> est un vecteur de même taille que
            <literal>x</literal>, dimensionné à la valeur absolue de
            <literal>x</literal>.
        </para>
        <para>
            Dans l'exemple suivant, on calcule la dérivée de la fonction racine carrée.
            Le script suivant affiche un graphe représentant l'erreur relative de la
            dérivée numérique en fonction du pas.
            On peut voir qu'il y a un pas optimal qui minimise l'erreur relative.
        </para>
        <programlisting role="example">
            <![CDATA[
// Sa dérivée exacte
function y = mydsqrt (x)
    y = 0.5 * x^(-0.5)
endfunction

x = 1.0;
n = 1000;
logharray = linspace (-16, 0, n);
for i = 1:n
    h = 10^(logharray(i));
    expected = mydsqrt(x);
    computed = numderivative (sqrt, x, h);
    relerr = abs(computed - expected) / abs(expected);
    logearray (i) = log10 (relerr);
end
scf();
plot (logharray, logearray);
xtitle("Erreur relative de numderivative (x = 1.0)", ..
  "log(h)", "log(ER)");
]]>
        </programlisting>
        <para>
            La stratégie de <literal>numderivative</literal> fournit une précision suffisante dans
            beaucoup de cas, mais peut ne pas être assez précise dans certains cas.
            En fait, le pas optimal dépend aussi de la valeur de la fonction <literal>f(x)</literal>
            et de sa dérivée seconde, lesquelles sont inconnues au moment ou le pas par défaut est calculé.
            Voir "Practical optimization", par Gill, Murray et Wright, Academic Press, 1981,
            pour plus de détails.
            Les sections pertinentes sont "4.6.1. Finite-difference Approximations to First
            Derivatives" et "8.6 Computing finite differences".
        </para>
        <para>
        </para>
    </refsection>
    <refsection>
        <title>Historique</title>
        <revhistory>
            <revision>
                <revnumber>5.5.0</revnumber>
                <revdescription>
                    Fonction <literal>numderivative</literal> introduite.
                </revdescription>
            </revision>
        </revhistory>
    </refsection>
</refentry>
