• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dance858 / PSLP / 21523923575

30 Jan 2026 05:03PM UTC coverage: 89.013% (+0.03%) from 88.988%
21523923575

Pull #34

github

web-flow
Merge 30140dc5c into 8137ce2f7
Pull Request #34: fix-windows-build

1249 of 1400 branches covered (89.21%)

Branch coverage included in aggregate %.

222 of 232 new or added lines in 20 files covered. (95.69%)

1 existing line in 1 file now uncovered.

3855 of 4334 relevant lines covered (88.95%)

3375.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.22
/src/core/Postsolver.c
1
/*
2
 * Copyright 2025 Daniel Cederberg
3
 *
4
 * This file is part of the PSLP project (LP Presolver).
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18

19
#include "Postsolver.h"
20
#include "Bounds.h"
21
#include "Constraints.h"
22
#include "Matrix.h"
23
#include "Numerics.h"
24
#include "State.h"
25
#include "dVec.h"
26
#include "debug_macros.h"
27
#include "glbopts.h"
28
#include "iVec.h"
29
#include "u16Vec.h"
30
#include <PSLP_warnings.h>
31
#include <assert.h>
32

33
#define INIT_FRAC_POSTSOLVE 0.3
34
#define COL_NOT_RETRIEVED INF
35
#define ROW_NOT_RETRIEVED INF
36
#define DUMMY_VALUE -382749
37

38
PostsolveInfo *postsolve_info_new(size_t n_rows, size_t n_cols)
110✔
39
{
40
    PostsolveInfo *info = (PostsolveInfo *) ps_malloc(1, sizeof(PostsolveInfo));
110✔
41

42
    size_t init_size;
43

44
    /* disable conversion compiler warning*/
45
    PSLP_DIAG_PUSH();
46
    PSLP_DIAG_IGNORE_CONVERSION();
47
    // plus one to avoid zero size allocations
48
    init_size = ((size_t) (INIT_FRAC_POSTSOLVE * (n_rows + n_cols))) + 1;
110✔
49
    /* enable conversion compiler warnings */
50
    PSLP_DIAG_POP();
51

52
    info->starts = iVec_new(init_size);
110✔
53
    info->indices = iVec_new(init_size);
110✔
54
    info->vals = dVec_new(init_size);
110✔
55
    info->type = u16Vec_new(init_size);
110✔
56
    if (!info->starts || !info->indices || !info->vals || !info->type)
110✔
57
    {
58
        PS_FREE(info->starts);
×
59
        PS_FREE(info->indices);
×
60
        PS_FREE(info->vals);
×
61
        PS_FREE(info->type);
×
62
        PS_FREE(info);
×
63
        return NULL;
×
64
    }
65

66
    iVec_append(info->starts, 0);
110✔
67
    return info;
110✔
68
}
69

70
void postsolve_info_free(PostsolveInfo *info)
110✔
71
{
72
    if (!info)
110✔
73
    {
74
        return;
×
75
    }
76

77
    iVec_free(info->starts);
110✔
78
    iVec_free(info->indices);
110✔
79
    dVec_free(info->vals);
110✔
80
    u16Vec_free(info->type);
110✔
81
    PS_FREE(info);
110✔
82
}
83

84
static inline void copy_reduced_sol_to_orginal(Solution *sol, const double *x,
18✔
85
                                               const double *y, const double *z,
86
                                               const int *col_map,
87
                                               const int *row_map)
88
{
89
    size_t dim_x = sol->dim_x;
18✔
90
    for (size_t i = 0; i < dim_x; ++i)
118✔
91
    {
92
        if (col_map[i] == -1)
100✔
93
        {
94
            sol->x[i] = COL_NOT_RETRIEVED;
30✔
95
            sol->z[i] = COL_NOT_RETRIEVED;
30✔
96
            continue;
30✔
97
        }
98

99
        sol->x[i] = x[col_map[i]];
70✔
100
        sol->z[i] = z[col_map[i]];
70✔
101
    }
102

103
    size_t dim_y = sol->dim_y;
18✔
104
    for (size_t i = 0; i < dim_y; ++i)
89✔
105
    {
106
        if (row_map[i] == -1)
71✔
107
        {
108
            sol->y[i] = ROW_NOT_RETRIEVED;
29✔
109
            continue;
29✔
110
        }
111

112
        sol->y[i] = y[row_map[i]];
42✔
113
    }
114
}
18✔
115

116
// fixes xk and recovers zk = ck - ak^T y
117
static void retrieve_fix_col(Solution *sol, int col, double val, double ck,
11✔
118
                             const double *ak_vals, const int *ak_rows, int len)
