lib/osqp/solver.rb in osqp-0.1.0 vs lib/osqp/solver.rb in osqp-0.1.1

- old
+ new

@@ -1,60 +1,47 @@ module OSQP class Solver def setup(p, q, a, l, u, **settings) # settings - set = FFI::Settings.malloc - FFI.osqp_set_default_settings(set) + set = create_settings(settings) - # hack for setting members with []= - # safer than send("#{k}=", v) - entity = set.to_ptr - settings.each do |k, v| - entity[k.to_s] = settings_value(v) - end - - m, n = shape(a) - # data + m, n = shape(a) data = FFI::Data.malloc data.n = n data.m = m - data.p = csc_matrix(p) + data.p = csc_matrix(p, upper: true) data.q = float_array(q) data.a = csc_matrix(a) data.l = float_array(l) data.u = float_array(u) # work - work = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP) - check_result FFI.osqp_setup(work.ref, data, set) - + work = FFI::Workspace.malloc + check_result FFI.osqp_setup(work.to_ptr.ref, data, set) @work = work end def solve(*args, **settings) setup(*args, **settings) if args.any? || settings.any? check_result FFI.osqp_solve(@work) - workspace = FFI::Workspace.new(@work) # data - data = FFI::Data.new(workspace.data) - x = read_float_array(workspace.x, data.n) - y = read_float_array(workspace.y, data.m) + data = FFI::Data.new(@work.data) + x = read_float_array(@work.x, data.n) + y = read_float_array(@work.y, data.m) # info - info = FFI::Info.new(workspace.info) - status = info.status - idx = status.index { |v| v == 0 } + info = FFI::Info.new(@work.info) # TODO prim_inf_cert and dual_inf_cert { x: x, y: y, iter: info.iter, - status: status[0, idx].map(&:chr).join, + status: read_string(info.status), status_val: info.status_val, status_polish: info.status_polish, obj_val: info.obj_val, pri_res: info.pri_res, dua_res: info.dua_res, @@ -68,14 +55,13 @@ } end def warm_start(x, y) # check dimensions - workspace = FFI::Workspace.new(@work) - data = FFI::Data.new(workspace.data) - raise Error, "Expected x to be size #{data.n}, got #{x.size}" if x && x.size != data.n - raise Error, "Expected y to be size #{data.m}, got #{y.size}" if y && y.size != data.m + m, n = dimensions + raise Error, "Expected x to be size #{n}, got #{x.size}" if x && x.size != n + raise Error, "Expected y to be size #{m}, got #{y.size}" if y && y.size != m if x && y check_result FFI.osqp_warm_start(@work, float_array(x), float_array(y)) elsif x check_result FFI.osqp_warm_start_x(@work, float_array(x)) @@ -129,13 +115,17 @@ def read_float_array(ptr, size) # OSQP float = double ptr[0, size * Fiddle::SIZEOF_DOUBLE].unpack("d*") end + def read_string(char_ptr) + idx = char_ptr.index { |v| v == 0 } + char_ptr[0, idx].map(&:chr).join + end + # TODO add support sparse matrices - # TODO convert to upper triangle like Python - def csc_matrix(mtx) + def csc_matrix(mtx, upper: false) mtx = mtx.to_a m, n = shape(mtx) cx = [] @@ -143,15 +133,15 @@ cp = [] # CSC format # https://www.gormanalysis.com/blog/sparse-matrix-storage-formats/ cp << 0 - n.times do |i| - mtx.each_with_index do |row, j| - if row[i] != 0 - cx << row[i] - ci << j + n.times do |j| + mtx.each_with_index do |row, i| + if row[j] != 0 && (!upper || i <= j) + cx << row[j] + ci << i end end # cumulative column values cp << cx.size end @@ -162,17 +152,36 @@ cp = int_array(cp) FFI.csc_matrix(m, n, nnz, cx, ci, cp) end + def dimensions + data = FFI::Data.new(@work.data) + [data.m, data.n] + end + def shape(a) if defined?(Matrix) && a.is_a?(Matrix) [a.row_count, a.column_count] elsif defined?(Numo::NArray) && a.is_a?(Numo::NArray) a.shape else [a.size, a.first.size] end + end + + def create_settings(settings) + set = FFI::Settings.malloc + FFI.osqp_set_default_settings(set) + + # hack for setting members with []= + # safer than send("#{k}=", v) + entity = set.to_ptr + settings.each do |k, v| + entity[k.to_s] = settings_value(v) + end + + set end # handle booleans def settings_value(v) case v