nysol-mining 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/mbopt.rb +522 -0
- data/bin/mburst.rb +716 -0
- data/bin/mgfeatures.rb +340 -0
- data/bin/mglmnet.rb +843 -0
- data/bin/mgnfeatures.rb +369 -0
- data/bin/mgpmetis.rb +449 -0
- data/bin/midxmine.rb +484 -0
- data/bin/mnb.rb +631 -0
- data/bin/mnetsimile.rb +572 -0
- data/bin/mnewman.rb +345 -0
- data/bin/msketchsort.rb +243 -0
- data/bin/msm.rb +172 -0
- data/ext/sketchsortrun/Main.cpp +161 -0
- data/ext/sketchsortrun/Main.hpp +24 -0
- data/ext/sketchsortrun/SketchSort.cpp +526 -0
- data/ext/sketchsortrun/SketchSort.hpp +138 -0
- data/ext/sketchsortrun/extconf.rb +26 -0
- data/ext/sketchsortrun/sketchsortrun.cpp +56 -0
- data/lib/nysol/mining.rb +24 -0
- metadata +89 -0
data/bin/mgnfeatures.rb
ADDED
@@ -0,0 +1,369 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
# 1.0 initial development: 2016/1/27
|
5
|
+
$version="1.0"
|
6
|
+
$revision="###VERSION###"
|
7
|
+
CMD="mgnfeatures.rb"
|
8
|
+
|
9
|
+
def help
|
10
|
+
|
11
|
+
STDERR.puts <<EOF
|
12
|
+
----------------------------
|
13
|
+
#{CMD} version #{$version}
|
14
|
+
----------------------------
|
15
|
+
概要) ノードの特徴量を計算
|
16
|
+
特徴) 以下のノードの特徴量を出力
|
17
|
+
degree : 各ノードの次数
|
18
|
+
cc : クラスタ係数
|
19
|
+
components : 連結成分 (連結成分をクラスタとする)
|
20
|
+
betweenness : 媒介中心性 (ある点が他の点間の最短経路に位置する程度)
|
21
|
+
closeness : 近接中心性 (他の点への最短経路の合計の逆数)
|
22
|
+
page_rank : 各ノードの重要度をpage_rankで計算
|
23
|
+
|
24
|
+
書式) #{CMD} I=|(ei= [ni=]) ef= [nf=] [ew=] [mode=] O= [-directed] [-normalize] [T=] [-verbose] [--help]
|
25
|
+
I= : 入力パス
|
26
|
+
: パス中の枝ファイルは.edge拡張子が前提
|
27
|
+
: パス中の点ファイルは.node拡張子が前提
|
28
|
+
ei= : 枝データファイル(I=とは一緒に指定できない)
|
29
|
+
ef= : 枝データ上の2つの節点項目名
|
30
|
+
ni= : 節点データファイル(I=とは一緒に指定できない)
|
31
|
+
nf= : 節点データ上の節点項目名(省略時は"node")
|
32
|
+
ew= : 枝ファイル上の重み項目名【省略時は全ての枝の重みを1と見なす】
|
33
|
+
mode= : in|out|all (-directedを指定した場合のみ有向。省略時は"all"。詳しくは詳細)を参照)
|
34
|
+
O= : 出力パス
|
35
|
+
-directed : 有向グラフ
|
36
|
+
-normalize : 基準化
|
37
|
+
|
38
|
+
その他
|
39
|
+
T= : ワークディレクトリ(default:/tmp)
|
40
|
+
-verbose : show the END messages of MCMD and R used in this command
|
41
|
+
--help : ヘルプの表示
|
42
|
+
|
43
|
+
必要なソフトウェア)
|
44
|
+
1) R
|
45
|
+
2) igraph package for R
|
46
|
+
|
47
|
+
詳細)
|
48
|
+
1.オプション一覧
|
49
|
+
| mode | 重み | 基準化
|
50
|
+
---------------------------------------------------------------------
|
51
|
+
degree |in,out,all | 無し | n-1で割る
|
52
|
+
cc | 無し | 有り | 無し
|
53
|
+
components | 無し | 無し | 無し
|
54
|
+
betweenness | 無し | 有り | 2B/(n^2-3n+2) [B:raw betweenness]
|
55
|
+
closeness |in,out,all | 有り | n-1で割る
|
56
|
+
page_rank | 無し | 有り | 無し
|
57
|
+
--------------------------------------------------------------------
|
58
|
+
modeは-directedを指定された場合に有効になる。
|
59
|
+
in:入枝が対象, out:出枝が対象, all: 両方対象を意味する
|
60
|
+
|
61
|
+
2. ccは-directedが指定されていても無視される。
|
62
|
+
3. componentsは-directedが指定された場合には強連結を求める。
|
63
|
+
4. pageRankは"prpack"を利用。
|
64
|
+
|
65
|
+
入力データ)
|
66
|
+
2つの節点からなる枝データ
|
67
|
+
|
68
|
+
入力データ)
|
69
|
+
節点ペアのCSVファイル(ファイル名はei=にて指定)
|
70
|
+
例)
|
71
|
+
$ cat data/dat1.edge
|
72
|
+
n1,n2
|
73
|
+
a,b
|
74
|
+
a,c
|
75
|
+
a,d
|
76
|
+
a,e
|
77
|
+
a,f
|
78
|
+
a,g
|
79
|
+
b,c
|
80
|
+
b,d
|
81
|
+
b,e
|
82
|
+
b,f
|
83
|
+
c,h
|
84
|
+
d,g
|
85
|
+
e,f
|
86
|
+
|
87
|
+
$ mgnfeatures.rb ei=data/dat1.edge ef=n1,n2 O=rsl01
|
88
|
+
$ cat rsl01/dat1.csv
|
89
|
+
node,degree,components,betweenness,closeness,page_rank
|
90
|
+
a,6,1,8.5,0.125,0.216364844231035
|
91
|
+
b,5,1,4,0.111111111111111,0.181335882714211
|
92
|
+
c,3,1,6,0.0909090909090909,0.126673483636635
|
93
|
+
d,3,1,0.5,0.0833333333333333,0.11508233404895
|
94
|
+
e,3,1,0,0.0833333333333333,0.111947143712761
|
95
|
+
f,3,1,0,0.0833333333333333,0.111947143712761
|
96
|
+
g,2,1,0,0.0769230769230769,0.0820083475799325
|
97
|
+
h,1,1,0,0.0588235294117647,0.0546408203637134
|
98
|
+
|
99
|
+
# Copyright(c) NYSOL 2014- All Rights Reserved.
|
100
|
+
EOF
|
101
|
+
exit
|
102
|
+
end
|
103
|
+
|
104
|
+
def ver()
|
105
|
+
$revision ="0" if $revision =~ /VERSION/
|
106
|
+
STDERR.puts "version #{$version} revision #{$revision}"
|
107
|
+
exit
|
108
|
+
end
|
109
|
+
|
110
|
+
help() if ARGV[0]=="--help" or ARGV.size <= 0
|
111
|
+
ver() if ARGV[0]=="--version"
|
112
|
+
|
113
|
+
require "rubygems"
|
114
|
+
require "nysol/mcmd"
|
115
|
+
|
116
|
+
# Rライブラリ実行可能確認
|
117
|
+
exit(1) unless(MCMD::chkRexe("igraph"))
|
118
|
+
|
119
|
+
####
|
120
|
+
# converting original graph file with text to one with integer
|
121
|
+
# output #{numFile} and #{mapFile}, then return the number of nodes of the graph
|
122
|
+
#
|
123
|
+
# ei ni xxnum xxmap
|
124
|
+
# v1,v2 v node%1,flag%0,num
|
125
|
+
# E,J A 0 3 A,0,0
|
126
|
+
# E,A B 0 4 B,0,1
|
127
|
+
# J,D C 0 6 D,0,2
|
128
|
+
# J,A D => 1 5 E,0,3
|
129
|
+
# J,H E 2 4 F,0,4
|
130
|
+
# D,H F 2 5 H,0,5
|
131
|
+
# D,F G 2 6 J,0,6
|
132
|
+
# H,F H 3 6 C,1,7
|
133
|
+
# A,F I 4 5 G,1,8
|
134
|
+
# B,H J 5 6 I,1,9
|
135
|
+
#
|
136
|
+
# return value is 10 (nodes)
|
137
|
+
# "flag" on xxmap: 0:nodes in "ei", 1:nodes only in "ni".
|
138
|
+
def g2pair(ni,nf,ei,ef1,ef2,ew,numFile,mapFile,weightFile)
|
139
|
+
#MCMD::msgLog("converting graph files into a pair of numbered nodes ...")
|
140
|
+
wf=MCMD::Mtemp.new
|
141
|
+
wf1=wf.file
|
142
|
+
wf2=wf.file
|
143
|
+
wf3=wf.file
|
144
|
+
|
145
|
+
system "mcut f=#{ef1}:node i=#{ei} | msetstr v=0 a=flag o=#{wf1}"
|
146
|
+
system "mcut f=#{ef2}:node i=#{ei} | msetstr v=0 a=flag o=#{wf2}"
|
147
|
+
system "mcut f=#{nf}:node i=#{ni} | msetstr v=1 a=flag o=#{wf3}" if nf
|
148
|
+
|
149
|
+
f=""
|
150
|
+
if nf
|
151
|
+
f << "mcat i=#{wf1},#{wf2},#{wf3} f=node,flag |"
|
152
|
+
f << "mbest k=node s=flag from=0 size=1 |"
|
153
|
+
else
|
154
|
+
f << "mcat i=#{wf1},#{wf2} f=node,flag |"
|
155
|
+
f << "muniq k=node |"
|
156
|
+
end
|
157
|
+
# isolated nodes are set to the end of position in mapping file.
|
158
|
+
# S= must start from 0 (but inside R vertex number will be added one)
|
159
|
+
f << "mnumber s=flag,node a=num S=0 o=#{mapFile}"
|
160
|
+
system(f)
|
161
|
+
|
162
|
+
f=""
|
163
|
+
f << "mcut f=#{ef1},#{ef2} i=#{ei} |"
|
164
|
+
f << "msortf f=#{ef1} |"
|
165
|
+
f << "mjoin k=#{ef1} K=node m=#{mapFile} f=num:num1 |"
|
166
|
+
f << "msortf f=#{ef2} |"
|
167
|
+
f << "mjoin k=#{ef2} K=node m=#{mapFile} f=num:num2 |"
|
168
|
+
f << "mcut f=num1,num2 |"
|
169
|
+
f << "mfsort f=num1,num2 |"
|
170
|
+
f << "msortf f=num1%n,num2%n -nfno | tr ',' ' ' >#{numFile}"
|
171
|
+
system(f)
|
172
|
+
|
173
|
+
nodeSize=MCMD::mrecount("i=#{mapFile}")
|
174
|
+
|
175
|
+
if ew
|
176
|
+
system "mcut f=#{ew} i=#{ei} o=#{weightFile}"
|
177
|
+
else
|
178
|
+
ew="weight"
|
179
|
+
system "msetstr v=1 a=#{ew} i=#{ei} |mcut f=#{ew} o=#{weightFile}"
|
180
|
+
end
|
181
|
+
|
182
|
+
return nodeSize
|
183
|
+
end
|
184
|
+
|
185
|
+
def genRscript(directed,norm,mode,eFile,wFile,ew,nodeSize,oFile,scpFile)
|
186
|
+
dir="FALSE"
|
187
|
+
dir="TRUE" if directed
|
188
|
+
normalize="FALSE"
|
189
|
+
normalize="TRUE" if norm
|
190
|
+
|
191
|
+
r_proc = <<EOF
|
192
|
+
library(igraph)
|
193
|
+
#### reading edge file
|
194
|
+
g=read.graph("#{eFile}",format="edgelist",directed="#{dir}",n="#{nodeSize}")
|
195
|
+
# reading weight file
|
196
|
+
w=read.csv("#{wFile}")
|
197
|
+
E(g)$weight=as.list(w$"#{ew}")
|
198
|
+
|
199
|
+
####
|
200
|
+
deg=degree(g,mode="#{mode}",normalized="#{normalize}")
|
201
|
+
### ew=がnullの場合はweight=1として扱っているので以下で問題ない
|
202
|
+
cc=transitivity(g,type="weight")
|
203
|
+
cls=components(g,mode="strong")
|
204
|
+
|
205
|
+
# -normalizeと-directedは一緒に指定できないため以下の処理を行う
|
206
|
+
if ("#{dir}"=="TRUE") {
|
207
|
+
norm2 = "FALSE"
|
208
|
+
betweenness=betweenness(g,directed="#{dir}",weight=E(g)$weight,normalized=norm2)
|
209
|
+
} else {
|
210
|
+
norm2 = "#{normalize}"
|
211
|
+
betweenness=betweenness(g,directed="#{dir}",weight=E(g)$weight,normalized=norm2)
|
212
|
+
}
|
213
|
+
|
214
|
+
closeness=closeness(g,weight=E(g)$weight,mode="#{mode}",normalized="#{normalize}")
|
215
|
+
pgrank=page.rank(g,weight=E(g)$weight,directed="#{dir}")$vector
|
216
|
+
|
217
|
+
dat=data.frame(
|
218
|
+
degree=deg,
|
219
|
+
cc=cc,
|
220
|
+
components=cls$membership,
|
221
|
+
betweenness=betweenness,
|
222
|
+
closeness=closeness,
|
223
|
+
page_rank=pgrank
|
224
|
+
)
|
225
|
+
|
226
|
+
write.csv(dat,file="#{oFile}",quote=FALSE,row.names=FALSE)
|
227
|
+
|
228
|
+
EOF
|
229
|
+
|
230
|
+
File.open(scpFile,"w"){|fpw|
|
231
|
+
fpw.write(r_proc)
|
232
|
+
}
|
233
|
+
end
|
234
|
+
|
235
|
+
#################################################################################################
|
236
|
+
#### Entry point
|
237
|
+
|
238
|
+
args=MCMD::Margs.new(ARGV,"I=,ei=,ef=,ni=,nf=,ew=,O=,mode=,-directed,-normalize,-verbose,mp=","ef=,O=")
|
239
|
+
|
240
|
+
|
241
|
+
# suppress the end message of MCMD
|
242
|
+
ENV["KG_VerboseLevel"]="2" unless args.bool("-verbose")
|
243
|
+
|
244
|
+
# work file path
|
245
|
+
if args.str("T=")!=nil then
|
246
|
+
ENV["KG_TmpPath"] = args.str("T=").sub(/\/$/,"")
|
247
|
+
end
|
248
|
+
|
249
|
+
# setting variables for edge file(s) and its field name
|
250
|
+
iPath = args.file("I=","r")
|
251
|
+
oPath = args.file("O=","w")
|
252
|
+
|
253
|
+
# 枝データの扱い
|
254
|
+
edgeFiles=nil
|
255
|
+
ef1 =nil
|
256
|
+
ef2 =nil
|
257
|
+
if iPath then
|
258
|
+
edgeFiles = Dir["#{iPath}/*.edge"]
|
259
|
+
if edgeFiles.size==0 then
|
260
|
+
raise "#ERROR# no edge file is found matching with #{iPath}/*.edge"
|
261
|
+
end
|
262
|
+
ef = args.field("ef=", edgeFiles[0])
|
263
|
+
ef1,ef2=ef["names"]
|
264
|
+
else
|
265
|
+
edgeFiles = args.file("ei=","r").split # edge file name
|
266
|
+
unless edgeFiles
|
267
|
+
raise "#ERROR# ei= or I= is mandatory"
|
268
|
+
end
|
269
|
+
ef = args.field("ef=", edgeFiles[0])
|
270
|
+
ef1,ef2=ef["names"]
|
271
|
+
end
|
272
|
+
|
273
|
+
# ---- 枝重み
|
274
|
+
ew = args.field("ew=", edgeFiles[0], nil, 1,1)
|
275
|
+
ew = ew["names"][0] if ew
|
276
|
+
|
277
|
+
# 節点データの扱い
|
278
|
+
# if nf= is not specified, only edge files are used for generating a graph.
|
279
|
+
ni=nil
|
280
|
+
nf=nil
|
281
|
+
if iPath then
|
282
|
+
nodeFile0=edgeFiles[0].sub(/\.edge/,".node")
|
283
|
+
if File.exists?(nodeFile0)
|
284
|
+
nf = args.field("nf=", nodeFile0)
|
285
|
+
if nf
|
286
|
+
nf=nf["names"][0]
|
287
|
+
end
|
288
|
+
else
|
289
|
+
nf = args.str("nf=")
|
290
|
+
if nf then
|
291
|
+
raise "#ERROR# nf= is specified, but no node file is found matching with #{iPath}/*.node"
|
292
|
+
end
|
293
|
+
end
|
294
|
+
else
|
295
|
+
ni = args.file("ni=","r") # node file name
|
296
|
+
if ni
|
297
|
+
nf = args.field("nf=", ni)
|
298
|
+
unless nf
|
299
|
+
raise "#ERROR# nf= is mandatory, when ni= is specified"
|
300
|
+
end
|
301
|
+
nf=nf["names"][0]
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
mode=args.str("mode=","all")
|
306
|
+
unless mode=="all" or mode=="in" or mode=="out"
|
307
|
+
raise "#ERROR# mode= can specify all|in|out"
|
308
|
+
end
|
309
|
+
|
310
|
+
directed=args.bool("-directed")
|
311
|
+
norm=args.bool("-normalize")
|
312
|
+
MP=args.int("mp=",4)
|
313
|
+
|
314
|
+
MCMD::mkDir(oPath)
|
315
|
+
|
316
|
+
edgeFiles.meach(MP){|edgeFile|
|
317
|
+
#MCMD::msgLog("START fearture extraction: #{edgeFile}")
|
318
|
+
|
319
|
+
baseName=edgeFile.sub(/\.edge$/,"")
|
320
|
+
name=baseName.sub(/^.*\//,"")
|
321
|
+
|
322
|
+
if ni
|
323
|
+
nodeFile=ni
|
324
|
+
else
|
325
|
+
nodeFile=edgeFile.sub(/\.edge$/,".node")
|
326
|
+
end
|
327
|
+
|
328
|
+
# convert the original graph to one igraph can handle
|
329
|
+
wf=MCMD::Mtemp.new
|
330
|
+
xxnum=wf.file
|
331
|
+
xxmap=wf.file
|
332
|
+
xxout=wf.file
|
333
|
+
xxscp=wf.file
|
334
|
+
xxweight=wf.file
|
335
|
+
|
336
|
+
nodeSize=g2pair(nodeFile,nf,edgeFile,ef1,ef2,ew,xxnum,xxmap,xxweight)
|
337
|
+
=begin
|
338
|
+
system "cat #{xxnum}"
|
339
|
+
system "cat #{xxmap}"
|
340
|
+
puts "nodeSize=#{nodeSize}"
|
341
|
+
=end
|
342
|
+
|
343
|
+
# generate R script, and run
|
344
|
+
genRscript(directed, norm, mode, xxnum, xxweight, ew, nodeSize, xxout, xxscp)
|
345
|
+
if args.bool("-verbose")
|
346
|
+
system "R --vanilla -q < #{xxscp}"
|
347
|
+
else
|
348
|
+
|
349
|
+
# system "R --vanilla -q < #{xxscp} &>/dev/null"
|
350
|
+
system "R --vanilla -q --slave < #{xxscp} 2>/dev/null "
|
351
|
+
#system "Rscript #{xxscp}"
|
352
|
+
|
353
|
+
end
|
354
|
+
|
355
|
+
# store the result
|
356
|
+
f=""
|
357
|
+
f << "mnumber -q S=0 a=num i=#{xxout} |"
|
358
|
+
f << "mjoin k=num f=node m=#{xxmap} |"
|
359
|
+
if nf
|
360
|
+
f << "mcut f=node:#{nf},degree,cc,components,betweenness,closeness,page_rank o=#{oPath}/#{name}.csv"
|
361
|
+
else
|
362
|
+
f << "mcut f=node,degree,cc,components,betweenness,closeness,page_rank o=#{oPath}/#{name}.csv"
|
363
|
+
end
|
364
|
+
system(f)
|
365
|
+
|
366
|
+
}
|
367
|
+
|
368
|
+
# end message
|
369
|
+
MCMD::endLog(args.cmdline)
|
data/bin/mgpmetis.rb
ADDED
@@ -0,0 +1,449 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "nysol/mcmd"
|
6
|
+
|
7
|
+
# 1.1: change from ufactor= to balance=, bug fix for isolated nodes
|
8
|
+
$version="1.1"
|
9
|
+
$revision="###VERSION###"
|
10
|
+
|
11
|
+
def help
|
12
|
+
|
13
|
+
STDERR.puts <<EOF
|
14
|
+
----------------------------
|
15
|
+
mgpmetis.rb version #{$version}
|
16
|
+
----------------------------
|
17
|
+
概要) METISを利用したグラフ分割(クラスタリング)
|
18
|
+
特徴) 1) 節点数をできるだけ同じようにして、枝のカット数を最小化するように分割する。
|
19
|
+
2) 節点と枝に重みを与えることも可能。
|
20
|
+
3) 一つの節点が複数のクラスタに属することはない(ハードクラスタリング)。
|
21
|
+
4) 内部ではgpmetisコマンドをコールしている。
|
22
|
+
用法) mgpmetis.rb kway= [ptype=rb|kway] ei= [ef=] [ew=] [ni=] [nf=] [nw=] [o=]
|
23
|
+
[balance=] [ncuts=] [dat=] [map=] [-noexe] [--help]
|
24
|
+
|
25
|
+
ファイル関連
|
26
|
+
ei= : 枝ファイル名(節点ペア)【必須】
|
27
|
+
ef= : 枝ファイル上の節点ペア項目名(2項目のみ)【デフォルト:"node1,node2"】
|
28
|
+
ew= : 枝ファイル上の重み項目名(1項目のみ)【オプション:省略時は全ての枝の重みを1と見なす】
|
29
|
+
: 重みは整数で指定しなければならない。
|
30
|
+
ni= : 節点ファイル名【オプション*注1】
|
31
|
+
nf= : 節点ファイル上の節点項目名(1項目のみ)【デフォルト:"node"】
|
32
|
+
nw= : 節点ファイル上の重み項目名(複数項目指定可)【オプション:省略時は全ての重みを1と見なす】
|
33
|
+
: 重みは整数で指定しなければならない。
|
34
|
+
o= : 出力ファイル名【オプション:defaultは標準出力】
|
35
|
+
|
36
|
+
動作の制御関連
|
37
|
+
kway= : 分割数【必須】
|
38
|
+
ptype= : 分割アルゴリズム【デフォルト:kway】
|
39
|
+
balance= : 分割アンバランスファクタ【デフォルト: ptype=rbの時は1.001、ptype=kwayの時は1.03】
|
40
|
+
ncuts= : 分割フェーズで、初期値を変えて試行する回数【オプション:default=1】
|
41
|
+
seed= : 乱数の種(0以上の整数)【オプション:default=-1(時間依存)】
|
42
|
+
|
43
|
+
gpmetis用のデータ生成
|
44
|
+
dat= : 指定されたファイルにgpmetisコマンド用のデータを出力する。
|
45
|
+
map= : 指定されたファイルにgpmetisコマンド用の節点番号とi=上の節点名のマッピングデータを出力する。
|
46
|
+
-noexe : 内部でgpmetisを実行しない。dat=,map=の出力だけが必要な場合に指定する。
|
47
|
+
|
48
|
+
その他
|
49
|
+
--help : ヘルプの表示
|
50
|
+
|
51
|
+
注1:節点ファイルは、孤立節点(一つの節点からのみ構成される部分グラフ)がある場合、
|
52
|
+
もしくは節点の重みを与えたいときのみ指定すればよい。
|
53
|
+
注2:節点もしくは枝の重みを与えない時は、内部的に全ての重みを1として計算する。
|
54
|
+
|
55
|
+
必要なソフトウェア)
|
56
|
+
gpmetis(metis-5.1.0)
|
57
|
+
インストールは以下のURLより行う。
|
58
|
+
http://glaros.dtc.umn.edu/gkhome/metis/metis/download
|
59
|
+
|
60
|
+
入力データ)
|
61
|
+
節点ペアのCSVファイル(ファイル名はei=にて指定)
|
62
|
+
例:
|
63
|
+
node1,node2,weight
|
64
|
+
a,b,1
|
65
|
+
a,c,2
|
66
|
+
a,e,1
|
67
|
+
b,c,2
|
68
|
+
b,d,1
|
69
|
+
c,d,2
|
70
|
+
c,e,3
|
71
|
+
d,f,2
|
72
|
+
d,g,5
|
73
|
+
e,f,2
|
74
|
+
f,g,6
|
75
|
+
|
76
|
+
|
77
|
+
出力データ)
|
78
|
+
節点とクラスタ番号のCSVデータ(ファイル名はo=にて指定)
|
79
|
+
node,cluster
|
80
|
+
a,2
|
81
|
+
b,1
|
82
|
+
c,2
|
83
|
+
d,0
|
84
|
+
e,0
|
85
|
+
f,0
|
86
|
+
g,1
|
87
|
+
|
88
|
+
# Copyright(c) NYSOL 2012- All Rights Reserved.
|
89
|
+
EOF
|
90
|
+
exit
|
91
|
+
end
|
92
|
+
|
93
|
+
def ver()
|
94
|
+
$revision ="0" if $revision =~ /VERSION/
|
95
|
+
STDERR.puts "version #{$version} revision #{$revision}"
|
96
|
+
exit
|
97
|
+
end
|
98
|
+
|
99
|
+
help() if ARGV[0]=="--help" or ARGV.size <= 0
|
100
|
+
ver() if ARGV[0]=="--version"
|
101
|
+
|
102
|
+
args=MCMD::Margs.new(ARGV,"ei=,ef=,ew=,ni=,nf=,nw=,o=,kway=,ptype=,balance=,ncuts=,dat=,map=,-noexe,seed=,T=,-verbose,T=","kway=,ei=")
|
103
|
+
|
104
|
+
# mcmdのメッセージは警告とエラーのみ
|
105
|
+
ENV["KG_VerboseLevel"]="2" unless args.bool("-verbose")
|
106
|
+
ENV["KG_ScpVerboseLevel"]="3" unless args.bool("-verbose")
|
107
|
+
|
108
|
+
# コマンド実行可能確認
|
109
|
+
CMD_gpmetis="gpmetis"
|
110
|
+
exit(1) unless(MCMD::chkCmdExe(CMD_gpmetis,"wc",120))
|
111
|
+
|
112
|
+
#ワークファイルパス
|
113
|
+
if args.str("T=")!=nil then
|
114
|
+
ENV["KG_TmpPath"] = args.str("T=").sub(/\/$/,"")
|
115
|
+
end
|
116
|
+
|
117
|
+
kway =args.int("kway=")
|
118
|
+
ofile =args.file("o=","w")
|
119
|
+
efile =args.file("ei=","r")
|
120
|
+
nfile =args.file("ni=","r")
|
121
|
+
dfile =args.file("dat=","o")
|
122
|
+
mfile =args.file("map=","o")
|
123
|
+
|
124
|
+
# ---- edge field names (two nodes) on ei=
|
125
|
+
ef1,ef2 = args.field("ef=", efile, "node1,node2",2,2)["names"]
|
126
|
+
|
127
|
+
# ---- field name for edge weight
|
128
|
+
ew = args.field("ew=", efile, nil, 1,1)
|
129
|
+
ew = ew["names"][0] if ew
|
130
|
+
|
131
|
+
# ---- node field name on ni=
|
132
|
+
nf = args.field("nf=", nfile, "node")
|
133
|
+
nf = nf["names"][0] if nf
|
134
|
+
|
135
|
+
# ---- field names for node weights on ni=
|
136
|
+
nw = args.field("nw=", nfile)
|
137
|
+
ncon=0
|
138
|
+
if nw then
|
139
|
+
ncon=nw["names"].size
|
140
|
+
nw=nw["names"].join(",")
|
141
|
+
end
|
142
|
+
|
143
|
+
# ---- other paramters
|
144
|
+
ptype=args.str("ptype=","kway")
|
145
|
+
ncuts=args.int("ncuts=",1,1)
|
146
|
+
balance=args.float("balance=",nil,1.0)
|
147
|
+
ufactor=nil
|
148
|
+
if balance then
|
149
|
+
ufactor=((balance-1.0)*1000).to_i
|
150
|
+
else
|
151
|
+
if ptype=="kway"
|
152
|
+
ufactor=30
|
153
|
+
else
|
154
|
+
ufactor=1
|
155
|
+
end
|
156
|
+
end
|
157
|
+
seed=args.int("seed=",-1)
|
158
|
+
noexe =args.bool("-noexe")
|
159
|
+
|
160
|
+
# 本コマンドへの入力データイメージ
|
161
|
+
# node1,node2
|
162
|
+
# a,b
|
163
|
+
# a,c
|
164
|
+
# a,e
|
165
|
+
# b,c
|
166
|
+
# b,e
|
167
|
+
# b,g
|
168
|
+
# c,d
|
169
|
+
# c,g
|
170
|
+
# d,e
|
171
|
+
# e,f
|
172
|
+
|
173
|
+
# input file for gpmetis command
|
174
|
+
# first line: n m fmt ncon
|
175
|
+
# n: # of nodes
|
176
|
+
# m: # of edges
|
177
|
+
# fmt: 000(3 digits)
|
178
|
+
# first degit: node size? (always 0 in this command)
|
179
|
+
# second degit: node weight is provided on the data
|
180
|
+
# third degit: edge weight is provided on the data
|
181
|
+
# 7 10 ( ノード数 エッジ数)
|
182
|
+
# 2 3 5 ( 1番ノードの接続ノード)
|
183
|
+
# 1 3 5 7 ( 2番ノードの接続ノード)
|
184
|
+
# 1 2 4 7
|
185
|
+
# 3 5
|
186
|
+
# 1 2 4 6
|
187
|
+
# 5
|
188
|
+
# 2 3
|
189
|
+
|
190
|
+
# gpmetisからの出力ファイルのイメージ
|
191
|
+
# 1 ( 1番ノードのクラスタ番号)
|
192
|
+
# 0 ( 2番ノードのクラスタ番号)
|
193
|
+
# 1
|
194
|
+
# 0
|
195
|
+
# 0
|
196
|
+
# 0
|
197
|
+
# 1
|
198
|
+
|
199
|
+
# 本コマンドの出力イメージ
|
200
|
+
# node,cluster
|
201
|
+
# a,1
|
202
|
+
# b,0
|
203
|
+
# c,1
|
204
|
+
# d,0
|
205
|
+
# e,0
|
206
|
+
# f,0
|
207
|
+
# g,1
|
208
|
+
|
209
|
+
# 一時ファイル
|
210
|
+
wf=MCMD::Mtemp.new
|
211
|
+
|
212
|
+
##########################################
|
213
|
+
# cleaning edge data (eliminate duplicate edge, add reverse directed edge for each existing edge)
|
214
|
+
xxedge =wf.file
|
215
|
+
xxnode =wf.file
|
216
|
+
xxnam2num=wf.file
|
217
|
+
xxnum2nam=wf.file
|
218
|
+
xxebase =wf.file
|
219
|
+
|
220
|
+
xxe1 =wf.file
|
221
|
+
xxe2 =wf.file
|
222
|
+
f=""
|
223
|
+
if ew then
|
224
|
+
f << "mcut f=#{ef1}:__node1,#{ef2}:__node2,#{ew}:__weight i=#{efile} |"
|
225
|
+
else
|
226
|
+
f << "mcut f=#{ef1}:__node1,#{ef2}:__node2 i=#{efile} |"
|
227
|
+
end
|
228
|
+
f << "msortf f=__node1,__node2 |"
|
229
|
+
f << "muniq k=__node1,__node2 o=#{xxe1}"
|
230
|
+
system(f)
|
231
|
+
system "mfldname f=__node2:__node1,__node1:__node2 i=#{xxe1} o=#{xxe2}"
|
232
|
+
|
233
|
+
f=""
|
234
|
+
f << "mcat i=#{xxe1},#{xxe2} |"
|
235
|
+
f << "msortf f=__node1,__node2 |"
|
236
|
+
f << "muniq k=__node1,__node2 o=#{xxedge}"
|
237
|
+
system(f)
|
238
|
+
|
239
|
+
# cleaning the node data (remove duplicate nodes)
|
240
|
+
if nfile then
|
241
|
+
f=""
|
242
|
+
if nw then
|
243
|
+
f << "mcut f=#{nf}:__node,#{nw} i=#{nfile} |"
|
244
|
+
else
|
245
|
+
f << "mcut f=#{nf}:__node i=#{nfile} |"
|
246
|
+
end
|
247
|
+
f << "msortf f=__node |"
|
248
|
+
f << "muniq k=__node o=#{xxnode}"
|
249
|
+
system(f)
|
250
|
+
else
|
251
|
+
xxeNode1 =wf.file
|
252
|
+
xxeNode2 =wf.file
|
253
|
+
system "mcut f=__node1:__node i=#{xxedge} o=#{xxeNode1}"
|
254
|
+
system "mcut f=__node2:__node i=#{xxedge} o=#{xxeNode2}"
|
255
|
+
f=""
|
256
|
+
f << "mcat i=#{xxeNode1},#{xxeNode2} |"
|
257
|
+
f << "msortf f=__node |"
|
258
|
+
f << "muniq k=__node o=#{xxnode}"
|
259
|
+
system(f)
|
260
|
+
end
|
261
|
+
|
262
|
+
# 節点名<=>節点番号変換表の作成
|
263
|
+
f=""
|
264
|
+
f << "mcut f=__node i=#{xxnode} |"
|
265
|
+
f << "mnumber a=__num S=1 -q o=#{xxnam2num}"
|
266
|
+
system(f)
|
267
|
+
system "msortf f=__num i=#{xxnam2num} o=#{xxnum2nam}"
|
268
|
+
|
269
|
+
# 節点ファイルが指定された場合は枝ファイルとの整合性チェック
|
270
|
+
if nfile then
|
271
|
+
xxdiff=wf.file
|
272
|
+
f=""
|
273
|
+
f << "mcut f=__node1:__node i=#{xxedge} |"
|
274
|
+
f << "muniq k=__node |"
|
275
|
+
f << "mcommon -r k=__node m=#{xxnam2num} o=#{xxdiff}"
|
276
|
+
system(f)
|
277
|
+
tbl=MCMD::Mtable.new("i=#{xxdiff}")
|
278
|
+
if tbl.size()>0 then
|
279
|
+
raise "#ERROR# the node named `#{tbl.cell(tbl.name2num["__node"],0)}' in the edge file doesn't exist in the node file."
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# metisのグラフファイルフォーマット
|
284
|
+
# 先頭行n m [fmt] [ncon]
|
285
|
+
# n: 節点数、m:枝数、ncon: 節点weightの数
|
286
|
+
# 1xx: 節点サイズ有り (not used, meaning always "0")
|
287
|
+
# x1x: 節点weight有り
|
288
|
+
# xx1: 枝がweightを有り
|
289
|
+
# s w_1 w_2 ... w_ncon v_1 e_1 v_2 e_2 ... v_k e_k
|
290
|
+
# s: 節点サイズ (節点サイズは利用不可)
|
291
|
+
# w_x: 節点weight
|
292
|
+
# v_x: 接続のある節点番号(行番号)
|
293
|
+
# e_x: 枝weight
|
294
|
+
|
295
|
+
# --------------------
|
296
|
+
# generate edge data using the integer numbered nodes
|
297
|
+
xxnnum=wf.file
|
298
|
+
xxenum=wf.file
|
299
|
+
f=""
|
300
|
+
f << "mcut f=__num:__node_n1 i=#{xxnam2num} |"
|
301
|
+
f << "msortf f=__node_n1 o=#{xxnnum}"
|
302
|
+
system(f)
|
303
|
+
|
304
|
+
f=""
|
305
|
+
f << "mjoin k=__node1 K=__node f=__num:__node_n1 m=#{xxnam2num} i=#{xxedge} |"
|
306
|
+
f << "msortf f=__node2 |"
|
307
|
+
f << "mjoin k=__node2 K=__node f=__num:__node_n2 m=#{xxnam2num} |"
|
308
|
+
f << "msortf f=__node_n1 o=#{xxenum}"
|
309
|
+
system(f)
|
310
|
+
|
311
|
+
f=""
|
312
|
+
# this generates the isolated nodes
|
313
|
+
f << "mnjoin k=__node_n1 m=#{xxenum} i=#{xxnnum} -n |"
|
314
|
+
f << "msortf f=__node_n1%n,__node_n2%n o=#{xxebase}"
|
315
|
+
system(f)
|
316
|
+
# xxebase
|
317
|
+
# __node1,__node2,__weight,__node_n1,__node_n2
|
318
|
+
# a,b,7,1,2
|
319
|
+
# a,c,8,1,3
|
320
|
+
# a,e,9,1,5
|
321
|
+
|
322
|
+
##########################################
|
323
|
+
# generate edge data for metis
|
324
|
+
xxebody =wf.file
|
325
|
+
xxnbody =wf.file
|
326
|
+
xxnbody1 =wf.file
|
327
|
+
xxwbody =wf.file
|
328
|
+
xxbody =wf.file
|
329
|
+
xxhead =wf.file
|
330
|
+
xxgraph =wf.file
|
331
|
+
|
332
|
+
unless ew then
|
333
|
+
f=""
|
334
|
+
f << "mcut f=__node_n1,__node_n2 i=#{xxebase} |"
|
335
|
+
f << "mtra k=__node_n1 f=__node_n2 -q |"
|
336
|
+
f << "mcut f=__node_n2 -nfno o=#{xxbody}"
|
337
|
+
system(f)
|
338
|
+
|
339
|
+
# if ew= is specified, merge the weight data into the edge data.
|
340
|
+
else
|
341
|
+
f=""
|
342
|
+
f << "mcut f=__node_n1,__node_n2:__v i=#{xxebase} |"
|
343
|
+
f << "mnumber S=0 I=2 a=__seq -q o=#{xxebody}"
|
344
|
+
system(f)
|
345
|
+
|
346
|
+
f=""
|
347
|
+
f << "mcut f=__node_n1,__weight:__v i=#{xxebase} |"
|
348
|
+
f << "mnumber S=1 I=2 a=__seq -q o=#{xxwbody}"
|
349
|
+
system(f)
|
350
|
+
|
351
|
+
f=""
|
352
|
+
f << "mcat i=#{xxwbody},#{xxebody} |"
|
353
|
+
f << "msortf f=__seq%n |"
|
354
|
+
f << "mtra k=__node_n1 f=__v -q |"
|
355
|
+
f << "mcut f=__v -nfno o=#{xxbody}"
|
356
|
+
system(f)
|
357
|
+
end
|
358
|
+
# xxbody
|
359
|
+
# 2 7 3 8 5 9
|
360
|
+
# 1 7 3 10 5 11 7 12
|
361
|
+
# 1 8 2 10 4 13 7 14
|
362
|
+
|
363
|
+
# --------------------
|
364
|
+
# generate node data using integer number
|
365
|
+
if nfile and nw then
|
366
|
+
# xxnode
|
367
|
+
# __node,v1,v2
|
368
|
+
# a,1,1
|
369
|
+
# b,1,1
|
370
|
+
# c,1,1
|
371
|
+
f=""
|
372
|
+
f << "msortf f=__node i=#{xxnode} |"
|
373
|
+
f << "mjoin k=__node f=__num m=#{xxnam2num} |"
|
374
|
+
f << "msortf f=__num%n |"
|
375
|
+
f << "mcut f=#{nw} -nfno |"
|
376
|
+
f << "tr ',' ' ' >#{xxnbody}" # tricky!!
|
377
|
+
system(f)
|
378
|
+
# xxnbody
|
379
|
+
# 1 1
|
380
|
+
# 1 1
|
381
|
+
# 1 1
|
382
|
+
# paste the node weight with edge body
|
383
|
+
system "mpaste -nfn m=#{xxbody} i=#{xxnbody} | tr ',' ' ' >#{xxnbody1}"
|
384
|
+
system "mv #{xxnbody1} #{xxbody}"
|
385
|
+
end
|
386
|
+
# xxbody
|
387
|
+
# 1 1 2 7 3 8 5 9
|
388
|
+
# 1 1 1 7 3 10 5 11 7 12
|
389
|
+
# 1 1 1 8 2 10 4 13 7 14
|
390
|
+
|
391
|
+
# 枝と節点のサイズ
|
392
|
+
eSize=MCMD::mrecount("i=#{xxedge}")
|
393
|
+
eSize/=2
|
394
|
+
nSize=MCMD::mrecount("i=#{xxnode}")
|
395
|
+
|
396
|
+
nwFlag=0
|
397
|
+
ewFlag=0
|
398
|
+
nwFlag=1 if nw
|
399
|
+
ewFlag=1 if ew
|
400
|
+
fmt="0#{nwFlag}#{ewFlag}"
|
401
|
+
|
402
|
+
system "echo '#{nSize} #{eSize} #{fmt} #{ncon}' > #{xxhead}"
|
403
|
+
|
404
|
+
system "cat #{xxhead} #{xxbody} > #{xxgraph}"
|
405
|
+
|
406
|
+
system "mfldname f=__num:num,__node:node i=#{xxnum2nam} o=#{mfile}" if mfile
|
407
|
+
system "cp #{xxgraph} #{dfile}" if dfile
|
408
|
+
|
409
|
+
##########################################
|
410
|
+
## execute metis
|
411
|
+
unless noexe
|
412
|
+
MCMD::msgLog "gpmetis -seed=#{seed} -ptype=#{ptype} -ncuts=#{ncuts} -ufactor=#{ufactor} #{xxgraph} #{kway}"
|
413
|
+
if args.bool("-verbose") then
|
414
|
+
system "gpmetis -seed=#{seed} -ptype=#{ptype} -ncuts=#{ncuts} -ufactor=#{ufactor} #{xxgraph} #{kway} "
|
415
|
+
else
|
416
|
+
system "gpmetis -seed=#{seed} -ptype=#{ptype} -ncuts=#{ncuts} -ufactor=#{ufactor} #{xxgraph} #{kway} > /dev/null"
|
417
|
+
end
|
418
|
+
|
419
|
+
if Dir["#{xxgraph}.part.*"].size == 0
|
420
|
+
raise "#ERROR# command `gpmetis' didn't output any results"
|
421
|
+
end
|
422
|
+
|
423
|
+
# 節点名を数字から元に戻す
|
424
|
+
# #{xxgraph}.part.#{kway}
|
425
|
+
# 1
|
426
|
+
# 0
|
427
|
+
# 1
|
428
|
+
f=""
|
429
|
+
f << "mcut f=0:cluster -nfni i=#{xxgraph}.part.#{kway} |"
|
430
|
+
f << "mnumber S=1 a=__num -q |"
|
431
|
+
f << "msortf f=__num |"
|
432
|
+
f << "mjoin k=__num f=__node m=#{xxnum2nam} |"
|
433
|
+
f << "msortf f=__node,cluster |"
|
434
|
+
if nf then
|
435
|
+
f << "mcut f=__node:#{nf},cluster o=#{ofile}"
|
436
|
+
else
|
437
|
+
f << "mcut f=__node:node,cluster o=#{ofile}"
|
438
|
+
end
|
439
|
+
system(f)
|
440
|
+
# #{ofile}
|
441
|
+
# n,cluster
|
442
|
+
# a,1
|
443
|
+
# b,0
|
444
|
+
# c,1
|
445
|
+
end
|
446
|
+
|
447
|
+
# 終了メッセージ
|
448
|
+
MCMD::endLog(args.cmdline)
|
449
|
+
|