Taming the verbosity.  :)

Taming the verbosity.  :)

git-svn-id: 88467cb1fb04b8a755be7e1ee1026be4190196ef@11420 3a8e244f-b0f2-452b-bcba-4c88e055c3ca

use Math;
use Tensor;
use FTree;
use Quadrature;
use TwoScale;
use Fn1d;

config const verbose = false;

class Function1d {
    var k             = 5;    // use first k Legendre polynomials as the basis in each box
    var thresh        = 1e-5; // truncation threshold for small wavelet coefficients
    var f: Fn1d       = nil;  // analytic f(x) to project into the numerical represntation
    var initial_level = 2;    // initial level of refinement
    var max_level     = 30;   // maximum level of refinement mostly as a sanity check
    var compressed    = false;// keep track of what basis we are in

    // Sum and Difference coefficients
    var s = FTree(order=k);
    var d = FTree(order=k);

    // Two-Scale relationship matrices
    var hgDom = [0..2*k-1, 0..2*k-1];
    var hg    : [hgDom] real;
    var hgT   : [hgDom] real;

    // Quadrature coefficients
    var quadDom   = [0..k-1];
    var quad_x    : [quadDom] real; // points
    var quad_w    : [quadDom] real; // weights

    var quad_phiDom = [0..k-1, 0..k-1];
    var quad_phi    : [quad_phiDom] real; // phi[point,i]
    var quad_phiT   : [quad_phiDom] real; // phi[point,i] transpose
    var quad_phiw   : [quad_phiDom] real; // phi[point,i]*weight[point]

    // blocks of the block tridiagonal derivative operator
    var dcDom = [0..k-1, 0..k-1];
    var rm    : [dcDom] real;
    var r0    : [dcDom] real;
    var rp    : [dcDom] real;

    def initialize() {
        writeln("Creating Function: k=", k, " thresh=", thresh);

        writeln("  initializing two-scale relation coefficients");
        hg  = hg_getCoeffs(k);
        transposeCopy(hgT, hg);

        writeln("  initializing quadrature coefficients");
        init_quadrature(k);

        // blocks of the block tridiagonal derivative operator
        writeln("  initializing tridiagonal derivative operator");
        make_dc_periodic(k);

        // initial refinement of analytic function f(x)
        if f != nil {
            writeln("  performing initial refinement of f(x)");
            for l in 0..2**initial_level-1 do
                refine(initial_level, l);
        }

        writeln("done.");
    }

    def init_quadrature(order: int) {
        quad_x   = gl_getPoints(k);
        quad_w   = gl_getWeights(k);

        for i in quad_phiDom.dim(1) {
            var p = phi(quad_x[i], k);
            for m in quad_phiDom.dim(2) {
                quad_phi[i, m]  = p[m];
                quad_phiw[i, m] = quad_w[i]*p[m];
            }
        }

        transposeCopy(quad_phiT, quad_phi);
    }

    /** Return the level-0 blocks rm, r0, rp of the central
        difference derivative operator with periodic boundary
        conditions on either side.
     */
    def make_dc_periodic(k) {
        var iphase = 1.0;
        for i in 0..k-1 {
            var jphase = 1.0;
            for j in 0..k-1 {
                var gammaij = sqrt((2*i+1) * (2*j+1));
                var Kij =
                    if (i-j) > 0 && ((i-j) % 2) == 1 then
                        2.0 else 0.0;
                r0[i,j] = 0.5*(1.0 - iphase*jphase - 2.0*Kij)*gammaij;
                rm[i,j] = 0.5*jphase*gammaij;
                rp[i,j] =-0.5*iphase*gammaij;
                jphase = -jphase;
            }
            iphase = -iphase;
        }
    }

    /** s[n][l] = integral(phi[n][l](x) * f(x))
        for box (n,l) project f(x) using quadrature rule
        into scaling function basis
     */
    def project(n: int, l: int) {
        var s     : [quadDom] real;
        var h     = 0.5 ** n;
        var scale = sqrt(h);

        for mu in quad_phiDom.dim(1) {
            var x  = (l + quad_x[mu]) * h;
            var fx = f(x);
            for i in quad_phiDom.dim(2) do
                s[i] += scale * fx * quad_phiw[mu, i];
        }

        return s;
    }

    /** Refine numerical representation of f(x) to desired tolerance
     */
    def refine(n: int, l: int) {
        // project f(x) at next level
        var s0 = project(n+1, 2*l);
        var s1 = project(n+1, 2*l+1);
        var s: [0..2*k-1] real;

        s[0..k-1]   = s0;
        s[k..2*k-1] = s1;

        // apply the two scale relationship to get difference coeff
        // in 1d this is O(k^2) flops
        //var d = transform(s, hgT);
        var d = s*hgT;

        // check to see if within tolerance
        // normf() is Frobenius norm == 2-norm for vectors
        var nf = normf(d[k..2*k-1]);
        if((nf < thresh) || (n >= (max_level-1))) {
            this.s[n+1, 2*l]   = s0;
            this.s[n+1, 2*l+1] = s1;
        }
        else {
            // these recursive calls on sub-trees can go in parallel
            // if the HashMap is syncronized
            refine(n+1, 2*l  );
            refine(n+1, 2*l+1);
        }
    }

    /** Evaluate numerical representation of f at x.  Performed in scaling
        function basis only.
     */
    def this(x) {
        if compressed then reconstruct();
        return evaluate(0, 0, x);
    }

    /** eval f(x) using adaptively refined numerical representation of f(x)
        answer should be within tolerance of the analytical f(x)

        Descend tree looking for box (n,l) with scaling function
        coefficients containing the point x.
     */
    def evaluate(in n=0, in l=0, in x): real {
        if s.has_coeffs(n, l) {
            var p = phi(x, k);
            return inner(s[n, l], p)*sqrt(2.0**n);

        } else {
            (n, l, x) = (n+1, 2*l, 2*x);
            if x >= 1 then (l, x) = (l+1, x-1);
            return evaluate(n, l, x);
        }
    }

