// File: /workspace/github_c_makefile_star2_O0/debbiemarkslab[P]plmc/src/inference.c
// Function: SGDOptimize
void SGDOptimize(gradfun_t grad, void *data, numeric_t *x, const int n,
    const int maxIter, const numeric_t crit) {
    /* Opitimize an objective function by Stochastic Gradient Descent (Adam)
       Arguments:
            grad            gradient of objective
            data            pointer to data
            x               estimated parameters (length n)
            n               number of parameters
            eps             learning rate
            maxIter         maximum number of iterations
            crit            stop when ||grad|| / ||x|| < crit
    */
    // numeric_t ALPHA0 = 0.001;
    // numeric_t ALPHAT = 0.00001;
    numeric_t BETA1 = 0.9;
    numeric_t BETA2 = 0.99;
    numeric_t EPSILON = 1E-8;

    numeric_t *g = (numeric_t *) malloc(n * sizeof(numeric_t));
    numeric_t criterion = crit + 1.0;

    /* Begin profiling */
    struct timeval start;
    gettimeofday(&start, NULL);

    /* Initialize estimates of first and second moments of the gradient */
    numeric_t *meanX = (numeric_t *) malloc(n * sizeof(numeric_t));
    numeric_t *meanG = (numeric_t *) malloc(n * sizeof(numeric_t));
    numeric_t *squareG = (numeric_t *) malloc(n * sizeof(numeric_t));
    for (int i = 0; i < n; i++) meanX[i] = 0;
    for (int i = 0; i < n; i++) meanG[i] = 0;
    for (int i = 0; i < n; i++) squareG[i] = 0;

    /* Optimization loop */
    int t = 1;
    do {
        /* Estimate the gradient */
        for (int i = 0; i < n; i++) g[i] = 0;
        numeric_t f = grad(data, x, g, n);

        /* Update estimates of moments */
        for (int i = 0; i < n; i++)
            meanG[i] = BETA1 * meanG[i] + (1.0 - BETA1) * g[i];
        for (int i = 0; i < n; i++)
            squareG[i] = BETA2 * squareG[i] + (1.0 - BETA2) * g[i] * g[i];

        /* Update Q with Adam learning rates */
        // numeric_t schedule = ALPHA;
        // numeric_t frac = (numeric_t) t / (numeric_t) maxIter;
        // frac = floor(frac * 5) / 5.;
        // numeric_t schedule = exp((1 - frac) * log(ALPHA0) + frac * log(ALPHAT));
        // Anneal strategy #2
        numeric_t schedule = 0.01 * pow(0.5, (t / 50));
        // numeric_t schedule = 0.01;
        numeric_t alpha = schedule
                          * sqrt(1.0 - pow(BETA2, (numeric_t) t)) 
                              / (1.0 - pow(BETA1, (numeric_t) t));
        for (int i = 0; i < n; i++)
            x[i] -= meanG[i] * alpha / (sqrt(squareG[i]) + EPSILON);

        /* Update Polyak average */
        for (int i = 0; i < n; i++)
            meanX[i] = BETA1 * meanX[i] + (1 - BETA1) * x[i];

        /* Stopping criterion: ||grad(params)|| / ||params|| */
        numeric_t paramNorm = 1E-6;
        for (int i = 0; i < n; i++) paramNorm += fabs(x[i]) / (numeric_t) n;
        numeric_t gradNorm = 1E-6;
        for (int i = 0; i < n; i++)
            gradNorm += fabs(meanG[i]) / (numeric_t) n;
        criterion = gradNorm;

        if (t == 1)
            fprintf(stderr, "iter\ttime\tobj\t|x|\t|g|\tcrit\n");
            fprintf(stderr, "%d\t%.1f\t%.1f\t%.1f\t%.1f\t%.1f\n",
            t, ElapsedTime(&start), f, paramNorm, gradNorm, criterion);
        t++;
    } while (t <= maxIter && criterion > crit);
    // for (int i = 0; i < n; i++) x[i] = meanX[i] / ((numeric_t) t - 1);
    for (int i = 0; i < n; i++) x[i] = meanX[i];
    free(meanX);
    free(meanG);
    free(squareG);
    free(g);
}

