Skip to content

Commit 58904fc

Browse files
committed
Expose Anderson acceleration stats in solve info
1 parent 0597555 commit 58904fc

8 files changed

Lines changed: 115 additions & 4 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ nuclear norm, sum-of-largest eigenvalues).
3434

3535
- Multiple linear solver backends: sparse direct (QDLDL), dense direct (LAPACK),
3636
iterative (CG), Intel MKL Pardiso, Apple Accelerate (macOS), NVIDIA cuDSS, GPU
37-
- Anderson acceleration for faster convergence
37+
- Anderson acceleration for faster convergence, including solve diagnostics
3838
- Problem data normalization/equilibration for numerical stability
3939
- Warm-starting and incremental `b`/`c` updates via `scs_update`
4040
- Ctrl-C signal handling for graceful interruption

docs/src/algorithm/acceleration.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ linearly dependent near convergence. An SVD-based solve would be similarly
156156
rank-revealing but is substantially more expensive per iteration and has not
157157
been benchmarked here.
158158

159+
SCS reports detailed AA solve diagnostics in the :code:`aa_stats` field of the
160+
returned :ref:`info` object. These include solve acceptances, rejection causes,
161+
the rank of the most recent AA solve, the most recent AA weight norm, and the
162+
regularization used in that solve.
163+
159164
Regularization
160165
""""""""""""""
161166

docs/src/api/c.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,11 @@ See :ref:`info` for details on each of these.
134134
.. doxygenstruct:: ScsInfo
135135
:members:
136136

137+
.. doxygenstruct:: ScsAaStats
138+
:members:
139+
137140
Workspace
138141
---------
139142

140143
The user should not need to interact with the :code:`ScsWork` struct,
141144
which contains the internal workspace allocated and maintained by SCS.
142-

docs/src/api/info.rst

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ the following fields.
7070
* - :code:`accepted_accel_steps`
7171
- :code:`scs_int`
7272
- Number of times an AA update was accepted by the safeguarding check (see :ref:`acceleration`)
73+
* - :code:`aa_stats`
74+
- :code:`ScsAaStats`
75+
- Detailed AA solve diagnostics, including rejection causes, last rank, last weight norm, and last regularization
7376
* - :code:`lin_sys_time`
7477
- :code:`scs_float`
7578
- Total time (milliseconds) spent in the :ref:`linear system solver <linear_solver>`
@@ -80,3 +83,47 @@ the following fields.
8083
- :code:`scs_float`
8184
- Total time (milliseconds) spent in the :ref:`acceleration routine <acceleration>`
8285

86+
Anderson acceleration statistics
87+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
88+
89+
The :code:`aa_stats` field contains detailed diagnostics from the Anderson
90+
acceleration linear solves. These counters are useful for diagnosing whether AA
91+
is active and, when AA updates are rejected, why they were rejected.
92+
93+
.. list-table::
94+
:widths: 25 15 60
95+
:header-rows: 1
96+
97+
* - Name
98+
- Type
99+
- Description
100+
* - :code:`iter`
101+
- :code:`scs_int`
102+
- Internal AA iteration counter
103+
* - :code:`n_accept`
104+
- :code:`scs_int`
105+
- Number of AA updates accepted by :code:`aa_apply` before safeguarding
106+
* - :code:`n_reject_lapack`
107+
- :code:`scs_int`
108+
- Number of AA updates rejected because the LAPACK solve failed
109+
* - :code:`n_reject_rank0`
110+
- :code:`scs_int`
111+
- Number of AA updates rejected because rank truncation produced rank zero
112+
* - :code:`n_reject_nonfinite`
113+
- :code:`scs_int`
114+
- Number of AA updates rejected because the AA weight norm was non-finite
115+
* - :code:`n_reject_weight_cap`
116+
- :code:`scs_int`
117+
- Number of AA updates rejected because the AA weight norm exceeded the configured cap
118+
* - :code:`n_safeguard_reject`
119+
- :code:`scs_int`
120+
- Number of AA updates rejected by the safeguarding check
121+
* - :code:`last_rank`
122+
- :code:`scs_int`
123+
- Rank used in the most recent AA solve
124+
* - :code:`last_aa_norm`
125+
- :code:`scs_float`
126+
- AA weight norm from the most recent AA solve, or NaN if no AA solve was attempted
127+
* - :code:`last_regularization`
128+
- :code:`scs_float`
129+
- Regularization value used in the most recent AA solve

docs/src/api/python.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ the warm-start.
133133
At termination :code:`sol` is a dict with fields :code:`x, y, s, info` where
134134
:code:`x, y, s` contains the primal-dual :ref:`solution <optimality>` or the
135135
:ref:`certificate of infeasibility <infeasibility>`, and :code:`info` is a dict
136-
containing the solve :ref:`info`.
136+
containing the solve :ref:`info`. Detailed Anderson acceleration diagnostics are
137+
available in :code:`sol["info"]["aa_stats"]`.
137138

138139
To re-use the workspace and solve a similar problem with new :code:`b`
139140
and / or :code:`c` data, we can update the solver using:
@@ -143,4 +144,3 @@ and / or :code:`c` data, we can update the solver using:
143144
solver.update(b=new_b, c=new_c) # update b and c vectors (can be None)
144145
solver.solve() # solve new problem with updated b and c
145146
146-

include/scs.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,30 @@ typedef struct {
178178
scs_float *s;
179179
} ScsSolution;
180180

