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

dance858 / PSLP / 22279069886

22 Feb 2026 02:32PM UTC coverage: 89.311% (+0.3%) from 89.013%
22279069886

Pull #38

github

web-flow
Merge bae50547e into e13878911
Pull Request #38: [WIP] Speedup

1281 of 1432 branches covered (89.46%)

Branch coverage included in aggregate %.

181 of 193 new or added lines in 10 files covered. (93.78%)

67 existing lines in 7 files now uncovered.

3983 of 4462 relevant lines covered (89.26%)

6008.65 hits per line

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

80.62
/src/explorers/StonCols.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 "StonCols.h"
20
#include "Activity.h"
21
#include "Bounds.h"
22
#include "Constraints.h"
23
#include "CoreTransformations.h"
24
#include "Debugger.h"
25
#include "Locks.h"
26
#include "Matrix.h"
27
#include "Numerics.h"
28
#include "Postsolver.h"
29
#include "Problem.h"
30
#include "RowColViews.h"
31
#include "State.h"
32

33
/* Helper for handling the case where a column-singleton variable in an
34
   equality row is only implied free from above. */
35
static void handle_impl_free_from_above_eq(RowView *row, int k, double Aik,
6✔
36
                                           double lb, double ub, ColTag *col_tag,
37
                                           Activity *act, Lock *locks, size_t *A_nnz,
38
                                           const Bound *bounds,
39
                                           PostsolveInfo *postsolve_info, double ck)
40
{
41
    /* dual postsolve */
42
    save_retrieval_eq_to_ineq(postsolve_info, row->i, ck / Aik);
6✔
43
    save_retrieval_sub_col(postsolve_info, k, row->cols, row->vals,
6✔
44
                           (size_t) *row->len, *row->rhs, row->i, 0.0);
6✔
45

46
    assert(!IS_ABS_INF(lb) && !HAS_TAG(*col_tag, C_TAG_LB_INF));
6✔
47

48
    /* remove coefficient from A */
49
    remove_coeff(row, k);
6✔
50
    (*A_nnz)--;
6✔
51

52
    if (Aik > 0)
6✔
53
    {
54
        /* modify constraint */
55
        *row->rhs -= lb * Aik;
4✔
56
        *row->lhs = -INF;
4✔
57
        RESET_TAG(*row->tag, R_TAG_LHS_INF);
4✔
58

59
        /* update locks */
60
        update_locks_eq_to_ineq(locks, row->vals, row->cols, *row->len, true);
4✔
61

62
        /* update activity */
63
        if (act->n_inf_min == 0)
4✔
64
        {
65
            act->min -= lb * Aik;
2✔
66
        }
67

68
        if (HAS_TAG(*col_tag, C_TAG_UB_INF))
4✔
69
        {
70
            act->n_inf_max--;
4✔
71

72
            if (act->n_inf_max == 0)
4✔
73
            {
74
                act->max =
×
75
                    compute_max_act_no_tags(row->vals, row->cols, *row->len, bounds);
×
76
                assert(!IS_ABS_INF(act->max));
×
77
            }
78
        }
79
        else
80
        {
81
            assert(!IS_ABS_INF(ub));
×
82
            if (act->n_inf_max == 0)
×
83
            {
84
                act->max -= ub * Aik;
×
85
            }
86
        }
87
    }
88
    else
89
    {
90
        /* modify constraint and update locks */
91
        *row->lhs -= lb * Aik;
2✔
92
        *row->rhs = INF;
2✔
93
        RESET_TAG(*row->tag, R_TAG_RHS_INF);
2✔
94
        update_locks_eq_to_ineq(locks, row->vals, row->cols, *row->len, false);
2✔
95

96
        /* update activity */
97
        if (act->n_inf_max == 0)
2✔
98
        {
99
            act->max -= lb * Aik;
×
100
        }
101

102
        if (HAS_TAG(*col_tag, C_TAG_UB_INF))
2✔
103
        {
104
            act->n_inf_min--;
2✔
105

106
            if (act->n_inf_min == 0)
2✔
107
            {
108
                act->min =
2✔
109
                    compute_min_act_no_tags(row->vals, row->cols, *row->len, bounds);
2✔
110
                assert(!IS_ABS_INF(act->min));
2✔
111
            }
112
        }
113
        else
114
        {
115
            assert(!IS_ABS_INF(ub));
×
116
            if (act->n_inf_min == 0)
×
117
            {
118
                act->min -= ub * Aik;
×
119
            }
120
        }
121
    }
122
}
6✔
123