    /** change from scaling function basis to multi-wavelet basis (s -> d)
        tree is filled out with s[0][0] and d
        n is level in tree
        l is box index
     */
    def compress(n=0, l=0) {
        if compressed then return;

        // sub-trees can be done in parallel
        if !s.has_coeffs(n+1,   2*l) then compress(n+1,   2*l);
        if !s.has_coeffs(n+1, 2*l+1) then compress(n+1, 2*l+1);

        var sc: [0..2*k-1] real;
        sc[0..k-1]   = s[n+1,   2*l];
        sc[k..2*k-1] = s[n+1, 2*l+1];

        // apply the two scale relationship to get difference coeff
        // in 1d this is O(k^2) flops (in 3d this is O(k^4) flops)
        var dc = sc*hgT;

        var dc_upper: [0..k-1] => dc[k..2*k-1];  // Reindex the upper half of dc
        s[n, l] = dc[0..k-1];
        d[n, l] = dc_upper;

        s.remove(n+1,   2*l);
        s.remove(n+1, 2*l+1);

        if n==0 then compressed = true;
    }

    /** change from multi-wavelet basis to scaling function basis (d -> s)
        tree just has s at leaves
        n is level in tree
        l is box index
     */
    def reconstruct(n=0, l=0) {
        if !compressed then return;

        if d.has_coeffs(n, l) {
            var dc: [0..2*k-1] real;
            dc[0..k-1]   = s[n, l];
            dc[k..2*k-1] = d[n, l];
            d.remove(n, l);
            s.remove(n, l);

            // apply the two scale relationship to get difference coeff
            // in 1d this is O(k^2) flops (in 3d this is O(k^4) flops)
            var sc = dc*hg;

            var sc_upper: [0..k-1] => sc[k..2*k-1]; // Reindex the upper half of s
            s[n+1, 2*l  ] = sc[0..k-1];
            s[n+1, 2*l+1] = sc_upper;

            // sub-trees can be done in parallel
            reconstruct(n+1, 2*l);
            reconstruct(n+1, 2*l+1);
        }

        if n == 0 then compressed = false;
    }

    /** In s are scaling coefficients for box n,l ... apply twoscale to generate
        the corresponding coefficients on level n+1 and insert the results into
        the tree of scaling function coefficients.
     */
    def recur_down(n, l) {
        if verbose then writeln(" + recur_down(", n, ", ", l, ")");

        var sc : [0..2*k-1] real;
        sc[0..k-1] = s[n, l];

        var new_sc = sc*hg;
        var new_sc_upper : [0..k-1] => new_sc[k..2*k-1];
        s[n+1, 2*l  ] = new_sc[0..k-1];
        s[n+1, 2*l+1] = new_sc_upper;
    }

    /** If the scaling coefficeints in box n,l exist, return them.
        (allow here for zero boundary conditions for boxes just off
        the ends of the domain)

        Else recur up to the next level looking for a parent.  If a
        parent exists, use two scale to recur those coefficients down
        to make n,l.  Note that this modifies the tree in place and
        you should eventually call sclean to tidy up when finished.

        Else, return None (corresponding child boxes exist at a finer scale)
     */
    def get_coeffs(_n, _l) {
        if verbose then writeln(" @ get_coeffs(", _n, ", ", _l, ")");
        var n = _n, l = _l;

        // Walk up the tree to find scaling coeffs
        while n >= 0 {
            if l < 0 || l >= 2**n {
                var coeffs : [0..k-1] real;
                return coeffs;
            }

            if s.has_coeffs(n, l) then break;

            n -= 1;
            l  = l/2;
        }

        // Didn't find coeffs, they must be below (_n, _l)
        if n < 0 then return None;

        // Recur down to (_n, _l)
        for i in n.._n-1 {
            l = _l/(2**(_n-i));
            recur_down(i, l);
        }

        return s[_n, _l];
    }

    /** Differentiation (also inner and mul) may leave scaling function
        coefficients below their original level.  Recur down to the
        locally first box with scaling function coefficients.
        Delete all children below there.
     */
    def sclean(n=0, l=0, in cleaning=false) {
        if cleaning { //then
            if verbose then writeln(" + sclean deleting (", n, ", ", l, ")");
            s.remove(n, l);
        } else
            cleaning = s.has_coeffs(n, l);

        // Sub trees can run in parallel
        if n < max_level {
            if !cleaning || s.has_coeffs(n+1, 2*l) then
                sclean(n+1, 2*l, cleaning);
            if !cleaning || s.has_coeffs(n+1, 2*l+1) then
                sclean(n+1, 2*l+1, cleaning);
        }
    }

    /** Differentiate the function, which corresponds to application
      of a block triadiagonal matrix.  For an adaptively refined
      target function we may need to refine boxes down until three
      boxes exist in the same scale.
     */
    def diff() {
        def diffHelper(n = 0, l = 0, result) {
            if verbose then writeln(" * diff(", n, ", ", l, ")");
            if !s.has_coeffs(n, l) {
                // Sub trees can run in parallel
                // Run down tree until we hit scaling function coefficients
                diffHelper(n+1, 2*l  , result);
                diffHelper(n+1, 2*l+1, result);
            } else {
                // These can also go in parallel since may involve
                // recurring up & down the tree.
                if verbose then writeln(" * coeffs at (", n, ", ", l, ")");
                var sm = get_coeffs(n, l-1);
                var sp = get_coeffs(n, l+1);
                var s0 = s[n, l];

                if !isNone(sm) /* && !isNone(s0) */ && !isNone(sp) {
                    if verbose then writeln(" * performing differentiation");
                    var r = rp*sm + r0*s0 + rm*sp;
                    result.s[n, l] = r * 2.0**n;
                } else {
                    recur_down(n, l);
                    // Sub trees can run in parallel
                    diffHelper(n+1, 2*l  , result);
                    diffHelper(n+1, 2*l+1, result);
                }
            }
        }

        if compressed then reconstruct();
        var result = Function1d(k, thresh);

        diffHelper(0, 0, result);

        sclean();
        return result;
    }

    /** Return sqrt(integral(f(x)**2))
     */
    def norm2() {
        if compressed then reconstruct();

        var sum = 0.0;
        for i in s do
            sum += normf(s[i(1), i(2)])**2;

        return sqrt(sum);
    }