119
{
120
    assert(sol->x[col] == COL_NOT_RETRIEVED);
11✔
121
    assert(sol->z[col] == COL_NOT_RETRIEVED);
11✔
122

123
    sol->x[col] = val;
11✔
124
    sol->z[col] = ck;
11✔
125
    for (int i = 0; i < len; ++i)
26✔
126
    {
127
        assert(sol->y[ak_rows[i]] != ROW_NOT_RETRIEVED);
15✔
128
        sol->z[col] -= ak_vals[i] * sol->y[ak_rows[i]];
15✔
129
    }
130
}
11✔
131

132
// retrieves xk and zk
133
static void retrieve_sub_col(Solution *sol, int k, const int *cols,
9✔
134
                             const double *vals, int len, double rhs, int i,
135
                             double ck)
136
{
137
    assert(sol->y[i] != ROW_NOT_RETRIEVED);
9✔
138
    assert(sol->x[k] == COL_NOT_RETRIEVED);
9✔
139
    assert(sol->z[k] == COL_NOT_RETRIEVED);
9✔
140

141
    sol->x[k] = rhs;
9✔
142
    double aik = 0.0;
9✔
143
    for (int ii = 0; ii < len; ++ii)
56✔
144
    {
145
        if (cols[ii] == k)
47✔
146
        {
147
            aik = vals[ii];
9✔
148
            continue;
9✔
149
        }
150

151
        assert(sol->x[cols[ii]] != COL_NOT_RETRIEVED);
38✔
152
        sol->x[k] -= vals[ii] * sol->x[cols[ii]];
38✔
153
    }
154

155
    sol->z[k] = ck - aik * sol->y[i];
9✔
156
    sol->x[k] /= aik;
9✔
157
}
9✔
158

159
static void retrieve_fix_col_inf(Solution *sol, const int *indices,
2✔
160
                                 const double *vals)
161
{
162
    int i, j, counter, row_len;
163
    const int *cols;
164
    double coeff = 0;
2✔
165
    double val, side;
166
    const double *coeffs;
167
    int n_rows = (int) vals[0];
2✔
168
    double extreme_val = vals[1];
2✔
169
    bool fix_to_pos_inf = (indices[0] > 0);
2✔
170
    int col = indices[1];
2✔
171
    assert(sol->x[col] == COL_NOT_RETRIEVED);
2✔
172
    assert(sol->z[col] == COL_NOT_RETRIEVED);
2✔
173

174
    counter = 2;
2✔
175
    for (i = 0; i < n_rows; ++i)
6✔
176
    {
177
        side = vals[counter];
4✔
178
        coeffs = vals + counter + 1;
4✔
179
        row_len = indices[counter];
4✔
180
        cols = indices + counter + 1;
4✔
181
        counter += row_len + 1;
4✔
182

183
        for (j = 0; j < row_len; ++j)
20✔
184
        {
185
            if (cols[j] == col)
16✔
186
            {
187
                coeff = coeffs[j];
4✔
188
                continue;
4✔
189
            }
190

191
            //  If two columns are fixed to pos inf in the same row, we
192
            //  pretend one of them is zero while we compute the other one
193
            if (sol->x[cols[j]] == COL_NOT_RETRIEVED)
12✔
194
            {
195
                continue;
×
196
            }
197

198
            assert(sol->x[cols[j]] != COL_NOT_RETRIEVED);
12✔
199
            side -= coeffs[j] * sol->x[cols[j]];
12✔
200
        }
201

202
        val = side / coeff;
4✔
203
        if (fix_to_pos_inf)
4✔
204
        {
205
            extreme_val = MAX(extreme_val, val);
2✔
206
        }
207
        else
208
        {
209
            extreme_val = MIN(extreme_val, val);
2✔
210
        }
211
    }
212

213
    assert(!IS_ABS_INF(extreme_val));
2✔
214
    sol->x[col] = extreme_val;
2✔
215
    sol->z[col] = 0.0;
2✔
216
}
2✔
217

218
static void retrieve_parallel_col(Solution *sol, const int *indices,
8✔
219
                                  const double *vals)
