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.
@@ -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
+