    /** Mostly for debugging, print summary of coefficients,
        optionally printing the norm of each block
     */
    def summarize() {
        writeln("\n-----------------------------------------------------");
        writeln("sum coefficients:");
        for n in 0..max_level {
            var sum     = 0.0;
            var ncoeffs = 0;
            for i in s {
                var lvl = i(1);
                var idx = i(2);
                if (lvl == n && s.has_coeffs(lvl, idx)) {
                    var t = normf(s[lvl, idx]);
                    sum  += t**2;
                    ncoeffs += 1;
                }
            }
            if ncoeffs != 0 then
                writeln("   level ", n, "   #boxes=", ncoeffs, "  norm=", sqrt(sum));
        }

        writeln("difference coefficients:");
        for n in 0..max_level {
            var sum     = 0.0;
            var ncoeffs = 0;
            for i in d {
                var lvl = i(1);
                var idx = i(2);
                if (lvl == n && d.has_coeffs(lvl, idx)) {
                    var t = normf(d[lvl, idx]);
                    sum  += t**2;
                    ncoeffs += 1;
                }
            }
            if ncoeffs != 0 then
                writeln("   level ", n, "   #boxes=", ncoeffs, "  norm=", sqrt(sum));
        }

        writeln("-----------------------------------------------------\n");
    }

    /** Evaluate the analytic and numerical functions over the given interval
        and print the error.
    */
    def evalNPT(npt) {
        for i in 1..npt do
            writeln(" -- ", i/npt:real, ": f_analytic()=", f(i/npt:real),
                    " F_numeric()=", this(i/npt:real), " err=", this(i/npt:real)-f(i/npt:real));
    }
}

def main() {
    use MadFn1d;
    var npt = 10;

    writeln("Mad Chapel -- One Step Beyond\n");

    writeln("** var F1 : Function1d = nil;");
    var F1 : Function1d = nil;

    writeln("\n** var F2 = Function1d();");
    var F2 = Function1d();

    writeln("\n** var F3 = Function1d(f=test3);");
    var F3 = Function1d(k=5, thresh=1e-5, f=Fn_Test3());
    var a = F3.get_coeffs(20, 5);
    writeln("Coeffs at (20, 5) = ", a);
    F3.sclean();

    writeln("F3.norm2() = ", F3.norm2());

    F3.summarize();

    writeln("\nEvaluating F3 on [0, 1] (singularity at 0.5):");
    F3.evalNPT(10);

    writeln("\nCompressing F3 ...");
    F3.compress();
    F3.summarize();

    writeln("Reconstructing F3 ...");
    F3.reconstruct();
    F3.summarize();

    writeln("\nEvaluating F3 on [0, 1] (singularity at 0.5):");
    F3.evalNPT(10);

    writeln("\nDifferentiating F3 ...");
    var dF3 = F3.diff();
    dF3.f = Fn_dTest3():Fn1d; // Fudge it for the sake of evalNPT()
    dF3.summarize();
    writeln("\nEvaluating dF3 on [0, 1] (singularity at 0.5):");
    dF3.evalNPT(10);
}

use Math;
use Tensor;
use FTree;
use Quadrature;
use TwoScale;
use Fn1d;

class Function1d {
    var k             = 5;    // use first k Legendre polynomials as the basis in each box
    var thresh        = 1e-5; // truncation threshold for small wavelet coefficients
    var f: Fn1d       = nil;  // analytic f(x) to project into the numerical represntation
    var initial_level = 2;    // initial level of refinement
    var max_level     = 30;   // maximum level of refinement mostly as a sanity check
    var compressed    = false;// keep track of what basis we are in

    // Sum and Difference coefficients
    var s = FTree(order=k);
    var d = FTree(order=k);

    // Two-Scale relationship matrices
    var hgDom = [0..2*k-1, 0..2*k-1];
    var hg    : [hgDom] real;
    var hgT   : [hgDom] real;

    // Quadrature coefficients
    var quadDom   = [0..k-1];
    var quad_x    : [quadDom] real; // points
    var quad_w    : [quadDom] real; // weights

    var quad_phiDom = [0..k-1, 0..k-1];
    var quad_phi    : [quad_phiDom] real; // phi[point,i]
    var quad_phiT   : [quad_phiDom] real; // phi[point,i] transpose
    var quad_phiw   : [quad_phiDom] real; // phi[point,i]*weight[point]

    // blocks of the block tridiagonal derivative operator
    var dcDom = [0..k-1, 0..k-1];
    var rm    : [dcDom] real;
    var r0    : [dcDom] real;
    var rp    : [dcDom] real;

    def initialize() {
        writeln("Creating Function: k=", k, " thresh=", thresh);

        writeln("  initializing two-scale relation coefficients");
        hg  = hg_getCoeffs(k);
        transposeCopy(hgT, hg);

        writeln("  initializing quadrature coefficients");
        init_quadrature(k);

        // blocks of the block tridiagonal derivative operator
        writeln("  initializing tridiagonal derivative operator");
        make_dc_periodic(k);

        // initial refinement of analytic function f(x)
        if f != nil {
            writeln("  performing initial refinement of f(x)");
            for l in 0..2**initial_level-1 do
                refine(initial_level, l);
        }

        writeln("done.");
    }

    def init_quadrature(order: int) {
        quad_x   = gl_getPoints(k);
        quad_w   = gl_getWeights(k);

        for i in quad_phiDom.dim(1) {
            var p = phi(quad_x[i], k);
            for m in quad_phiDom.dim(2) {
                quad_phi[i, m]  = p[m];
                quad_phiw[i, m] = quad_w[i]*p[m];
            }
        }

        transposeCopy(quad_phiT, quad_phi);
    }

    /** Return the level-0 blocks rm, r0, rp of the central
        difference derivative operator with periodic boundary
        conditions on either side.
     */
    def make_dc_periodic(k) {
        var iphase = 1.0;
        for i in 0..k-1 {
            var jphase = 1.0;
            for j in 0..k-1 {
                var gammaij = sqrt((2*i+1) * (2*j+1));
                var Kij =
                    if (i-j) > 0 && ((i-j) % 2) == 1 then
                        2.0 else 0.0;
                r0[i,j] = 0.5*(1.0 - iphase*jphase - 2.0*Kij)*gammaij;
                rm[i,j] = 0.5*jphase*gammaij;
                rp[i,j] =-0.5*iphase*gammaij;
                jphase = -jphase;
            }
            iphase = -iphase;
        }
    }

