|
| 1 | +#include <stdio.h> |
| 2 | +#include <math.h> // for fabs |
| 3 | + |
| 4 | +// Find an approximate row of \hat{Sigma}^{-1} |
| 5 | + |
| 6 | +// Solves a dual version of problem (4) of https://arxiv.org/pdf/1306.3171.pdf |
| 7 | + |
| 8 | +// Dual problem: \text{min}_{\theta} 1/2 \theta^T \Sigma \theta - e_i^T\theta + \mu \|\theta\|_1 |
| 9 | + |
| 10 | +// This is the "negative" of the problem as in https://gist.github.com/jonathan-taylor/07774d209173f8bc4e42aa37712339bf |
| 11 | +// Therefore we don't have to negate the answer to get theta. |
| 12 | +// Update one coordinate |
| 13 | + |
| 14 | +double objective(double *Sigma, /* A covariance matrix: X^TX/n */ |
| 15 | + int nrow, /* how many rows in Sigma */ |
| 16 | + int row, /* which row: 0-based */ |
| 17 | + double bound, /* Lagrange multipler for \ell_1 */ |
| 18 | + double *theta) /* current value */ |
| 19 | +{ |
| 20 | + int irow, icol; |
| 21 | + double value = 0; |
| 22 | + double *Sigma_ptr = Sigma; |
| 23 | + double *theta_row_ptr, *theta_col_ptr; |
| 24 | + |
| 25 | + theta_row_ptr = theta; |
| 26 | + theta_col_ptr = theta; |
| 27 | + |
| 28 | + for (irow=0; irow<nrow; irow++) { |
| 29 | + double *theta_col_ptr = theta; |
| 30 | + if (*theta_row_ptr != 0) { |
| 31 | + for (icol=0; icol<nrow; icol++) { |
| 32 | + value += 0.5 * (*Sigma_ptr) * (*theta_row_ptr) * (*theta_col_ptr); |
| 33 | + Sigma_ptr++; |
| 34 | + theta_col_ptr++; |
| 35 | + } |
| 36 | + } |
| 37 | + if (irow == row) { |
| 38 | + value -= (*theta_row_ptr); // the elementary basis vector term |
| 39 | + } |
| 40 | + value = value + bound * fabs((*theta_row_ptr)); // the \ell_1 term |
| 41 | + theta_row_ptr++; |
| 42 | + } |
| 43 | + |
| 44 | + return(value); |
| 45 | +} |
| 46 | + |
| 47 | + |
| 48 | +double update_one_coord(double *Sigma, /* A covariance matrix: X^TX/n */ |
| 49 | + double *Sigma_diag, /* Diagonal entries of Sigma */ |
| 50 | + double *Sigma_theta, /* Sigma times theta */ |
| 51 | + int nrow, /* How many rows in Sigma */ |
| 52 | + double bound, /* feasibility parameter */ |
| 53 | + double *theta, /* current value */ |
| 54 | + int row, /* which row: 0-based */ |
| 55 | + int coord) /* which coordinate to update: 0-based */ |
| 56 | +{ |
| 57 | + |
| 58 | + double delta; |
| 59 | + double linear_term = 0; |
| 60 | + double value = 0; |
| 61 | + double old_value; |
| 62 | + double *Sigma_ptr; |
| 63 | + double *Sigma_theta_ptr; |
| 64 | + double *theta_ptr; |
| 65 | + int icol = 0; |
| 66 | + |
| 67 | + double *quadratic_ptr = ((double *) Sigma_diag + coord); |
| 68 | + double quadratic_term = *quadratic_ptr; |
| 69 | + |
| 70 | + Sigma_theta_ptr = ((double *) Sigma_theta + coord); |
| 71 | + linear_term = *Sigma_theta_ptr; |
| 72 | + |
| 73 | + theta_ptr = ((double *) theta + coord); |
| 74 | + old_value = *theta_ptr; |
| 75 | + |
| 76 | + // The coord entry of Sigma_theta term has a diagonal term in it: |
| 77 | + // Sigma[coord, coord] * theta[coord] |
| 78 | + // This removes it. |
| 79 | + linear_term -= quadratic_term * old_value; |
| 80 | + |
| 81 | + if (row == coord) { |
| 82 | + linear_term -= 1; |
| 83 | + } |
| 84 | + |
| 85 | + // Now soft-threshold the coord entry of theta |
| 86 | + |
| 87 | + // Objective is t \mapsto q/2 * t^2 + l * t + bound |t| |
| 88 | + // with q=quadratic_term and l=linear_term |
| 89 | + |
| 90 | + // With a negative linear term, solution should be |
| 91 | + // positive |
| 92 | + |
| 93 | + if (linear_term < -bound) { |
| 94 | + value = (-linear_term - bound) / quadratic_term; |
| 95 | + } |
| 96 | + else if (linear_term > bound) { |
| 97 | + value = -(linear_term - bound) / quadratic_term; |
| 98 | + } |
| 99 | + |
| 100 | + if (fabs(old_value - value) > 1.e-6 * (fabs(value) + fabs(old_value))) { // Update the linear term |
| 101 | + |
| 102 | + delta = value - old_value; |
| 103 | + Sigma_ptr = ((double *) Sigma + coord * nrow); |
| 104 | + Sigma_theta_ptr = ((double *) Sigma_theta); |
| 105 | + |
| 106 | + for (icol=0; icol<nrow; icol++) { |
| 107 | + *Sigma_theta_ptr = *Sigma_theta_ptr + delta * (*Sigma_ptr); |
| 108 | + Sigma_theta_ptr += 1; |
| 109 | + Sigma_ptr += 1; |
| 110 | + } |
| 111 | + |
| 112 | + theta_ptr = ((double *) theta + coord); |
| 113 | + *theta_ptr = value; |
| 114 | + |
| 115 | + } |
| 116 | + |
| 117 | + return(value); |
| 118 | + |
| 119 | +} |
| 120 | + |
| 121 | +void find_one_row(double *Sigma, /* A covariance matrix: X^TX/n */ |
| 122 | + double *Sigma_diag, /* Diagonal entry of covariance matrix */ |
| 123 | + double *Sigma_theta, /* Sigma times theta */ |
| 124 | + int *nrow_ptr, /* How many rows in Sigma */ |
| 125 | + double *bound_ptr, /* feasibility parameter */ |
| 126 | + double *theta, /* current value */ |
| 127 | + int *maxiter_ptr, /* how many iterations */ |
| 128 | + int *row_ptr) /* which coordinate to update: 0-based */ |
| 129 | +{ |
| 130 | + |
| 131 | + int maxiter = *maxiter_ptr; |
| 132 | + int iter = 0; |
| 133 | + int icoord = 0; |
| 134 | + int row = *row_ptr; |
| 135 | + double bound = *bound_ptr; |
| 136 | + int nrow = *nrow_ptr; |
| 137 | + |
| 138 | + double old_value = objective(Sigma, |
| 139 | + nrow, |
| 140 | + row, |
| 141 | + bound, |
| 142 | + theta); |
| 143 | + double new_value; |
| 144 | + double tol=1.e-10; |
| 145 | + |
| 146 | + for (iter=0; iter<maxiter; iter++) { |
| 147 | + |
| 148 | + // Update the diagonal first |
| 149 | + |
| 150 | + update_one_coord(Sigma, |
| 151 | + Sigma_diag, |
| 152 | + Sigma_theta, |
| 153 | + nrow, |
| 154 | + bound, |
| 155 | + theta, |
| 156 | + row, |
| 157 | + row); |
| 158 | + |
| 159 | + for (icoord=0; icoord<nrow; icoord++) { |
| 160 | + |
| 161 | + update_one_coord(Sigma, |
| 162 | + Sigma_diag, |
| 163 | + Sigma_theta, |
| 164 | + nrow, |
| 165 | + bound, |
| 166 | + theta, |
| 167 | + row, |
| 168 | + icoord); |
| 169 | + } |
| 170 | + |
| 171 | + new_value = objective(Sigma, |
| 172 | + nrow, |
| 173 | + row, |
| 174 | + bound, |
| 175 | + theta); |
| 176 | + |
| 177 | + if (((old_value - new_value) < tol * fabs(new_value)) && (iter > 0)) { |
| 178 | + break; |
| 179 | + } |
| 180 | + |
| 181 | + old_value = new_value; |
| 182 | + } |
| 183 | + |
| 184 | + *nrow_ptr = iter-1; |
| 185 | +} |
| 186 | + |
0 commit comments