├── Makefile ├── README.md ├── LICENSE └── mixing.c /Makefile: -------------------------------------------------------------------------------- 1 | CC := gcc 2 | CFLAGS := -g -Wall -O3 -std=gnu11 3 | LDFLAGS := -lm 4 | UNAME_S := $(shell uname -s) 5 | ifeq ($(UNAME_S),Linux) 6 | LDFLAGS += -lrt 7 | endif 8 | 9 | 10 | all: mixing 11 | 12 | mixing: mixing.c 13 | $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mixing 2 | The Mixing method for maximum cut (MAXCUT) and maximum satisfiability (MAXSAT) problem. 3 | 4 | # Usage 5 | 6 | ``` 7 | ./mixing [OPTIONS] INPUT 8 | OPTIONS: 9 | -s SOLVER: type of solver 10 | "-s maxcut" for maximum cut 11 | "-s maxsat" for maximum SAT (default) 12 | -k RANK: rank of solution (default auto) 13 | use "-k /2" to divide the rank by 2 14 | -e EPS: stopping threshold (default 1.0000e-03) 15 | -t MAX_ITER: maximum iteration (default 1000) 16 | use "-t max" for INT_MAX 17 | -r N_TRIAL: number of trial in evaluation (default 1) 18 | -u: use unspeficied wcnf format 19 | -v: verbose 20 | 21 | OUTPUTS: 22 | (factorized) SDP solution to INPUT.sol 23 | (when doing maxcut) rounded assignments to INPUT.rounding 24 | 25 | NOTE: 26 | For better randomized rounding results for discrete MAXCUT / MAXSAT, 27 | pleases increase N_TRIAL (e.g., -r 10000). 28 | ``` 29 | 30 | To compile the file, please use 31 | ``` 32 | $ make 33 | ``` 34 | 35 | # More Info 36 | This repository is by [Po-Wei Wang](http://powei.tw), 37 | [Wei-Cheng Chang](https://octoberchang.github.io/), 38 | and [J. Zico Kolter](http://zicokolter.com) 39 | and contains the source code to 40 | reproduce the experiments in our paper 41 | [The Mixing method: coordinate descent for low-rank semidefinite programming](http://arxiv.org/abs/1706.00476). 42 | If you find this repository helpful in your publications, please consider citing our paper. 43 | ``` 44 | @article{wang2017mixing, 45 | title = {The Mixing method: coordinate descent for low-rank semidefinite programming}, 46 | author = {Po-Wei Wang and Wei-Cheng Chang and J. Zico Kolter}, 47 | journal = {arXiv preprint arXiv:1706.00476}, 48 | year = {2017} 49 | } 50 | ``` 51 | 52 | For any questions and comments, please send your email to 53 | [poweiw@cs.cmu.edu](mailto:poweiw@cs.cmu.edu) 54 | 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /mixing.c: -------------------------------------------------------------------------------- 1 | #include 2 | #define __USE_XOPEN 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #ifndef __unix__ 14 | #include 15 | #endif 16 | 17 | double MEPS = 1e-24; 18 | 19 | #define NS_PER_SEC 1000000000 20 | int64_t wall_clock_ns() 21 | { 22 | #ifdef __unix__ 23 | struct timespec tspec; 24 | int r = clock_gettime(CLOCK_MONOTONIC, &tspec); 25 | assert(r==0); 26 | return tspec.tv_sec*NS_PER_SEC + tspec.tv_nsec; 27 | #else 28 | struct timeval tv; 29 | int r = gettimeofday( &tv, NULL ); 30 | assert(r==0); 31 | return tv.tv_sec*NS_PER_SEC + tv.tv_usec*1000; 32 | #endif 33 | } 34 | 35 | #define Malloc(size) malloc_or_abort((size), __LINE__) 36 | void* malloc_or_abort(size_t size, int line) 37 | { 38 | void *ptr = malloc(size); 39 | if(ptr == NULL) { 40 | fprintf(stderr, "Error not enough memory at %d (size=%zu)\n", line, size); 41 | exit(1); 42 | } 43 | return ptr; 44 | } 45 | 46 | #define Calloc(count, size) calloc_or_abort(count, (size), __LINE__) 47 | void* calloc_or_abort(size_t count, size_t size, int line) 48 | { 49 | void *ptr = calloc(count, size); 50 | if(ptr == NULL) { 51 | fprintf(stderr, "Errror not enough memory at %d (count=%zu, size=%zu)\n", line, count, size); 52 | exit(1); 53 | } 54 | return ptr; 55 | } 56 | 57 | double wall_time_diff(int64_t ed, int64_t st) 58 | { 59 | return (double)(ed-st)/(double)NS_PER_SEC; 60 | } 61 | 62 | struct Node { 63 | int index; 64 | double value; 65 | }; 66 | typedef struct Node Node; 67 | 68 | struct CUTProblem { 69 | int n; 70 | int nnz; 71 | Node **C; 72 | }; 73 | typedef struct CUTProblem CUTProblem; 74 | 75 | struct Model { 76 | int n, k; 77 | double **V; 78 | }; 79 | typedef struct Model Model; 80 | 81 | enum {MAXCUT=0, MAXSAT}; 82 | struct Parameter { 83 | int solver; 84 | int k; 85 | double eps; 86 | int max_iter; 87 | int is_unspeficied_wcnf; 88 | int verbose; 89 | int n_trial; 90 | FILE *fin; 91 | char *fin_name; 92 | }; 93 | typedef struct Parameter Parameter; 94 | 95 | struct SATProblem { 96 | int m; // number of clauses 97 | int n; // number of variables 98 | int nnz; // number of literals 99 | int **index; // index[j] = [1 2 -4] 100 | int *clause_len, *var_len; 101 | int *clause_weight; 102 | }; 103 | typedef struct SATProblem SATProblem; 104 | 105 | static char *line = NULL; 106 | static int max_line_len; 107 | 108 | static char* readline(FILE *input) 109 | { 110 | int len; 111 | // init 112 | if(max_line_len == 0) { 113 | max_line_len = 1024; 114 | line = (char *)Malloc(max_line_len*sizeof(char)); 115 | } 116 | 117 | if(fgets(line,max_line_len,input) == NULL) 118 | return NULL; 119 | 120 | while(strrchr(line,'\n') == NULL) 121 | { 122 | max_line_len *= 2; 123 | line = (char *) realloc(line,max_line_len); 124 | len = (int) strlen(line); 125 | if(fgets(line+len,max_line_len-len,input) == NULL) 126 | break; 127 | } 128 | return line; 129 | } 130 | 131 | int is_int(char *s) 132 | { 133 | if(!s || *s == '\0') 134 | return 0; 135 | if(*s!='-' && !isdigit(*s)) 136 | return 0; 137 | s++; 138 | for(; *s; s++) { 139 | if(!isdigit(*s)) 140 | return 0; 141 | } 142 | return 1; 143 | } 144 | 145 | int is_valid_literal(char *s, int nvar) 146 | { 147 | if(!is_int(s)) return 0; 148 | int var = abs(atoi(s)); 149 | if(var != 0 && var <= nvar) 150 | return 1; 151 | return 0; 152 | } 153 | 154 | // DIMAC format 155 | // Error detection: If cnf and all clause start with 1, then the format is wcnf. 156 | void read_sat_problem(FILE *fin, SATProblem *prob, int is_unspeficied_wcnf) 157 | { 158 | char *delim = " \t\r\n"; 159 | int has_weight = 0; 160 | int nnz=0, n=0, m=0; 161 | 162 | int *var_len = NULL; 163 | int *clause_len = NULL; 164 | 165 | int clause_cnt = 1; 166 | int lineno = 1; 167 | for(; readline(fin); lineno++) { 168 | if(line[0] == '\0' || strchr("c\r\n", line[0])) 169 | continue; 170 | if(line[0] == 'p') { 171 | char *p = strtok(line+1, delim); 172 | if(!strcmp(p, "cnf")) { 173 | has_weight = 0; 174 | } else if(!strcmp(p, "wcnf")) { 175 | has_weight = 1; 176 | } else { 177 | fprintf(stderr, "(line %d) No format specified (cnf or wcnf).\n", lineno); 178 | exit(1); 179 | } 180 | // some public dataset is wcnf but marked as cnf 181 | if(is_unspeficied_wcnf) has_weight = 1; 182 | 183 | p = strtok(NULL, delim); 184 | if(p) 185 | n = atoi(p); 186 | p = strtok(NULL, delim); 187 | if(p) 188 | m = atoi(p); 189 | if(m == 0 || n == 0) { 190 | fprintf(stderr, "(line %d) Wrong format in parameter line\n", lineno); 191 | exit(1); 192 | } 193 | clause_len = Calloc(m+1, sizeof(*clause_len)); 194 | var_len = Calloc(n+1, sizeof(*var_len)); 195 | continue; 196 | } 197 | if(var_len == NULL || clause_len == NULL) { 198 | fprintf(stderr, "(line %d) Clause appears before parameters\n", lineno); 199 | exit(1); 200 | } 201 | 202 | // count nnz and check has_weight 203 | int has_error = 0; 204 | char *p = strtok(line, delim); 205 | if(!p){ 206 | fprintf(stderr, "(line %d) Empty line in clause\n", lineno); 207 | exit(1); 208 | } 209 | if(!strncmp(p, "1 %", 3)) // ending symbol in some format 210 | break; 211 | if(has_weight){ 212 | if(!is_int(p) || *p=='-' || !strcmp(p, "0")){ 213 | fprintf(stderr, "(line %d) Only accept positve integer weight\n", lineno); 214 | exit(1); 215 | } 216 | p = strtok(NULL, delim); 217 | } 218 | 219 | int is_zero_terminated = 0; 220 | 221 | for(; p; ){ 222 | if(!strcmp(p, "0")){ 223 | is_zero_terminated = 1; 224 | break; 225 | } 226 | nnz++; 227 | if(!is_valid_literal(p, n)){ 228 | has_error = 1; 229 | break; 230 | }else{ 231 | int var = abs(atoi(p)); 232 | var_len[var]++; 233 | clause_len[clause_cnt]++; 234 | } 235 | p = strtok(NULL, delim); 236 | } 237 | if(clause_len[clause_cnt] == 0){ 238 | fprintf(stderr, "(line %d) Clause has no literal\n", lineno); 239 | exit(1); 240 | } 241 | if(!is_zero_terminated){ 242 | fprintf(stderr, "(line %d) Clause need to be terminated with 0\n", lineno); 243 | } 244 | if(has_error) { 245 | fprintf(stderr, "(line %d) Wrong format in clause\n", lineno); 246 | exit(1); 247 | } 248 | clause_cnt++; 249 | if(is_unspeficied_wcnf && clause_cnt == m+1) { 250 | fprintf(stderr, "Read all the clauses for irregular WCNF format. Ignored error check since line %d.\n", lineno); 251 | break; 252 | }else if(clause_cnt > m+1){ 253 | fprintf(stderr, "(line %d) More clause then speficied in parameters (%d)\n", lineno, m); 254 | exit(1); 255 | } 256 | } 257 | if(clause_cnt < m+1){ // include the 0-th clause 258 | fprintf(stderr, "(error) Fewer clauses (%d) then speficied in parameters (%d) \n", clause_cnt, m); 259 | exit(1); 260 | } 261 | 262 | int *pool = (int*) Calloc(nnz+n + m+1, sizeof(*pool)); 263 | int cur = 0; 264 | 265 | int **index = (int**) Calloc(n+1, sizeof(*index)); 266 | int *clause_weight = (int*) Calloc(m+1, sizeof(*clause_weight)); 267 | 268 | int *var_pos = (int*) Calloc(n+1, sizeof(*var_pos)); 269 | var_pos[0] = 0; 270 | var_len[0] = m; 271 | for(int j=1; j<=n; j++) { 272 | var_pos[j] = var_pos[j-1] + var_len[j-1]+1; 273 | index[j] = pool + var_pos[j]; 274 | } 275 | #if 0 276 | printf("var_pos : "); 277 | for(int j=0; j<=n; j++) 278 | printf("%d ", var_pos[j]); 279 | printf("\n"); 280 | #endif 281 | 282 | // initialize truth vector 283 | index[0] = pool; 284 | for(int i=1; i<=m; i++) { 285 | pool[cur++] = i; 286 | } 287 | pool[cur++] = 0; 288 | 289 | // initialize variables 290 | fseek(fin, 0, SEEK_SET); 291 | 292 | clause_cnt = 1; 293 | lineno = 1; 294 | for(; readline(fin); lineno++) { 295 | if(line[0] == '\0' || strchr("c\n", line[0])) 296 | continue; 297 | if(line[0] == 'p') 298 | continue; 299 | char *p = strtok(line, delim); 300 | if(has_weight){ 301 | clause_weight[clause_cnt] = atoi(p); 302 | p = strtok(NULL, delim); 303 | }else{ 304 | clause_weight[clause_cnt] = 1; 305 | } 306 | while(p){ 307 | int literal = atoi(p); 308 | if(literal == 0) 309 | break; 310 | int sign = (literal>0)? 1 : -1; 311 | int var = abs(literal); 312 | //printf("lit %d at pos %d = %d\n", literal, var_pos[var], sign*clause_cnt); 313 | pool[var_pos[var]++] = sign * clause_cnt; 314 | 315 | p = strtok(NULL, delim); 316 | } 317 | clause_cnt++; 318 | } 319 | 320 | prob->m = m; 321 | prob->n = n; 322 | prob->nnz = nnz; 323 | prob->clause_len = clause_len; 324 | prob->var_len = var_len; 325 | prob->index = index; 326 | prob->clause_weight = clause_weight; 327 | free(var_pos); 328 | } 329 | 330 | void print_struct(SATProblem *prob) 331 | { 332 | printf("n %d m %d nnz %d\n", prob->n, prob->m, prob->nnz); 333 | printf("clause_len: "); 334 | for(int i=1; i<=prob->m; i++) 335 | printf("%d ", prob->clause_len[i]); 336 | printf("\n var:\n"); 337 | for(int j=1; j<=prob->n; j++){ 338 | printf("len %d : ", prob->var_len[j]); 339 | int *p = prob->index[j]; 340 | for(; *p; p++){ 341 | printf("%d ", *p); 342 | } 343 | printf("\n"); 344 | } 345 | } 346 | 347 | void dzero(double *v, int l); 348 | void daxpy(double *restrict y, double a, const double *restrict x, int l); 349 | double dnrm2(const double *x, int l); 350 | void dscal(double *x, double a, int l); 351 | double ddot(const double *x, const double *y, int l); 352 | void dswap(double *x, double *y, int n); 353 | void dcopy(double *x, double *y, int l); 354 | 355 | void dzero(double *v, int l) 356 | { 357 | memset(v, 0, sizeof(*v)*l); 358 | } 359 | void daxpy(double *restrict y, double a, const double *restrict x, int l) 360 | { 361 | int m = l-3; 362 | int i; 363 | for (i = 0; i < m; i += 4) 364 | { 365 | y[i] += a * x[i]; 366 | y[i+1] += a * x[i+1]; 367 | y[i+2] += a * x[i+2]; 368 | y[i+3] += a * x[i+3]; 369 | } 370 | for ( ; i < l; ++i) /* clean-up loop */ 371 | y[i] += a * x[i]; 372 | } 373 | double dnrm2(const double *x, int l) 374 | { 375 | double xx = ddot(x, x, l); 376 | return sqrt(xx); 377 | } 378 | void dscal(double *x, double a, int l) 379 | { 380 | int m = l-4; 381 | int i; 382 | for (i = 0; i < m; i += 5){ 383 | x[i] *= a; 384 | x[i+1] *= a; 385 | x[i+2] *= a; 386 | x[i+3] *= a; 387 | x[i+4] *= a; 388 | } 389 | 390 | for ( ; i < l; i++) /* clean-up loop */ 391 | x[i] *= a; 392 | } 393 | double ddot(const double *x, const double *y, int l) 394 | { 395 | double s = 0; 396 | int m = l-4; 397 | int i; 398 | for (i = 0; i < m; i += 5) 399 | s += x[i] * y[i] + x[i+1] * y[i+1] + x[i+2] * y[i+2] + 400 | x[i+3] * y[i+3] + x[i+4] * y[i+4]; 401 | 402 | for ( ; i < l; i++) /* clean-up loop */ 403 | s += x[i] * y[i]; 404 | 405 | return s; 406 | } 407 | void dswap(double *x, double *y, int n) 408 | { 409 | for(int i=0; i q[0].index) 426 | return 1; 427 | else if (p[1].index < q[1].index) 428 | return -1; 429 | else if (p[1].index > q[1].index) 430 | return 1; 431 | else 432 | return 0; 433 | } 434 | 435 | // Read the undiercted symmetric adjancency matrix 436 | // into a csr format with duplicated edges (in place). 437 | // Note that the order in each row is not guarenteed. 438 | void read_undirected_adjmatrix(FILE *fin, CUTProblem *prob) 439 | { 440 | int n, nnz; 441 | 442 | int buf_size = 120; 443 | char buf[buf_size]; 444 | int is_mm_format = 0; 445 | int is_binary = 0; 446 | 447 | if(NULL == fgets(buf, buf_size, fin)) { 448 | fprintf(stderr, "Error: readline\n"); 449 | exit(1); 450 | } 451 | if(buf[0] == '%') { 452 | is_mm_format = 1; 453 | if(NULL != strstr(buf, "pattern")) 454 | is_binary = 1; 455 | } 456 | fseek(fin, 0, SEEK_SET); 457 | 458 | int lineno = 1; 459 | // handle coments for matrix market 460 | while(NULL != fgets(buf, buf_size, fin) 461 | && buf[0] == '%') 462 | lineno++; 463 | 464 | if(is_mm_format) { 465 | int n_; 466 | int ret = sscanf(buf, " %d %d %d", &n, &n_, &nnz); 467 | if(ret != 3) { 468 | fprintf(stderr, "Error: matrix size\n"); 469 | exit(1); 470 | } 471 | if(n != n_) { 472 | fprintf(stderr, "Error: not square matrix\n"); 473 | exit(1); 474 | } 475 | } else { 476 | int ret = sscanf(buf, " %d %d", &n, &nnz); 477 | if(ret != 2) { 478 | fprintf(stderr, "Error: matrix size\n"); 479 | exit(1); 480 | } 481 | } 482 | 483 | //printf("n=%d nnz=%d\n", n, nnz); 484 | 485 | prob->n = n; 486 | prob->C = (Node**) Malloc(sizeof(Node*)*n); 487 | // Space usage: nnz*(double+int)*2 + nnz*int. 488 | // The nnz*int part is for construction, will free after use. 489 | // Time: O(nnz). 490 | // We store the COO format in ipart and jpart, 491 | // construct the CSR format for the upper triangle stored in COO, 492 | // allocate space for the transpose part, 493 | // and finally fill in the CSR format for the lower triangle. 494 | Node *pool = (Node*) Malloc(sizeof(Node)*(nnz*2+n)); 495 | Node *jpart = pool+nnz+n; 496 | int *ipart = (int*) Malloc(sizeof(*ipart)*nnz); 497 | 498 | int *cap = (int*) Calloc(n, sizeof(*cap)); 499 | Node **rear = (Node**) Malloc (n*sizeof(Node*)); 500 | Node end_node = {.index = -1, .value = 0}; 501 | 502 | // read COO format 503 | int cur=0; 504 | while(1) { 505 | int i=0, j=0, t; 506 | double w=0; 507 | int ret, expect; 508 | 509 | if(NULL == fgets(buf, buf_size, fin)) { 510 | if(cur < nnz){ 511 | fprintf(stderr, "ERROR less edges than specified\n"); 512 | exit(1); 513 | } 514 | break; 515 | } else if (cur >= nnz) { 516 | fprintf(stderr, "ERROR more edges than specified\n"); 517 | exit(1); 518 | } 519 | lineno++; 520 | 521 | if (is_binary) { 522 | ret = sscanf(buf, " %d %d", &i, &j); 523 | expect = 2; 524 | w = 1; 525 | } else { 526 | ret = sscanf(buf, " %d %d %lf", &i, &j, &w); 527 | expect = 3; 528 | } 529 | if (ret != expect) { 530 | fprintf(stderr, "ERROR reading file at %d\n", lineno); 531 | exit(1); 532 | } 533 | 534 | --i, --j; 535 | if (!(0<=i && ij) { 542 | t=i, i=j, j=t; 543 | } 544 | 545 | ipart[cur] = i; 546 | jpart[cur].index = j; 547 | jpart[cur].value = w; 548 | cur++, cap[i]++; 549 | } 550 | 551 | // construct CSR for the upper traingle 552 | rear[0] = pool; 553 | for (int i=1; i0; i--) { 569 | Node* dst = poolend - cap[i]; 570 | Node* src = rear[i-1]; 571 | size_t len = rear[i] - rear[i-1]; 572 | 573 | memmove(dst, src, len*sizeof(Node)); 574 | rear[i] += dst-src; 575 | poolend -= cap[i]; 576 | } 577 | 578 | // fill in the lower triangle 579 | Node *front = pool; 580 | for (int i=0; iindex; 583 | if (i>j) break; 584 | rear[j]->index = i; 585 | rear[j]->value = p->value; 586 | rear[j]++; 587 | } 588 | prob->C[i] = front; 589 | front += cap[i]; 590 | } 591 | for (int i=0; innz = nnz; 595 | 596 | free(ipart); 597 | free(rear); 598 | free(cap); 599 | } 600 | 601 | void permutation(int *perm, int l) 602 | { 603 | for(int i=0; i=0) ? x : 0.; 627 | } 628 | 629 | void eval_maxsat(SATProblem *prob, double **V, double **Z, int k, double *fval, int *rval, double *upper, Parameter *param) 630 | { 631 | int m = prob->m, n = prob->n; 632 | int **index = prob->index; 633 | int *clen = prob->clause_len; 634 | int verbose = param->verbose; 635 | 636 | int *clause_sat = Calloc(m+1, sizeof(*clause_sat)); 637 | double *r = Calloc(k, sizeof(*r)); 638 | 639 | double ravg = 0; 640 | for(int trial=0; trialn_trial; trial++){ 641 | memset(clause_sat, 0, (m+1)*sizeof(*clause_sat)); 642 | if(verbose) printf("v "); 643 | // rounding 644 | rand_unit(r, k); 645 | double wsign = ddot(r, V[0], k); 646 | //printf("v[0] = %f %f\n", V[0][0], V[0][1]); 647 | for(int j=1; j<=n; j++) { 648 | int bvar = (wsign * ddot(r, V[j], k) > 0) ? 1 : -1; 649 | int *p = index[j]; 650 | for(; *p != 0; p++) { 651 | double sign = (*p > 0) ? 1. : -1; 652 | int i = abs(*p); 653 | if(sign * bvar > 0) 654 | clause_sat[i] = sign*j; 655 | } 656 | if(verbose) printf("%d ", j * bvar); 657 | } 658 | if(verbose) printf("\n"); 659 | 660 | if(verbose) printf("unsat clause "); 661 | *fval = *rval = *upper = 0; 662 | for(int i=1; i<=m; i++) { 663 | int div = (clen[i]+1)*clen[i]+2; // (len+1)^2-(len-1) 664 | double zi = square(dnrm2(Z[i], k)); 665 | *fval += zi / div; 666 | if(isnan(*fval) || isinf(*fval)){ 667 | fprintf(stderr, "zi %g i %d\n", zi, i); 668 | for(int kk=0; kkn_trial; 682 | *rval = ravg; 683 | 684 | free(clause_sat); 685 | free(r); 686 | } 687 | 688 | void do_maxsat(SATProblem *prob, Model *model, Parameter *param) 689 | { 690 | int max_iter = param->max_iter; 691 | double eps = param->eps; 692 | 693 | int m = prob->m, n = prob->n; 694 | int k = (int)ceil(sqrt(2*n)); 695 | if(param->k > 0){ 696 | k = param->k; 697 | }else if(param->k < 0){ 698 | k /= -param->k; 699 | if(k==0) k=1; 700 | } 701 | 702 | 703 | int **index = prob->index; 704 | int *clen = prob->clause_len; 705 | 706 | printf("MAXSAT m %d n %d k %d nnz %d eps %.4e max_iter %d\n", m, n, k, prob->nnz, eps, max_iter); 707 | 708 | double **V = Calloc(n+1, sizeof(*V)); 709 | for(int j=0; j<=n; j++) { 710 | V[j] = Calloc(k, sizeof(**V)); 711 | } 712 | double **Z = Calloc(m+1, sizeof(*Z)); 713 | for(int i=0; i<=m; i++) { 714 | Z[i] = Calloc(k, sizeof(**Z)); 715 | } 716 | double *g = Calloc(k, sizeof(*g)); 717 | double *ci = Calloc(k, sizeof(*ci)); 718 | double *r = Calloc(k, sizeof(*r)); 719 | 720 | for(int j=0; j<=n; j++) { 721 | rand_unit(V[j], k); 722 | } 723 | 724 | // init Ci = \sum_j +-vj - (len(Ci)-1)w 725 | for(int i=1; i<=m; i++) { 726 | dzero(g, k); 727 | daxpy(g, -1., V[0], k); 728 | daxpy(Z[i], 1., g, k); 729 | } 730 | for(int j=1; j<=n; j++) { 731 | int *p = index[j]; 732 | for(; *p != 0; p++) { 733 | double sign = (*p > 0) ? 1. : -1; 734 | int i = abs(*p); 735 | daxpy(Z[i], sign, V[j], k); 736 | } 737 | } 738 | 739 | double fval=0, upper=0, fval_prev=0; 740 | int rval; 741 | eval_maxsat(prob, V, Z, k, &fval, &rval, &upper, param); 742 | printf("iter %3d fval %.14e delta %.4e rval %d/%d %.4e upper %.4e time %.14e\n", 743 | 0, fval, 0., rval, m, rval*1./m, upper/m, 0.); 744 | 745 | fval_prev = fval; 746 | 747 | int64_t time_st = wall_clock_ns(); 748 | int64_t time_eval = 0; 749 | int iter = 0; 750 | for(; iter < max_iter; iter++) { 751 | for(int j=1; j<=n; j++) { 752 | // Remove vj from Ci 753 | int *p; 754 | for(p = index[j]; *p != 0; p++) { 755 | double sign = (*p>0) ? 1. : -1.; 756 | int i = abs(*p); 757 | daxpy(Z[i], -sign, V[j], k); 758 | } 759 | // Do Mixing 760 | dzero(g, k); 761 | for(p = index[j]; *p; p++) { 762 | double sign = (*p>0) ? 1. : -1.; 763 | int i = abs(*p); 764 | daxpy(g, sign/((clen[i]+1)*clen[i]+2), Z[i], k); 765 | } 766 | double gnrm = dnrm2(g, k); 767 | if(gnrm >= MEPS){ 768 | dscal(g, -1/gnrm, k); 769 | dcopy(g, V[j], k); 770 | } 771 | // Add vj back to Ci 772 | for(p = index[j]; *p != 0; p++) { 773 | double sign = (*p>0) ? 1. : -1.; 774 | int i = abs(*p); 775 | daxpy(Z[i], sign, V[j], k); 776 | } 777 | } 778 | int64_t time_now = wall_clock_ns(); 779 | 780 | eval_maxsat(prob, V, Z, k, &fval, &rval, &upper, param); 781 | int64_t time_eval_ed = wall_clock_ns(); 782 | time_eval += time_eval_ed-time_now; 783 | 784 | double delta = fval_prev - fval; 785 | printf("iter %3d fval %.14e delta %.4e rval %d/%d %.4e upper %.4e time %.14e\n", 786 | iter+1, fval, delta, rval, m, rval*1./m, upper/m, wall_time_diff(time_eval_ed-time_eval, time_st)); 787 | fflush(stdout); 788 | if(delta < eps) 789 | break; 790 | fval_prev = fval; 791 | } 792 | 793 | model->n = n, model->k = k; 794 | model->V = V; 795 | } 796 | 797 | double eval_maxcut(CUTProblem *prob, Model *model, Parameter *param, int *rounding) 798 | { 799 | int n = model->n, k = model->k; 800 | double **V = model->V; 801 | double *r = (double*) Calloc(k, sizeof *r); 802 | int *best_rounding = (int*) Calloc(n, sizeof *best_rounding); 803 | double best_cut = -INFINITY; 804 | Node **C = prob->C; 805 | 806 | double sum_edge = 0; 807 | for(int i=0; iindex != -1; p++) sum_edge += p->value; 809 | } 810 | sum_edge /= 2; 811 | 812 | printf("\nstart rounding (time resetted). Total edge weight: %f\n", sum_edge); 813 | int64_t time_st = wall_clock_ns(); 814 | for (int it=0; it < param->n_trial; it++) { 815 | rand_unit(r, k); 816 | 817 | for (int i=0; i 0)*2-1; 818 | 819 | double cut = 0; 820 | for(int i=0; iindex != -1; p++) { 822 | cut -= p->value * rounding[i] * rounding[p->index]; 823 | cut += p->value; 824 | } 825 | } 826 | if (best_cut < cut) { 827 | int64_t time_now = wall_clock_ns(); 828 | printf("trial %d\ttime %.16g\tcut %f\n", 829 | it, 830 | wall_time_diff(time_now, time_st), cut/4); 831 | best_cut = cut; 832 | for (int i=0; ifin_name)+10); 840 | strcpy(fout_name, param->fin_name); 841 | strcat(fout_name, ".rounding"); 842 | FILE *fout = fopen(fout_name, "w"); 843 | for (int i=0; imax_iter; 854 | int n = prob->n; 855 | int k = (int)ceil(sqrt(2*n)); 856 | double eps = param->eps; 857 | if(param->k > 0){ 858 | k = param->k; 859 | }else if(param->k < 0){ 860 | k /= -param->k; 861 | if(k==0) k=1; 862 | } 863 | 864 | printf("MAXCUT n=%d k=%d nnz=%d eps %.4e max_iter %d\n", n, k, prob->nnz, eps, max_iter); 865 | 866 | double **V = (double**) Malloc (sizeof(*V)*n); 867 | V[0] = (double*) Calloc (n*k, sizeof(**V)); 868 | 869 | for(int i=0; iC; 875 | double *g = (double*) Malloc (sizeof(*g)*n); 876 | //int *perm = (int*) Malloc(sizeof(*perm)*n); 877 | //permutation(perm, n); 878 | 879 | // calculate function value 880 | double fval = 0; 881 | for(int i=0; iindex != -1; p++) { 883 | fval += p->value * ddot(V[i], V[p->index], k); 884 | fval -= p->value; 885 | } 886 | } 887 | printf("iter %4d time %.16g\tfval %.22g\tdelta %.16g\n", 888 | 0, 0., 889 | fval/4, 0.); 890 | fflush(stdout); 891 | 892 | int64_t time_st = wall_clock_ns(); 893 | 894 | for(iter=0; iter < max_iter; iter++) { 895 | double delta = 0; 896 | for(int i=0; iindex != -1; p++) { 900 | daxpy(g, p->value, V[p->index], k); 901 | } 902 | double gnrm = dnrm2(g, k); 903 | if(gnrm > MEPS) { 904 | dscal(g, -1/gnrm, k); 905 | } 906 | delta += 2*gnrm*(1. - ddot(g, V[i], k)); 907 | dcopy(g, V[i], k); 908 | } 909 | fval -= delta; 910 | int64_t time_now = wall_clock_ns(); 911 | printf("iter %4d time %.16g\tfval %.22g\tdelta %.16g\n", 912 | iter+1, 913 | wall_time_diff(time_now, time_st), 914 | fval/4, delta/4); 915 | fflush(stdout); 916 | if(delta < eps) 917 | break; 918 | } 919 | 920 | model->n = n, model->k = k; 921 | model->V = V; 922 | 923 | int *rounding = (int*) Calloc(n, sizeof *rounding); 924 | eval_maxcut(prob, model, param, rounding); 925 | } 926 | 927 | void get_clause_stat(SATProblem *prob, int *min_len, int *max_len, double *avg_len) 928 | { 929 | int *len = prob->clause_len; 930 | *max_len = 0; 931 | *min_len = INT_MAX; 932 | *avg_len = 0.; 933 | for(int i=1; i<=prob->m; i++){ 934 | if(*max_len < len[i]) 935 | *max_len = len[i]; 936 | if(*min_len > len[i]) 937 | *min_len = len[i]; 938 | *avg_len += len[i]; 939 | } 940 | *avg_len /= prob->m; 941 | } 942 | 943 | void print_usage(char* prog_name, Parameter *param) 944 | { 945 | printf( "Mixing method for SDP\n"); 946 | printf( "\t%s [OPTIONS] INPUT\n", prog_name); 947 | printf( "\nOPTIONS:\n"); 948 | printf( "\t-s SOLVER: type of solver\n"); 949 | printf( "\t \"-s maxcut\" for maximum cut\n"); 950 | printf( "\t \"-s maxsat\" for maximum SAT (default)\n"); 951 | printf( "\t-k RANK: rank of solution (default auto)\n"); 952 | printf( "\t use \"-k /2\" to divide the rank by 2\n"); 953 | printf( "\t-e EPS: stopping threshold (default %.4e)\n", param->eps); 954 | printf( "\t-t MAX_ITER: maximum iteration (default %d)\n", param->max_iter); 955 | printf( "\t use \"-t max\" for INT_MAX\n"); 956 | printf( "\t-r N_TRIAL: number of trial in evaluation (default %d)\n", param->n_trial); 957 | printf( "\t-u: use unspeficied wcnf format\n"); 958 | printf( "\t-v: verbose\n"); 959 | printf( "\nOUTPUTS:\n"); 960 | printf( "\t(factorized) SDP solution to INPUT.sol\n"); 961 | printf( "\t(when doing maxcut) rounded assignments to INPUT.rounding\n"); 962 | printf( "\nNOTE:\n"); 963 | printf( "\tFor better randomized rounding results for discrete MAXCUT / MAXSAT,\n"); 964 | printf( "\tpleases increase N_TRIAL (e.g., -r 10000).\n"); 965 | } 966 | 967 | void get_parameter(int argc, char **argv, Parameter *param) 968 | { 969 | Parameter _param = { 970 | .solver = MAXSAT, 971 | .k = -1, 972 | .eps = 1e-3, 973 | .max_iter = 1000, 974 | .is_unspeficied_wcnf = 0, 975 | .verbose = 0, 976 | .n_trial = 1, 977 | .fin = NULL, 978 | .fin_name = NULL, 979 | }; 980 | 981 | if(argc <= 1){ 982 | print_usage(argv[0], &_param); 983 | exit(0); 984 | } 985 | 986 | char **p = argv+1; 987 | int i; 988 | for(i=1; i= argc) break; 991 | if(!strcmp(p[1], "maxcut")){ 992 | _param.solver = MAXCUT; 993 | }else if(!strcmp(p[1], "maxsat")){ 994 | _param.solver = MAXSAT; 995 | }else { 996 | int ret = sscanf(p[1], "%d", &_param.solver); 997 | if(ret != 1 || !(_param.solver >=0 && _param.solver <= 1)) break; 998 | } 999 | i++, p++; 1000 | }else if(!strcmp(*p, "-k")){ 1001 | if(i+1 >= argc) break; 1002 | int ret = sscanf(p[1], "/%d", &_param.k); 1003 | if(ret==1){ 1004 | _param.k *= -1; 1005 | }else{ 1006 | ret = sscanf(p[1], "%d", &_param.k); 1007 | if(ret != 1 || _param.k <= 0) break; 1008 | } 1009 | i++, p++; 1010 | }else if(!strcmp(*p, "-e")){ 1011 | if(i+1 >= argc) break; 1012 | int ret = sscanf(p[1], "%lf", &_param.eps); 1013 | if(ret != 1) break; 1014 | i++, p++; 1015 | }else if(!strcmp(*p, "-t")){ 1016 | if(i+1 >= argc) break; 1017 | if(!strcmp(p[1], "max")){ 1018 | _param.max_iter = INT_MAX; 1019 | }else{ 1020 | int ret = sscanf(p[1], "%d", &_param.max_iter); 1021 | if(ret != 1) break; 1022 | } 1023 | i++, p++; 1024 | }else if(!strcmp(*p, "-r")){ 1025 | if(i+1 >= argc) break; 1026 | int ret = sscanf(p[1], "%d", &_param.n_trial); 1027 | if(ret != 1) break; 1028 | if(_param.n_trial < 1) 1029 | _param.n_trial = 1; 1030 | i++, p++; 1031 | }else if(!strcmp(*p, "-u")){ 1032 | _param.is_unspeficied_wcnf = 1; 1033 | }else if(!strcmp(*p, "-v")){ 1034 | _param.verbose = 1; 1035 | }else if(i+1 == argc){ 1036 | _param.fin = fopen(*p, "r"); 1037 | if(!_param.fin){ 1038 | fprintf(stderr, "%s\n", strerror(errno)); 1039 | exit(1); 1040 | } 1041 | _param.fin_name = strdup(*p); 1042 | }else{ 1043 | printf("Error: no such parameter\n"); 1044 | break; 1045 | } 1046 | } 1047 | if(i != argc || !_param.fin){ 1048 | print_usage(argv[0], &_param); 1049 | exit(0); 1050 | } 1051 | *param = _param; 1052 | } 1053 | 1054 | int main(int argc, char **argv) 1055 | { 1056 | Parameter param; 1057 | get_parameter(argc, argv, ¶m); 1058 | srand48(0); 1059 | 1060 | Model model; 1061 | if(param.solver == MAXCUT){ 1062 | printf("Solving maximum cut. Reading %s\n", param.fin_name); 1063 | CUTProblem cut_prob; 1064 | read_undirected_adjmatrix(param.fin, &cut_prob); 1065 | do_maxcut(&cut_prob, &model, ¶m); 1066 | }else if(param.solver == MAXSAT){ 1067 | SATProblem sat_prob; 1068 | printf("Solving maximum SAT. Reading %s\n", param.fin_name); 1069 | read_sat_problem(param.fin, &sat_prob, param.is_unspeficied_wcnf); 1070 | if(param.verbose) 1071 | print_struct(&sat_prob); 1072 | 1073 | int min_len, max_len; 1074 | double avg_len; 1075 | get_clause_stat(&sat_prob, &min_len, &max_len, &avg_len); 1076 | printf("clauses len : min %d max %d avg %.4f\n", min_len, max_len, avg_len); 1077 | 1078 | do_maxsat(&sat_prob, &model, ¶m); 1079 | } 1080 | 1081 | char *fout_name = (char *) Malloc(strlen(param.fin_name)+5); 1082 | strcpy(fout_name, param.fin_name); 1083 | strcat(fout_name, ".sol"); 1084 | FILE *fout = fopen(fout_name, "w"); 1085 | for (int i=0; i