    /** s[n][l] = integral(phi[n][l](x) * f(x))
        for box (n,l) project f(x) using quadrature rule
        into scaling function basis
     */
    def project(n: int, l: int) {
        var s     : [quadDom] real;
        var h     = 0.5 ** n;
        var scale = sqrt(h);

        for mu in quad_phiDom.dim(1) {
            var x  = (l + quad_x[mu]) * h;
            var fx = f(x);
            for i in quad_phiDom.dim(2) do
                s[i] += scale * fx * quad_phiw[mu, i];
        }

        return s;
    }

    /** Refine numerical representation of f(x) to desired tolerance
     */
    def refine(n: int, l: int) {
        // project f(x) at next level
        var s0 = project(n+1, 2*l);
        var s1 = project(n+1, 2*l+1);
        var s: [0..2*k-1] real;

        s[0..k-1]   = s0;
        s[k..2*k-1] = s1;

        // apply the two scale relationship to get difference coeff
        // in 1d this is O(k^2) flops
        //var d = transform(s, hgT);
        var d = s*hgT;

        // check to see if within tolerance
        // normf() is Frobenius norm == 2-norm for vectors
        var nf = normf(d[k..2*k-1]);
        if((nf < thresh) || (n >= (max_level-1))) {
            this.s[n+1, 2*l]   = s0;
            this.s[n+1, 2*l+1] = s1;
        }
        else {
            // these recursive calls on sub-trees can go in parallel
            // if the HashMap is syncronized
            refine(n+1, 2*l  );
            refine(n+1, 2*l+1);
        }
    }

    /** Evaluate numerical representation of f at x.  Performed in scaling
        function basis only.
     */
    def this(x) {
        if compressed then reconstruct();
        return evaluate(0, 0, x);
    }

    /** eval f(x) using adaptively refined numerical representation of f(x)
        answer should be within tolerance of the analytical f(x)

        Descend tree looking for box (n,l) with scaling function
        coefficients containing the point x.
     */
    def evaluate(in n=0, in l=0, in x): real {
        if s.has_coeffs(n, l) {
            var p = phi(x, k);
            return inner(s[n, l], p)*sqrt(2.0**n);

        } else {
            (n, l, x) = (n+1, 2*l, 2*x);
            if x >= 1 then (l, x) = (l+1, x-1);
            return evaluate(n, l, x);
        }
    }

    /** change from scaling function basis to multi-wavelet basis (s -> d)
        tree is filled out with s[0][0] and d
        n is level in tree
        l is box index
     */
    def compress(n=0, l=0) {
        if compressed then return;

        // sub-trees can be done in parallel
        if !s.has_coeffs(n+1,   2*l) then compress(n+1,   2*l);
        if !s.has_coeffs(n+1, 2*l+1) then compress(n+1, 2*l+1);

        var sc: [0..2*k-1] real;
        sc[0..k-1]   = s[n+1,   2*l];
        sc[k..2*k-1] = s[n+1, 2*l+1];

        // apply the two scale relationship to get difference coeff
        // in 1d this is O(k^2) flops (in 3d this is O(k^4) flops)
        var dc = sc*hgT;

        var dc_upper: [0..k-1] => dc[k..2*k-1];  // Reindex the upper half of dc
        s[n, l] = dc[0..k-1];
        d[n, l] = dc_upper;

        s.remove(n+1,   2*l);
        s.remove(n+1, 2*l+1);

        if n==0 then compressed = true;
    }

    /** change from multi-wavelet basis to scaling function basis (d -> s)
        tree just has s at leaves
        n is level in tree
        l is box index
     */
    def reconstruct(n=0, l=0) {
        if !compressed then return;

        if d.has_coeffs(n, l) {
            var dc: [0..2*k-1] real;
            dc[0..k-1]   = s[n, l];
            dc[k..2*k-1] = d[n, l];
            d.remove(n, l);
            s.remove(n, l);

            // apply the two scale relationship to get difference coeff
            // in 1d this is O(k^2) flops (in 3d this is O(k^4) flops)
            var sc = dc*hg;

            var sc_upper: [0..k-1] => sc[k..2*k-1]; // Reindex the upper half of s
            s[n+1, 2*l  ] = sc[0..k-1];
            s[n+1, 2*l+1] = sc_upper;

            // sub-trees can be done in parallel
            reconstruct(n+1, 2*l);
            reconstruct(n+1, 2*l+1);
        }

        if n == 0 then compressed = false;
    }

    /** In s are scaling coefficients for box n,l ... apply twoscale to generate
        the corresponding coefficients on level n+1 and insert the results into
        the tree of scaling function coefficients.
     */
    def recur_down(n, l) {
        writeln(" + recur_down(", n, ", ", l, ")");

        var sc : [0..2*k-1] real;
        sc[0..k-1] = s[n, l];

        var new_sc = sc*hg;
        var new_sc_upper : [0..k-1] => new_sc[k..2*k-1];
        s[n+1, 2*l  ] = new_sc[0..k-1];
        s[n+1, 2*l+1] = new_sc_upper;
    }

    /** If the scaling coefficeints in box n,l exist, return them.
        (allow here for zero boundary conditions for boxes just off
        the ends of the domain)

        Else recur up to the next level looking for a parent.  If a
        parent exists, use two scale to recur those coefficients down
        to make n,l.  Note that this modifies the tree in place and
        you should eventually call sclean to tidy up when finished.

        Else, return None (corresponding child boxes exist at a finer scale)
     */
    def get_coeffs(_n, _l) {
        writeln(" @ get_coeffs(", _n, ", ", _l, ")");
        var n = _n, l = _l;

        // Walk up the tree to find scaling coeffs
        while n >= 0 {
            if l < 0 || l >= 2**n {
                var coeffs : [0..k-1] real;
                return coeffs;
            }

            if s.has_coeffs(n, l) then break;

            n -= 1;
            l  = l/2;
        }

        // Didn't find coeffs, they must be below (_n, _l)
        if n < 0 then return None;

        // Recur down to (_n, _l)
        for i in n.._n-1 {
            l = _l/(2**(_n-i));
            recur_down(i, l);
        }

        return s[_n, _l];
    }