// Assembly:
subq $0x108, %rsp            # imm = 0x108
movq %rdi, 0x100(%rsp)
movq %rsi, 0xf8(%rsp)
movq %rdx, 0xf0(%rsp)
movl %ecx, 0xec(%rsp)
movl %r8d, 0xe8(%rsp)
movsd %xmm0, 0xe0(%rsp)
movsd 0x60d9(%rip), %xmm0     # 0x193d8
movsd %xmm0, 0xd8(%rsp)
movsd 0x60c0(%rip), %xmm0     # 0x193d0
movsd %xmm0, 0xd0(%rsp)
movsd 0x60a7(%rip), %xmm0     # 0x193c8
movsd %xmm0, 0xc8(%rsp)
movslq 0xec(%rsp), %rdi
shlq $0x3, %rdi
callq 0x1170
movq %rax, 0xc0(%rsp)
movsd 0x5d05(%rip), %xmm0     # 0x19050
addsd 0xe0(%rsp), %xmm0
movsd %xmm0, 0xb8(%rsp)
leaq 0xa8(%rsp), %rdi
xorl %eax, %eax
movl %eax, %esi
callq 0x10e0
movslq 0xec(%rsp), %rdi
shlq $0x3, %rdi
callq 0x1170
movq %rax, 0xa0(%rsp)
movslq 0xec(%rsp), %rdi
shlq $0x3, %rdi
callq 0x1170
movq %rax, 0x98(%rsp)
movslq 0xec(%rsp), %rdi
shlq $0x3, %rdi
callq 0x1170
movq %rax, 0x90(%rsp)
movl $0x0, 0x8c(%rsp)
movl 0x8c(%rsp), %eax
cmpl 0xec(%rsp), %eax
jge 0x133ff
movq 0xa0(%rsp), %rax
movslq 0x8c(%rsp), %rcx
xorps %xmm0, %xmm0
movsd %xmm0, (%rax,%rcx,8)
movl 0x8c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x8c(%rsp)
jmp 0x133c4
movl $0x0, 0x88(%rsp)
movl 0x88(%rsp), %eax
cmpl 0xec(%rsp), %eax
jge 0x13445
movq 0x98(%rsp), %rax
movslq 0x88(%rsp), %rcx
xorps %xmm0, %xmm0
movsd %xmm0, (%rax,%rcx,8)
movl 0x88(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x88(%rsp)
jmp 0x1340a
movl $0x0, 0x84(%rsp)
movl 0x84(%rsp), %eax
cmpl 0xec(%rsp), %eax
jge 0x1348b
movq 0x90(%rsp), %rax
movslq 0x84(%rsp), %rcx
xorps %xmm0, %xmm0
movsd %xmm0, (%rax,%rcx,8)
movl 0x84(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x84(%rsp)
jmp 0x13450
movl $0x1, 0x80(%rsp)
movl $0x0, 0x7c(%rsp)
movl 0x7c(%rsp), %eax
cmpl 0xec(%rsp), %eax
jge 0x134cd
movq 0xc0(%rsp), %rax
movslq 0x7c(%rsp), %rcx
xorps %xmm0, %xmm0
movsd %xmm0, (%rax,%rcx,8)
movl 0x7c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x7c(%rsp)
jmp 0x1349e
movq 0x100(%rsp), %rax
movq 0xf8(%rsp), %rdi
movq 0xf0(%rsp), %rsi
movq 0xc0(%rsp), %rdx
movl 0xec(%rsp), %ecx
callq *%rax
movsd %xmm0, 0x70(%rsp)
movl $0x0, 0x6c(%rsp)
movl 0x6c(%rsp), %eax
cmpl 0xec(%rsp), %eax
jge 0x13576
movsd 0xd8(%rsp), %xmm0
movq 0x98(%rsp), %rax
movslq 0x6c(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm2
movsd 0x5b1c(%rip), %xmm1     # 0x19050
subsd 0xd8(%rsp), %xmm1
movq 0xc0(%rsp), %rax
movslq 0x6c(%rsp), %rcx
mulsd (%rax,%rcx,8), %xmm1
mulsd %xmm2, %xmm0
addsd %xmm1, %xmm0
movq 0x98(%rsp), %rax
movslq 0x6c(%rsp), %rcx
movsd %xmm0, (%rax,%rcx,8)
movl 0x6c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x6c(%rsp)
jmp 0x13504
movl $0x0, 0x68(%rsp)
movl 0x68(%rsp), %eax
cmpl 0xec(%rsp), %eax
jge 0x13605
movsd 0xd0(%rsp), %xmm0
movq 0x90(%rsp), %rax
movslq 0x68(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm2
movsd 0x5aa2(%rip), %xmm1     # 0x19050
subsd 0xd0(%rsp), %xmm1
movq 0xc0(%rsp), %rax
movslq 0x68(%rsp), %rcx
mulsd (%rax,%rcx,8), %xmm1
movq 0xc0(%rsp), %rax
movslq 0x68(%rsp), %rcx
mulsd (%rax,%rcx,8), %xmm1
mulsd %xmm2, %xmm0
addsd %xmm1, %xmm0
movq 0x90(%rsp), %rax
movslq 0x68(%rsp), %rcx
movsd %xmm0, (%rax,%rcx,8)
movl 0x68(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x68(%rsp)
jmp 0x1357e
movl 0x80(%rsp), %eax
movl $0x32, %ecx
cltd
idivl %ecx
cvtsi2sd %eax, %xmm1
movsd 0x59f8(%rip), %xmm0     # 0x19018
callq 0x1090
movaps %xmm0, %xmm1
movsd 0x5ae0(%rip), %xmm0     # 0x19110
mulsd %xmm1, %xmm0
movsd %xmm0, 0x60(%rsp)
movsd 0x60(%rsp), %xmm0
movsd %xmm0, 0x20(%rsp)
movsd 0xd0(%rsp), %xmm0
cvtsi2sdl 0x80(%rsp), %xmm1
callq 0x1090
movaps %xmm0, %xmm1
movsd 0x59e8(%rip), %xmm0     # 0x19050
subsd %xmm1, %xmm0
callq 0x11e0
movaps %xmm0, %xmm1
movsd 0x20(%rsp), %xmm0
mulsd %xmm1, %xmm0
movsd %xmm0, 0x28(%rsp)
movsd 0xd8(%rsp), %xmm0
cvtsi2sdl 0x80(%rsp), %xmm1
callq 0x1090
movaps %xmm0, %xmm2
movsd 0x28(%rsp), %xmm0
movsd 0x59a4(%rip), %xmm1     # 0x19050
subsd %xmm2, %xmm1
divsd %xmm1, %xmm0
movsd %xmm0, 0x58(%rsp)
movl $0x0, 0x54(%rsp)
movl 0x54(%rsp), %eax
cmpl 0xec(%rsp), %eax
jge 0x1373f
movq 0x98(%rsp), %rax
movslq 0x54(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
mulsd 0x58(%rsp), %xmm0
movsd %xmm0, 0x18(%rsp)
movq 0x90(%rsp), %rax
movslq 0x54(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
callq 0x11e0
movsd 0x18(%rsp), %xmm1
addsd 0xc8(%rsp), %xmm0
divsd %xmm0, %xmm1
movq 0xf0(%rsp), %rax
movslq 0x54(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
subsd %xmm1, %xmm0
movsd %xmm0, (%rax,%rcx,8)
movl 0x54(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x54(%rsp)
jmp 0x136c2
movl $0x0, 0x50(%rsp)
movl 0x50(%rsp), %eax
cmpl 0xec(%rsp), %eax
jge 0x137b9
movsd 0xd8(%rsp), %xmm0
movq 0xa0(%rsp), %rax
movslq 0x50(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm2
movsd 0x58d9(%rip), %xmm1     # 0x19050
subsd 0xd8(%rsp), %xmm1
movq 0xf0(%rsp), %rax
movslq 0x50(%rsp), %rcx
mulsd (%rax,%rcx,8), %xmm1
mulsd %xmm2, %xmm0
addsd %xmm1, %xmm0
movq 0xa0(%rsp), %rax
movslq 0x50(%rsp), %rcx
movsd %xmm0, (%rax,%rcx,8)
movl 0x50(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x50(%rsp)
jmp 0x13747
movsd 0x5c1f(%rip), %xmm0     # 0x193e0
movsd %xmm0, 0x48(%rsp)
movl $0x0, 0x44(%rsp)
movl 0x44(%rsp), %eax
cmpl 0xec(%rsp), %eax
jge 0x1381f
movq 0xf0(%rsp), %rax
movslq 0x44(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
movaps 0x588b(%rip), %xmm1     # 0x19080
pand %xmm1, %xmm0
cvtsi2sdl 0xec(%rsp), %xmm1
divsd %xmm1, %xmm0
addsd 0x48(%rsp), %xmm0
movsd %xmm0, 0x48(%rsp)
movl 0x44(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x44(%rsp)
jmp 0x137cf
movsd 0x5bb9(%rip), %xmm0     # 0x193e0
movsd %xmm0, 0x38(%rsp)
movl $0x0, 0x34(%rsp)
movl 0x34(%rsp), %eax
cmpl 0xec(%rsp), %eax
jge 0x13885
movq 0x98(%rsp), %rax
movslq 0x34(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
movaps 0x5825(%rip), %xmm1     # 0x19080
pand %xmm1, %xmm0
cvtsi2sdl 0xec(%rsp), %xmm1
divsd %xmm1, %xmm0
addsd 0x38(%rsp), %xmm0
movsd %xmm0, 0x38(%rsp)
movl 0x34(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x34(%rsp)
jmp 0x13835
movsd 0x38(%rsp), %xmm0
movsd %xmm0, 0xb8(%rsp)
cmpl $0x1, 0x80(%rsp)
jne 0x138b6
movq 0x8753(%rip), %rax      # 0x1bff8
movq (%rax), %rdi
leaq 0x5bfc(%rip), %rsi      # 0x194ab
movb $0x0, %al
callq 0x1140
movq 0x873b(%rip), %rax      # 0x1bff8
movq (%rax), %rax
movq %rax, 0x8(%rsp)
movl 0x80(%rsp), %eax
movl %eax, 0x14(%rsp)
leaq 0xa8(%rsp), %rdi
callq 0x161f0
movq 0x8(%rsp), %rdi
movl 0x14(%rsp), %edx
movsd 0x70(%rsp), %xmm1
movsd 0x48(%rsp), %xmm2
movsd 0x38(%rsp), %xmm3
movsd 0xb8(%rsp), %xmm4
leaq 0x5bbf(%rip), %rsi      # 0x194c7
movb $0x5, %al
callq 0x1140
movl 0x80(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x80(%rsp)
movl 0x80(%rsp), %ecx
xorl %eax, %eax
cmpl 0xe8(%rsp), %ecx
movb %al, 0x7(%rsp)
jg 0x1394f
movsd 0xb8(%rsp), %xmm0
ucomisd 0xe0(%rsp), %xmm0
seta %al
movb %al, 0x7(%rsp)
movb 0x7(%rsp), %al
testb $0x1, %al
jne 0x13496
movl $0x0, 0x30(%rsp)
movl 0x30(%rsp), %eax
cmpl 0xec(%rsp), %eax
jge 0x139a1
movq 0xa0(%rsp), %rax
movslq 0x30(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
movq 0xf0(%rsp), %rax
movslq 0x30(%rsp), %rcx
movsd %xmm0, (%rax,%rcx,8)
movl 0x30(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x30(%rsp)
jmp 0x13963
movq 0xa0(%rsp), %rdi
callq 0x1030
movq 0x98(%rsp), %rdi
callq 0x1030
movq 0x90(%rsp), %rdi
callq 0x1030
movq 0xc0(%rsp), %rdi
callq 0x1030
addq $0x108, %rsp            # imm = 0x108
retq
nopl (%rax)