124
/* Helper for handling the case where a column-singleton variable in an
125
   equality row is only implied free from below. */
126
static void handle_impl_free_from_below_eq(RowView *row, int k, double Aik,
2✔
127
                                           double lb, double ub, ColTag *col_tag,
128
                                           Activity *act, Lock *locks, size_t *A_nnz,
129
                                           const Bound *bounds,
130
                                           PostsolveInfo *postsolve_info, double ck)
131
{
132
    save_retrieval_eq_to_ineq(postsolve_info, row->i, ck / Aik);
2✔
133
    save_retrieval_sub_col(postsolve_info, k, row->cols, row->vals,
2✔
134
                           (size_t) *row->len, *row->rhs, row->i, 0.0);
2✔
135
    assert(!IS_ABS_INF(ub) && !HAS_TAG(*col_tag, C_TAG_UB_INF));
2✔
136

137
    /* remove coefficient from A */
138
    remove_coeff(row, k);
2✔
139
    (*A_nnz)--;
2✔
140

141
    if (Aik > 0)
2✔
142
    {
143
        /* modify constraint */
144
        *row->lhs -= ub * Aik;
1✔
145
        *row->rhs = INF;
1✔
146
        RESET_TAG(*row->tag, R_TAG_RHS_INF);
1✔
147

148
        /* update locks */
149
        update_locks_eq_to_ineq(locks, row->vals, row->cols, *row->len, false);
1✔
150

151
        /* update activity */
152
        if (act->n_inf_max == 0)
1✔
153
        {
154
            act->max -= ub * Aik;
×
155
        }
156

157
        if (HAS_TAG(*col_tag, C_TAG_LB_INF))
1✔
158
        {
159
            act->n_inf_min--;
1✔
160

161
            if (act->n_inf_min == 0)
1✔
162
            {
163
                act->min =
1✔
164
                    compute_min_act_no_tags(row->vals, row->cols, *row->len, bounds);
1✔
165
                assert(!IS_ABS_INF(act->min));
1✔
166
            }
167
        }
168
        else
169
        {
170
            assert(!IS_ABS_INF(lb));
×
171
            if (act->n_inf_min == 0)
×
172
            {
173
                act->min -= lb * Aik;
×
174
            }
175
        }
176
    }
177
    else
178
    {
179
        /* modify constraint and update locks*/
180
        *row->rhs -= ub * Aik;
1✔
181
        *row->lhs = -INF;
1✔
182
        RESET_TAG(*row->tag, R_TAG_LHS_INF);
1✔
183
        update_locks_eq_to_ineq(locks, row->vals, row->cols, *row->len, true);
1✔
184

185
        /* update activity */
186
        if (act->n_inf_min == 0)
1✔
187
        {
188
            act->min -= ub * Aik;
1✔
189
        }
190
        if (HAS_TAG(*col_tag, C_TAG_LB_INF))
1✔
191
        {
192
            act->n_inf_max--;
1✔
193

194
            if (act->n_inf_max == 0)
1✔
195
            {
196
                act->max =
×
197
                    compute_max_act_no_tags(row->vals, row->cols, *row->len, bounds);
×
198
                assert(!IS_ABS_INF(act->max));
×
199
            }
200
        }
201
        else
202
        {
203
            assert(!IS_ABS_INF(lb));
×
204
            if (act->n_inf_max == 0)
×
205
            {
206
                act->max -= lb * Aik;
×
207
            }
208
        }
209
    }
210
}
2✔
211

212
static PresolveStatus process_colston_eq(RowView *row, ColView *col, Objective *obj,
29✔
213
                                         double Aik, bool impl_free_from_above,
214
                                         bool impl_free_from_below, Activity *act,
215
                                         Lock *locks, iVec *rows_to_delete,
216
                                         size_t *A_nnz, const Bound *bounds,
217
                                         iVec *ston_rows, iVec *dton_rows,
218
                                         PostsolveInfo *postsolve_info)