    /** Differentiation (also inner and mul) may leave scaling function
        coefficients below their original level.  Recur down to the
        locally first box with scaling function coefficients.
        Delete all children below there.
     */
    def sclean(n=0, l=0, in cleaning=false) {
        if cleaning { //then
            writeln(" + sclean deleting (", n, ", ", l, ")");
            s.remove(n, l);
        } else
            cleaning = s.has_coeffs(n, l);

        // Sub trees can run in parallel
        if n < max_level {
            if !cleaning || s.has_coeffs(n+1, 2*l) then
                sclean(n+1, 2*l, cleaning);
            if !cleaning || s.has_coeffs(n+1, 2*l+1) then
                sclean(n+1, 2*l+1, cleaning);
        }
    }

    /** Differentiate the function, which corresponds to application
      of a block triadiagonal matrix.  For an adaptively refined
      target function we may need to refine boxes down until three
      boxes exist in the same scale.
     */
    def diff() {
        def diffHelper(n = 0, l = 0, result) {
            writeln(" * diff(", n, ", ", l, ")");
            if !s.has_coeffs(n, l) {
                // Sub trees can run in parallel
                // Run down tree until we hit scaling function coefficients
                diffHelper(n+1, 2*l  , result);
                diffHelper(n+1, 2*l+1, result);
            } else {
                // These can also go in parallel since may involve
                // recurring up & down the tree.
                writeln(" * coeffs at (", n, ", ", l, ")");
                var sm = get_coeffs(n, l-1);
                var sp = get_coeffs(n, l+1);
                var s0 = s[n, l];

                if !isNone(sm) /* && !isNone(s0) */ && !isNone(sp) {
                    writeln(" * performing differentiation");
                    var r = rp*sm + r0*s0 + rm*sp;
                    result.s[n, l] = r * 2.0**n;
                } else {
                    recur_down(n, l);
                    // Sub trees can run in parallel
                    diffHelper(n+1, 2*l  , result);
                    diffHelper(n+1, 2*l+1, result);
                }
            }
        }

        if compressed then reconstruct();
        var result = Function1d(k, thresh);

        diffHelper(0, 0, result);

        sclean();
        return result;
    }

    /** Return sqrt(integral(f(x)**2))
     */
    def norm2() {
        if compressed then reconstruct();

        var sum = 0.0;
        for i in s do
            sum += normf(s[i(1), i(2)])**2;

        return sqrt(sum);
    }

    /** Mostly for debugging, print summary of coefficients,
        optionally printing the norm of each block
     */
    def summarize() {
        writeln("\n-----------------------------------------------------");
        writeln("sum coefficients:");
        for n in 0..max_level {
            var sum     = 0.0;
            var ncoeffs = 0;
            for i in s {
                var lvl = i(1);
                var idx = i(2);
                if (lvl == n && s.has_coeffs(lvl, idx)) {
                    var t = normf(s[lvl, idx]);
                    sum  += t**2;
                    ncoeffs += 1;
                }
            }
            if ncoeffs != 0 then
                writeln("   level ", n, "   #boxes=", ncoeffs, "  norm=", sqrt(sum));
        }

        writeln("difference coefficients:");
        for n in 0..max_level {
            var sum     = 0.0;
            var ncoeffs = 0;
            for i in d {
                var lvl = i(1);
                var idx = i(2);
                if (lvl == n && d.has_coeffs(lvl, idx)) {
                    var t = normf(d[lvl, idx]);
                    sum  += t**2;
                    ncoeffs += 1;
                }
            }
            if ncoeffs != 0 then
                writeln("   level ", n, "   #boxes=", ncoeffs, "  norm=", sqrt(sum));
        }

        writeln("-----------------------------------------------------\n");
    }

    /** Evaluate the analytic and numerical functions over the given interval
        and print the error.
    */
    def evalNPT(npt) {
        for i in 1..npt do
            writeln(" -- ", i/npt:real, ": f_analytic()=", f(i/npt:real),
                    " F_numeric()=", this(i/npt:real), " err=", this(i/npt:real)-f(i/npt:real));
    }
}

def main() {
    use MadFn1d;
    var npt = 10;

    writeln("Mad Chapel -- One Step Beyond\n");

    writeln("** var F1 : Function1d = nil;");
    var F1 : Function1d = nil;

    writeln("\n** var F2 = Function1d();");
    var F2 = Function1d();

    writeln("\n** var F3 = Function1d(f=test3);");
    var F3 = Function1d(k=5, thresh=1e-5, f=Fn_Test3());
    var a = F3.get_coeffs(20, 5);
    writeln("Coeffs at (20, 5) = ", a);
    F3.sclean();

    writeln("F3.norm2() = ", F3.norm2());

    F3.summarize();

    writeln("\nEvaluating F3 on [0, 1] (singularity at 0.5):");
    F3.evalNPT(10);

    writeln("\nCompressing F3 ...");
    F3.compress();
    F3.summarize();

    writeln("Reconstructing F3 ...");
    F3.reconstruct();
    F3.summarize();

    writeln("\nEvaluating F3 on [0, 1] (singularity at 0.5):");
    F3.evalNPT(10);

    writeln("\nTaking derivative of F3");
    var dF3 = F3.diff();
    dF3.f = Fn_dTest3():Fn1d;
    dF3.summarize();
    writeln("\nEvaluating dF3 on [0, 1] (singularity at 0.5):");
    dF3.evalNPT(10);
}


moved time start to after initialization

git-svn-id: 88467cb1fb04b8a755be7e1ee1026be4190196ef@14882 3a8e244f-b0f2-452b-bcba-4c88e055c3ca

//
// Use standard modules for Block distributions and Timing routines
//
use BlockDist, Time;

//
// Use the user modules for computing HPCC problem sizes and for
// defining RA's random stream of values
//
use HPCCProblemSize, RARandomStream;

//
// The number of tables as well as the element and index types of
// that table
//
const numTables = 1;
type elemType = randType,
     indexType = randType;