220
{
221
    int j = indices[0];
8✔
222
    int k = indices[1];
8✔
223
    ColTag cTag_j = (ColTag) indices[2];
8✔
224
    ColTag cTag_k = (ColTag) indices[3];
8✔
225
    assert(indices[4] == DUMMY_VALUE);
8✔
226
    double lb_j = vals[0];
8✔
227
    double ub_j = vals[1];
8✔
228
    double lb_k = vals[2];
8✔
229
    double ub_k = vals[3];
8✔
230
    double ratio = vals[4];
8✔
231
    assert(sol->x[j] != COL_NOT_RETRIEVED && sol->x[k] == COL_NOT_RETRIEVED);
8✔
232
    assert(sol->z[j] != COL_NOT_RETRIEVED && sol->z[k] == COL_NOT_RETRIEVED);
8✔
233
    double x_new_sol = sol->x[j];
8✔
234
    double xj_val, xk_val;
235

236
    // ----------------------------------------------------------------
237
    //          try to set xj equal to one of its bounds
238
    // ----------------------------------------------------------------
239
    if (!HAS_TAG(cTag_j, C_TAG_LB_INF))
8✔
240
    {
241
        xj_val = lb_j;
8✔
242
    }
243
    else if (!HAS_TAG(cTag_j, C_TAG_UB_INF))
×
244
    {
245
        xj_val = ub_j;
×
246
    }
247
    else
248
    {
249
        xj_val = 0;
×
250
    }
251

252
    // ----------------------------------------------------------------
253
    //     check if the corresponding value on xk is feasible
254
    // ----------------------------------------------------------------
255
    xk_val = (x_new_sol - xj_val) / ratio;
8✔
256

257
    if (!HAS_TAG(cTag_k, C_TAG_LB_INF) && lb_k >= xk_val + FEAS_TOL)
8✔
258
    {
259
        xk_val = lb_k;
6✔
260
        xj_val = x_new_sol - ratio * xk_val;
6✔
261
    }
262
    else if (!HAS_TAG(cTag_k, C_TAG_UB_INF) && xk_val >= ub_k + FEAS_TOL)
2✔
263
    {
264
        xk_val = ub_k;
2✔
265
        xj_val = x_new_sol - ratio * xk_val;
2✔
266
    }
267

268
    assert(!IS_ABS_INF(xj_val) && !IS_ABS_INF(xk_val));
8✔
269
    sol->x[j] = xj_val;
8✔
270
    sol->x[k] = xk_val;
8✔
271

272
    // dual postsolve
273
    bool is_xj_at_bound =
8✔
274
        (!HAS_TAG(cTag_j, C_TAG_LB_INF) && IS_EQUAL_FEAS_TOL(xj_val, lb_j)) ||
16✔
275
        (!HAS_TAG(cTag_j, C_TAG_UB_INF) && IS_EQUAL_FEAS_TOL(xj_val, ub_j));
8✔
276
    bool is_xk_at_bound =
8✔
277
        (!HAS_TAG(cTag_k, C_TAG_LB_INF) && IS_EQUAL_FEAS_TOL(xk_val, lb_k)) ||
10✔
278
        (!HAS_TAG(cTag_k, C_TAG_UB_INF) && IS_EQUAL_FEAS_TOL(xk_val, ub_k));
2✔
279

280
    if (is_xj_at_bound && is_xk_at_bound)
8✔
281
    {
282
        sol->z[k] = ratio * sol->z[j];
8✔
283
    }
284
    else
285
    {
286
        sol->z[k] = 0.0;
×
287
    }
288

289
#ifndef NDEBUG
290
    SET_ZERO_IF_SMALL_DUAL_POSTSOLVE(sol->z[j]);
8✔
291

292
    // if the multiplier is positive the variable should be at its lower bound
293
    if (sol->z[j] > 0)
8✔
294
    {
295
        assert(!HAS_TAG(cTag_j, C_TAG_LB_INF) && IS_EQUAL_FEAS_TOL(sol->x[j], lb_j));
×
296
    }
297
    // if the multiplier is negative the variable should be at its upper bound
298
    else if (sol->z[j] < 0)
8✔
299
    {
300
        assert(!HAS_TAG(cTag_j, C_TAG_UB_INF) && IS_EQUAL_FEAS_TOL(sol->x[j], ub_j));
8✔
301
    }
302

303
    // similar checks for variable k
304
    if (sol->z[k] > 0)
8✔
305
    {
306
        assert(!HAS_TAG(cTag_k, C_TAG_LB_INF) && IS_EQUAL_FEAS_TOL(sol->x[k], lb_k));
6✔
307
    }
308
    else if (sol->z[k] < 0)
2✔
309
    {
310
        assert(!HAS_TAG(cTag_k, C_TAG_UB_INF) && IS_EQUAL_FEAS_TOL(sol->x[k], ub_k));
2✔
311
    }
312
#endif
313
}
8✔
314

315
void retrieve_deleted_row(Solution *sol, int row, double val)
29✔
316
{
317
    assert(sol->y[row] == ROW_NOT_RETRIEVED);
29✔
318
    sol->y[row] = val;
29✔
319
}
29✔
320

321
// yi = yi - (ajk / aik) yj whenever we make the jth constraint sparser by
322
// zeroing out xk using equality row i
323
void retrieve_added_row(Solution *sol, const int *rows, const double *vals)
6✔
324
{
325
    int i = rows[0];
6✔
326
    int j = rows[1];
6✔
327
    assert(sol->y[i] != ROW_NOT_RETRIEVED);
6✔
328
    assert(sol->y[j] != ROW_NOT_RETRIEVED);
6✔
329
    assert(vals[1] == DUMMY_VALUE);
6✔
330
    // it should be a plus sign because the minus sign is included in
331
    // save_retrieval_added_row
332
    sol->y[i] += sol->y[j] * vals[0];
6✔
333
}
6✔
334

