1 module kkt;
2 
3 nothrow @nogc extern(C):
4 
5 import glob_opts;
6 import cs;
7 import types; // CSC matrix type
8 import constants; // for OSQP_NULL
9 
10 version(EMBEDDED){}
11 else {
12 csc* form_KKT(const csc  *P,
13               const  csc *A,
14               c_int       format,
15               c_float     param1,
16               c_float    *param2,
17               c_int      *PtoKKT,
18               c_int      *AtoKKT,
19               c_int     **Pdiag_idx,
20               c_int      *Pdiag_n,
21               c_int      *param2toKKT) {
22   c_int  nKKT;            // Size, number of nonzeros and max number of nonzeros in KKT matrix
23   c_int  nnzKKTmax;       // Size, number of nonzeros and max number of nonzeros in KKT matrix
24   csc   *KKT_trip;        // KKT matrix in triplet format and CSC format
25   csc   *KKT;             // KKT matrix in triplet format and CSC format
26   c_int  ptr, i, j;       // Counters for elements (i,j) and index pointer
27   c_int  zKKT = 0;        // Counter for total number of elements in P and in
28                           // KKT
29   c_int *KKT_TtoC;        // Pointer to vector mapping from KKT in triplet form
30                           // to CSC
31 
32   // Get matrix dimensions
33   nKKT = P.m + A.m;
34 
35   // Get maximum number of nonzero elements (only upper triangular part)
36   nnzKKTmax = P.p[P.n] + // Number of elements in P
37               P.m +       // Number of elements in param1 * I
38               A.p[A.n] + // Number of nonzeros in A
39               A.m;        // Number of elements in - diag(param2)
40 
41   // Preallocate KKT matrix in triplet format
42   KKT_trip = csc_spalloc(nKKT, nKKT, nnzKKTmax, 1, 1);
43 
44   if (!KKT_trip) return OSQP_NULL;  // Failed to preallocate matrix
45 
46   // Allocate vector of indices on the diagonal. Worst case it has m elements
47   if (Pdiag_idx != OSQP_NULL) {
48     (*Pdiag_idx) = cast(c_int*)c_malloc(P.m * c_int.sizeof);
49     *Pdiag_n     = 0; // Set 0 diagonal elements to start
50   }
51 
52   // Allocate Triplet matrices
53   // P + param1 I
54   for (j = 0; j < P.n; j++) { // cycle over columns
55     // No elements in column j => add diagonal element param1
56     if (P.p[j] == P.p[j + 1]) {
57       KKT_trip.i[zKKT] = j;
58       KKT_trip.p[zKKT] = j;
59       KKT_trip.x[zKKT] = param1;
60       zKKT++;
61     }
62 
63     for (ptr = P.p[j]; ptr < P.p[j + 1]; ptr++) { // cycle over rows
64       // Get current row
65       i = P.i[ptr];
66 
67       // Add element of P
68       KKT_trip.i[zKKT] = i;
69       KKT_trip.p[zKKT] = j;
70       KKT_trip.x[zKKT] = P.x[ptr];
71 
72       if (PtoKKT != OSQP_NULL) PtoKKT[ptr] = zKKT;  // Update index from P to
73                                                     // KKTtrip
74 
75       if (i == j) {                                 // P has a diagonal element,
76                                                     // add param1
77         KKT_trip.x[zKKT] += param1;
78 
79         // If index vector pointer supplied . Store the index
80         if (Pdiag_idx != OSQP_NULL) {
81           (*Pdiag_idx)[*Pdiag_n] = ptr;
82           (*Pdiag_n)++;
83         }
84       }
85       zKKT++;
86 
87       // Add diagonal param1 in case
88       if ((i < j) &&                  // Diagonal element not reached
89           (ptr + 1 == P.p[j + 1])) { // last element of column j
90         // Add diagonal element param1
91         KKT_trip.i[zKKT] = j;
92         KKT_trip.p[zKKT] = j;
93         KKT_trip.x[zKKT] = param1;
94         zKKT++;
95       }
96     }
97   }
98 
99   if (Pdiag_idx != OSQP_NULL) {
100     // Realloc Pdiag_idx so that it contains exactly *Pdiag_n diagonal elements
101     (*Pdiag_idx) = cast(c_int*)c_realloc((*Pdiag_idx), (*Pdiag_n) * c_int.sizeof);
102   }
103 
104 
105   // A' at top right
106   for (j = 0; j < A.n; j++) {                      // Cycle over columns of A
107     for (ptr = A.p[j]; ptr < A.p[j + 1]; ptr++) {
108       KKT_trip.p[zKKT] = P.m + A.i[ptr];         // Assign column index from
109                                                     // row index of A
110       KKT_trip.i[zKKT] = j;                        // Assign row index from
111                                                     // column index of A
112       KKT_trip.x[zKKT] = A.x[ptr];                // Assign A value element
113 
114       if (AtoKKT != OSQP_NULL) AtoKKT[ptr] = zKKT;  // Update index from A to
115                                                     // KKTtrip
116       zKKT++;
117     }
118   }
119 
120   // - diag(param2) at bottom right
121   for (j = 0; j < A.m; j++) {
122     KKT_trip.i[zKKT] = j + P.n;
123     KKT_trip.p[zKKT] = j + P.n;
124     KKT_trip.x[zKKT] = -param2[j];
125 
126     if (param2toKKT != OSQP_NULL) param2toKKT[j] = zKKT;  // Update index from
127                                                           // param2 to KKTtrip
128     zKKT++;
129   }
130 
131   // Allocate number of nonzeros
132   KKT_trip.nz = zKKT;
133 
134   // Convert triplet matrix to csc format
135   if (!PtoKKT && !AtoKKT && !param2toKKT) {
136     // If no index vectors passed, do not store KKT mapping from Trip to CSC/CSR
137     if (format == 0) KKT = triplet_to_csc(KKT_trip, OSQP_NULL);
138     else KKT = triplet_to_csr(KKT_trip, OSQP_NULL);
139   }
140   else {
141     // Allocate vector of indices from triplet to csc
142     KKT_TtoC = cast(c_int*)c_malloc((zKKT) * c_int.sizeof);
143 
144     if (!KKT_TtoC) {
145       // Error in allocating KKT_TtoC vector
146       csc_spfree(KKT_trip);
147       c_free(*Pdiag_idx);
148       return OSQP_NULL;
149     }
150 
151     // Store KKT mapping from Trip to CSC/CSR
152     if (format == 0)
153       KKT = triplet_to_csc(KKT_trip, KKT_TtoC);
154     else
155       KKT = triplet_to_csr(KKT_trip, KKT_TtoC);
156 
157     // Update vectors of indices from P, A, param2 to KKT (now in CSC format)
158     if (PtoKKT != OSQP_NULL) {
159       for (i = 0; i < P.p[P.n]; i++) {
160         PtoKKT[i] = KKT_TtoC[PtoKKT[i]];
161       }
162     }
163 
164     if (AtoKKT != OSQP_NULL) {
165       for (i = 0; i < A.p[A.n]; i++) {
166         AtoKKT[i] = KKT_TtoC[AtoKKT[i]];
167       }
168     }
169 
170     if (param2toKKT != OSQP_NULL) {
171       for (i = 0; i < A.m; i++) {
172         param2toKKT[i] = KKT_TtoC[param2toKKT[i]];
173       }
174     }
175 
176     // Free mapping
177     c_free(KKT_TtoC);
178   }
179 
180   // Clean matrix in triplet format and return result
181   csc_spfree(KKT_trip);
182 
183   return KKT;
184 }
185 
186 } /* ifndef EMBEDDED */
187 
188 
189 version (EMBEDDED_1){}
190 else {
191 
192 void update_KKT_P(csc          *KKT,
193                   const csc    *P,
194                   const c_int  *PtoKKT,
195                   const c_float param1,
196                   const c_int  *Pdiag_idx,
197                   const c_int   Pdiag_n) {
198   c_int i, j; // Iterations
199 
200   // Update elements of KKT using P
201   for (i = 0; i < P.p[P.n]; i++) {
202     KKT.x[PtoKKT[i]] = P.x[i];
203   }
204 
205   // Update diagonal elements of KKT by adding sigma
206   for (i = 0; i < Pdiag_n; i++) {
207     j                  = Pdiag_idx[i]; // Extract index of the element on the
208                                        // diagonal
209     KKT.x[PtoKKT[j]] += param1;
210   }
211 }
212 
213 void update_KKT_A(csc *KKT, const csc *A, const c_int *AtoKKT) {
214   c_int i; // Iterations
215 
216   // Update elements of KKT using A
217   for (i = 0; i < A.p[A.n]; i++) {
218     KKT.x[AtoKKT[i]] = A.x[i];
219   }
220 }
221 
222 void update_KKT_param2(csc *KKT, const c_float *param2,
223                        const c_int *param2toKKT, const c_int m) {
224   c_int i; // Iterations
225 
226   // Update elements of KKT using param2
227   for (i = 0; i < m; i++) {
228     KKT.x[param2toKKT[i]] = -param2[i];
229   }
230 }
231 
232 } // EMBEDDED != 1