codegen_utils.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. # Compatibility with Python 2
  2. from __future__ import print_function
  3. from scipy import sparse
  4. import numpy as np
  5. def write_int(f, x, name, *args):
  6. if any(args):
  7. for arg in args:
  8. f.write("%s->" % arg)
  9. f.write("%s = %i;\n" % (name, x))
  10. else:
  11. f.write("c_int %s = %i;\n" % (name, x))
  12. def write_float(f, x, name, *args):
  13. if any(args):
  14. for arg in args:
  15. f.write("%s->" % arg)
  16. f.write("%s = %.20f;\n" % (name, x))
  17. else:
  18. f.write("c_float %s = %.20f;\n" % (name, x))
  19. def write_vec_int(f, x, name, *args):
  20. n = len(x)
  21. if any(args):
  22. for arg in args:
  23. f.write("%s->" % arg)
  24. else:
  25. f.write("c_int * ")
  26. f.write("%s = (c_int*) c_malloc(%i * sizeof(c_int));\n" % (name, n))
  27. for i in range(n):
  28. for arg in args:
  29. f.write("%s->" % arg)
  30. f.write("%s[%i] = " % (name, i))
  31. f.write("%i;\n" % x[i])
  32. def write_vec_float(f, x, name, *args):
  33. n = len(x)
  34. if any(args):
  35. for arg in args:
  36. f.write("%s->" % arg)
  37. else:
  38. f.write("c_float * ")
  39. f.write("%s = (c_float*) c_malloc(%i * sizeof(c_float));\n" % (name, n))
  40. for i in range(n):
  41. for arg in args:
  42. f.write("%s->" % arg)
  43. f.write("%s[%i] = " % (name, i))
  44. if x[i] == np.inf:
  45. f.write("OSQP_INFTY;\n")
  46. elif x[i] == -np.inf:
  47. f.write("-OSQP_INFTY;\n")
  48. else:
  49. f.write("%.20f;\n" % x[i])
  50. def clean_vec(f, name, *args):
  51. f.write("c_free(")
  52. if any(args):
  53. for arg in args:
  54. f.write("%s->" % arg)
  55. # else:
  56. # f.write("c_float * ")
  57. f.write("%s);\n" % name)
  58. def write_mat_sparse(f, A, name, *args):
  59. m = A.shape[0]
  60. n = A.shape[1]
  61. f.write("\n// Matrix " + name + "\n")
  62. f.write("//")
  63. f.write("-"*(len("Matrix ") + len(name)) + "\n")
  64. # Allocate Matrix
  65. if any(args):
  66. for arg in args:
  67. f.write("%s->" % arg)
  68. else:
  69. f.write("csc * ")
  70. f.write(name + " = (csc*) c_malloc(sizeof(csc));\n")
  71. # Write dimensions and number of nonzeros
  72. if any(args):
  73. write_int(f, m, "m", args, name)
  74. write_int(f, n, "n", args, name)
  75. write_int(f, -1, "nz", args, name)
  76. write_int(f, A.nnz, "nzmax", args, name)
  77. else:
  78. write_int(f, m, "m", name)
  79. write_int(f, n, "n", name)
  80. write_int(f, -1, "nz", name)
  81. write_int(f, A.nnz, "nzmax", name)
  82. for arg in args:
  83. f.write("%s->" % arg)
  84. if min(m,n) == 0:
  85. f.write("%s->x = OSQP_NULL;\n" % name)
  86. else:
  87. f.write("%s->" % name)
  88. f.write("x = (c_float*) c_malloc(%i * sizeof(c_float));\n" % A.nnz)
  89. for i in range(A.nnz):
  90. for arg in args:
  91. f.write("%s->" % arg)
  92. f.write("%s->" % name)
  93. f.write("x[%i] = %.20f;\n" % (i, A.data[i]))
  94. for arg in args:
  95. f.write("%s->" % arg)
  96. if min(m,n) == 0:
  97. f.write("%s->i = OSQP_NULL;\n" % name)
  98. else:
  99. f.write("%s->" % name)
  100. f.write("i = (c_int*) c_malloc(%i * sizeof(c_int));\n" % A.nnz)
  101. for i in range(A.nnz):
  102. for arg in args:
  103. f.write("%s->" % arg)
  104. f.write("%s->" % name)
  105. f.write("i[%i] = %i;\n" % (i, A.indices[i]))
  106. for arg in args:
  107. f.write("%s->" % arg)
  108. f.write("%s->" % name)
  109. f.write("p = (c_int*) c_malloc((%i + 1) * sizeof(c_int));\n" % n)
  110. for i in range(A.shape[1] + 1):
  111. for arg in args:
  112. f.write("%s->" % arg)
  113. f.write("%s->" % name)
  114. f.write("p[%i] = %i;\n" % (i, A.indptr[i]))
  115. # Do the same for i and p
  116. f.write("\n")
  117. def clean_mat(f, name, *args):
  118. # Clean data vector
  119. f.write("c_free(")
  120. if any(args):
  121. for arg in args:
  122. f.write("%s->" % arg)
  123. f.write("%s->x);\n" % name)
  124. # Clean index vector
  125. f.write("c_free(")
  126. if any(args):
  127. for arg in args:
  128. f.write("%s->" % arg)
  129. f.write("%s->i);\n" % name)
  130. # Clean col pointer vector
  131. f.write("c_free(")
  132. if any(args):
  133. for arg in args:
  134. f.write("%s->" % arg)
  135. f.write("%s->p);\n" % name)
  136. # Clean matrix
  137. f.write("c_free(")
  138. if any(args):
  139. for arg in args:
  140. f.write("%s->" % arg)
  141. f.write("%s);\n" % name)
  142. def generate_problem_data(P, q, A, l, u, problem_name, sols_data={}):
  143. """
  144. Generate test problem data.
  145. The additional structure sols_data defines the additional vectors/scalars
  146. we need to perform the tests
  147. """
  148. # Get problem dimension
  149. n = P.shape[0]
  150. m = A.shape[0]
  151. #
  152. # GENERATE HEADER FILE
  153. #
  154. f = open(problem_name + "/data.h", "w")
  155. # Add definition check
  156. f.write("#ifndef " + problem_name.upper() + "_DATA_H\n")
  157. f.write("#define " + problem_name.upper() + "_DATA_H\n")
  158. # Add Includes
  159. f.write("#include \"osqp.h\"\n")
  160. f.write("\n\n")
  161. #
  162. # Create additional data structure
  163. #
  164. f.write("/* create additional data and solutions structure */\n")
  165. f.write("typedef struct {\n")
  166. # Generate further data and solutions
  167. for key, value in sols_data.items():
  168. if isinstance(value, str):
  169. # Status test get from C code
  170. f.write("c_int %s;\n" % key)
  171. # Check if it is an array or a scalar
  172. elif isinstance(value, np.ndarray):
  173. if isinstance(value.flatten(order='F')[0], int):
  174. f.write("c_int * %s;\n" % key)
  175. elif isinstance(value.flatten(order='F')[0], float):
  176. f.write("c_float * %s;\n" % key)
  177. else:
  178. if isinstance(value, int):
  179. f.write("c_int %s;\n" % key)
  180. elif isinstance(value, float):
  181. f.write("c_float %s;\n" % key)
  182. f.write("} %s_sols_data;\n\n" % problem_name)
  183. # prototypes
  184. f.write("/* function prototypes */\n")
  185. f.write("OSQPData * generate_problem_%s();\n" % problem_name)
  186. f.write("void clean_problem_%s(OSQPData * data);\n" % problem_name)
  187. f.write("%s_sols_data * generate_problem_%s_sols_data();\n" % (problem_name, problem_name))
  188. f.write("void clean_problem_%s_sols_data(%s_sols_data * data);\n" % (problem_name, problem_name))
  189. f.write("\n\n")
  190. #
  191. # Generate QP problem data
  192. #
  193. f.write("/* function to generate QP problem data */\n")
  194. f.write("OSQPData * generate_problem_%s(){\n\n" % problem_name)
  195. # Initialize structure data
  196. f.write("OSQPData * data = (OSQPData *)c_malloc(sizeof(OSQPData));\n\n")
  197. # Write problem dimensions
  198. f.write("// Problem dimensions\n")
  199. write_int(f, n, "n", "data")
  200. write_int(f, m, "m", "data")
  201. f.write("\n")
  202. # Write problem vectors
  203. f.write("// Problem vectors\n")
  204. write_vec_float(f, l, "l", "data")
  205. write_vec_float(f, u, "u", "data")
  206. write_vec_float(f, q, "q", "data")
  207. f.write("\n")
  208. # Write matrix A
  209. write_mat_sparse(f, A, "A", "data")
  210. write_mat_sparse(f, P, "P", "data")
  211. # Return data and end function
  212. f.write("return data;\n\n")
  213. f.write("}\n\n")
  214. #
  215. # Generate QP problem data
  216. #
  217. f.write("/* function to clean problem data structure */\n")
  218. f.write("void clean_problem_%s(OSQPData * data){\n\n" % problem_name)
  219. # Free vectors
  220. f.write("// Clean vectors\n")
  221. clean_vec(f, "l", "data")
  222. clean_vec(f, "u", "data")
  223. clean_vec(f, "q", "data")
  224. f.write("\n")
  225. # Free matrices
  226. f.write("//Clean Matrices\n")
  227. clean_mat(f, "A", "data")
  228. clean_mat(f, "P", "data")
  229. f.write("\n")
  230. f.write("c_free(data);\n\n")
  231. f.write("}\n\n")
  232. #
  233. # Generate additional problem data for solutions
  234. #
  235. f.write("/* function to define solutions and additional data struct */\n")
  236. f.write("%s_sols_data * generate_problem_%s_sols_data(){\n\n" % (problem_name, problem_name))
  237. # Initialize structure data
  238. f.write("%s_sols_data * data = (%s_sols_data *)c_malloc(sizeof(%s_sols_data));\n\n" % (problem_name, problem_name, problem_name))
  239. # Generate further data and solutions
  240. for key, value in sols_data.items():
  241. if isinstance(value, str):
  242. # Status test get from C code
  243. if value == 'optimal':
  244. f.write("data->%s = %s;\n" % (key, 'OSQP_SOLVED'))
  245. elif value == 'optimal_inaccurate':
  246. f.write("data->%s = %s;\n" % (key, 'OSQP_SOLVED_INACCURATE'))
  247. elif value == 'primal_infeasible':
  248. f.write("data->%s = %s;\n" % (key, 'OSQP_PRIMAL_INFEASIBLE'))
  249. elif value == 'primal_infeasible_inaccurate':
  250. f.write("data->%s = %s;\n" %
  251. (key, 'OSQP_PRIMAL_INFEASIBLE_INACCURATE'))
  252. elif value == 'dual_infeasible':
  253. f.write("data->%s = %s;\n" % (key, 'OSQP_DUAL_INFEASIBLE'))
  254. elif value == 'dual_infeasible_inaccurate':
  255. f.write("data->%s = %s;\n" % (key, 'OSQP_DUAL_INFEASIBLE_INACCURATE'))
  256. # Check if it is an array or a scalar
  257. if type(value) is np.ndarray:
  258. if isinstance(value.flatten(order='F')[0], int):
  259. write_vec_int(f, value.flatten(order='F'), key, "data")
  260. elif isinstance(value.flatten(order='F')[0], float):
  261. write_vec_float(f, value.flatten(order='F'), key, "data")
  262. else:
  263. if isinstance(value, int):
  264. write_int(f, value, key, "data")
  265. elif isinstance(value, float):
  266. write_float(f, value, key, "data")
  267. # Return data and end function
  268. f.write("\nreturn data;\n\n")
  269. f.write("}\n\n")
  270. #
  271. # Clean additional problem data for solutions
  272. #
  273. f.write("/* function to clean solutions and additional data struct */\n")
  274. f.write("void clean_problem_%s_sols_data(%s_sols_data * data){\n\n" % (problem_name, problem_name))
  275. # Generate further data and solutions
  276. for key, value in sols_data.items():
  277. # Check if it is an array or a scalar
  278. if type(value) is np.ndarray:
  279. clean_vec(f, key, "data")
  280. f.write("\nc_free(data);\n\n")
  281. f.write("}\n\n")
  282. f.write("#endif\n")
  283. f.close()
  284. def generate_data(problem_name, sols_data):
  285. """
  286. Generate test data vectors.
  287. The additional structure sols_data defines the additional vectors/scalars
  288. we need to perform the tests
  289. """
  290. #
  291. # GENERATE HEADER FILE
  292. #
  293. f = open(problem_name + "/data.h", "w")
  294. # Add definition check
  295. f.write("#ifndef " + problem_name.upper() + "_DATA_H\n")
  296. f.write("#define " + problem_name.upper() + "_DATA_H\n")
  297. # Add Includes
  298. f.write("#include \"osqp.h\"\n")
  299. f.write("\n\n")
  300. #
  301. # Create additional data structure
  302. #
  303. f.write("/* create data and solutions structure */\n")
  304. f.write("typedef struct {\n")
  305. # Generate further data and solutions
  306. for key, value in sols_data.items():
  307. if isinstance(value, str):
  308. # Status test get from C code
  309. f.write("c_int %s;\n" % key)
  310. # Check if it is an array or a scalar
  311. elif sparse.issparse(value): # Sparse matrix
  312. f.write("csc * %s;\n" % key)
  313. elif isinstance(value, np.ndarray):
  314. if isinstance(value.flatten(order='F')[0], int):
  315. f.write("c_int * %s;\n" % key)
  316. elif isinstance(value.flatten(order='F')[0], float):
  317. f.write("c_float * %s;\n" % key)
  318. else:
  319. if isinstance(value, int):
  320. f.write("c_int %s;\n" % key)
  321. elif isinstance(value, float):
  322. f.write("c_float %s;\n" % key)
  323. f.write("} %s_sols_data;\n\n" % problem_name)
  324. # prototypes
  325. f.write("/* function prototypes */\n")
  326. f.write("%s_sols_data * generate_problem_%s_sols_data();\n" % (problem_name, problem_name))
  327. f.write("void clean_problem_%s_sols_data(%s_sols_data * data);\n" % (problem_name, problem_name))
  328. f.write("\n\n")
  329. #
  330. # Generate additional problem data for solutions
  331. #
  332. f.write("/* function to define problem data */\n")
  333. f.write("%s_sols_data * generate_problem_%s_sols_data(){\n\n" % (problem_name, problem_name))
  334. # Initialize structure data
  335. f.write("%s_sols_data * data = (%s_sols_data *)c_malloc(sizeof(%s_sols_data));\n\n" % (problem_name, problem_name, problem_name))
  336. # Generate further data and solutions
  337. for key, value in sols_data.items():
  338. if isinstance(value, str):
  339. # Status test get from C code
  340. if value == 'optimal':
  341. f.write("data->%s = %s;\n" % (key, 'OSQP_SOLVED'))
  342. elif value == 'primal_infeasible':
  343. f.write("data->%s = %s;\n" % (key, 'OSQP_PRIMAL_INFEASIBLE'))
  344. elif value == 'dual_infeasible':
  345. f.write("data->%s = %s;\n" % (key, 'OSQP_DUAL_INFEASIBLE'))
  346. # Check if it is an array or a scalar
  347. elif sparse.issparse(value): # Sparse matrix
  348. write_mat_sparse(f, value, key, "data")
  349. elif type(value) is np.ndarray:
  350. if isinstance(value.flatten(order='F')[0], int):
  351. write_vec_int(f, value.flatten(order='F'), key, "data")
  352. elif isinstance(value.flatten(order='F')[0], float):
  353. write_vec_float(f, value.flatten(order='F'), key, "data")
  354. else:
  355. if isinstance(value, int):
  356. write_int(f, value, key, "data")
  357. elif isinstance(value, float):
  358. write_float(f, value, key, "data")
  359. # Return data and end function
  360. f.write("\nreturn data;\n\n")
  361. f.write("}\n\n")
  362. #
  363. # Clean data
  364. #
  365. f.write("/* function to clean data struct */\n")
  366. f.write("void clean_problem_%s_sols_data(%s_sols_data * data){\n\n" % (problem_name, problem_name))
  367. # Generate further data and solutions
  368. for key, value in sols_data.items():
  369. # Check if it is an array or a scalar
  370. if sparse.issparse(value): # Sparse matrix
  371. clean_mat(f, key, "data")
  372. elif type(value) is np.ndarray:
  373. clean_vec(f, key, "data")
  374. f.write("\nc_free(data);\n\n")
  375. f.write("}\n\n")
  376. f.write("#endif\n")
  377. f.close()