335
void retrieve_added_rows(Solution *sol, int i, const int *rows, const double *vals,
4✔
336
                         int len, double aik)
337
{
338
    assert(sol->y[i] != ROW_NOT_RETRIEVED);
4✔
339

340
    for (int j = 0; j < len; ++j)
17✔
341
    {
342
        if (rows[j] != i)
13✔
343
        {
344
            assert(sol->y[rows[j]] != ROW_NOT_RETRIEVED);
9✔
345
            sol->y[i] -= (vals[j] / aik) * sol->y[rows[j]];
9✔
346
        }
347
    }
348
}
4✔
349

350
void retrieve_bound_change(Solution *sol, int i, int j, const int *cols,
8✔
351
                           const double *vals, int len, double implied_bound,
352
                           double original_other_bound,
353
                           int is_original_other_bound_lower_bound)
354
{
355
    int k, ii;
356
    double aij = 1.0;
8✔
357
    assert(sol->x[j] != COL_NOT_RETRIEVED);
8✔
358
    assert(sol->y[i] != ROW_NOT_RETRIEVED);
8✔
359
    assert(sol->z[j] != COL_NOT_RETRIEVED);
8✔
360

361
    // if the variable is equal to one of its original bounds and
362
    // has the right sign on its multiplier we do nothing
363
    if (IS_EQUAL_FEAS_TOL(sol->x[j], original_other_bound))
8✔
364
    {
365
        if ((is_original_other_bound_lower_bound && sol->z[j] >= 0) ||
5✔
366
            (!is_original_other_bound_lower_bound && sol->z[j] <= 0))
×
367
        {
368
            return;
4✔
369
        }
370
    }
371

372
    // if the implied bound is not active we do nothing
373
    if (!IS_EQUAL_FEAS_TOL(sol->x[j], implied_bound))
4✔
374
    {
375
        // sol->y[i] does not always have to be zero since we possibly got the
376
        // bound from a doubleton equality row assert(sol->y[i] == 0.0);
377
        return;
1✔
378
    }
379

380
    // find aij
381
    for (ii = 0; ii < len; ++ii)
3✔
382
    {
383
        if (cols[ii] == j)
3✔
384
        {
385
            aij = vals[ii];
3✔
386
            break;
3✔
387
        }
388
    }
389
    assert(ii != len && aij != 0.0);
3✔
390

391
    // update yi for row i that was used in the bound change
392
    sol->y[i] += sol->z[j] / aij;
3✔
393
    //
394
    // update zk for all variables k appearing in row i
395
    for (ii = 0; ii < len; ++ii)
7✔
396
    {
397
        k = cols[ii];
4✔
398
        if (k == j)
4✔
399
        {
400
            continue;
3✔
401
        }
402

403
        // for now we cheat for dual postsolve of primal propagation fixing variables
404
        if (sol->z[k] == COL_NOT_RETRIEVED)
1✔
405
        {
406
            continue;
×
407
        }
408

409
        assert(sol->z[k] != COL_NOT_RETRIEVED);
1✔
410
        sol->z[k] -= (vals[ii] / aij) * sol->z[j];
1✔
411
    }
412

413
    sol->z[j] = 0.0;
3✔
414
}
415

416
void retrieve_lhs_change(Solution *sol, int i, const double *vals, const int *cols,
1✔
417
                         int len, double new_side, int j, double ratio)
418
{
419
    assert(sol->y[i] != ROW_NOT_RETRIEVED);
1✔
420
    assert(sol->y[j] == ROW_NOT_RETRIEVED || sol->y[j] == 0.0);
1✔
421

422
    // compute the residual of constraint i
423
    double residual = -new_side;
1✔
424
    for (int k = 0; k < len; ++k)
4✔
425
    {
426
        assert(sol->x[cols[k]] != COL_NOT_RETRIEVED);
3✔
427
        residual += vals[k] * sol->x[cols[k]];
3✔
428
    }
429

430
    if (!IS_ZERO_FEAS_TOL(residual))
1✔
431
    {
432
        return;
×
433
    }
434

435
    double cand_val = ratio * sol->y[i];
1✔
436
    if (ratio * cand_val < 0)
1✔
437
    {
438
        sol->y[j] = 0.0;
×
439
        return;
×
440
    }
441

442
    sol->y[j] = cand_val;
1✔
443
    sol->y[i] = 0.0;
1✔
444
}
445

446
void retrieve_rhs_change(Solution *sol, int i, const double *vals, const int *cols,
×
447
                         int len, double new_side, int j, double ratio)