219
{
220

221
    assert(*row->lhs == *row->rhs);
29✔
222
    assert(!HAS_TAG(*row->tag, (R_TAG_INACTIVE | R_TAG_LHS_INF | R_TAG_RHS_INF)));
29✔
223

224
    // No reduction is possible for a column singleton that is neither
225
    // implied free from below nor above, so we skip these.
226
    if (!impl_free_from_above && !impl_free_from_below)
29✔
227
    {
228
        return UNCHANGED;
8✔
229
    }
230

231
    // must store for dual postsolve
232
    double ck = obj->c[col->k];
21✔
233

234
    // substitute variable in objective and mark it as substituted
235
    // (note that we don't push it to list of substituted cols)
236
    sub_var_in_obj(obj, row->vals, row->cols, *row->len, col->k, Aik, *row->rhs);
21✔
237
    UPDATE_TAG(*col->tag, C_TAG_SUBSTITUTED);
21✔
238
    *col->len = SIZE_INACTIVE_COL;
21✔
239
    col->range->end = col->range->start;
21✔
240

241
    // -------------------------------------------------------------------------
242
    // if var is implied free, the constraint is redundant and we remove it.
243
    // -------------------------------------------------------------------------
244
    if (impl_free_from_above && impl_free_from_below)
21✔
245
    {
246
        save_retrieval_sub_col(postsolve_info, col->k, row->cols, row->vals,
13✔
247
                               (size_t) *row->len, *row->rhs, row->i, ck);
13✔
248
        set_row_to_inactive(row->i, row->tag, rows_to_delete, postsolve_info,
13✔
249
                            ck / Aik);
250
        return REDUCED;
13✔
251
    }
252
    // -------------------------------------------------------------------------
253
    // If the variable is only implied free from above we must modify the
254
    // constraint when removing it.
255
    // -------------------------------------------------------------------------
256
    else if (impl_free_from_above)
8✔
257
    {
258
        handle_impl_free_from_above_eq(row, col->k, Aik, *col->lb, *col->ub,
6✔
259
                                       col->tag, act, locks, A_nnz, bounds,
260
                                       postsolve_info, ck);
261
    }
262
    // --------------------------------------------------------------------------
263
    // If the variable is only implied free from below we must modify the
264
    // constraint when removing it.
265
    // --------------------------------------------------------------------------
266
    else
267
    {
268
        assert(impl_free_from_below);
2✔
269
        handle_impl_free_from_below_eq(row, col->k, Aik, *col->lb, *col->ub,
2✔
270
                                       col->tag, act, locks, A_nnz, bounds,
271
                                       postsolve_info, ck);
272
    }
273

274
    if (*row->len == 1)
8✔
275
    {
276
        assert(!iVec_contains(ston_rows, row->i));
×
277
        iVec_append(ston_rows, row->i);
×
278
    }
279
    else if (*row->len == 2 && HAS_TAG(*row->tag, R_TAG_EQ))
8✔
280
    {
281
        assert(!iVec_contains(dton_rows, row->i));
×
282
        iVec_append(dton_rows, row->i);
×
283
    }
284

285
    assert(*row->len != 0);
8✔
286

287
    return REDUCED;
8✔
288
}
289

290
/* Fixes a variable that is a column singleton.
291
    Parameters:
292
    * val: value that variable should be fixed to
293
    * Aik: coefficient in the matrix A of the fixed variable
294
    * (lhs, rhs) belong to the row that the fixed variable appears in
295
    * (vals, cols, len, row_range) correspond to the row of the col_ston
296
    * (col_range, col_tag, col_size) are attributes of the variable that
297
                                      is fixed
298
    * obj: objective function
299
    * k: index of the fixed variable
300
    * A_nnz: number of nonzeros in the matrix A (OBS: this is modified)
301
    * row_tag: tag of the row that the fixed variable appears in
302
    * act: activity of the row that the fixed variable appears in
303
    * bounds: variable bounds
304

305
    @Note: we don't have to check for infeasibility here, because
306
            we only call this function with val equal to one of the
307
            bounds.
308
 */
309
static void fix_col_ston(double val, double Aik, RowView *row, ColView *col,
3✔
310
                         Activity *act, Objective *obj, size_t *A_nnz,
311
                         const Bound *bounds, PostsolveInfo *postsolve_info)
