1 module scaling;
2 
3 nothrow @nogc extern(C):
4 
5 import glob_opts;
6 import types;
7 import lin_alg;
8 import constants;
9 
10 
11 version(EMBEDDED_1){}
12 else {
13 
14 // Set values lower than threshold SCALING_REG to 1
15 void limit_scaling(c_float *D, c_int n) {
16   c_int i;
17 
18   for (i = 0; i < n; i++) {
19     D[i] = D[i] < MIN_SCALING ? 1.0 : D[i];
20     D[i] = D[i] > MAX_SCALING ? MAX_SCALING : D[i];
21   }
22 }
23 
24 /**
25  * Compute infinite norm of the columns of the KKT matrix without forming it
26  *
27  * The norm is stored in the vector v = (D, E)
28  *
29  * @param P        Cost matrix
30  * @param A        Constraints matrix
31  * @param D        Norm of columns related to variables
32  * @param D_temp_A Temporary vector for norm of columns of A
33  * @param E        Norm of columns related to constraints
34  * @param n        Dimension of KKT matrix
35  */
36 void compute_inf_norm_cols_KKT(const csc *P, const csc *A,
37                                c_float *D, c_float *D_temp_A,
38                                c_float *E, c_int n) {
39   // First half
40   //  [ P ]
41   //  [ A ]
42   mat_inf_norm_cols_sym_triu(P, D);
43   mat_inf_norm_cols(A, D_temp_A);
44   vec_ew_max_vec(D, D_temp_A, D, n);
45 
46   // Second half
47   //  [ A']
48   //  [ 0 ]
49   mat_inf_norm_rows(A, E);
50 }
51 
52 c_int scale_data(OSQPWorkspace *work) {
53   // Scale KKT matrix
54   //
55   //    [ P   A']
56   //    [ A   0 ]
57   //
58   // with diagonal matrix
59   //
60   //  S = [ D    ]
61   //      [    E ]
62   //
63 
64   c_int   i;          // Iterations index
65   c_int   n, m;       // Number of constraints and variables
66   c_float c_temp;     // Cost function scaling
67   c_float inf_norm_q; // Infinity norm of q
68 
69   n = work.data.n;
70   m = work.data.m;
71 
72   // Initialize scaling to 1
73   work.scaling.c = 1.0;
74   vec_set_scalar(work.scaling.D,    1., work.data.n);
75   vec_set_scalar(work.scaling.Dinv, 1., work.data.n);
76   vec_set_scalar(work.scaling.E,    1., work.data.m);
77   vec_set_scalar(work.scaling.Einv, 1., work.data.m);
78 
79 
80   for (i = 0; i < work.settings.scaling; i++) {
81     //
82     // First Ruiz step
83     //
84 
85     // Compute norm of KKT columns
86     compute_inf_norm_cols_KKT(work.data.P, work.data.A,
87                               work.D_temp, work.D_temp_A,
88                               work.E_temp, n);
89 
90     // Set to 1 values with 0 norms (avoid crazy scaling)
91     limit_scaling(work.D_temp, n);
92     limit_scaling(work.E_temp, m);
93 
94     // Take square root of norms
95     vec_ew_sqrt(work.D_temp, n);
96     vec_ew_sqrt(work.E_temp, m);
97 
98     // Divide scalings D and E by themselves
99     vec_ew_recipr(work.D_temp, work.D_temp, n);
100     vec_ew_recipr(work.E_temp, work.E_temp, m);
101 
102     // Equilibrate matrices P and A and vector q
103     // P <- DPD
104     mat_premult_diag(work.data.P, work.D_temp);
105     mat_postmult_diag(work.data.P, work.D_temp);
106 
107     // A <- EAD
108     mat_premult_diag(work.data.A, work.E_temp);
109     mat_postmult_diag(work.data.A, work.D_temp);
110 
111     // q <- Dq
112     vec_ew_prod(work.D_temp,     work.data.q, work.data.q,    n);
113 
114     // Update equilibration matrices D and E
115     vec_ew_prod(work.scaling.D, work.D_temp,  work.scaling.D, n);
116     vec_ew_prod(work.scaling.E, work.E_temp,  work.scaling.E, m);
117 
118     //
119     // Cost normalization step
120     //
121 
122     // Compute avg norm of cols of P
123     mat_inf_norm_cols_sym_triu(work.data.P, work.D_temp);
124     c_temp = vec_mean(work.D_temp, n);
125 
126     // Compute inf norm of q
127     inf_norm_q = vec_norm_inf(work.data.q, n);
128 
129     // If norm_q == 0, set it to 1 (ignore it in the scaling)
130     // NB: Using the same function as with vectors here
131     limit_scaling(&inf_norm_q, 1);
132 
133     // Compute max between avg norm of cols of P and inf norm of q
134     c_temp = c_max(c_temp, inf_norm_q);
135 
136     // Limit scaling (use same function as with vectors)
137     limit_scaling(&c_temp, 1);
138 
139     // Invert scaling c = 1 / cost_measure
140     c_temp = 1. / c_temp;
141 
142     // Scale P
143     mat_mult_scalar(work.data.P, c_temp);
144 
145     // Scale q
146     vec_mult_scalar(work.data.q, c_temp, n);
147 
148     // Update cost scaling
149     work.scaling.c *= c_temp;
150   }
151 
152 
153   // Store cinv, Dinv, Einv
154   work.scaling.cinv = 1. / work.scaling.c;
155   vec_ew_recipr(work.scaling.D, work.scaling.Dinv, work.data.n);
156   vec_ew_recipr(work.scaling.E, work.scaling.Einv, work.data.m);
157 
158 
159   // Scale problem vectors l, u
160   vec_ew_prod(work.scaling.E, work.data.l, work.data.l, work.data.m);
161   vec_ew_prod(work.scaling.E, work.data.u, work.data.u, work.data.m);
162 
163   return 0;
164 }
165 
166 } // EMBEDDED
167 
168 c_int unscale_data(OSQPWorkspace *work) {
169   // Unscale cost
170   mat_mult_scalar(work.data.P, work.scaling.cinv);
171   mat_premult_diag(work.data.P, work.scaling.Dinv);
172   mat_postmult_diag(work.data.P, work.scaling.Dinv);
173   vec_mult_scalar(work.data.q, work.scaling.cinv, work.data.n);
174   vec_ew_prod(work.scaling.Dinv, work.data.q, work.data.q, work.data.n);
175 
176   // Unscale constraints
177   mat_premult_diag(work.data.A, work.scaling.Einv);
178   mat_postmult_diag(work.data.A, work.scaling.Dinv);
179   vec_ew_prod(work.scaling.Einv, work.data.l, work.data.l, work.data.m);
180   vec_ew_prod(work.scaling.Einv, work.data.u, work.data.u, work.data.m);
181 
182   return 0;
183 }
184 
185 c_int unscale_solution(OSQPWorkspace *work) {
186   // primal
187   vec_ew_prod(work.scaling.D,
188               work.solution.x,
189               work.solution.x,
190               work.data.n);
191 
192   // dual
193   vec_ew_prod(work.scaling.E,
194               work.solution.y,
195               work.solution.y,
196               work.data.m);
197   vec_mult_scalar(work.solution.y, work.scaling.cinv, work.data.m);
198 
199   return 0;
200 }