//
// Configuration constants defining log2(problem size) -- n -- and
// the number of updates -- N_U
//
config const n = computeProblemSize(numTables, elemType,
                                    returnLog2=true): indexType,
             N_U = 2**(n+2);

//
// Constants defining the problem size (m) and a bit mask for table
// indexing
//
const m = 2**n,
      indexMask = m-1;

//
// Configuration constant defining the number of errors to allow (as a
// fraction of the number of updates, N_U)
//
config const errorTolerance = 1e-2;

//
// Configuration constants to control what's printed -- benchmark
// parameters, input and output arrays, and/or statistics
//
config const printParams = true,
             printArrays = false,
             printStats = true;

//
// TableDist is a 1D block distribution for domains storing indices
// of type "indexType", and it is computed by blocking the bounding
// box 0..m-1 across the set of locales.  UpdateDist is a similar
// distribution that is computed by blocking the indices 0..N_U-1
// across the locales.
//
const TableDist = new Block1D(indexType, bbox=[0..m-1]),
      UpdateDist = new Block1D(indexType, bbox=[0..N_U-1]);

//
// TableSpace describes the index set for the table.  It is a 1D
// domain storing indices of type indexType, it is distributed
// according to TableDist, and it contains the indices 0..m-1.
// Updates is an index set describing the set of updates to be made.
// It is distributed according to UpdateDist and contains the
// indices 0..N_U-1.
//
const TableSpace: domain(1, indexType) distributed TableDist = [0..m-1],
      Updates: domain(1, indexType) distributed UpdateDist = [0..N_U-1];

//
// T is the distributed table itself, storing a variable of type
// elemType for each index in TableSpace.
//
var T: [TableSpace] elemType;

//
// The program entry point
//
def main() {
  printConfiguration();   // print the problem size, number of trials, etc.

  //
  // In parallel, initialize the table such that each position
  // contains its index.  "[i in TableSpace]" is shorthand for "forall
  // i in TableSpace"
  //
  [i in TableSpace] T(i) = i;

  const startTime = getCurrentTime();              // capture the start time

  //
  // The main computation: Iterate over the set of updates and the
  // stream of random values in a parallel, zippered manner, dropping
  // the update index on the ground ("_") and storing the random value
  // in r.  Use r both to index into the table and as the update
  // value.
  //
  forall (_, r) in (Updates, RAStream()) do
    on T.domain.dist.ind2loc(r&indexMask) do  // TODO: rewrite this
      T(r & indexMask) ^= r;

  const execTime = getCurrentTime() - startTime;   // capture the elapsed time

  const validAnswer = verifyResults(T, Updates);   // verify the updates
  printResults(validAnswer, execTime);             // print the results
}

//
// Print the problem size and number of updates
//
def printConfiguration() {
  if (printParams) {
    printProblemSize(elemType, numTables, m);
    writeln("Number of updates = ", N_U, "\n");
  }
}

//
// Verify that the computation is correct
//
def verifyResults(T: [?TDom], Updates) {
  //
  // Print the table, if requested
  //
  if (printArrays) then writeln("After updates, T is: ", T, "\n");

  //
  // Reverse the updates by recomputing them, this time using an
  // atomic statement to ensure no conflicting updates
  //
  // TODO: atomic isn't implemented
  //
  forall (_,r) in (Updates, RAStream()) do
    atomic T(r & indexMask) ^= r;

  //
  // Print the table again after the updates have been reversed
  //
  if (printArrays) then writeln("After verification, T is: ", T, "\n");

  //
  // Compute the number of table positions that weren't reverted
  // correctly.  This is an indication of the number of conflicting
  // updates.
  //
  const numErrors = + reduce [i in TDom] (T(i) != i);
  if (printStats) then writeln("Number of errors is: ", numErrors, "\n");

  //
  // Return whether or not the number of errors was within the benchmark's
  // tolerance.
  //
  return numErrors <= (errorTolerance * N_U);
}

//
// Print out success/failure, the execution time, and the GUPS value
//
def printResults(successful, execTime) {
  writeln("Validation: ", if successful then "SUCCESS" else "FAILURE");
  if (printStats) {
    writeln("Execution time = ", execTime);
    writeln("Performance (GUPS) = ", (N_U / execTime) * 1e-9);
  }
}

//
// Use standard modules for Block distributions and Timing routines
//
use BlockDist, Time;

//
// Use the user modules for computing HPCC problem sizes and for
// defining RA's random stream of values
//
use HPCCProblemSize, RARandomStream;

//
// The number of tables as well as the element and index types of
// that table
//
const numTables = 1;
type elemType = randType,
     indexType = randType;

//
// Configuration constants defining log2(problem size) -- n -- and
// the number of updates -- N_U
//
config const n = computeProblemSize(numTables, elemType,
                                    returnLog2=true): indexType,
             N_U = 2**(n+2);

//
// Constants defining the problem size (m) and a bit mask for table
// indexing
//
const m = 2**n,
      indexMask = m-1;

//
// Configuration constant defining the number of errors to allow (as a
// fraction of the number of updates, N_U)
//
config const errorTolerance = 1e-2;

//
// Configuration constants to control what's printed -- benchmark
// parameters, input and output arrays, and/or statistics
//
config const printParams = true,
             printArrays = false,
             printStats = true;

//
// TableDist is a 1D block distribution for domains storing indices
// of type "indexType", and it is computed by blocking the bounding
// box 0..m-1 across the set of locales.  UpdateDist is a similar
// distribution that is computed by blocking the indices 0..N_U-1
// across the locales.
//
const TableDist = new Block1D(indexType, bbox=[0..m-1]),
      UpdateDist = new Block1D(indexType, bbox=[0..N_U-1]);

//
// TableSpace describes the index set for the table.  It is a 1D
// domain storing indices of type indexType, it is distributed
// according to TableDist, and it contains the indices 0..m-1.
// Updates is an index set describing the set of updates to be made.
// It is distributed according to UpdateDist and contains the
// indices 0..N_U-1.
//
const TableSpace: domain(1, indexType) distributed TableDist = [0..m-1],
      Updates: domain(1, indexType) distributed UpdateDist = [0..N_U-1];

