nysol-mining 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)
@@ -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
+