448
{
449
    assert(sol->y[i] != ROW_NOT_RETRIEVED);
×
450
    assert(sol->y[j] == ROW_NOT_RETRIEVED || sol->y[j] == 0.0);
×
451

452
    // compute the residual of constraint i
453
    double residual = -new_side;
×
454
    for (int k = 0; k < len; ++k)
×
455
    {
456
        assert(sol->x[cols[k]] != COL_NOT_RETRIEVED);
×
457
        residual += vals[k] * sol->x[cols[k]];
×
458
    }
459

460
    if (!IS_ZERO_FEAS_TOL(residual))
×
461
    {
462
        return;
×
463
    }
464

465
    double cand_val = ratio * sol->y[i];
×
466
    if (ratio * cand_val > 0)
×
467
    {
468
        sol->y[j] = 0.0;
×
469
        return;
×
470
    }
471

472
    sol->y[j] = cand_val;
×
473
    sol->y[i] = 0.0;
×
474
}
475

476
void retrieve_eq_to_ineq(Solution *sol, int row, double val)
×
477
{
478
    assert(sol->y[row] != ROW_NOT_RETRIEVED);
×
479
    sol->y[row] += val;
×
480
}
×
481

482
void postsolver_update(PostsolveInfo *info, size_t n_cols_reduced,
18✔
483
                       size_t n_rows_reduced, const int *col_map, const int *row_map)
484
{
485
    info->n_cols_reduced = n_cols_reduced;
18✔
486
    info->n_rows_reduced = n_rows_reduced;
18✔
487
    info->col_map = col_map;
18✔
488
    info->row_map = row_map;
18✔
489
}
18✔
490

491
void polish_z(Solution *sol, const double *lbs, const double *ubs)
×
492
{
NEW
493
    size_t n_cols = sol->dim_x;
×
494
    double *x = sol->x;
×
495
    double *z = sol->z;
×
NEW
496
    for (size_t k = 0; k < n_cols; ++k)
×
497
    {
498
        bool is_xk_equal_to_lb = IS_EQUAL_FEAS_TOL(x[k], lbs[k]);
×
499
        bool is_xk_equal_to_ub = IS_EQUAL_FEAS_TOL(x[k], ubs[k]);
×
500

501
        // if wrong sign we set it to zero
502
        if (is_xk_equal_to_lb && !is_xk_equal_to_ub && z[k] < 0)
×
503
        {
504
            z[k] = 0.0;
×
505
        }
506
        else if (!is_xk_equal_to_lb && is_xk_equal_to_ub && z[k] > 0)
×
507
        {
508
            z[k] = 0.0;
×
509
        }
510
    }
511
}
×
512

513
void postsolver_run(const PostsolveInfo *info, Solution *sol, const double *x,
18✔
514
                    const double *y, const double *z)
