nysol-mining 3.0.0
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 +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
|
+
|