312
{
313
    // remove coefficient from A (which removes the variable from A)
314
    remove_coeff(row, col->k);
3✔
315
    (*A_nnz)--;
3✔
316

317
    // set row k from AT to inactive
318
    col->range->end = col->range->start;
3✔
319
    *col->len = SIZE_INACTIVE_COL;
3✔
320
    UPDATE_TAG(*col->tag, C_TAG_FIXED);
3✔
321

322
    // update lhs
323
    if (!HAS_TAG(*row->tag, R_TAG_LHS_INF))
3✔
324
    {
325
        *row->lhs -= Aik * val;
2✔
326
    }
327

328
    // update rhs
329
    if (!HAS_TAG(*row->tag, R_TAG_RHS_INF))
3✔
330
    {
331
        *row->rhs -= Aik * val;
1✔
332
    }
333

334
    // update min activity
335
    if (act->n_inf_min == 0)
3✔
336
    {
UNCOV
337
        act->min -= Aik * val;
×
338
    }
339
    else if ((Aik > 0 && HAS_TAG(*col->tag, C_TAG_LB_INF)) ||
3✔
340
             (Aik < 0 && HAS_TAG(*col->tag, C_TAG_UB_INF)))
3✔
341
    {
342
        act->n_inf_min--;
2✔
343

344
        if (act->n_inf_min == 0)
2✔
345
        {
UNCOV
346
            act->min =
×
347
                compute_min_act_no_tags(row->vals, row->cols, *row->len, bounds);
×
348
            assert(!IS_ABS_INF(act->min));
×
349
        }
350
    }
351

352
    // update max activity
353
    if (act->n_inf_max == 0)
3✔
354
    {
UNCOV
355
        act->max -= Aik * val;
×
356
    }
357
    else if ((Aik > 0 && HAS_TAG(*col->tag, C_TAG_UB_INF)) ||
3✔
358
             (Aik < 0 && HAS_TAG(*col->tag, C_TAG_LB_INF)))
3✔
359
    {
UNCOV
360
        act->n_inf_max--;
×
361

UNCOV
362
        if (act->n_inf_max == 0)
×
363
        {
UNCOV
364
            act->max =
×
365
                compute_max_act_no_tags(row->vals, row->cols, *row->len, bounds);
×
366
            assert(!IS_ABS_INF(act->max));
×
367
        }
368
    }
369

370
    // fix variable in objective
371
    fix_var_in_obj(obj, col->k, val);
3✔
372
    save_retrieval_fixed_col(postsolve_info, col->k, val, obj->c[col->k], &Aik,
3✔
373
                             &row->i, 1);
3✔
374
}
3✔
375

376
static inline PresolveStatus
377
process_colston_ineq(RowView *row, ColView *col, Objective *obj, double Aik,
18✔
378
                     bool impl_free_from_above, bool impl_free_from_below,
379
                     Activity *act, Lock *locks, iVec *rows_to_delete, size_t *A_nnz,
380
                     const Bound *bounds, iVec *ston_rows, iVec *dton_rows,
381
                     PostsolveInfo *postsolve_info)