515
{
516
    const int *col_map = info->col_map;
18✔
517
    const int *row_map = info->row_map;
18✔
518
    int n_reductions = (int) info->type->len;
18✔
519
    ReductionType *reductions = info->type->data;
18✔
520
    const int *indices = info->indices->data;
18✔
521
    const double *vals = info->vals->data;
18✔
522
    const int *starts = info->starts->data;
18✔
523
    assert(n_reductions == info->starts->len - 1);
18✔
524
    ReductionType type;
525
    int start, len;
526

527
    copy_reduced_sol_to_orginal(sol, x, y, z, col_map, row_map);
18✔
528

529
    for (int i = n_reductions - 1; i >= 0; --i)
96✔
530
    {
531
        type = reductions[i];
78✔
532
        start = starts[i];
78✔
533

534
        if (type == FIXED_COL)
78✔
535
        {
536
            len = starts[i + 1] - start - 2;
11✔
537
            // we might have fixed an empty column so len can be 0
538
            assert(len >= 0);
11✔
539
            retrieve_fix_col(sol, indices[start], vals[start], vals[start + 1],
11✔
540
                             vals + start + 2, indices + start + 2, len);
11✔
541
        }
542
        else if (type == SUB_COL)
67✔
543
        {
544
            len = starts[i + 1] - start - 2;
9✔
545
            assert(len > 1);
9✔
546
            retrieve_sub_col(sol, indices[start], indices + start + 1,
9✔
547
                             vals + start + 1, len, vals[start],
9✔
548
                             indices[start + len + 1], vals[start + len + 1]);
9✔
549
        }
550
        else if (type == FIXED_COL_INF)
58✔
551
        {
552
            retrieve_fix_col_inf(sol, indices + start, vals + start);
2✔
553
        }
554
        else if (type == PARALLEL_COL)
56✔
555
        {
556
            assert(starts[i + 1] - start == 5);
8✔
557
            retrieve_parallel_col(sol, indices + start, vals + start);
8✔
558
        }
559
        else if (type == DELETED_ROW)
48✔
560
        {
561
            assert(starts[i + 1] - start == 1);
29✔
562
            retrieve_deleted_row(sol, indices[start], vals[start]);
29✔
563
        }
564
        else if (type == ADDED_ROW)
19✔
565
        {
566
            assert(starts[i + 1] - start == 2);
6✔
567
            retrieve_added_row(sol, indices + start, vals + start);
6✔
568
        }
569
        else if (type == ADDED_ROWS)
13✔
570
        {
571
            len = starts[i + 1] - start - 1;
4✔
572
            assert(len >= 1);
4✔
573
            retrieve_added_rows(sol, indices[start], indices + start + 1,
4✔
574
                                vals + start + 1, len, vals[start]);
4✔
575
        }
576
        else if (type == BOUND_CHANGE_THE_ROW)
9✔
577
        {
578
            // get the row that was used to derive the bound changes
579
            len = starts[i + 1] - start - 1;
8✔
580
            assert(len > 0);
8✔
581
            int num_of_bound_changes = (int) vals[start];
8✔
582
            int row = indices[start];
8✔
583
            const int *row_cols = indices + start + 1;
8✔
584
            const double *row_vals = vals + start + 1;
8✔
585
            int bound_changes_processed = 0;
8✔
586
            int j = i - 1;
8✔
587

588
            while (bound_changes_processed < num_of_bound_changes)
16✔
589
            {
590
                type = reductions[j];
8✔
591
                start = starts[j];
8✔
592
                assert(type == BOUND_CHANGE_NO_ROW || type == FIXED_COL);
8✔
593

594
                if (type == FIXED_COL)
8✔
595
                {
596
                    retrieve_fix_col(sol, indices[start], vals[start],
×
597
                                     vals[start + 1], vals + start + 2,
×
598
                                     indices + start + 2, starts[j + 1] - start - 2);
×
599
                    assert(reductions[j - 1] == BOUND_CHANGE_NO_ROW);
×
600
                }
601
                else
602
                {
603
                    bound_changes_processed += 1;
8✔
604
                    retrieve_bound_change(sol, row, indices[start], row_cols,
8✔
605
                                          row_vals, len, vals[start],
8✔
606
                                          vals[start + 1], indices[start + 1]);
8✔
607
                }
608

609
                j -= 1;
8✔
610
            }
611

612
            i = j + 1;
8✔
613
            assert(i >= 0);
8✔
614
            assert(i == 0 || reductions[i - 1] != BOUND_CHANGE_NO_ROW);
8✔
615
        }
616

617
        else if (type == LHS_CHANGE)
1✔
618
        {
619
            len = starts[i + 1] - start - 2;
1✔
620
            assert(len > 1);
1✔
621
            retrieve_lhs_change(sol, indices[start], vals + start + 2,
1✔
622
                                indices + start + 2, len, vals[start],
1✔
623
                                indices[start + 1], vals[start + 1]);
1✔
624
        }
625
        else if (type == RHS_CHANGE)
×
626
        {
627
            len = starts[i + 1] - start - 2;
×
628
            assert(len > 1);
×
629
            retrieve_rhs_change(sol, indices[start], vals + start + 2,
×
630
                                indices + start + 2, len, vals[start],
×
631
                                indices[start + 1], vals[start + 1]);
×
632
        }
633
        else if (type == EQ_TO_INEQ)
×
634
        {
635
            assert(starts[i + 1] - start == 1);
×
636
            retrieve_eq_to_ineq(sol, indices[start], vals[start]);
×
637
        }
638
        else
639
        {
640
            assert(type != BOUND_CHANGE_NO_ROW);
×
641
            assert(false);
×
642
        }
643
    }
644

645
    // polish_z(sol, lbs, ubs);
646

647
#ifndef NDEBUG
648
    for (int i = 0; i < sol->dim_x; ++i)
118✔
649
    {
650
        if (sol->x[i] == COL_NOT_RETRIEVED || sol->z[i] == COL_NOT_RETRIEVED)
100✔
651
        {
652
            printf("col %d not fully retrieved \n", i);
×
653
        }
654

655
        assert(sol->x[i] != COL_NOT_RETRIEVED);
100✔
656
        assert(sol->z[i] != COL_NOT_RETRIEVED);
100✔
657
    }
658

659
    for (int i = 0; i < sol->dim_y; ++i)
89✔
660
    {
661
        assert(sol->y[i] != ROW_NOT_RETRIEVED);
71✔
662
    }
663
#endif
664
}
18✔
665

666
// here we should probably store information so zk = ck - ak^T y
667
// so must save rows and vals
668
void save_retrieval_fixed_col(PostsolveInfo *info, int col, double val, double ck,
41✔
669
                              const double *vals, const int *rows, size_t len)
670
{
671
    u16Vec_append(info->type, FIXED_COL);
41✔
672
    iVec_append(info->indices, col);
41✔
673
    iVec_append(info->indices, DUMMY_VALUE);
41✔
674
    iVec_append_array(info->indices, rows, len);
41✔
675
    dVec_append(info->vals, val);
41✔
676
    dVec_append(info->vals, ck);
41✔
677
    dVec_append_array(info->vals, vals, len);
41✔
678
    iVec_append(info->starts, (int) info->indices->len);
41✔
679
    assert(info->starts->len == info->type->len + 1);
41✔
680
    assert(info->vals->len == info->indices->len);
41✔
681
}
41✔
682