//
// T is the distributed table itself, storing a variable of type
// elemType for each index in TableSpace.
//
var T: [TableSpace] elemType;

//
// The program entry point
//
def main() {
  printConfiguration();   // print the problem size, number of trials, etc.

  const startTime = getCurrentTime();              // capture the start time

  //
  // In parallel, initialize the table such that each position
  // contains its index.  "[i in TableSpace]" is shorthand for "forall
  // i in TableSpace"
  //
  [i in TableSpace] T(i) = i;

  //
  // The main computation: Iterate over the set of updates and the
  // stream of random values in a parallel, zippered manner, dropping
  // the update index on the ground ("_") and storing the random value
  // in r.  Use r both to index into the table and as the update
  // value.
  //
  forall (_, r) in (Updates, RAStream()) do
    on T.domain.dist.ind2loc(r&indexMask) do  // TODO: rewrite this
      T(r & indexMask) ^= r;

  const execTime = getCurrentTime() - startTime;   // capture the elapsed time

  const validAnswer = verifyResults(T, Updates);   // verify the updates
  printResults(validAnswer, execTime);             // print the results
}

//
// Print the problem size and number of updates
//
def printConfiguration() {
  if (printParams) {
    printProblemSize(elemType, numTables, m);
    writeln("Number of updates = ", N_U, "\n");
  }
}

//
// Verify that the computation is correct
//
def verifyResults(T: [?TDom], Updates) {
  //
  // Print the table, if requested
  //
  if (printArrays) then writeln("After updates, T is: ", T, "\n");

  //
  // Reverse the updates by recomputing them, this time using an
  // atomic statement to ensure no conflicting updates
  //
  // TODO: atomic isn't implemented
  //
  forall (_,r) in (Updates, RAStream()) do
    atomic T(r & indexMask) ^= r;

  //
  // Print the table again after the updates have been reversed
  //
  if (printArrays) then writeln("After verification, T is: ", T, "\n");

  //
  // Compute the number of table positions that weren't reverted
  // correctly.  This is an indication of the number of conflicting
  // updates.
  //
  const numErrors = + reduce [i in TDom] (T(i) != i);
  if (printStats) then writeln("Number of errors is: ", numErrors, "\n");

  //
  // Return whether or not the number of errors was within the benchmark's
  // tolerance.
  //
  return numErrors <= (errorTolerance * N_U);
}

//
// Print out success/failure, the execution time, and the GUPS value
//
def printResults(successful, execTime) {
  writeln("Validation: ", if successful then "SUCCESS" else "FAILURE");
  if (printStats) {
    writeln("Execution time = ", execTime);
    writeln("Performance (GUPS) = ", (N_U / execTime) * 1e-9);
  }
}


Split a couple of lines and comments for readability.

git-svn-id: 88467cb1fb04b8a755be7e1ee1026be4190196ef@23120 3a8e244f-b0f2-452b-bcba-4c88e055c3ca

/*
 * Iterators Primer
 *
 * This primer contains several examples of iterators:
 *   an iterator to generate the Fibonacci numbers,
 *   an iterator defined by multiple loops
 *   and a recursive iterator over a tree.
 *
 * It also contains examples of the two kinds of parallel iteration:
 *  data-parallel (forall), and
 *  task-parallel (coforall).
 */

//
// fibonacci - generates the first n Fibonacci numbers
//
// The state of the iterator is stored in the tuple (current, next).
// Each time the iterator runs, it returns the current Fibonacci number,
// and then updates the state to the next one.
iter fibonacci(n: int) {
  var (current, next) = (0, 1);
  for 1..n {
    yield current;
      // The first time this iterator is run, it proceeds this far
      // and then yields (returns) the first value of current (== 0),
      // and its state is preserved.
      // The next time it is called, execution resumes here.
      // It continues until another yield is reached.
      // If the iterator completes or encounters a return statement,
      // execution of the enclosing loop terminates immediately.
    (current, next) = (next, current + next);
  }
}

//
// This example uses zipper iteration to iterate over the unbounded range 1..
// and the fibonacci iterator with n set to ten.
// Zipper iteration means that each iterator is advanced to the next yield
// and the two values they return are returned as a tuple.
//
writeln("Fibonacci Numbers");
for (i, j) in zip(1.., fibonacci(10)) {
  write("The ", i);
  select i {
    when 1 do write("st");
    when 2 do write("nd");
    when 3 do write("rd");
    otherwise write("th");
  }
  writeln(" Fibonacci number is ", j);
}
writeln();

//
// multiloop - generate the outer (Cartesian) product of indices in two ranges
// and yield them as tuples.
//
iter multiloop(n: int) {
  for i in 1..n do
    for j in 1..n do
      yield (i, j);
}

//
// Use writeln to output the values that the iterator generates.
//
// This is an example of promotion.
// Promotion means that a procedure containing an iterator call is repeated
// for each value the iterator returns.
//
// In this case, writeln() is called with each value returned by the multiloop()
// iterator.
//
writeln("Multiloop Tuples");
writeln(multiloop(3));
writeln(); // line break

//
// define a tree class and initialize an instance to
//
//      a
//     / \
//    b   c
//       / \
//      d   e
//
class Tree {
  var data: string;
  var left, right: Tree;
}

var tree = new Tree("a", new Tree("b"), new Tree("c", new Tree("d"),
                                                 new Tree("e")));

//
// postorder - iterate over the Tree in postorder using recursion
//
// Each yield statement returns a node,
// or equivalently the subtree rooted at that node.
//
iter postorder(tree: Tree): Tree {
  if tree != nil {
    // Call the iterator recursively on the left subtree and then expand
    // the result.
    for child in postorder(tree.left) do
      yield child;

    // Call the iterator recursively on the right subtree and then expand
    // the result.
    for child in postorder(tree.right) do
      yield child;

    // Finally, yield the node itself.
    yield tree;
  }
}

//
// This visits the nodes of the tree in postorder and prints them out.
// It uses the "first" flag to avoid printing a leading space.
//
proc Tree.writeThis(x: Writer)
{
  var first = true;
  for node in postorder(tree) {
    if first then first = false;
      else x.write(" ");
    x.write(node.data);
  }
}

//
// Output the data in the tree using the postorder iterator.
//
writeln("Tree Data");
writeln(tree);
writeln();

