实验记录之划分得到特征
6月25日
nodcfeature下的数据,是PR之后的训练数据。
每行都是sink pin节点的数据。
6/25日:(已经commit:graph partitioning before)
- 为gate设置一个字段num,表示超图中的节点编号。再设置一个字段partNo,表示划分之后的所属的partition编号。
- 为每个gate编号,插入gate时,进行编号。
- 设置一个map,gate name和gate num一一对应。
6/26日,已实现上面功能:
通过节点编号查找gate name:
_numToGateNames[i] ===> gatename
通过gate name查找节点编号:
_gateNamesToNum[“U462”] ==>num
std::unordered_map<int,std::string> _numToGateNames;
std::unordered_map<std::string,int> _gateNamesToNum;
计划如下工作:
-
遍历所有net,构建超图。得到格式:
net数量,gate 数量。
要把那些pin是port的线网去掉。
26号下午4点:超图构建完成
在read_verilog / _verilog / _insert_gate 里面添加了
gate.num = _gates.size(); _numToGateNames.emplace(gate.num,gate._name); _gateNamesToNum.emplace(gate._name,gate.num);
读入verilog文件构建超图文件的代码:
void Timer::_build_HmetisHyperGraph(std::ostream & os) const{ int net_size = 0; int gate_size = _gates.size(); std::vector<std::vector<int>> GraphFile; for(const auto &[net_name,net] : _nets){ bool flag = true;//remove nets whose pin is port for(const auto pin : net._pins){ if(pin->_primary_input() || pin->_primary_output()){ flag = false; break; } } if(flag == true){ GraphFile.push_back(vector<int>());//fixed bug for(const auto pin : net._pins){ //add a hypger edge GraphFile[net_size].push_back(pin->_gate->getNum()); } net_size++; } }
shell接口:(-o 后面接的是存放超图文件的路径)
build_HmetisHyperGraph -o [path to save the hypergraph]
6月30日
6/30日进展:
在multilevel recursive bisection的情况下,假设划分npart为2的幂次方次的个数,划分后的结果的二进制就是它的划分路径!先左为0再右1。比如节点0的最后划分结果是8(二进制为1000),那么划分路径就是1000.支持的可执行文件有 shmetis/hmetis
./shmetis b11_1_mode_0_hyper.txt 4 5
7月1日
计划7/1日:将gate的字段partNo填上。
测试:
timerPr.set_pr_data(fileIndex_1,fileIndex_2,mode);
timerPr.readPartionResult(logos);
下午16:51已经实现了:将gate的字段partNo填上。
void Timer::_readPartionResult(std::istream &iss){
std::string str;
int count = 0;
while(!iss.eof()){
if (iss.bad()) {
std::cout << "input stream corrupted" << std::endl;
break;
}
if (iss.fail()) {
std::cout << "bad data" << std::endl;
iss.clear(std::istream::failbit);
iss.ignore(256, '\n');
continue;
}
std::getline(iss, str);
if(str.length() != 0){
count++;
int part = std::stoi(str);
auto gate = _numToGate.find(count);
if(gate != _numToGate.end())
gate->second.partNo = part;
else{
OT_LOGW("_numToGate != find(count) ",count);
}
}
}
}
ps更改timer.hpp中相关数据结构:
std::unordered_map<int,Gate &> _numToGate;
将上面的接口添加到shell中,已经添加完成了。
shell接口:
read_partion_result [the path of partion result file]
在最后提取特征的时候,碰到之前留下的问题,有点模糊了。
使用布局布线之后netlist,使用线负载模型提取的spef进行的时序信息的计算,拿到的也是布局之后的label进行的训练。
所以read_spef 应该读取 线负载得到的spef。可能会有布局之后的位置信息,但是不用它就好了。
逻辑综合的时候,虽然提取了线网使得整张图不能很好的进行切割,但是在提取之前,可以事先就对网表进行超图分割,从而得到每个gate的划分结果。
- 提取dc阶段的特征(逻辑综合的提取一一对应的线网-网表):dump_rf_feature_dc
void Net::dump_rf_feature_dc(std::ostream &os,Split el,Tran rf,int groupId,ot::Timer *prTimer) const{
//os<<"groupId,part,pin_name,drive_load,wire_input_slew,D2M,d_x,d_y,s_x,s_y,dis,strength,sink_load,sink_pin_capacitance,elmore_delay,wire_slew,wire_delay"<<std::endl;
const Rct* rctree= rct();
int strength = drive_strength();
for(auto [name,rcnode]:(*rctree)._nodes)
{
if(rcnode._name != rctree->_root->_name )
{
if(rcnode._pin == nullptr)
{
continue;
}
else
{
auto itr = prTimer->_pins.find(rcnode._pin->_name);
assert(itr != prTimer->_pins.end());
const auto & pr_pin = itr->second;
for(auto arc: pr_pin._fanin)
{
if(arc->_from.name() == _root->name() )
{
Loc sub = _root->_loc - rcnode._pin->_loc;
// double dis = sub.P2Odis();
//sink_pin
os<<groupId<<"," << rcnode._pin->_gate->getPart() <<"," <<rcnode._pin->_name<<","<<rctree->_root->_load[el][rf]<<","<<*_root->slew(el,rf)<<","<<\
rcnode._ldelay[el][rf]<<","<<_root->_loc._x<<","<<_root->_loc._y<<","<<rcnode._pin->_loc._x<<","<<rcnode._pin->_loc._y<<","<<sub.P2Odis()<<","<<strength<<\
","<<rcnode._load[el][rf]<<","<<rcnode._pin->cap(el,rf)<<","<<rcnode.delay(el,rf)<<\
"," <<*(pr_pin.slew(el,rf)) <<","<<*arc->_delay[el][rf][rf]<<std::endl;
}
}
}
}
else
{
continue;
}
}
}
- 去掉布局信息的,使用线负载模型得到逻辑综合的特征(布局阶段时候的线网)
void Net::dump_rf_feature(std::ostream &os,Split el,Tran rf,int groupId) const{
//os<<"groupId,part,pin_name,drive_load,wire_input_slew,D2M,d_x,d_y,s_x,s_y,dis,strength,sink_load,sink_pin_capacitance,elmore_delay,wire_slew,wire_delay"<<std::endl;
const Rct* rctree= rct();
int strength = drive_strength();
for(auto [name,rcnode]:(*rctree)._nodes)
{
if(rcnode._name != rctree->_root->_name )
{
if(rcnode._pin == nullptr)
{
continue;
}
else
{
for(auto arc:rcnode._pin->_fanin)
{
if(arc->_from.name() == _root->name() )
{
Loc sub = _root->_loc - rcnode._pin->_loc;
// double dis = sub.P2Odis();
//sink_pin
os<<groupId<<"," << rcnode._pin->_gate->getPart() << "," <<rcnode._pin->_name<<","<<rctree->_root->_load[el][rf]<<","<<*_root->slew(el,rf)<<","<<\
rcnode._ldelay[el][rf]<<","<<_root->_loc._x<<","<<_root->_loc._y<<","<<rcnode._pin->_loc._x<<","<<rcnode._pin->_loc._y<<","<<sub.P2Odis()<<","<<strength<<\
","<<rcnode._load[el][rf]<<","<<rcnode._pin->cap(el,rf)<<","<<rcnode.delay(el,rf)<<\
"," <<*arc->_to.slew(el,rf) <<","<<*arc->_delay[el][rf][rf]<<std::endl;
}
}
}
}
else
{
//root_node ��ʱ��������
continue;
}
}
}
提取特征的脚本/代码如下:
- DC阶段提取的一一对应的线网,提取相应的特征,添加布局之后的label**(dump_rf_feature_dc shell接口不可用!必须用下面的代码)**
timerSyn.set_syn_data(fileIndex_1,fileIndex_2,mode);
timerSyn.prTimer = new ot::Timer();
(*timerSyn.prTimer).set_pr_data(fileIndex_1,fileIndex_2,mode);
timerSyn.dump_rf_feature_dc("./logs/DcNetDcFea/b" + fileIndex +"_mode_"+mode);
- PR阶段的网表,提取去掉布局位置的特征。记住一定要添加read_spef ./logs/spef/11_1.spef
read_celllib ./data/lib/data.lib
read_multi_verilog ./data/netlist/b11_1_pr_mode_0.v
read_sdc ./data/sdc/b11/b11_1_pr.sdc
read_place ./data/DataPT/data/b11_1_mode_0
read_spef ./logs/spef/11_1.spef
update_timing
dump_rf_feature ./logs/prNetDcFea/b11_1_mode_0_rf.txt
为了方便调试,单独写了一个函数:set_prNetDcFea_data
void Timer::set_prNetDcFea_data(std::string fileIndex_1,std::string fileIndex_2,std::string mode){
std::string fileIndex = fileIndex_1 + "_" + fileIndex_2;
std::string synNetFileName = "b" + fileIndex + "_SYN.v";
std::string prNetFileName = "b" + fileIndex + "_pr_mode_" + mode + ".v";
std::string synSDCfileName = "b" + fileIndex + "_syn.sdc";
std::string prSDCfileName = "b" + fileIndex + "_pr.sdc";
std::string prSPEFfileName = "b" + fileIndex + "_pr_mode_" + mode + ".spef";
std::string place = "./data/DataPT/data/b"+fileIndex + "_mode_" + mode;
read_celllib("./data/lib/data.lib")
.read_verilog("./data/netlist/" + prNetFileName)
.read_place(place)
.read_spef("./logs/spef/"+ fileIndex + "_" + mode + ".spef")
.read_sdc("./data/sdc/b" + fileIndex_1 + "/" + prSDCfileName);
update_timing();
}
bug的在于,有些port也作为数据集,没有gate,所以指针错误。(已解决,最终的数据里面去掉了这些port)在dump_rf_feature和dump_rf_feature_dc函数里面,不把这些port相应的特征dump出来。因为它们不属于任何gate,自然没有part结果。
已经提取完特征。
7/2日计划:批量提取所有特征,然后进行第一次模型训练。
7月2日
1、批量生成超图文件存放在logs/hypergraph文件夹下。包括b14_1。脚本hyperCreate.py如下:
import os,sys,subprocess
#os.chdir("../")
for base in range(11,15):
for clock in range(0,5):
for mode in range(0,4):
place_name = "b"+str(base)+"_"+str(clock)+"_mode_"+str(mode)
verilog_name = "b"+str(base)+"_"+str(clock) + "_pr_mode_"+ str(mode)+".v"
sdc_name = "b"+str(base)+"_"+str(clock) + "_pr.sdc"
cmd ="read_celllib ./data/lib/data.lib\n"
cmd+="read_multi_verilog ./data/netlist/"+verilog_name+"\n"
cmd+="read_sdc ./data/sdc/b"+str(base) +"/"+sdc_name+"\n"
cmd+="read_place ./data/DataPT/data/"+place_name+"\n"
cmd+="read_spef ./logs/spef/" + str(base) + "_" + str(clock) + "_" + str(mode) + ".spef" + "\n"
cmd+="update_timing\n"
cmd+="build_HmetisHyperGraph -o ./logs/hypergraph/"+place_name+".hgr\n"
try:
data = subprocess.check_output(
["./build/ot-shell"],
input=bytes(cmd,encoding="UTF-8"),
)
print(data)
except:
data=None
print(cmd)
2、将所有的超图进行切割,存放在如上目录。脚本cutGraph.py如下:
import subprocess
for index1 in range(11,15):
for index2 in range(0,5):
for index3 in range(0,4):
file = "b" + str(index1) + "_" + str(index2) + "_mode_" + str(index3)+".hgr"
cmd = "logs/partresult/shmetis logs/hypergraph/" + file +" 4" + " 5"
print(cmd)
subprocess.run(cmd,shell=True)
3、读入划分结果文件,批量生成特征文件。脚本injectPartion.py如下:
import os,sys,subprocess
#os.chdir("../")
for base in range(11,15):
for clock in range(0,5):
for mode in range(0,4):
place_name = "b"+str(base)+"_"+str(clock)+"_mode_"+str(mode)
verilog_name = "b"+str(base)+"_"+str(clock) + "_pr_mode_"+ str(mode)+".v"
sdc_name = "b"+str(base)+"_"+str(clock) + "_pr.sdc"
cmd ="read_celllib ./data/lib/data.lib\n"
cmd+="read_multi_verilog ./data/netlist/"+verilog_name+"\n"
cmd+="read_sdc ./data/sdc/b"+str(base) +"/"+sdc_name+"\n"
cmd+="read_place ./data/DataPT/data/"+place_name+"\n"
cmd+="read_spef ./logs/spef/" + str(base) + "_" + str(clock) + "_" + str(mode) + ".spef" + "\n"
cmd+="update_timing\n"
cmd+="read_partion_result ./logs/hypergraph/"+place_name+".hgr.part.4\n"
cmd+="dump_rf_feature ./logs/prNetDcFea/"+place_name+"\n"
try:
data = subprocess.check_output(
["./build/ot-shell"],
input=bytes(cmd,encoding="UTF-8"),
)
print(data)
except:
data=None
print(cmd)
- 根据节点个数来确定划分的次数。log(n) / log(2)取下整。hgr文件的第二个节点才是节点个数。
ps:遇到一个问题,那就是每次跑出来的结果都一样的。设置随机数种子的函数如下,最后添加的torch.backends.cudnn.enabled = True稳定下来了。
def set_random_seed(seed, deterministic=False):
"""Set random seed.
Args:
seed (int): Seed to be used.
deterministic (bool): Whether to set the deterministic option for
CUDNN backend, i.e., set `torch.backends.cudnn.deterministic`
to True and `torch.backends.cudnn.benchmark` to False.
Default: False.
"""
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
if deterministic:
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.enabled = True
由于不同update_timing,所以 逻辑综合阶段的特征会有点差别。解决这个问题的办法是,一次update timing,然后多次read_partion_result / dump_rf_feature 。
for base in range(11,15):
for clock in range(0,5):
for mode in range(0,4):
place_name = "b"+str(base)+"_"+str(clock)+"_mode_"+str(mode)
verilog_name = "b"+str(base)+"_"+str(clock) + "_pr_mode_"+ str(mode)+".v"
sdc_name = "b"+str(base)+"_"+str(clock) + "_pr.sdc"
cmd ="read_celllib ./data/lib/data.lib\n"
cmd+="read_multi_verilog ./data/netlist/"+verilog_name+"\n"
cmd+="read_sdc ./data/sdc/b"+str(base) +"/"+sdc_name+"\n"
cmd+="read_place ./data/DataPT/data/"+place_name+"\n"
cmd+="read_spef ./logs/spef/" + str(base) + "_" + str(clock) + "_" + str(mode) + ".spef" + "\n"
cmd+="update_timing\n"
cmd+="read_partion_result ./logs/hypergraph/"+place_name+".hgr.part.8\n"
cmd+="dump_rf_feature ./logs/prNetDcFea/8-way/"+place_name+"\n"
cmd+="read_partion_result ./logs/hypergraph/"+place_name+".hgr.part.4\n"
cmd+="dump_rf_feature ./logs/prNetDcFea/4-way/"+place_name+"\n"
try:
data = subprocess.check_output(
["./build/ot-shell"],
input=bytes(cmd,encoding="UTF-8"),
)
print(data)
except:
data=None
print(cmd)
7月5日
根据划分的结果,取其二进制的编码。
根据最终划分粒度(不多于5个),划分的way的如下所示:
for index1 in range(11,15):
for index2 in range(0,5):
for index3 in range(0,4):
file = "b" + str(index1) + "_" + str(index2) + "_mode_" + str(index3)+".hgr"
filepath = "logs/hypergraph/" + file
with open(filepath) as f:
nodeNetstr = f.readline()
f.close()
listNetNode = nodeNetstr.split(" ")
part = int(math.log2(int(listNetNode[1]) / 5))
print(part)
二进制最高不超过10位。
为了日后可以读入划分的结果,现在需要将划分的次数进行记录到文件中。
cutGraph.py如下所示:
import subprocess
import pandas as pd
import math
name = ["file","kway"]
res_list=[]
for index1 in range(11,15):
for index2 in range(0,5):
for index3 in range(0,4):
file = "b" + str(index1) + "_" + str(index2) + "_mode_" + str(index3)+".hgr"
filepath = "logs/hypergraph/" + file
with open(filepath) as f:
nodeNetstr = f.readline()
f.close()
listNetNode = nodeNetstr.split(" ")
part = int(math.log2(int(listNetNode[1]) / 5))
print(part)
res_list.append([file,part])
cmd = "logs/partresult/shmetis " + filepath + " " + str(part) + " 5"
print(cmd)
subprocess.run(cmd,shell=True)
res_csv =pd.DataFrame(columns = name,data = res_list)
res_csv.to_csv("./logs/hypergraph/" + "filesKway.csv")
记录文件为filesKway.csv存放在logs/hypergraph目录下。
![img](https://i-blog.csdnimg.cn/blog_migrate/64afb648635dc21968d0e1d00d220545.png)
所有的划分次数都大于4。
然后读入划分的结果,将特征dump_rf_feature。
injectPartion.py如下所示:
import os,sys,subprocess
import pandas as pd
#os.chdir("../")
df = pd.read_csv("./logs/hypergraph/filesKway.csv")
df.set_index(df["file"],inplace=True)
for base in range(11,15):
for clock in range(0,5):
for mode in range(0,4):
place_name = "b"+str(base)+"_"+str(clock)+"_mode_"+str(mode)
verilog_name = "b"+str(base)+"_"+str(clock) + "_pr_mode_"+ str(mode)+".v"
sdc_name = "b"+str(base)+"_"+str(clock) + "_pr.sdc"
cmd ="read_celllib ./data/lib/data.lib\n"
cmd+="read_multi_verilog ./data/netlist/"+verilog_name+"\n"
cmd+="read_sdc ./data/sdc/b"+str(base) +"/"+sdc_name+"\n"
cmd+="read_place ./data/DataPT/data/"+place_name+"\n"
cmd+="read_spef ./logs/spef/" + str(base) + "_" + str(clock) + "_" + str(mode) + ".spef" + "\n"
cmd+="update_timing\n"
print(str(df.loc[place_name,"kway"]))
cmd+="read_partion_result ./logs/hypergraph/"+place_name+".hgr.part." + str(df.loc[place_name,"kway"]) +"\n"
cmd+="dump_rf_feature ./logs/prNetDcFea/k-way/"+place_name+"\n"
try:
data = subprocess.check_output(
["./build/ot-shell"],
input=bytes(cmd,encoding="UTF-8"),
)
print(data)
except:
data=None
print(cmd)
下面更改特征:
添加10个字段,记录了part的二进制表示。
if(rcnode._pin->_name.find(":") != std::string::npos){
int part = rcnode._pin->_gate->getPart() ;
int parts[10] = {0};
int tpart = part;
for(int i = 9;i >=0 && tpart!=0;i --){
parts[i] = tpart & 1;
tpart = tpart >> 1;
}
os<<groupId<<"," << part << ","
<< parts[0] << ","
<< parts[1] << ","
<< parts[2] << ","
<< parts[3] << ","
<< parts[4] << ","
<< parts[5] << ","
<< parts[6] << ","
<< parts[7] << ","
<< parts[8] << ","
<< parts[9] << ","
<<rcnode._pin->_name<<","<<rctree->_root->_load[el][rf]<<","<<*_root->slew(el,rf)<<","<<\
rcnode._ldelay[el][rf]<<","<<_root->_loc._x<<","<<_root->_loc._y<<","<<rcnode._pin->_loc._x<<","<<rcnode._pin->_loc._y<<","<<sub.P2Odis()<<","<<strength<<\
","<<rcnode._load[el][rf]<<","<<rcnode._pin->cap(el,rf)<<","<<rcnode.delay(el,rf)<<\
"," <<*arc->_to.slew(el,rf) <<","<<*arc->_delay[el][rf][rf]<<std::endl;
}
这里有个小技巧,就是再创建一个小project来进行针对性的测试,寻找bug,这样很容易发现问题、解决问题。而且很快。
特征生成完毕。
二话不说,进行一波训练。
现在把所有的数据放到Data/prNetDcFea_kway/data下。预测的结果放在Data/prNetDcFea_kway/predictRes下。
用cro_lg.py也就是lightgbm进行交叉验证。第一次实验如下:
先不加这10个特征,进行wire_delay的训练。
加入10个特征之后,进行wire_delay的训练。
效果反而不好了。
9:1下,没有加特征的两次的效果:
取上的
加了特征的效果:
差别不大。。
split_lg.py slew上加入10个特征的效果:
不加的效果如下:
不加反而效果更好。
cro_lg.py 上的 加了10个特征的wire_slew的效果。
不加的效果:
明天跟老师汇报一下,看能不能换种思路。
7月6日
特征仅使用part字段。进行第二次实验。
在wire slew上,仅加入part字段的效果如下所示,分别是交叉验证cro.lg,以及9:1split.lg的实验结果。
在wire delay上,仅加入part字段的效果如下所示,分别是交叉验证cro.lg,以及9:1split.lg的实验结果。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aIcIuoYM-1653533351037)(https://i.loli.net/2021/07/14/bpQFKaSuiEBej5G.png)]
7月7日
之前cro.rf结果跑不出来,内存不足,电脑卡住了。
下面试一下split.rf效果:文件是split_rf.py
import pickle
import random
import joblib
import numpy as np
import matplotlib.pyplot as plt
import sklearn.ensemble as se # 集合算法模块
import pandas as pd
from torch.utils.data import Dataset, DataLoader
import torch
import config
import sklearn.metrics as sm
from sklearn.model_selection import cross_val_score
def setup_seed(seed):
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
np.random.seed(seed)
random.seed(seed)
torch.backends.cudnn.deterministic = True
class MyDataSetBase():
def __init__(self,base):
super(MyDataSetBase,self).__init__()
self.data = pd.DataFrame()
for clock in range(0, config.clock):
for mode in range(0, config.mode):#b11_0_mode_0_rf.csv
name = "b" + str(base) + "_" + str(clock) + "_mode_" + str(mode)
drive_node_name = config.datapath + "/" + name + "_rf.csv"
#drive_node_name = config.datapath + "/" + name + "_drive_node.csv"
print("read name", drive_node_name)
tmp = pd.read_csv(drive_node_name)
self.data = self.data.append(tmp)
#print(self.__len__())
def __getitem__(self, item):#drive_load,wire_input_slew,D2M,d_x,d_y,s_x,s_y,dis,strength,sink_load,sink_pin_capacitance,elmore_delay
feature = self.data[names].to_numpy()[item]
#feature = self.data[["capacitance", "ldelay","impulse","strength"]].to_numpy()[item]
label = self.data[[config.type]].to_numpy()[item]
return feature,label
def __len__(self):
return len(self.data)
class RandomModel():
def __init__(self):
super(RandomModel,self).__init__()
def load_data9to1(self):
custom_datasets = []
for base in range(config.train_start_id, config.train_end_id):
if base == 16:
continue
subData = MyDataSetBase(base)
custom_datasets.append(subData)
# print(subData.__len__())
custom_datasets_test = []
train_dataset_feature = []
train_dataset_label = []
test_dataset_feature = []
test_dataset_label = []
dataset_feature = []
dataset_label = []
for custom_dataset in custom_datasets:
train_size = int(len(custom_dataset) * 0.9)
test_size = len(custom_dataset) - train_size
# print(len(custom_dataset))
train_dataset, test_dataset = torch.utils.data.random_split(custom_dataset, [train_size, test_size])
dataset_feature.append(custom_dataset[:][0])
dataset_label.append(custom_dataset[:][1])
train_dataset_feature.append(train_dataset[:][0])
train_dataset_label.append(train_dataset[:][1])
test_dataset_feature.append(test_dataset[:][0])
test_dataset_label.append(test_dataset[:][1])
custom_datasets_test.append(test_dataset)
# model train
train_feature = train_dataset_feature[0]
train_label = train_dataset_label[0]
test_feature = test_dataset_feature[0]
test_label = test_dataset_label[0]
feature = dataset_feature[0]
label = dataset_label[0]
for i in range(len(train_dataset_feature)):
if i == 0:
continue
train_feature = np.vstack((train_feature, train_dataset_feature[i]))
train_label = np.vstack((train_label, train_dataset_label[i]))
test_feature = np.vstack((test_feature, test_dataset_feature[i]))
test_label = np.vstack((test_label, test_dataset_label[i]))
feature = np.vstack((feature, dataset_feature[i]))
label = np.vstack((label, dataset_label[i]))
# print(len(train_feature))
# print(len(train_label))
self.test = custom_datasets_test
self.data = train_feature, train_label.ravel(), test_feature, test_label.ravel()
self.total = feature,label.ravel()
#return train_feature, train_label.ravel(), custom_datasets_test, test_feature, test_label.ravel()
def test_RandomForestRegressor_num(self):
'''
测试 RandomForestRegressor 的预测性能随 n_estimators 参数的影响
:param data: 可变参数。它是一个元组,这里要求其元素依次为:训练样本集、测试样本集、训练样本的值、测试样本的值
:return: None
'''
X_train, y_train,X_test,y_test = self.data
feature,label = self.total
nums=np.arange(1,50,step=2)
fig=plt.figure()
ax=fig.add_subplot(1,1,1)
testing_scores=[]
training_scores=[]
cross_scores = []
for num in nums:
regr=se.RandomForestRegressor(n_estimators=num)
regr.fit(X_train,y_train)
train_score = regr.score(X_train,y_train)
test_score = regr.score(X_test,y_test)
training_scores.append(train_score)
testing_scores.append(test_score)
# score_pre = cross_val_score(regr, feature, label, cv=10).mean()
# cross_scores.append(score_pre)
print("num " + str(num) + " ---------------")
print("train_score " + str(train_score))
print("test_score "+ str(test_score))
#print("cross_score " + str(score_pre))
test_score_max = max(testing_scores)
print('testing_scores最大得分:{}'.format(test_score_max),
'testing_scores子树数量为:{}'.format(testing_scores.index(test_score_max) * 2))
train_score_max = max(training_scores)
print('train_scores最大得分:{}'.format(train_score_max),
'train_scores子树数量为:{}'.format(training_scores.index(train_score_max) * 2))
# crosss_score_max = max(cross_scores)
# print('cross_scores最大得分:{}'.format(crosss_score_max),
# 'cross_scores子树数量为:{}'.format(cross_scores.index(crosss_score_max) * 2))
ax.plot(nums,training_scores,label="Training Score")
ax.plot(nums,testing_scores,label="Testing Score")
#ax.plot(nums,cross_scores,label="Cross Score")
ax.set_xlabel("estimator num")
ax.set_ylabel("score")
ax.legend(loc="lower right")
ax.set_ylim(-1,1)
plt.suptitle("RandomForestRegressor")
# 设置 X 轴的网格线,风格为 点画线
plt.grid(axis='x',linestyle='-.')
plt.show()
def test_RandomForestRegressor_max_depth(self,n_esimator):
'''
测试 RandomForestRegressor 的预测性能随 max_depth 参数的影响
:param data: 可变参数。它是一个元组,这里要求其元素依次为:训练样本集、测试样本集、训练样本的值、测试样本的值
:return: None
'''
X_train, y_train,X_test,y_test = self.data
feature, label = self.total
maxdepths=range(1,30)
fig=plt.figure()
ax=fig.add_subplot(1,1,1)
testing_scores=[]
training_scores=[]
cross_scores = []
for max_depth in maxdepths:
regr= se.RandomForestRegressor(max_depth=max_depth,n_estimators=n_esimator)
regr.fit(X_train,y_train)
train_score = regr.score(X_train, y_train)
test_score = regr.score(X_test, y_test)
training_scores.append(train_score)
testing_scores.append(test_score)
# score_pre = cross_val_score(regr, feature, label, cv=10).mean()
# cross_scores.append(score_pre)
print("max_depth " + str(max_depth) + " ---------------")
print("train_score " + str(train_score))
print("test_score " + str(test_score))
#print("cross_score " + str(score_pre))
test_score_max = max(testing_scores)
print('testing_scores最大得分:{}'.format(test_score_max),
'testing_scores max_depth:{}'.format(testing_scores.index(test_score_max)))
train_score_max = max(training_scores)
print('train_scores最大得分:{}'.format(train_score_max),
'train_scores max_depth:{}'.format(training_scores.index(train_score_max)))
# crosss_score_max = max(cross_scores)
# print('cross_scores最大得分:{}'.format(crosss_score_max),
# 'cross_scores子树数量为:{}'.format(cross_scores.index(crosss_score_max) * 2))
ax.plot(maxdepths,training_scores,label="Training Score")
ax.plot(maxdepths,testing_scores,label="Testing Score")
ax.plot(maxdepths, cross_scores, label="Cross Score")
ax.set_xlabel("max_depth")
ax.set_ylabel("score")
ax.legend(loc="lower right")
ax.set_ylim(0,1.05)
plt.suptitle("RandomForestRegressor")
# 设置 X 轴的网格线,风格为 点画线
plt.grid(axis='x',linestyle='-.')
plt.show()
# def load_data(self):
# X_train, y_train,custom_datasets_test,X_test,y_test = load_data9to1()
# self.test = custom_datasets_test
# self.data = X_train,y_train,X_test,y_test
def train(self):
train_feature,train_label,X_test,y_test = self.data
print("RUN train")
model = se.RandomForestRegressor(max_depth= dic[config.type]["max_depth"],n_estimators=dic[config.type]["n"], min_samples_split=2,random_state= 1, warm_start=True)
model.fit(train_feature, train_label)
score = model.score(X_test, y_test)
print(score)
joblib.dump(model, config.modelname + config.type + ".m")
def predict(self):
# model_test
self.model = joblib.load(config.modelname + config.type + ".m")
custom_datasets_test = self.test
index = config.train_start_id
self.lists=[]
for test_dataset in custom_datasets_test:
predict = self.model.predict(test_dataset[:][0])
_cor = self.model.score(test_dataset[:][0], test_dataset[:][1].ravel())
mse = sm.mean_squared_error(predict, test_dataset[:][1])
_max_error = round(sm.max_error(test_dataset[:][1], predict) * 1000, 2)
_mean_error = round(sm.mean_absolute_error(test_dataset[:][1], predict) * 1000, 2)
if config.flag_to_net == 1:
if index == 16:
index = index + 1
data = pd.DataFrame(columns=names, data = test_dataset[:][0])
data['label'] = test_dataset[:][1].ravel()
data['predict'] = predict
data['abs_error'] = abs(predict - test_dataset[:][1].ravel())
data['abs_error/label'] = abs(predict - test_dataset[:][1].ravel()) / test_dataset[:][1].ravel()
# data['abs_error'] = abs(predict-test_dataset)
data.to_csv("./testPredictrf/b" + str(index) + config.type + "_test.csv", index=False, sep=',')
self.lists.append([index,_cor, mse,_max_error,_mean_error])
index = index + 1
colName = ["file","_cor","mse","max_error","mean_error"]
names = ["drive_load","wire_input_slew","strength","elmore_delay","sink_load","sink_pin_capacitance"]
#mode 0 dic = {"delay": {"n": 96, "max_depth": 9}, "slew": {"n": 48, "max_depth": 15}}
#dic = {"delay": {"n": 40, "max_depth": 12}, "slew": {"n": 48, "max_depth": 15}} #mode 1
#groupId,pin_name,drive_load,D2M,dis,strength,sink_load,sink_pin_capacitance,elmore_delay,sink_pin_num,output_capacitance
#names = ["drive_load","D2M","strength","elmore_delay","sink_load","sink_pin_capacitance","elmore_delay"] #slew,delay的特征
#names = ["drive_load","strength","sink_load","sink_pin_capacitance","elmore_delay","sink_pin_num"] #output load的特征
dic = {"wire_delay": {"n": 200, "max_depth": 11}, "wire_slew": {"n": 46, "max_depth": 40},"output_capacitance":{"n": 46, "max_depth": 22}} #all mode
setup_seed(24) #delay best 24
#groupId,pin_name,drive_load,wire_input_slew,D2M,d_x,d_y,s_x,s_y,dis,strength,sink_load,sink_pin_capacitance,elmore_delay,wire_slew,wire_delay
#groupId,mode,pin_name,drive_load,wire_input_slew,D2M,d_x,d_y,s_x,s_y,dis,strength,sink_load,sink_pin_capacitance,elmore_delay,wire_slew,wire_delay
def main():
model = RandomModel()
model.load_data9to1()
train(model)
#parameter(model)
to_csv(model)
def train(model):
if config.train_flag == 0:
model.train()
model.predict()
def parameter(model):
#model.test_RandomForestRegressor_num()
model.test_RandomForestRegressor_max_depth(48)
def to_csv(model):
res_csv = pd.DataFrame(columns=colName, data=model.lists)
import time
now = int(round(time.time() * 1000))
now02 = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(now / 1000))
res_csv.to_csv("./OursDC/" + config.type + "_m" + str(dic[config.type]["max_depth"]) + "_n" + str(dic[config.type]["n"]) + "_" + now02 + ".csv")
main()
以上实验的基本特征如下:
names = [“drive_load”,“wire_input_slew”,“D2M”,“strength”,“elmore_delay”,“sink_load”,“sink_pin_capacitance”]
顺便说一句,“D2M”在随机森林里面作用不大,在lightgbm里面作用挺大。
加入part:多一个part字段
加入10个特征:多加了P10-P1(10个字段)
没有加入part的效果如下split.rf:
加入part的效果如下split.rf:
加入10个特征的效果如下split.rf:
7月14日
下午,我打算把实验数据代码迁移到这个电脑上。重新整理一下。
代码迁移到hp上暂时失败
7月15日
在ubuntu上将划分的特征进行异或,作为dist特征。
int dist = _root->_gate->getPart() & rcnode._pin->_gate->getPart()
批量生成dist特征,使用scripts/injectPartion.py
特征提取完成,生成到prNetDcFea/dist-way文件夹下。
用cro.lg跑一次
明天要搞清楚dump_rf_feature的逻辑关系,否则特征提取会有问题!
7月16日
![image-20210716101241073](https://i-blog.csdnimg.cn/blog_migrate/a7462557d4123ee3789b5516fe56d436.png)
RCTree多了一个中间节点,它是null。
- 第一层循环:遍历RCTree的所有RCNode sink节点(除了null)。
- 第二层循环:遍历rcNode sink的pin,回到上面的net。找fanin,也就是找出时序弧from为root,to为rcNode._pin。
- 最后就是特征提取:节点特征从rcnode中提取,label就是从arc中提取slew/delay。
Pin类里面写一个成员函数,然后使用Net*,找出其它sink节点driver异或。
14:31 先取均值
float Pin::getContextMean(){
auto pins = (*_net).getAllPin();
auto root_pin = (*_net).rootPin();
float sum = 0;
int count = 0;
for(auto pin : pins){
if(pin != root_pin && pin->name() != name() && pin->_name.find(":") != std::string::npos){
sum += pin->_gate->getPart() & root_pin->_gate->getPart();
count++;
}
}
return count !=0 ? sum / count : 0;
}
bug: return count !=0 ? sum / count : 0;
有些线网就一个pin,那么其它sink pin的与驱动dist就是0.这里出现-nan,是因为有除以count0的存在。
![image-20210716152914843](https://i-blog.csdnimg.cn/blog_migrate/624f1291cb27eedcf1ef42c8aa85916a.png)
将数据迁移到hp电脑上了!!!
OSError: [Errno 22] Invalid argument已解决(to_csv()时遇到以时间为文件名)
吓一跳,应该是将中文换成英文后,路径出现变化,桌面上图标就不见了。
PyCharm也能正常跑模型代码。
现在将特征数据提取到pycharm中,进行模型训练。
void Net::dump_rf_feature(std::ostream &os,Split el,Tran rf,int groupId) const{
//os<<"groupId,part,pin_name,drive_load,wire_input_slew,D2M,d_x,d_y,s_x,s_y,dis,strength,sink_load,sink_pin_capacitance,elmore_delay,wire_slew,wire_delay"<<std::endl;
const Rct* rctree= rct();
// Loc rootLoc = _root->_loc;
int strength = drive_strength();
//_root������ǿ�� (Net��)
//_sink_pin��root_pin�ľ��루sink_pin�
//net ��context_dis����???����
for(auto [name,rcnode]:(*rctree)._nodes)
{
if(rcnode._name != rctree->_root->_name )
{
if(rcnode._pin == nullptr)
{
//rc_node ��ʱ��������
continue;
}
else
{
for(auto arc:rcnode._pin->_fanin)
{
if(arc->_from.name() == _root->name())
{
Loc sub = _root->_loc - rcnode._pin->_loc;
// double dis = sub.P2Odis();
//sink_pin
//std::cout << rcnode._pin->_name <<std::endl;
if(rcnode._pin->_name.find(":") != std::string::npos && _root->gate() != nullptr){
int dist = _root->gate()->getPart() & rcnode._pin->_gate->getPart();
int part = rcnode._pin->_gate->getPart() ;
int parts[10] = {0};
int tpart = part;
for(int i = 9;i >=0 && tpart!=0;i --){
parts[i] = tpart & 1;
tpart = tpart >> 1;
}
os
<< groupId << ","
<< part << ","
<< dist << ","
<< rcnode._pin->getContextMean() << ","
<< parts[0] << ","
<< parts[1] << ","
<< parts[2] << ","
<< parts[3] << ","
<< parts[4] << ","
<< parts[5] << ","
<< parts[6] << ","
<< parts[7] << ","
<< parts[8] << ","
<< parts[9] << ","
<<rcnode._pin->_name<<","<<rctree->_root->_load[el][rf]<<","<<*_root->slew(el,rf)<<","<<\
rcnode._ldelay[el][rf]<<","<<_root->_loc._x<<","<<_root->_loc._y<<","<<rcnode._pin->_loc._x<<","<<rcnode._pin->_loc._y<<","<<sub.P2Odis()<<","<<strength<<\
","<<rcnode._load[el][rf]<<","<<rcnode._pin->cap(el,rf)<<","<<rcnode.delay(el,rf)<<\
"," <<*arc->_to.slew(el,rf) <<","<<*arc->_delay[el][rf][rf]<<std::endl;
}
}
}
}
}
else
{
//root_node ��ʱ��������
continue;
}
}
}
7月18日
修改小论文,pycharm 全部文件搜索关键词(全局文件搜索) - 博二爷 - 博客园 (cnblogs.com)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-id1Yr4zk-1653533351040)(https://i.loli.net/2021/07/18/Yh1B9OASKePtJUs.png)]
如何用sklearn对随机森林调参? - 知乎 (zhihu.com)
“DC Ours" 采用最大深度25,子树数量46.
"Ours"采用最大深度为22,子树数量为46.
7月30日
上回说道:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yj62faDy-1653533351042)(https://i.loli.net/2021/07/30/DkN3q4vaLpcKEbT.png)]
目前已经将driver-sink之间的part异或作为dist特征
还有其余sink的part(与driver异或)得到均值。如下图所示:
下面对它们进行训练。
随机森林训练效果演示
- 用cro.rf 在不加划分特征的情况下,进行训练。特征:[“drive_load”,“wire_input_slew”,“D2M”,“strength”,“elmore_delay”,“sink_load”,“sink_pin_capacitance”]
-
加入[“drive_load”,“dist”,“meanvalue”,“wire_input_slew”,“D2M”,“strength”,“elmore_delay”,“sink_load”,“sink_pin_capacitance”] dist和meanvalue进行训练
-
下面使用meanvalue进行训练,[“drive_load”,“meanvalue”,“wire_input_slew”,“D2M”,“strength”,“elmore_delay”,“sink_load”,“sink_pin_capacitance”]
9月15日
问题:我们使用的是delay/slew是用布局布线后通过PT跑出来的时序结果,将它们作为label进行训练和预测。之后就是算时序路径的slack,同一个逻辑综合的netlist,会有多个布局布线的模式,那么选择哪个模式作为标准答案进行对比呢?怎么判断算出来的时序路径(时序违例)是否正确呢?定性分析(时序违例判断是否正确)还要定量分析(slack差值大不大)
方案:根据《Net2》论文中提出的方案:PR只用一种策略。也就是在我们之后的模型训练和预测、以及最后slack的比较中,只参考一种PR方式。
明日计划:只使用一种PR的数据进行模型的训练和预测。重新跑一下数据。之前我们都是使用了几种PR,混在一起进行模型的训练和预测的。接下来我会只使用一种PR来做实验。
每个Design有5种逻辑综合的策略,对应的又有4种PR的策略。
9月16日
解决多模块问题
- 利用线负载模型生成逻辑综合SPEF
debug调试 spef.cpp 是用来debug 利用线负载模型生成逻辑综合阶段下的SPEF文件
spef.cpp如下:
#include <ot/timer/timer.hpp>
#include <sstream>
#include <fstream>
#include <iomanip>
int main(int argc, char* argv[]) {
ot::Timer timerSyn;
std::string filepath = argv[1];// 输出spef文件路径
std::string fileIndex_1 = argv[2];//哪种desgin
std::string fileIndex_2 = argv[3];//design之下哪种synthesis
std::string fileIndex = fileIndex_1 + "_" + fileIndex_2;
std::ofstream logofs(filepath + "/" + fileIndex + ".spef");
std::ostream &logos = logofs.good()?logofs:std::cout;
// Builder
// - read cell library for both early/late mode (nullopt)
// - read the verilog
// - read the parasitics
// - read the design constraints
std::string synNetFileName = "b" + fileIndex + "_SYN.v";
std::string prNetFileName = "b" + fileIndex + "_pr_mode_0.v";//默认使用第一种PR策略
std::string synSDCfileName = "b" + fileIndex + "_syn.sdc";
std::string prSDCfileName = "b" + fileIndex + "_pr.sdc";
std::string prSPEFfileName = "b" + fileIndex + "_pr_mode_0.spef";//默认使用第一种PR策略
//读取celllib
timerSyn.read_celllib("./data/lib/data.lib")
//读取 多模块的逻辑综合netlist
.read_multi_verilog("./data/netlist/" + synNetFileName)
//读取 逻辑综合的sdc约束文件
.read_sdc("./data/sdc/b" + fileIndex_1 + "/" + synSDCfileName);
timerSyn.update_timing();
timerSyn.write_spef(logos);
//timerSyn.dump_verilog(logos,fileIndex+"_"+mode);
return 0;
}
尝试生成b20_0 多模块下的spef如下图所示:
-
读取多模块的netlist以及相应生成的SPEF
multimodule.cpp如下:
/*
这个文件用来debug 多模块读取
date: 2021/9/16 下午17:07
*/
#include <ot/timer/timer.hpp>
#include <sstream>
#include <fstream>
#include <iomanip>
int main(int argc, char* argv[]) {
ot::Timer timerSyn;
std::string fileIndex_1 = argv[2];//哪个design
std::string fileIndex_2 = argv[3];//逻辑综合的第n个策略
std::string fileIndex = fileIndex_1 + "_" + fileIndex_2;//
std::string synNetFileName = "b" + fileIndex + "_SYN.v";
std::string prNetFileName = "b" + fileIndex + "_pr_mode_0.v";//默认使用第一种PR策略
std::string synSDCfileName = "b" + fileIndex + "_syn.sdc";
std::string prSDCfileName = "b" + fileIndex + "_pr.sdc";
std::string prSPEFfileName = "b" + fileIndex + "_pr_mode_0.spef";//默认使用第一种PR策略
//读取celllib
timerSyn.read_celllib("./data/lib/data.lib")
//读取 多模块的逻辑综合netlist
.read_multi_verilog("./data/netlist/" + synNetFileName)
//读取线负载模型下生成的逻辑综合的spef
.read_spef("./logs/spef2/"+fileIndex +".spef")
//读取 逻辑综合的sdc约束文件
.read_sdc("./data/sdc/b" + fileIndex_1 + "/" + synSDCfileName);
timerSyn.update_timing();
return 0;
}
从下图来看,spef读取成功了。
- 尝试读取PT时序报告
/*
这个文件用来debug 多模块读取
date: 2021/9/16 下午17:07
*/
#include <ot/timer/timer.hpp>
#include <sstream>
#include <fstream>
#include <iomanip>
int main(int argc, char* argv[]) {
ot::Timer timerSyn;
std::string fileIndex_1 = argv[2];//哪个design
std::string fileIndex_2 = argv[3];//逻辑综合的第n个策略
std::string fileIndex = fileIndex_1 + "_" + fileIndex_2;//
std::string synNetFileName = "b" + fileIndex + "_SYN.v";
std::string prNetFileName = "b" + fileIndex + "_pr_mode_0.v";//默认使用第一种PR策略
std::string synSDCfileName = "b" + fileIndex + "_syn.sdc";
std::string prSDCfileName = "b" + fileIndex + "_pr.sdc";
std::string prSPEFfileName = "b" + fileIndex + "_pr_mode_0.spef";//默认使用第一种PR策略
//读取celllib
timerSyn.read_celllib("./data/lib/data.lib")
//读取 多模块的逻辑综合netlist
.read_multi_verilog("./data/netlist/" + synNetFileName)
//读取 逻辑综合之下的PT时序报告
.read_place("./data/DataDPT/data/b"+fileIndex)
//读取线负载模型下生成的逻辑综合的spef
.read_spef("./logs/spef2/"+fileIndex +".spef")
//读取 逻辑综合的sdc约束文件
.read_sdc("./data/sdc/b" + fileIndex_1 + "/" + synSDCfileName);
timerSyn.update_timing();
return 0;
}
时序报告里面的格式:每个instance的各个pin
- 生成多模块下的特征文件,一种PR策略。提取一一对应的线网。
10月13日
看师兄的专利。
特征和时延,也要看信号的跳变。分情况,例如Gate Delay的输入引脚信号是上升沿,输出引脚信号是下降沿,此时对应的特征和时延,作为模型的样本。其它情况对应的跳变情况也要考虑。
10月14日
from math import sqrt
points = []
graph = []
def findSmallest(key, mstSet):
min = float("inf")
for v in range(len(graph)):
if key[v] < min and mstSet[v] == False:
min = key[v]
min_index = v
return min_index
def getMSTWeight(parent):
global graph
weight = 0
for i in range(1, len(graph)):
weight = weight + graph[i][parent[i]]
return weight
def primMST():
global graph
key = [float("+inf")] * len(graph)
parent = [None] * len(graph)
key[0] = 0
mstSet = [False] * len(graph)
parent[0] = -1
for cout in range(len(graph)):
u = findSmallest(key, mstSet)
mstSet[u] = True
for v in range(len(graph)):
if graph[u][v] > 0 and mstSet[v] == False and key[v] > graph[u][v]:
key[v] = graph[u][v]
parent[v] = u
return getMSTWeight(parent)
def findLeftMostPoint(points):
return sorted(points)[0]
def findRightMostPoint(points):
return sorted(points)[-1]
def findTopMostPoint(points):
return sorted(points, key = lambda x : x[1])[-1]
def findBottomMostPoint(points):
return sorted(points, key = lambda x : x[1])[0]
def findTopLeftPoint(topPoint, leftPoint):
return [leftPoint[0], topPoint[1]]
def findTopRightPoint(topPoint, rightPoint):
return [rightPoint[0], topPoint[1]]
def findBottomLeftPoint(bottomPoint, leftPoint):
return [leftPoint[0], bottomPoint[1]]
def findDist(point1, point2):
x1, y1 = point1
x2, y2 = point2
return abs(x2 - x1) + abs(y2 - y1)
def HPWL(points):
leftPoint = findLeftMostPoint(points)
topPoint = findTopMostPoint(points)
bottomPoint = findBottomMostPoint(points)
rightPoint = findRightMostPoint(points)
rectTopLeftPoint = findTopLeftPoint(topPoint, leftPoint)
rectBottomLeftPoint = findBottomLeftPoint(bottomPoint, leftPoint)
rectTopRightPoint = findTopRightPoint(topPoint, rightPoint)
rectLength = findDist(rectTopLeftPoint, rectBottomLeftPoint)
rectBreadth = findDist(rectTopLeftPoint, rectTopRightPoint)
return rectLength + rectBreadth
def getSteinerPoints(points):
steinerPoints = []
for pt1 in points:
for pt2 in points:
if pt1 == pt2:
continue
x1, y1 = pt1
x2, y2 = pt2
steiner_pt1 = [x1, y2]
steiner_pt2 = [x2, y1]
if steiner_pt1 not in points + steinerPoints:
steinerPoints.append(steiner_pt1)
if steiner_pt2 not in points + steinerPoints:
steinerPoints.append(steiner_pt2)
return steinerPoints
def computeMST(points):
global graph
adjMatrix = []
for point1 in points:
adjMatrix.append([])
for point2 in points:
adjMatrix[-1].append(findDist(point1, point2))
graph = adjMatrix
return primMST()
with open("net7", "r") as file:
lines = file.read().split("\n")
for line in lines:
line = line.strip()
if len(line) == 0:
continue
nums = line.split(" ")
points.append([int(nums[0]), int(nums[1])])
print ("# Points: " + str(len(points)))
print ("HPWL: " + str(HPWL(points)))
currentMST_weight = computeMST(points)
print ("MST: " + str(currentMST_weight))
while True:
bestMST = currentMST_weight + 1
bestSteinerPoint = []
steinerPoint = getSteinerPoints(points)
for s_pt in steinerPoint:
MSTwt = computeMST(points + [s_pt])
if MSTwt < bestMST:
bestMST = MSTwt
bestSteinerPoint = s_pt
if bestMST < currentMST_weight:
currentMST_weight = bestMST
points = points + [bestSteinerPoint]
else:
break
print("Steiner tree: " + str(currentMST_weight))