683
void save_retrieval_fixed_col_inf(PostsolveInfo *info, int col, int pos_inf,
2✔
684
                                  const Constraints *constraints, double bound)
685
{
686
    assert(pos_inf == 1 || pos_inf == -1);
2✔
687
    int i, row;
688
    const Matrix *AT = constraints->AT;
2✔
689
    const Matrix *A = constraints->A;
2✔
690
    int *row_sizes = constraints->state->row_sizes;
2✔
691
    double *lhs = constraints->lhs;
2✔
692
    double *rhs = constraints->rhs;
2✔
693
    RowTag *row_tags = constraints->row_tags;
2✔
694
    double side;
695

696
    int *rows = AT->i + AT->p[col].start;
2✔
697
    int n_rows = AT->p[col].end - constraints->AT->p[col].start;
2✔
698

699
    u16Vec_append(info->type, FIXED_COL_INF);
2✔
700
    iVec_append(info->indices, pos_inf);
2✔
701
    iVec_append(info->indices, col);
2✔
702
    dVec_append(info->vals, (double) n_rows);
2✔
703
    dVec_append(info->vals, bound);
2✔
704

705
    for (i = 0; i < n_rows; ++i)
6✔
706
    {
707
        row = rows[i];
4✔
708
        side = (HAS_TAG(row_tags[row], R_TAG_LHS_INF)) ? rhs[row] : lhs[row];
4✔
709
        dVec_append(info->vals, side);
4✔
710
        dVec_append_array(info->vals, A->x + A->p[row].start,
4✔
711
                          (size_t) row_sizes[row]);
4✔
712
        iVec_append(info->indices, row_sizes[row]);
4✔
713
        iVec_append_array(info->indices, A->i + A->p[row].start,
4✔
714
                          (size_t) row_sizes[row]);
4✔
715

716
        assert(!(HAS_TAG(row_tags[row], R_TAG_LHS_INF) &&
4✔
717
                 HAS_TAG(row_tags[row], R_TAG_RHS_INF)));
718
        assert(HAS_TAG(row_tags[row], (R_TAG_LHS_INF | R_TAG_RHS_INF)));
4✔
719
    }
720

721
    iVec_append(info->starts, (int) info->indices->len);
2✔
722
    assert(info->starts->len == info->type->len + 1);
2✔
723
    assert(info->vals->len == info->indices->len);
2✔
724
}
2✔
725

726
void save_retrieval_sub_col(PostsolveInfo *info, int col, int *cols, double *coeffs,
47✔
727
                            size_t len, double rhs, int i, double ck)
728
{
729
    u16Vec_append(info->type, SUB_COL);
47✔
730
    iVec_append(info->indices, col);
47✔
731
    iVec_append_array(info->indices, cols, len);
47✔
732
    iVec_append(info->indices, i);
47✔
733
    dVec_append(info->vals, rhs);
47✔
734
    dVec_append_array(info->vals, coeffs, len);
47✔
735
    dVec_append(info->vals, ck);
47✔
736
    iVec_append(info->starts, (int) info->indices->len);
47✔
737
    assert(info->starts->len == info->type->len + 1);
47✔
738
    assert(info->indices->len == info->vals->len);
47✔
739
}
47✔
740

741
void save_retrieval_parallel_col(PostsolveInfo *info, double ub_j, double lb_j,
18✔
742
                                 double lb_k, double ub_k, double ratio, int j,
743
                                 int k, ColTag cTag_j, ColTag cTag_k)
744
{
745
    u16Vec_append(info->type, PARALLEL_COL);
18✔
746
    iVec_append(info->indices, j);
18✔
747
    iVec_append(info->indices, k);
18✔
748
    iVec_append(info->indices, (int) (cTag_j));
18✔
749
    iVec_append(info->indices, (int) (cTag_k));
18✔
750
    iVec_append(info->indices, DUMMY_VALUE);
18✔
751
    dVec_append(info->vals, lb_j);
18✔
752
    dVec_append(info->vals, ub_j);
18✔
753
    dVec_append(info->vals, lb_k);
18✔
754
    dVec_append(info->vals, ub_k);
18✔
755
    dVec_append(info->vals, ratio);
18✔
756
    iVec_append(info->starts, (int) info->indices->len);
18✔
757
    assert(info->starts->len == info->type->len + 1);
18✔
758
    assert(info->vals->len == info->indices->len);
18✔
759
}
18✔
760

761
void save_retrieval_deleted_row(PostsolveInfo *info, int row, double val)
83✔
762
{
763
    u16Vec_append(info->type, DELETED_ROW);
83✔
764
    iVec_append(info->indices, row);
83✔
765
    dVec_append(info->vals, val);
83✔
766
    iVec_append(info->starts, (int) info->indices->len);
83✔
767
    assert(info->starts->len == info->type->len + 1);
83✔
768
    assert(info->vals->len == info->indices->len);
83✔
769
}
83✔
770