382
{
383
    bool is_lhs_inf = HAS_TAG(*row->tag, R_TAG_LHS_INF);
18✔
384
    bool is_rhs_inf = HAS_TAG(*row->tag, R_TAG_RHS_INF);
18✔
385
    bool is_ub_inf = HAS_TAG(*col->tag, C_TAG_UB_INF);
18✔
386
    bool is_lb_inf = HAS_TAG(*col->tag, C_TAG_LB_INF);
18✔
387
    double ck = obj->c[col->k];
18✔
388

389
    // ------------------------------------------------------------------------
390
    //                    dual fix to lower bound
391
    // ------------------------------------------------------------------------
392
    if ((ck > 0 && Aik > 0 && is_lhs_inf) || (ck > 0 && Aik < 0 && is_rhs_inf))
18✔
393
    {
394
        if (is_lb_inf)
2✔
395
        {
396
            return UNBNDORINFEAS;
×
397
        }
398
        assert(!IS_ABS_INF(*col->lb));
2✔
399

400
        fix_col_ston(*col->lb, Aik, row, col, act, obj, A_nnz, bounds,
2✔
401
                     postsolve_info);
402

403
        // should not check for dton row here since the constraint is an ineq
404
        if (*row->len == 1)
2✔
405
        {
UNCOV
406
            assert(!iVec_contains(ston_rows, row->i));
×
UNCOV
407
            iVec_append(ston_rows, row->i);
×
408
        }
409

410
        assert(*row->len != 0);
2✔
411
        return REDUCED;
2✔
412
    }
413

414
    // ------------------------------------------------------------------------
415
    //                    dual fix to upper bound
416
    // ------------------------------------------------------------------------
417
    if ((ck < 0 && Aik < 0 && is_lhs_inf) || (ck < 0 && Aik > 0 && is_rhs_inf))
16✔
418
    {
419
        if (is_ub_inf)
2✔
420
        {
421
            return UNBNDORINFEAS;
1✔
422
        }
423
        assert(!IS_ABS_INF(*col->ub));
1✔
424

425
        fix_col_ston(*col->ub, Aik, row, col, act, obj, A_nnz, bounds,
1✔
426
                     postsolve_info);
427

428
        // should not check for dton row here since the constraint is an ineq
429
        if (*row->len == 1)
1✔
430
        {
UNCOV
431
            assert(!iVec_contains(ston_rows, row->i));
×
UNCOV
432
            iVec_append(ston_rows, row->i);
×
433
        }
434

435
        assert(*row->len != 0);
1✔
436
        return REDUCED;
1✔
437
    }
438

439
    // ------------------------------------------------------------------------
440
    //        Reduce column singletons that are (implied) free
441
    // ------------------------------------------------------------------------
442
    if (impl_free_from_above && impl_free_from_below)
14✔
443
    {
444
        double new_side;
445

446
        // lhs active at an optimal solution
447
        if ((ck > 0 && Aik > 0) || (ck < 0 && Aik < 0))
2✔
448
        {
449
            if (is_lhs_inf)
2✔
450
            {
UNCOV
451
                return UNBNDORINFEAS;
×
452
            }
453

454
            new_side = *row->lhs;
2✔
455
        }
456
        // rhs active at an optimal solution
UNCOV
457
        else if ((ck > 0 && Aik < 0) || (ck < 0 && Aik > 0))
×
458
        {
UNCOV
459
            if (is_rhs_inf)
×
460
            {
UNCOV
461
                return UNBNDORINFEAS;
×
462
            }
463

UNCOV
464
            new_side = *row->rhs;
×
465
        }
466
        else
467
        {
468
            assert(ck == 0);
×
UNCOV
469
            new_side = (is_lhs_inf) ? *row->rhs : *row->lhs;
×
470
        }
471

472
        // substitute variable in objective and mark it as substituted
473
        // (note that we don't push it to list of substituted cols)
474
        sub_var_in_obj(obj, row->vals, row->cols, *row->len, col->k, Aik, new_side);
2✔
475
        col->range->end = col->range->start;
2✔
476
        UPDATE_TAG(*col->tag, C_TAG_SUBSTITUTED);
2✔
477
        *col->len = SIZE_INACTIVE_COL;
2✔
478

479
        save_retrieval_sub_col(postsolve_info, col->k, row->cols, row->vals,
2✔
480
                               (size_t) *row->len, new_side, row->i, ck);
2✔
481
        set_row_to_inactive(row->i, row->tag, rows_to_delete, postsolve_info,
2✔
482
                            ck / Aik);
483

484
        return REDUCED;
2✔
485
    }
486

487
    // ----------------------------------------------------------------
488
    // try to conclude that the upper part of the constraint is active
489
    // at an optimal solution
490
    // ----------------------------------------------------------------
491
    if ((ck < 0 && Aik > 0 && impl_free_from_above) ||
12✔
492
        (ck > 0 && Aik < 0 && impl_free_from_below))
9✔
493
    {
494
        // update locks in case the old lhs was infinite
495
        if (is_lhs_inf)
2✔
496
        {
497
            update_locks_ineq_to_eq(locks, row->vals, row->cols, *row->len, true);
1✔
498
        }
499

500
        assert(!is_rhs_inf);
2✔
501
        // update lhs = rhs
502
        *row->lhs = *row->rhs;
2✔
503
        RESET_TAG(*row->tag, R_TAG_EQ);
2✔
504

505
        if (*row->len == 2)
2✔
506
        {
UNCOV
507
            assert(!iVec_contains(dton_rows, row->i));
×
UNCOV
508
            iVec_append(dton_rows, row->i);
×
509
        }
510
        return REDUCED;
2✔
511
    }
512

513
    // ----------------------------------------------------------------
514
    // try to conclude that the lower part of the constraint is active
515
    // at an optimal solution
516
    // ----------------------------------------------------------------
517
    if ((ck > 0 && Aik > 0 && impl_free_from_below) ||
10✔
518
        (ck < 0 && Aik < 0 && impl_free_from_above))
1✔
519
    {
520
        // update locks in case the old rhs was infinite
521
        if (is_rhs_inf)
1✔
522
        {
UNCOV
523
            update_locks_ineq_to_eq(locks, row->vals, row->cols, *row->len, false);
×
524
        }
525

526
        assert(!is_lhs_inf);
1✔
527
        // update rhs = lhs
528
        *row->rhs = *row->lhs;
1✔
529
        RESET_TAG(*row->tag, R_TAG_EQ);
1✔
530

531
        if (*row->len == 2)
1✔
532
        {
UNCOV
533
            assert(!iVec_contains(dton_rows, row->i));
×
UNCOV
534
            iVec_append(dton_rows, row->i);
×
535
        }
536

537
        return REDUCED;
1✔
538
    }
539

540
    return UNCHANGED;
9✔
541
}
542

