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