771
void save_retrieval_added_row(PostsolveInfo *info, int i, int j, double ratio)
70✔
772
{
773
    u16Vec_append(info->type, ADDED_ROW);
70✔
774
    iVec_append(info->indices, i);
70✔
775
    iVec_append(info->indices, j);
70✔
776
    dVec_append(info->vals, ratio);
70✔
777
    dVec_append(info->vals, DUMMY_VALUE);
70✔
778
    iVec_append(info->starts, (int) info->indices->len);
70✔
779
    assert(info->starts->len == info->type->len + 1);
70✔
780
    assert(info->vals->len == info->indices->len);
70✔
781
}
70✔
782

783
void save_retrieval_added_rows(PostsolveInfo *info, int i, const int *rows,
5✔
784
                               const double *vals, size_t len, double aik)
785
{
786
    u16Vec_append(info->type, ADDED_ROWS);
5✔
787
    iVec_append(info->indices, i);
5✔
788
    iVec_append_array(info->indices, rows, len);
5✔
789
    dVec_append(info->vals, aik);
5✔
790
    dVec_append_array(info->vals, vals, len);
5✔
791
    iVec_append(info->starts, (int) info->indices->len);
5✔
792
    assert(info->starts->len == info->type->len + 1);
5✔
793
    assert(info->vals->len == info->indices->len);
5✔
794
}
5✔
795

796
// original bound can be infinite
797
void save_retrieval_bound_change_no_row(PostsolveInfo *info, int j,
94✔
798
                                        double implied_bound,
799
                                        double original_other_bound,
800
                                        int is_original_other_bound_lower_bound)
801
{
802
    u16Vec_append(info->type, BOUND_CHANGE_NO_ROW);
94✔
803
    iVec_append(info->indices, j);
94✔
804
    iVec_append(info->indices, is_original_other_bound_lower_bound);
94✔
805
    dVec_append(info->vals, implied_bound);
94✔
806
    dVec_append(info->vals, original_other_bound);
94✔
807
    iVec_append(info->starts, (int) info->indices->len);
94✔
808
    assert(info->starts->len == info->type->len + 1);
94✔
809
    assert(info->vals->len == info->indices->len);
94✔
810
}
94✔
811

812
void save_retrieval_bound_change_the_row(PostsolveInfo *info, int i, const int *cols,
58✔
813
                                         const double *vals, size_t len,
814
                                         int num_of_bound_changes)
815
{
816
    u16Vec_append(info->type, BOUND_CHANGE_THE_ROW);
58✔
817
    iVec_append(info->indices, i);
58✔
818
    iVec_append_array(info->indices, cols, len);
58✔
819
    dVec_append(info->vals, (double) num_of_bound_changes);
58✔
820
    dVec_append_array(info->vals, vals, len);
58✔
821
    iVec_append(info->starts, (int) info->indices->len);
58✔
822
    assert(info->starts->len == info->type->len + 1);
58✔
823
    assert(info->vals->len == info->indices->len);
58✔
824
}
58✔
825

826
void save_retrieval_rhs_or_lhs_change(PostsolveInfo *info, int i, const double *vals,
3✔
827
                                      const int *cols, size_t len, double new_side,
828
                                      int j, double ratio, bool is_lhs_change)
829
{
830
    if (is_lhs_change)
3✔
831
    {
832
        u16Vec_append(info->type, LHS_CHANGE);
1✔
833
    }
834
    else
835
    {
836
        u16Vec_append(info->type, RHS_CHANGE);
2✔
837
    }
838

839
    iVec_append(info->indices, i);
3✔
840
    iVec_append(info->indices, j);
3✔
841
    iVec_append_array(info->indices, cols, len);
3✔
842
    dVec_append(info->vals, new_side);
3✔
843
    dVec_append(info->vals, ratio);
3✔
844
    dVec_append_array(info->vals, vals, len);
3✔
845
    iVec_append(info->starts, (int) info->indices->len);
3✔
846
    assert(info->starts->len == info->type->len + 1);
3✔
847
    assert(info->vals->len == info->indices->len);
3✔
848
}
3✔
849

850
void save_retrieval_eq_to_ineq(PostsolveInfo *info, int row, double val)
8✔
851
{
852
    u16Vec_append(info->type, EQ_TO_INEQ);
8✔
853
    iVec_append(info->indices, row);
8✔
854
    dVec_append(info->vals, val);
8✔
855
    iVec_append(info->starts, (int) info->indices->len);
8✔
856
    assert(info->starts->len == info->type->len + 1);
8✔
857
    assert(info->vals->len == info->indices->len);
8✔
858
}
8✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc