rbmetis 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fbae1487ebf509c40e38f3f8faf4215cf990aeb2
4
+ data.tar.gz: c72a1b49ba3d6b7546fafbd0cf8c6d212056d316
5
+ SHA512:
6
+ metadata.gz: e580d9355397280750f1643e5d112ffd8d175bd1a6f2787bfa8d0767110d8144c4b077c201425a377ad9eed35cc0277b223b4ce2c212450ccd940180bda8d2f4
7
+ data.tar.gz: 17def092502f7ec8cd3a55dce354151b4bb7f4afea8e1b641fac5bb9f07283c0b093385ef4a6b94bcb160549419c4f06f710097bfe4c9dbd9f00b7517f5a9369
data/.gitignore ADDED
@@ -0,0 +1,32 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .config
19
+ *~
20
+ */*~
21
+ */*/*~
22
+ *.bak
23
+ */*.bak
24
+ */*/*.bak
25
+ .#*
26
+ */.#*
27
+ */*/.#*
28
+ rhosts
29
+ spec/*/*.dat
30
+ spec/*/*.csv
31
+ ext/Makefile
32
+ lib/rbmetis/config.rb
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rbmetis.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2014 Masahiro TANAKA
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
data/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # RbMetis
2
+
3
+ FFI wrapper of [METIS graph partitioning library](http://glaros.dtc.umn.edu/gkhome/metis/metis/overview)
4
+
5
+ * [GitHub](https://github.com/masa16/rbmetis)
6
+ * [RubyGems](https://rubygems.org/gems/rbmetis)
7
+ * [Class Documentation](http://rubydoc.info/gems/rbmetis/frames/)
8
+
9
+ ## Requirement
10
+
11
+ * [METIS version 5.1.0](http://glaros.dtc.umn.edu/gkhome/metis/metis/overview)
12
+ * [Ruby-FFI](https://rubygems.org/gems/ffi)
13
+
14
+ ## Installation
15
+
16
+ Install from RubyGems:
17
+
18
+ $ gem install rbmetis
19
+
20
+ Or download source tree from [releases](https://github.com/masa16/rbmetis/releases),
21
+ cd to tree top and run:
22
+
23
+ $ ruby setup.rb
24
+
25
+ ### Installation Option
26
+
27
+ * Required METIS files:
28
+ * C header: metis.h
29
+ * Library: libmetis.so or libmetis.a
30
+
31
+ * option for extconf.rb
32
+
33
+ --with-metis-dir=path
34
+ --with-metis-include=path
35
+ --with-metis-lib=path
36
+
37
+ * How to pass option to extconf.rb
38
+
39
+ $ gem install rbmetis -- --with-metis-dir=/opt/metis
40
+ $ ruby setup.rb -- --with-metis-dir=/opt/metis
41
+
42
+ ## Usage
43
+
44
+ Loading RbMeits:
45
+
46
+ require 'rbmetis'
47
+
48
+ Currently implemented APIs:
49
+
50
+ RbMetis.part_graph_recursive(xadj, adjncy, npart, opts = {})
51
+ RbMetis.part_graph_kway(xadj, adjncy, npart, opts = {})
52
+ RbMetis.default_options
53
+
54
+ See also [RbMeits API document](http://rubydoc.info/gems/rbmetis/frames)
55
+ and [METIS manual](http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/manual.pdf)
56
+
57
+ ## Contributing
58
+
59
+ 1. Fork it ( https://github.com/[my-github-username]/rbmetis/fork )
60
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
61
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
62
+ 4. Push to the branch (`git push origin my-new-feature`)
63
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/ext/extconf.rb ADDED
@@ -0,0 +1,56 @@
1
+ require "mkmf"
2
+
3
+ # configure options:
4
+ # --with-metis-dir=path
5
+ # --with-metis-include=path
6
+ # --with-metis-lib=path
7
+
8
+ dir_config("metis")
9
+
10
+ libdirs = $LIBPATH.dup
11
+ libdirs |= %w[/usr/lib64 /usr/lib /usr/local/lib]
12
+ incdirs = $CPPFLAGS.scan(/(?<=^-I|\s-I)\S+/)
13
+ incdirs |= %w[/usr/include /usr/local/include]
14
+
15
+ libmetis = %w[libmetis.so libmetis.a]
16
+
17
+ libdirs.each do |dir|
18
+ libmetis.each do |name|
19
+ path = File.join(dir,name)
20
+ if File.exist?(path)
21
+ $libmetis = path
22
+ break
23
+ end
24
+ end
25
+ break if $libmetis
26
+ end
27
+
28
+ incdirs.each do |dir|
29
+ path = File.join(dir,'metis.h')
30
+ if File.exist?(path)
31
+ puts "reading #{path}"
32
+ open(path,'r').each do |line|
33
+ case line
34
+ when /^\s*#define\s+IDXTYPEWIDTH\s+(\d+)/
35
+ $idxtypewidth = $1.to_i
36
+ when /^\s*#define\s+REALTYPEWIDTH\s+(\d+)/
37
+ $realtypewidth = $1.to_i
38
+ end
39
+ end
40
+ break
41
+ end
42
+ end
43
+
44
+ conf_file = File.join("..","lib","rbmetis","config.rb")
45
+ print "creating #{conf_file}\n"
46
+ open(conf_file, "w") do |f|
47
+ f.puts <<EOL
48
+ module RbMetis
49
+ IDXTYPEWIDTH=#{$idxtypewidth}
50
+ REALTYPEWIDTH=#{$realtypewidth}
51
+ LIBMETIS='#{$libmetis}'
52
+ end
53
+ EOL
54
+ end
55
+
56
+ create_makefile("rbmetis")
data/lib/rbmetis.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'ffi'
2
+ require "rbmetis/version"
3
+ require "rbmetis/config"
4
+ require "rbmetis/lib"
5
+ require "rbmetis/main"
@@ -0,0 +1,135 @@
1
+ module RbMetis
2
+
3
+ # @visibility private
4
+ module Lib
5
+
6
+ extend FFI::Library
7
+ ffi_lib LIBMETIS
8
+ attach_function :METIS_PartGraphRecursive, [:pointer]*13, :int
9
+ attach_function :METIS_PartGraphKway, [:pointer]*13, :int
10
+ attach_function :METIS_SetDefaultOptions, [:pointer], :int
11
+
12
+ case IDXTYPEWIDTH
13
+ when 32
14
+ def alloc_idx_ary(ary)
15
+ a = FFI::MemoryPointer.new(:int32, ary.size)
16
+ a.write_array_of_int32(ary)
17
+ a
18
+ end
19
+ def alloc_idx(num)
20
+ a = FFI::MemoryPointer.new(:int32)
21
+ a.write_int32(num)
22
+ a
23
+ end
24
+ def read_idx_ary(a,n)
25
+ a.read_array_of_int32(n)
26
+ end
27
+ def read_idx(a)
28
+ a.read_int32
29
+ end
30
+ when 64
31
+ def alloc_idx_ary(ary)
32
+ a = FFI::MemoryPointer.new(:int64, ary.size)
33
+ a.write_array_of_int64(ary)
34
+ a
35
+ end
36
+ def alloc_idx(num)
37
+ a = FFI::MemoryPointer.new(:int64)
38
+ a.write_int64(num)
39
+ a
40
+ end
41
+ def read_idx_ary(a,n)
42
+ a.read_array_of_int64(n)
43
+ end
44
+ def read_idx(a)
45
+ a.read_int64
46
+ end
47
+ end
48
+ module_function :alloc_idx
49
+ module_function :alloc_idx_ary
50
+ module_function :read_idx
51
+ module_function :read_idx_ary
52
+
53
+ case REALTYPEWIDTH
54
+ when 32
55
+ def alloc_real_ary(ary)
56
+ a = FFI::MemoryPointer.new(4, ary.size)
57
+ a.put_array_of_float32(0, ary)
58
+ a
59
+ end
60
+ when 64
61
+ def alloc_real_ary(ary)
62
+ a = FFI::MemoryPointer.new(8, ary.size)
63
+ a.put_array_of_float64(0, ary)
64
+ a
65
+ end
66
+ end
67
+ module_function :alloc_real_ary
68
+
69
+ def check_idx_array(args,name,size=nil,sizename=nil)
70
+ arg = args[name]
71
+ if arg
72
+ if !(Array===arg)
73
+ raise ArgumentError, "#{name} must be an array"
74
+ end
75
+ if size && arg.size != size
76
+ raise ArgumentError, "the size of #{name} must be #{sizename||size}"
77
+ end
78
+ alloc_idx_ary(arg)
79
+ else
80
+ nil
81
+ end
82
+ end
83
+ module_function :check_idx_array
84
+
85
+ def check_real_array(args,name,size=nil,sizename=nil)
86
+ arg = args[name]
87
+ if arg
88
+ if !(Array===arg)
89
+ raise ArgumentError, "#{name} must be an array"
90
+ end
91
+ if size && arg.size != size
92
+ raise ArgumentError, "the size of #{name} must be #{sizename||size}"
93
+ end
94
+ alloc_real_ary(arg)
95
+ else
96
+ nil
97
+ end
98
+ end
99
+ module_function :check_real_array
100
+
101
+ def check_idx(args,name,default_value=nil)
102
+ arg = args[name]
103
+ if arg
104
+ if !(Integer===arg)
105
+ raise ArgumentError, "#{name} must be an integer"
106
+ end
107
+ alloc_idx(arg)
108
+ else
109
+ if default_value
110
+ alloc_idx(default_value)
111
+ else
112
+ nil
113
+ end
114
+ end
115
+ end
116
+ module_function :check_idx
117
+
118
+ def postprocess(retval)
119
+ case retval
120
+ when METIS_OK
121
+ "that the function returned normally."
122
+ when METIS_ERROR_INPUT
123
+ raise MetisError, "input error"
124
+ when METIS_ERROR_MEMORY
125
+ raise MetisError, "memory allocation error"
126
+ when METIS_ERROR
127
+ raise MetisError, "other type of error"
128
+ else
129
+ raise MetisError, "unknown return code"
130
+ end
131
+ end
132
+ module_function :postprocess
133
+
134
+ end
135
+ end
@@ -0,0 +1,259 @@
1
+ module RbMetis
2
+ class MetisError < StandardError
3
+ end
4
+
5
+ # Returned normally
6
+ METIS_OK = 1
7
+ # Returned due to erroneous inputs and/or options
8
+ METIS_ERROR_INPUT = -2
9
+ # Returned due to insufficient memory
10
+ METIS_ERROR_MEMORY = -3
11
+ # Some other errors
12
+ METIS_ERROR = -4
13
+
14
+ NOPTIONS = 40
15
+
16
+ #/*! Operation type codes */
17
+ OP_PMETIS,
18
+ OP_KMETIS,
19
+ OP_OMETIS = 0..2
20
+
21
+ #/*! Options codes (i.e., options[]) */
22
+ OPTION_PTYPE,
23
+ OPTION_OBJTYPE,
24
+ OPTION_CTYPE,
25
+ OPTION_IPTYPE,
26
+ OPTION_RTYPE,
27
+ OPTION_DBGLVL,
28
+ OPTION_NITER,
29
+ OPTION_NCUTS,
30
+ OPTION_SEED,
31
+ OPTION_NO2HOP,
32
+ OPTION_MINCONN,
33
+ OPTION_CONTIG,
34
+ OPTION_COMPRESS,
35
+ OPTION_CCORDER,
36
+ OPTION_PFACTOR,
37
+ OPTION_NSEPS,
38
+ OPTION_UFACTOR,
39
+ OPTION_NUMBERING,
40
+ #/* Used for command-line parameter purposes */
41
+ OPTION_HELP,
42
+ OPTION_TPWGTS,
43
+ OPTION_NCOMMON,
44
+ OPTION_NOOUTPUT,
45
+ OPTION_BALANCE,
46
+ OPTION_GTYPE,
47
+ OPTION_UBVEC = 0..25
48
+
49
+ # /*! Partitioning Schemes */
50
+ PTYPE_RB,
51
+ PTYPE_KWAY = 0..1
52
+
53
+ # /*! Graph types for meshes */
54
+ GTYPE_DUAL,
55
+ GTYPE_NODAL = 0..1
56
+
57
+ # /*! Coarsening Schemes */
58
+ CTYPE_RM,
59
+ CTYPE_SHEM = 0..1
60
+
61
+ # /*! Initial partitioning schemes */
62
+ IPTYPE_GROW,
63
+ IPTYPE_RANDOM,
64
+ IPTYPE_EDGE,
65
+ IPTYPE_NODE,
66
+ IPTYPE_METISRB = 0..4
67
+
68
+ # /*! Refinement schemes */
69
+ RTYPE_FM,
70
+ RTYPE_GREEDY,
71
+ RTYPE_SEP2SIDED,
72
+ RTYPE_SEP1SIDED = 0..3
73
+
74
+ # /*! Debug Levels */
75
+ DBG_INFO = 1
76
+ DBG_TIME = 2
77
+ DBG_COARSEN = 4
78
+ DBG_REFINE = 8
79
+ DBG_IPART = 16
80
+ DBG_MOVEINFO = 32
81
+ DBG_SEPINFO = 64
82
+ DBG_CONNINFO = 128
83
+ DBG_CONTIGINFO = 256
84
+ DBG_MEMORY = 2048
85
+
86
+ # /* Types of objectives */
87
+ OBJTYPE_CUT,
88
+ OBJTYPE_VOL,
89
+ OBJTYPE_NODE = 0..2
90
+
91
+ # @visibility private
92
+ module Lib
93
+ def part_graph_preprocess(xadj_arg, adjncy_arg, np, args={})
94
+ nv = xadj_arg.size - 1
95
+
96
+ # The number of vertices in the graph.
97
+ nvtxs = alloc_idx(nv)
98
+ # The adjacency structure of the graph as described in Section 5.5.
99
+ xadj = alloc_idx_ary(xadj_arg)
100
+ adjncy = alloc_idx_ary(adjncy_arg)
101
+ # The number of parts to partition the graph.
102
+ nparts = alloc_idx(np)
103
+
104
+ ncon = check_idx(args, :ncon, 1)
105
+ nc = read_idx(ncon)
106
+
107
+ # The weights of the vertices as described in Section 5.5.
108
+ vwgt = check_idx_array(args, :vwgt, nv*nc, "nvtxs*ncon")
109
+
110
+ # The number of balancing constraints. It should be at least 1.
111
+ ncon = alloc_idx(nc)
112
+
113
+ # The size of the vertices for computing the total communication
114
+ # volume as described in Section 5.7.
115
+ vsize = check_idx(args, :vsize, 1)
116
+
117
+ # The weights of the edges as described in Section 5.5.
118
+ adjwgt = check_idx_array(args, :adjwgt, adjncy_arg.size, "size of adjncy")
119
+
120
+ # This is an array of size nparts*ncon that specifies the desired
121
+ # weight for each partition and constraint.
122
+ # The target partition weight for the ith partition and jth
123
+ # constraint is specified at tpwgts[i*ncon+j]
124
+ # For each constraint, the sum of the tpwgts[] entries must be 1.0.
125
+ tpwgts = check_real_array(args, :tpwgts, np*nc, "nparts*ncon")
126
+
127
+ # This is an array of size ncon that specifies the allowed load
128
+ # imbalance tolerance for each constraint.
129
+ # For the ith partition and jth constraint the allowed weight is
130
+ # the ubvec[j]*tpwgts[i*ncon+j] fraction of the jth’s constraint
131
+ # total weight. The load imbalances must be greater than 1.0.
132
+ # A NULL value can be passed indicating that the load imbalance
133
+ # tolerance for each constraint should be 1.001 (for ncon=1) or
134
+ # 1.01 (for ncon>1).
135
+ ubvec = check_real_array(args, :ubvec, nc, "ncon")
136
+
137
+ # This is the array of options as described in Section 5.4.
138
+ options = check_idx_array(args, :options, 40)
139
+
140
+ # Upon successful completion, this variable stores the edge-cut or
141
+ # the total communication volume of the partitioning solution. The
142
+ # value returned depends on the partitioning’s objective
143
+ # function.
144
+ objval = alloc_idx(0)
145
+
146
+ # This is a vector of size nvtxs that upon successful completion
147
+ # stores the partition vector of the graph. The numbering of this
148
+ # vector starts from either 0 or 1, depending on the value of
149
+ # options[METIS OPTION NUMBERING].
150
+ part = alloc_idx_ary([0]*nv)
151
+
152
+ return [nvtxs, ncon, xadj, adjncy, vwgt, vsize, adjwgt, nparts,
153
+ tpwgts, ubvec, options, objval, part]
154
+ end
155
+ module_function :part_graph_preprocess
156
+ end
157
+
158
+ # partition a graph into k parts using multilevel recursive bisection.
159
+ # @overload part_graph_recursive(xadj, adjncy, npart, opts={})
160
+ # @param [Array] xadj adjacency structure of the graph
161
+ # @param [Array] adjncy adjacency structure of the graph
162
+ # @param [Integer] npart the number of partitions
163
+ # @option opts [Array] :vwgt (nil) The weights of the vertices.
164
+ # @option opts [Array] :ncon (1) The number of balancing
165
+ # constraints. It should be at least 1.
166
+ # @option opts [Array] :vsize (nil) The size of the vertices for
167
+ # computing the total communication volume.
168
+ # @option opts [Array] :adjwgt (nil) The weights of the edges.
169
+ # @option opts [Array] :tpwgts (nil) an array of size nparts*ncon
170
+ # that specifies the desired weight for each partition and
171
+ # constraint. The target partition weight for the ith partition
172
+ # and jth constraint is specified at tpwgts[i*ncon+j] For each
173
+ # constraint, the sum of the tpwgts[] entries must be 1.0.
174
+ # @option opts [Array] :ubvec (nil) an array of size ncon that
175
+ # specifies the allowed load imbalance tolerance for each
176
+ # constraint. For the ith partition and jth constraint the allowed
177
+ # weight is the ubvec[j]*tpwgts[i*ncon+j] fraction of the jth’s
178
+ # constraint total weight. The load imbalances must be greater than
179
+ # 1.0. A NULL value can be passed indicating that the load
180
+ # imbalance tolerance for each constraint should be 1.001 (for
181
+ # ncon=1) or 1.01 (for ncon>1).
182
+ # @option opts [Array] :options (nil) the array of options.
183
+ # The following options are valid for METIS PartGraphRecursive:
184
+ # METIS_OPTION_CTYPE, METIS_OPTION_IPTYPE, METIS_OPTION_RTYPE,
185
+ # METIS_OPTION_NO2HOP, METIS_OPTION_NCUTS, METIS_OPTION_NITER,
186
+ # METIS_OPTION_SEED, METIS_OPTION_UFACTOR, METIS_OPTION_NUMBERING,
187
+ # METIS_OPTION_DBGLVL
188
+ # @return [Array] an array that stores the partition of the graph.
189
+ # @raise [RbMetis::MetisError]
190
+ def part_graph_recursive(xadj, adjncy, npart, opts={})
191
+ # args = [ nvtxs, ncon, xadj, adjncy, vwgt, vsize, adjwgt, nparts,
192
+ # tpwgts, ubvec, options, objval, part ]
193
+ args = Lib.part_graph_preprocess(xadj, adjncy, npart, opts)
194
+ retval = Lib.METIS_PartGraphRecursive(*args)
195
+ Lib.postprocess(retval)
196
+ part = args.last
197
+ nv = xadj.size - 1
198
+ #p read_idx(objval)
199
+ return Lib.read_idx_ary(part,nv)
200
+ end
201
+ module_function :part_graph_recursive
202
+
203
+
204
+ # partition a graph into k parts using multilevel k-way partitioning.
205
+ # @overload part_graph_kway(xadj, adjncy, npart, opts={})
206
+ # @param [Array] xadj adjacency structure of the graph
207
+ # @param [Array] adjncy adjacency structure of the graph
208
+ # @param [Integer] npart the number of partitions
209
+ # @option opts [Array] :vwgt (nil) The weights of the vertices.
210
+ # @option opts [Array] :ncon (1) The number of balancing
211
+ # constraints. It should be at least 1.
212
+ # @option opts [Array] :vsize (nil) The size of the vertices for
213
+ # computing the total communication volume.
214
+ # @option opts [Array] :adjwgt (nil) The weights of the edges.
215
+ # @option opts [Array] :tpwgts (nil) an array of size nparts*ncon
216
+ # that specifies the desired weight for each partition and
217
+ # constraint. The target partition weight for the ith partition
218
+ # and jth constraint is specified at tpwgts[i*ncon+j] For each
219
+ # constraint, the sum of the tpwgts[] entries must be 1.0.
220
+ # @option opts [Array] :ubvec (nil) an array of size ncon that
221
+ # specifies the allowed load imbalance tolerance for each
222
+ # constraint. For the ith partition and jth constraint the allowed
223
+ # weight is the ubvec[j]*tpwgts[i*ncon+j] fraction of the jth’s
224
+ # constraint total weight. The load imbalances must be greater than
225
+ # 1.0. A NULL value can be passed indicating that the load
226
+ # imbalance tolerance for each constraint should be 1.001 (for
227
+ # ncon=1) or 1.01 (for ncon>1).
228
+ # @option opts [Array] :options (nil) the array of options.
229
+ # The following options are valid for METIS PartGraphKway:
230
+ # METIS_OPTION_OBJTYPE, METIS_OPTION_CTYPE, METIS_OPTION_IPTYPE,
231
+ # METIS_OPTION_RTYPE, METIS_OPTION_NO2HOP, METIS_OPTION_NCUTS,
232
+ # METIS_OPTION_NITER, METIS_OPTION_UFACTOR, METIS_OPTION_MINCONN,
233
+ # METIS_OPTION_CONTIG, METIS_OPTION_SEED, METIS_OPTION_NUMBERING,
234
+ # METIS_OPTION_DBGLVL
235
+ # @return [Array] an array that stores the partition of the graph.
236
+ # @raise [RbMetis::MetisError]
237
+ def part_graph_kway(xadj, adjncy, npart, opts={})
238
+ # args = [ nvtxs, ncon, xadj, adjncy, vwgt, vsize, adjwgt, nparts,
239
+ # tpwgts, ubvec, options, objval, part ]
240
+ args = Lib.part_graph_preprocess(xadj, adjncy, npart, opts)
241
+ retval = Lib.METIS_PartGraphKway(*args)
242
+ Lib.postprocess(retval)
243
+ part = args.last
244
+ nv = xadj.size - 1
245
+ #p read_idx(objval)
246
+ return Lib.read_idx_ary(part,nv)
247
+ end
248
+ module_function :part_graph_kway
249
+
250
+ # Initializes the options array into its default values.
251
+ # @return [Array] The array of options that will be initialized.
252
+ # It’s size is METIS_NOPTIONS.
253
+ def default_options
254
+ options = Lib.alloc_idx_ary([0]*40)
255
+ Lib.METIS_SetDefaultOptions(options)
256
+ Lib.read_idx_ary(options,40)
257
+ end
258
+ module_function :default_options
259
+ end