//
// Iterators get more interesting in a parallel context.
// When invoked in a forall loop (or semantically equivalent context),
// iterators that create parallel tasks and assign work to
// those tasks are required.  These are known as leader/follower
// iterators in Chapel, and they are a fairly big topic of their
// own.  See the leaderfollower.chpl test in this directory for
// an extended example of defining and using leader/follower
// iterators.

//
// The coforall loop uses the serial version iterator
// to spawn a separate task for each of the values it yields.
// If you use coforall, you are asserting that the manipulations
// done with each yielded value can be done in parallel
// (i.e. in no particular order).
//
// All of the spawned tasks must complete before execution continues
// at the end of the coforall statement body.
//

// This just does something else noticeable to the tree data --
// prefixing each string with "node_".
proc decorate(s:string) return "node_" + s;

//
// This decorates each node in the tree in parallel, using a coforall.
// Then it writes out the resulting tree data using a postorder traversal.
//
writeln("Task parallel iteration");
coforall node in postorder(tree) do
  node.data = decorate(node.data);
writeln(tree);
writeln();

/*
 * Iterators Primer
 *
 * This primer contains several examples of iterators:
 *   an iterator to generate the Fibonacci numbers,
 *   an iterator defined by multiple loops
 *   and a recursive iterator over a tree.
 *
 * It also contains examples of the two kinds of parallel iteration:
 *  data-parallel (forall), and
 *  task-parallel (coforall).
 */

//
// fibonacci - generates the first n Fibonacci numbers
//
// The state of the iterator is stored in the tuple (current, next).
// Each time the iterator runs, it returns the current Fibonacci number,
// and then updates the state to the next one.
iter fibonacci(n: int) {
  var (current, next) = (0, 1);
  for 1..n {
    yield current;
      // The first time this iterator is run, it proceeds this far
      // and then yields (returns) the first value of current (== 0),
      // and its state is preserved.
      // The next time it is called, execution resumes here.
      // It continues until another yield is reached.
      // If the iterator completes or encounters a return statement,
      // execution of the enclosing loop terminates immediately.
    (current, next) = (next, current + next);
  }
}

//
// This example uses zipper iteration to iterate over the unbounded range 1..
// and the fibonacci iterator with n set to ten.
// Zipper iteration means that each iterator is advanced to the next yield
// and the two values they return are returned as a tuple.
//
writeln("Fibonacci Numbers");
for (i, j) in zip(1.., fibonacci(10)) {
  write("The ", i);
  select i {
    when 1 do write("st");
    when 2 do write("nd");
    when 3 do write("rd");
    otherwise write("th");
  }
  writeln(" Fibonacci number is ", j);
}
writeln();

//
// multiloop - generate the outer (Cartesian) product of indices in two ranges
// and yield them as tuples.
//
iter multiloop(n: int) {
  for i in 1..n do
    for j in 1..n do
      yield (i, j);
}

//
// Use writeln to output the values that the iterator generates.
//
// This is an example of promotion.
// Promotion means that a procedure containing an iterator call is repeated
// for each value the iterator returns.
//
// In this case, writeln() is called with each value returned by the multiloop()
// iterator.
//
writeln("Multiloop Tuples");
writeln(multiloop(3));
writeln(); // line break

//
// define a tree class and initialize an instance to
//
//      a
//     / \
//    b   c
//       / \
//      d   e
//
class Tree {
  var data: string;
  var left, right: Tree;
}

var tree = new Tree("a", new Tree("b"), new Tree("c", new Tree("d"), new Tree("e")));

//
// postorder - iterate over the Tree in postorder using recursion
//
// Each yield statement returns a node,
// or equivalently the subtree rooted at that node.
//
iter postorder(tree: Tree): Tree {
  if tree != nil {
    // Call the iterator recursively on the left subtree and then expand the result.
    for child in postorder(tree.left) do
      yield child;
    // Call the iterator recursively on the right subtree and then expand the result.
    for child in postorder(tree.right) do
      yield child;
    // Finally, yield the node itself.
    yield tree;
  }
}

//
// This visits the nodes of the tree in postorder and prints them out.
// It uses the "first" flag to avoid printing a leading space.
//
proc Tree.writeThis(x: Writer)
{
  var first = true;
  for node in postorder(tree) {
    if first then first = false;
      else x.write(" ");
    x.write(node.data);
  }
}

//
// Output the data in the tree using the postorder iterator.
//
writeln("Tree Data");
writeln(tree);
writeln();

//
// Iterators get more interesting in a parallel context.
// When invoked in a forall loop (or semantically equivalent context),
// iterators that create parallel tasks and assign work to
// those tasks are required.  These are known as leader/follower
// iterators in Chapel, and they are a fairly big topic of their
// own.  See the leaderfollower.chpl test in this directory for
// an extended example of defining and using leader/follower
// iterators.

//
// The coforall loop uses the serial version iterator
// to spawn a separate task for each of the values it yields.
// If you use coforall, you are asserting that the manipulations
// done with each yielded value can be done in parallel
// (i.e. in no particular order).
//
// All of the spawned tasks must complete before execution continues
// at the end of the coforall statement body.
//

// This just does something else noticeable to the tree data --
// prefixing each string with "node_".
proc decorate(s:string) return "node_" + s;

//
// This decorates each node in the tree in parallel, using a coforall.
// Then it writes out the resulting tree data using a postorder traversal.
//
writeln("Task parallel iteration");
coforall node in postorder(tree) do
  node.data = decorate(node.data);
writeln(tree);
writeln();


Updated this test to reflect the new year.

git-svn-id: 88467cb1fb04b8a755be7e1ee1026be4190196ef@15143 3a8e244f-b0f2-452b-bcba-4c88e055c3ca

use Time;
var year, month, day: int;
(year, month, day) = getCurrentDate();
if year > 2009 then
  writeln("I don't believe that you are still working on Chapel!");
else
  writeln("Chapel is a new parallel programming language.");

use Time;
var year, month, day: int;
(year, month, day) = getCurrentDate();
if year > 2008 then
  writeln("I don't believe that you are still working on Chapel!");
else
  writeln("Chapel is a new parallel programming language.");