543
/* Refresh the list of column singletons. A variable that used to be a
544
   column singleton may no longer be a column singleton for one of the
545
   following reasons:
546
  1. It has been processed as a column singleton and is therefore inactive.
547
  2. It has been fixed by some other reduction.
548
*/
549
static inline void refresh_ston_cols(const int *col_sizes, iVec *ston_cols)
87✔
550
{
551
    size_t n_ston_cols = 0;
87✔
552
    int shift = 0;
87✔
553
    int len = (int) ston_cols->len;
87✔
554

555
    for (int i = 0; i < len; i++)
164✔
556
    {
557
        if (col_sizes[ston_cols->data[i]] != 1)
77✔
558
        {
559
            shift++;
29✔
560
        }
561
        else
562
        {
563
            ston_cols->data[i - shift] = ston_cols->data[i];
48✔
564
            n_ston_cols++;
48✔
565
        }
566
    }
567

568
    ston_cols->len = n_ston_cols;
87✔
569
}
87✔
570

571
PresolveStatus remove_ston_cols__(Problem *prob)
87✔
572
{
573
    Constraints *constraints = prob->constraints;
87✔
574
    PostsolveInfo *postsolve_info = constraints->state->postsolve_info;
87✔
575
    const Matrix *A = constraints->A;
87✔
576
    size_t *A_nnz = &(constraints->A->nnz);
87✔
577
    const Matrix *AT = constraints->AT;
87✔
578
    RowTag *row_tags = constraints->row_tags;
87✔
579
    ColTag *col_tags = constraints->col_tags;
87✔
580
    double *lhs = constraints->lhs;
87✔
581
    double *rhs = constraints->rhs;
87✔
582
    Bound *bounds = constraints->bounds;
87✔
583
    int *row_sizes = constraints->state->row_sizes;
87✔
584
    int *col_sizes = constraints->state->col_sizes;
87✔
585
    iVec *ston_cols = constraints->state->ston_cols;
87✔
586
    iVec *ston_rows = constraints->state->ston_rows;
87✔
587
    iVec *dton_rows = constraints->state->dton_rows;
87✔
588
    Activity *acts = constraints->state->activities;
87✔
589
    Lock *locks = constraints->state->col_locks;
87✔
590
    iVec *rows_to_delete = constraints->state->rows_to_delete;
87✔
591
    double Aik;
592
    int i, k, kk;
593
    int *row_size;
594
    int *cols;
595
    double *vals;
596
    bool impl_free_from_above, impl_free_from_below;
597

598
    PresolveStatus status = UNCHANGED;
87✔
599

600
    // get up to date information about which columns are singletons
601
    refresh_ston_cols(col_sizes, ston_cols);
87✔
602

603
    // ------------------------------------------------------------------------
604
    // Loop over col_stons. We index the column with k and the row with i.
605
    // ------------------------------------------------------------------------
606
    for (kk = 0; kk < ston_cols->len; ++kk)
135✔
607
    {
608
        k = ston_cols->data[kk];
48✔
609
        i = AT->i[AT->p[k].start];
48✔
610
        Aik = AT->x[AT->p[k].start];
48✔
611

612
        assert(AT->p[k].end - AT->p[k].start == 1);
48✔
613
        assert(!HAS_TAG(col_tags[k], C_TAG_INACTIVE));
48✔
614

615
#ifndef NDEBUG
616
        if (IS_HUGE(Aik) || IS_HUGE(1 / Aik))
48✔
617
        {
UNCOV
618
            printf("debug warning: large coefficient when eliminating col ston\n");
×
619
        }
620
#endif
621

622
        // If two column singletons appear in the same constraint, the
623
        // constraint will be inactive when the second column singleton is
624
        // processed. Also skip column singletons appearing in singleton rows.
625
        // It is necessary to both check for inactivity and the row size here.
626
        if (HAS_TAG(row_tags[i], R_TAG_INACTIVE) || row_sizes[i] <= 1)
48✔
627
        {
628
            continue;
1✔
629
        }
630

631
        vals = A->x + A->p[i].start;
47✔
632
        cols = A->i + A->p[i].start;
47✔
633
        row_size = row_sizes + i;
47✔
634

635
        ConstRowView const_row_view = new_const_rowview(
47✔
636
            vals, cols, row_size, A->p + i, lhs + i, rhs + i, row_tags + i, i);
47✔
637

638
        impl_free_from_above = implied_free_from_above_by_row(
47✔
639
            Aik, &const_row_view, bounds[k].lb, bounds[k].ub, col_tags[k], acts + i,
47✔
640
            col_tags, bounds);
641

642
        impl_free_from_below = implied_free_from_below_by_row(
47✔
643
            Aik, &const_row_view, bounds[k].lb, bounds[k].ub, col_tags[k], acts + i,
47✔
644
            col_tags, bounds);
645

646
#ifndef NDEBUG
647
        acts[i].min = (acts[i].n_inf_min != 0) ? INVALID_ACT_DEBUG : acts[i].min;
47✔
648
        acts[i].max = (acts[i].n_inf_max != 0) ? INVALID_ACT_DEBUG : acts[i].max;
47✔
649
#endif
650

651
        RowView row_view = new_rowview(vals, cols, row_size, A->p + i, lhs + i,
47✔
652
                                       rhs + i, row_tags + i, i);
47✔
653

654
        ColView col_view =
655
            new_colview(NULL, NULL, col_sizes + k, AT->p + k, &bounds[k].lb,
47✔
656
                        &bounds[k].ub, col_tags + k, k);
47✔
657

658
        if (HAS_TAG(row_tags[i], R_TAG_EQ))
47✔
659
        {
660

661
            status |= process_colston_eq(
29✔
662
                &row_view, &col_view, prob->obj, Aik, impl_free_from_above,
663
                impl_free_from_below, acts + i, locks, rows_to_delete, A_nnz, bounds,
29✔
664
                ston_rows, dton_rows, postsolve_info);
665
        }
666
        else
667
        {
668
            status |= process_colston_ineq(
18✔
669
                &row_view, &col_view, prob->obj, Aik, impl_free_from_above,
670
                impl_free_from_below, acts + i, locks, rows_to_delete, A_nnz, bounds,
18✔
671
                ston_rows, dton_rows, postsolve_info);
672
        }
673
    }
674

675
    // process the rows that should be deleted
676
    constraints->AT->nnz = A->nnz;
87✔
677
    delete_inactive_rows(constraints);
87✔
678

679
    if (HAS_STATUS(status, UNBNDORINFEAS))
87✔
680
    {
681
        return UNBNDORINFEAS;
1✔
682
    }
683
    else if (HAS_STATUS(status, REDUCED))
86✔
684
    {
685
        return REDUCED;
27✔
686
    }
687
    else
688
    {
689
        assert(status == UNCHANGED);
59✔
690
        return UNCHANGED;
59✔
691
    }
692
}
693

694
PresolveStatus remove_ston_cols(Problem *prob)
60✔
695
{
696
    assert(prob->constraints->state->ston_rows->len == 0);
60✔
697
    assert(prob->constraints->state->empty_rows->len == 0);
60✔
698
    assert(prob->constraints->state->empty_cols->len == 0);
60✔
699
    DEBUG(verify_problem_up_to_date(prob->constraints));
60✔
700
    DEBUG(verify_no_duplicates_sort(prob->constraints->state->ston_cols));
60✔
701

702
    PresolveStatus status = UNCHANGED;
60✔
703

704
    do
705
    {
706
        status = remove_ston_cols__(prob);
87✔
707
    } while (status == REDUCED);
87✔
708

709
    DEBUG(verify_problem_up_to_date(prob->constraints));
60✔
710

711
    // status will always be UNBNDORINFEAS or UNCHANGED (never REDUCED).
712
    assert(status != REDUCED);
60✔
713
    return status;
60✔
714
}
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