osqp 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a723c2590dd5dea89dd98525009db82f6e3bf740e7a47032a822e23bab790eb0
4
- data.tar.gz: 4515ead926d38e72843c8193ec67f072d24189108ddcc538ad0c021ccae4c35c
3
+ metadata.gz: 1b2906722de1daf0e3f2441edee63d8cec2452012cd9fa91634f37418e66f6fc
4
+ data.tar.gz: ee0cf11cc8ea7d1676843699265ad27623d237940c756fdb9c49c8e404137b37
5
5
  SHA512:
6
- metadata.gz: ac250fdfa3927134aff7d609456382d3bd9d79e1252e42a2adeba7d79e33db4361c504aa06701bea0cf0fa7d92a843726115af0fcba93734cb619af0883b1068
7
- data.tar.gz: 84a1e39d8bbd810e49a0c2ab5dafd897208d87476dbfede8a292482cd74ee078d2e88b11424100b8ae72c6bdfb6c7dc76301ae5d40f513e575d6dea1bb4d851e
6
+ metadata.gz: 835c7ac35d791e794b7da902a1a248696860ed09d05e8831d0d4b6c54615758f6d930443e1f4b4f21d0b9d10453882653db60e2bda46b107d342a3cda304e889
7
+ data.tar.gz: 027ec047ca635e1b9f36377921110444a90226dfa85ebeac44ccab5b1945aac2d37f487f7725ba5604f264551f852392855e087c84afd1c39cd7790278fa3c68
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.2.1 (2022-07-05)
2
+
3
+ - Added `Matrix` class
4
+
1
5
  ## 0.2.0 (2022-06-12)
2
6
 
3
7
  - Added ARM shared library for Linux