181+
/** Anderson acceleration diagnostic counters. */
182+
typedef struct {
183+
/** Internal AA iteration counter. */
184+
scs_int iter;
185+
/** Number of accepted aa_apply steps before safeguarding. */
186+
scs_int n_accept;
187+
/** Number of AA rejections due to LAPACK errors. */
188+
scs_int n_reject_lapack;
189+
/** Number of AA rejections due to rank-zero reduced systems. */
190+
scs_int n_reject_rank0;
191+
/** Number of AA rejections due to non-finite weights. */
192+
scs_int n_reject_nonfinite;
193+
/** Number of AA rejections due to the weight-norm cap. */
194+
scs_int n_reject_weight_cap;
195+
/** Number of AA steps rejected by safeguarding. */
196+
scs_int n_safeguard_reject;
197+
/** Rank of the most recent AA solve. */
198+
scs_int last_rank;
199+
/** Weight norm from the most recent AA solve. NaN if no solve was attempted. */
200+
scs_float last_aa_norm;
201+
/** Regularization used in the most recent AA solve. */
202+
scs_float last_regularization;
203+
} ScsAaStats;
204+
181205
/** Contains information about the solve run at termination. */
182206
typedef struct {
183207
/** Number of iterations taken. */
@@ -218,6 +242,8 @@ typedef struct {
218242
scs_int rejected_accel_steps;
219243
/** Number of accepted AA steps. */
220244
scs_int accepted_accel_steps;
245+
/** Detailed Anderson acceleration diagnostics. */
246+
ScsAaStats aa_stats;
221247
/** Total time (milliseconds) spent in the linear system solver. */
222248
scs_float lin_sys_time;
223249
/** Total time (milliseconds) spent in the cone projection. */

src/scs.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ static void print_summary(ScsWork *w, scs_int i, SCS(timer) * solve_timer);
7979
static void print_footer(ScsInfo *info);
8080
static void free_residuals(ScsResiduals *r);
8181
static ScsResiduals *init_residuals(const ScsData *d);
82+
static void set_info_aa_stats(ScsInfo *info, const AaWork *accel);
8283
static void populate_on_failure(scs_int m, scs_int n, ScsSolution *sol,
8384
ScsInfo *info, scs_int status_val,
8485
const char *msg);
@@ -299,6 +300,31 @@ static ScsResiduals *init_residuals(const ScsData *d) {
299300
return r;
300301
}
301302

303+
static void set_info_aa_stats(ScsInfo *info, const AaWork *accel) {
304+
ScsAaStats *dst;
305+
AaStats src;
306+
if (!info) {
307+
return;
308+
}
309+
dst = &info->aa_stats;
310+
memset(dst, 0, sizeof(*dst));
311+
dst->last_aa_norm = NAN;
312+
if (!accel) {
313+
return;
314+
}
315+
src = aa_get_stats(accel);
316+
dst->iter = src.iter;
317+
dst->n_accept = src.n_accept;
318+
dst->n_reject_lapack = src.n_reject_lapack;
319+
dst->n_reject_rank0 = src.n_reject_rank0;
320+
dst->n_reject_nonfinite = src.n_reject_nonfinite;
321+
dst->n_reject_weight_cap = src.n_reject_weight_cap;
322+
dst->n_safeguard_reject = src.n_safeguard_reject;
323+
dst->last_rank = src.last_rank;
324+
dst->last_aa_norm = src.last_aa_norm;
325+
dst->last_regularization = src.last_regularization;
326+
}
327+
302328
/* ==================== Error / Failure Handling ===================== */
303329

304330
static void populate_on_failure(scs_int m, scs_int n, ScsSolution *sol,
@@ -340,6 +366,7 @@ static scs_int failure(ScsWork *w, scs_int m, scs_int n, ScsSolution *sol,
340366
const char *ststr) {
341367
scs_int status = stint;
342368
populate_on_failure(m, n, sol, info, status, ststr);
369+
set_info_aa_stats(info, w ? w->accel : SCS_NULL);
343370
scs_printf("Failure:%s\n", msg);
344371
scs_end_interrupt_listener();
345372
return status;
@@ -865,6 +892,7 @@ static void finalize(ScsWork *w, ScsSolution *sol, ScsInfo *info,
865892
info->scale_updates = w->scale_updates;
866893
info->rejected_accel_steps = w->rejected_accel_steps;
867894
info->accepted_accel_steps = w->accepted_accel_steps;
895+
set_info_aa_stats(info, w->accel);
868896
info->comp_slack = ABS(sty);
869897
#ifdef SPECTRAL_TIMING_FLAG
870898
info->ave_time_matrix_cone_proj = w->cone_work->tot_time_mat_cone_proj / iter;

test/problems/test_solver_options.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ static const char *test_no_acceleration(void) {
123123
mu_assert("test_no_acceleration: expected SCS_SOLVED", exitflag == SCS_SOLVED);
124124
mu_assert("test_no_acceleration: no AA steps should be accepted",
125125
info.accepted_accel_steps == 0);
126+
mu_assert("test_no_acceleration: AA stats should be zeroed",
127+
info.aa_stats.iter == 0 && info.aa_stats.n_accept == 0 &&
128+
info.aa_stats.n_safeguard_reject == 0);
126129
fail = verify_solution_correct(d, k, stgs, &info, sol, exitflag);
127130

128131
_OPTS_CLEANUP();

0 commit comments

Comments
 (0)