data/README.md CHANGED
@@ -17,9 +17,9 @@ gem "osqp"
17
17
  Prep the problem - here’s how it should be [setup](https://osqp.org/docs/examples/setup-and-solve.html)
18
18
 
19
19
  ```ruby
20
- p = [[4, 1], [0, 2]]
20
+ p = OSQP::Matrix.from_dense([[4, 1], [0, 2]])
21
21
  q = [1, 1]
22
- a = [[1, 1], [1, 0], [0, 1]]
22
+ a = OSQP::Matrix.from_dense([[1, 1], [1, 0], [0, 1]])
23
23
  l = [1, 0, 0]
24
24
  u = [1, 0.7, 0.7]
25
25
  ```
@@ -41,16 +41,26 @@ solver.warm_start(x, y)
41
41
 
42
42
  ## Data
43
43
 
44
- Arrays and matrices can be Ruby arrays
44
+ Matrices can be a sparse matrix
45
45
 
46
46
  ```ruby
47
- [[1, 2, 3], [4, 5, 6]]
47
+ a = OSQP::Matrix.new(3, 2)
48
+ a[0, 0] = 1
49
+ a[1, 0] = 2
50
+ # or
51
+ OSQP::Matrix.from_dense([[1, 0], [2, 0], [0, 0]])
52
+ ```
53
+
54
+ Arrays can be Ruby arrays
55
+
56
+ ```ruby
57
+ [1, 2, 3]
48
58
  ```
49
59
 
50
60
  Or Numo arrays
51
61
 
52
62
  ```ruby
53
- Numo::NArray.cast([[1, 2, 3], [4, 5, 6]])
63
+ Numo::NArray.cast([1, 2, 3])
54
64
  ```
55
65
 
56
66
  ## Resources
@@ -0,0 +1,64 @@
1
+ module OSQP
2
+ class Matrix
3
+ attr_reader :m, :n
4
+
5
+ def initialize(m, n)
6
+ @m = m
7
+ @n = n
8
+ @data = {}
9
+ end
10
+
11
+ def []=(row_index, column_index, value)
12
+ raise IndexError, "row index out of bounds" if row_index < 0 || row_index >= @m
13
+ raise IndexError, "column index out of bounds" if column_index < 0 || column_index >= @n
14
+ if value == 0
15
+ (@data[column_index] ||= {}).delete(row_index)
16
+ else
17
+ (@data[column_index] ||= {})[row_index] = value
18
+ end
19
+ end
20
+
21
+ def to_ptr
22
+ cx = []
23
+ ci = []
24
+ cp = []
25
+
26
+ # CSC format
27
+ # https://www.gormanalysis.com/blog/sparse-matrix-storage-formats/
28
+ cp << 0
29
+ n.times do |j|
30
+ (@data[j] || {}).sort_by { |k, v| k }.each do |k, v|
31
+ cx << v
32
+ ci << k
33
+ end
34
+ # cumulative column values
35
+ cp << cx.size
36
+ end
37
+
38
+ nnz = cx.size
39
+ cx = Utils.float_array(cx)
40
+ ci = Utils.int_array(ci)
41
+ cp = Utils.int_array(cp)
42
+
43
+ ptr = FFI.csc_matrix(m, n, nnz, cx, ci, cp)
44
+ # save refs
45
+ ptr.instance_variable_set(:@osqp_refs, [cx, ci, cp])
46
+ ptr
47
+ end
48
+
49
+ def self.from_dense(data)
50
+ data = data.to_a
51
+ m = data.size
52
+ n = m > 0 ? data.first.size : 0
53
+
54
+ mtx = Matrix.new(m, n)
55
+ data.each_with_index do |row, i|
56
+ raise ArgumentError, "row has different number of columns" if row.size != n
57
+ row.each_with_index do |v, j|
58
+ mtx[i, j] = v if v != 0
59
+ end
60
+ end
61
+ mtx
62
+ end
63
+ end
64
+ end
data/lib/osqp/solver.rb CHANGED
@@ -10,16 +10,15 @@ module OSQP
10
10
 
11
11
  # data
12
12
  # do not assign directly to struct to keep refs
13
- m, n = shape(a)
14
13
  p = csc_matrix(p, upper: true)
15
- q = float_array(q)
14
+ q = Utils.float_array(q)
16
15
  a = csc_matrix(a)
17
- l = float_array(l)
18
- u = float_array(u)
16
+ l = Utils.float_array(l)
17
+ u = Utils.float_array(u)
19
18
 
20
19
  data = FFI::Data.malloc
21
- data.n = n
22
- data.m = m
20
+ data.n = a.n
21
+ data.m = a.m
23
22
  data.p = p
24
23
  data.q = q
25
24
  data.a = a
@@ -74,11 +73,11 @@ module OSQP
74
73
  raise Error, "Expected y to be size #{m}, got #{y.size}" if y && y.size != m
75
74
 
76
75
  if x && y
77
- check_result FFI.osqp_warm_start(@work, float_array(x), float_array(y))
76
+ check_result FFI.osqp_warm_start(@work, Utils.float_array(x), Utils.float_array(y))
78
77
  elsif x
79
- check_result FFI.osqp_warm_start_x(@work, float_array(x))
78
+ check_result FFI.osqp_warm_start_x(@work, Utils.float_array(x))
80
79
  elsif y
81
- check_result FFI.osqp_warm_start_y(@work, float_array(y))
80
+ check_result FFI.osqp_warm_start_y(@work, Utils.float_array(y))
82
81
  else
83
82
  raise Error, "Must set x or y"
84
83
  end
@@ -114,16 +113,6 @@ module OSQP
114
113
  end
115
114
  end
116
115
 
117
- def float_array(arr)
118
- # OSQP float = double
119
- Fiddle::Pointer[arr.to_a.pack("d*")]
120
- end
121
-
122
- def int_array(arr)
123
- # OSQP int = long long
124
- Fiddle::Pointer[arr.to_a.pack("q*")]
125
- end
126
-
127
116
  def read_float_array(ptr, size)
128
117
  # OSQP float = double
129
118
  ptr[0, size * Fiddle::SIZEOF_DOUBLE].unpack("d*")
@@ -134,39 +123,18 @@ module OSQP
134
123
  char_ptr[0, idx].map(&:chr).join
135
124
  end
136
125
 
137
- # TODO add support sparse matrices
138
126
  def csc_matrix(mtx, upper: false)
139
- mtx = mtx.to_a
140
-
141
- m, n = shape(mtx)
142
-
143
- cx = []
144
- ci = []
145
- cp = []
146
-
147
- # CSC format
148
- # https://www.gormanalysis.com/blog/sparse-matrix-storage-formats/
149
- cp << 0
150
- n.times do |j|
151
- mtx.each_with_index do |row, i|
152
- if row[j] != 0 && (!upper || i <= j)
153
- cx << row[j]
154
- ci << i
127
+ mtx = Matrix.from_dense(mtx) unless mtx.is_a?(Matrix)
128
+
129
+ if upper
130
+ mtx.m.times do |i|
131
+ mtx.n.times do |j|
132
+ mtx[i, j] = 0 if i > j
155
133
  end
156
134
  end
157
- # cumulative column values
158
- cp << cx.size
159
135
  end
160
136
 
161
- nnz = cx.size
162
- cx = float_array(cx)
163
- ci = int_array(ci)
164
- cp = int_array(cp)
165
-
166
- ptr = FFI.csc_matrix(m, n, nnz, cx, ci, cp)
167
- # save refs
168
- ptr.instance_variable_set(:@osqp_refs, [cx, ci, cp])
169
- ptr
137
+ mtx
170
138
  end
171
139
 
172
140
  def dimensions
@@ -174,16 +142,6 @@ module OSQP
174
142
  [data.m, data.n]
175
143
  end
176
144
 
177
- def shape(a)
178
- if defined?(Matrix) && a.is_a?(Matrix)
179
- [a.row_count, a.column_count]
180
- elsif defined?(Numo::NArray) && a.is_a?(Numo::NArray)
181
- a.shape
182
- else
183
- [a.size, a.first.size]
184
- end
185
- end
186
-
187
145
  def create_settings(settings)
188
146
  set = FFI::Settings.malloc
189
147
  FFI.osqp_set_default_settings(set)
data/lib/osqp/utils.rb ADDED
@@ -0,0 +1,15 @@
1
+ module OSQP
2
+ module Utils
3
+ class << self
4
+ def float_array(arr)
5
+ # OSQP float = double
6
+ Fiddle::Pointer[arr.to_a.pack("d*")]
7
+ end
8
+
9
+ def int_array(arr)
10
+ # OSQP int = long long
11
+ Fiddle::Pointer[arr.to_a.pack("q*")]
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/osqp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module OSQP
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
data/lib/osqp.rb CHANGED
@@ -2,7 +2,9 @@
2
2
  require "fiddle/import"
3
3
 
4
4
  # modules
5
+ require "osqp/matrix"
5
6
  require "osqp/solver"
7
+ require "osqp/utils"
6
8
  require "osqp/version"
7
9
 
8
10
  module OSQP
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: osqp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-12 00:00:00.000000000 Z
11
+ date: 2022-07-05 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: andrew@ankane.org
@@ -22,7 +22,9 @@ files:
22
22
  - README.md
23
23
  - lib/osqp.rb
24
24
  - lib/osqp/ffi.rb
25
+ - lib/osqp/matrix.rb
25
26
  - lib/osqp/solver.rb
27
+ - lib/osqp/utils.rb
26
28
  - lib/osqp/version.rb
27
29
  - vendor/aarch64-linux/LICENSE-amd.txt
28
30
  - vendor/aarch64-linux/LICENSE-osqp.txt