Просмотр исходного кода

[research/lotterysim] update cascade report with pi vs pid, and pi scripts

ertosns 3 лет назад
Родитель
Сommit
fc012bb2c4

+ 2 - 2
script/research/lotterysim/acc_vs_staked_ratio.py

@@ -30,12 +30,12 @@ for nodes in numpy.linspace(1,NODES, 10):
         effective_airdrop*=float(staked_ratio)
         effective_airdrop*=float(staked_ratio)
         stake_portion = effective_airdrop/airdrop*100
         stake_portion = effective_airdrop/airdrop*100
         print("network airdrop: {}, staked token: {}/{}% on {} nodes".format(airdrop, effective_airdrop, stake_portion, len(darkies)))
         print("network airdrop: {}, staked token: {}/{}% on {} nodes".format(airdrop, effective_airdrop, stake_portion, len(darkies)))
-    avg_acc = sum(accs)/EXPS*100
+    avg_acc = sum(accs)/EXPS
     plot+=[(stake_portion, avg_acc)]
     plot+=[(stake_portion, avg_acc)]
 
 
 
 
 plt.plot([x[0] for x in plot], [x[1] for x in plot])
 plt.plot([x[0] for x in plot], [x[1] for x in plot])
 plt.xlabel('drk staked %')
 plt.xlabel('drk staked %')
 plt.ylabel('accuracy %')
 plt.ylabel('accuracy %')
-plt.savefig('img'+os.sep+'stake.png')
+plt.savefig('img'+os.sep+'stake_pid.png')
 plt.show()
 plt.show()

+ 41 - 0
script/research/lotterysim/acc_vs_staked_ratio_pi.py

@@ -0,0 +1,41 @@
+from core.lottery import *
+import os
+import numpy
+from matplotlib import pyplot as plt
+
+os.system("rm log/f_output.hist; rm log/f_feedback.hist")
+
+RUNNING_TIME = int(input("running time:"))
+ERC20DRK=2.1*10**9
+NODES=1000
+plot = []
+EXPS=10
+for nodes in numpy.concatenate((numpy.array([1,5]), numpy.linspace(10,NODES, 10))):
+    accs = []
+    for _ in range(EXPS):
+        darkies = []
+        egalitarian = ERC20DRK/NODES
+        darkies += [ Darkie(random.gauss(egalitarian, egalitarian*0.1), strategy=random_strategy(EPOCH_LENGTH)) for id in range(int(nodes)) ]
+        #darkies += [Darkie() for _ in range(NODES*2)]
+        airdrop = ERC20DRK
+        dt = DarkfiTable(airdrop, RUNNING_TIME, CONTROLLER_TYPE_DISCRETE, kp=-0.0104, ki=-0.0366, kd=0.0384,  r_kp=-2.53, r_ki=29.5, r_kd=53.77)
+        #dt = DarkfiTable(airdrop, RUNNING_TIME, CONTROLLER_TYPE_DISCRETE, kp=-0.0104, ki=-0.0366, kd=0,  r_kp=-2.53, r_ki=29.5, r_kd=0)
+        for darkie in darkies:
+            dt.add_darkie(darkie)
+        acc, apy, reward, staked_ratio, apr = dt.background(rand_running_time=False)
+        accs += [acc]
+        effective_airdrop  = 0
+        for darkie in darkies:
+            effective_airdrop+=darkie.stake
+        effective_airdrop*=float(staked_ratio)
+        stake_portion = effective_airdrop/airdrop*100
+        print("network airdrop: {}, staked token: {}/{}% on {} nodes".format(airdrop, effective_airdrop, stake_portion, len(darkies)))
+    avg_acc = sum(accs)/EXPS
+    plot+=[(stake_portion, avg_acc)]
+
+
+plt.plot([x[0] for x in plot], [x[1] for x in plot])
+plt.xlabel('drk staked %')
+plt.ylabel('accuracy %')
+plt.savefig('img'+os.sep+'stake_pi.png')
+plt.show()

+ 2 - 1
script/research/lotterysim/discrete_instance.py

@@ -28,7 +28,8 @@ if __name__ == "__main__":
         effective_airdrop+=darkie.stake
         effective_airdrop+=darkie.stake
     print("network airdrop: {}, staked token: {}/{}% on {} nodes".format(airdrop, effective_airdrop, effective_airdrop/airdrop*100, len(darkies)))
     print("network airdrop: {}, staked token: {}/{}% on {} nodes".format(airdrop, effective_airdrop, effective_airdrop/airdrop*100, len(darkies)))
     #dt = DarkfiTable(airdrop, RUNNING_TIME, CONTROLLER_TYPE_DISCRETE, kp=-0.010399999999938556, ki=-0.0365999996461878, kd=0.03840000000000491,  r_kp=-2.53, r_ki=29.5, r_kd=53.77)
     #dt = DarkfiTable(airdrop, RUNNING_TIME, CONTROLLER_TYPE_DISCRETE, kp=-0.010399999999938556, ki=-0.0365999996461878, kd=0.03840000000000491,  r_kp=-2.53, r_ki=29.5, r_kd=53.77)
-    dt = DarkfiTable(airdrop, RUNNING_TIME, CONTROLLER_TYPE_DISCRETE, kp=-0.010399999999938556, ki=-0.0365999996461878, kd=0.03840000000000491,  r_kp=-0.63, r_ki=3.35, r_kd=-1.11)
+    #dt = DarkfiTable(airdrop, RUNNING_TIME, CONTROLLER_TYPE_DISCRETE, kp=-0.010399999999938556, ki=-0.0365999996461878, kd=0.03840000000000491,  r_kp=-0.63, r_ki=3.35, r_kd=-1.11)
+    dt = DarkfiTable(airdrop, RUNNING_TIME, CONTROLLER_TYPE_DISCRETE, kp=-0.010399999999938556, ki=-0.0365999996461878, kd=0,  r_kp=-0.63, r_ki=3.35, r_kd=0)
     for darkie in darkies:
     for darkie in darkies:
         dt.add_darkie(darkie)
         dt.add_darkie(darkie)
     acc, avg_apy, avg_reward, stake_ratio, avg_apr = dt.background(rand_running_time=False)
     acc, avg_apy, avg_reward, stake_ratio, avg_apr = dt.background(rand_running_time=False)

BIN
script/research/lotterysim/img/apr_distribution.png


BIN
script/research/lotterysim/img/feedback_history_processed.png


BIN
script/research/lotterysim/img/output_history_processed.png


BIN
script/research/lotterysim/img/stake.png


BIN
script/research/lotterysim/img/stake_pi.png


+ 163 - 0
script/research/lotterysim/primary_discrete_auto_crawler_pi.py

@@ -0,0 +1,163 @@
+from argparse import ArgumentParser
+from core.lottery import DarkfiTable
+from core.utils import *
+from core.darkie import Darkie
+from tqdm import tqdm
+import os
+from core.strategy import random_strategy
+
+AVG_LEN = 10
+
+KP_STEP=0.01
+KP_SEARCH=-0.63
+
+KI_STEP=0.01
+KI_SEARCH=3.35
+
+RUNNING_TIME=1000
+NODES = 1000
+
+SHIFTING = 0.05
+
+highest_apr = 0.05
+highest_acc = 0.2
+highest_staked = 0.3
+lowest_apr2target_diff = 1
+
+KP='kp'
+KI='ki'
+
+KP_RANGE_MULTIPLIER = 2
+KI_RANGE_MULTIPLIER = 2
+
+
+highest_gain = (KP_SEARCH, KI_SEARCH)
+
+parser = ArgumentParser()
+parser.add_argument('-p', '--high-precision', action='store_false', default=False)
+parser.add_argument('-r', '--randomizenodes', action='store_true', default=True)
+parser.add_argument('-t', '--rand-running-time', action='store_true', default=True)
+parser.add_argument('-d', '--debug', action='store_false')
+args = parser.parse_args()
+high_precision = args.high_precision
+randomize_nodes = args.randomizenodes
+rand_running_time = args.rand_running_time
+debug = args.debug
+
+def experiment(controller_type=CONTROLLER_TYPE_DISCRETE, rkp=0, rki=0, distribution=[], hp=True):
+    dt = DarkfiTable(ERC20DRK, RUNNING_TIME, controller_type, kp=-0.010399999999938556, ki=-0.0365999996461878, kd=0, r_kp=rkp, r_ki=rki, r_kd=0)
+    RND_NODES = random.randint(5, NODES) if randomize_nodes else NODES
+    for idx in range(0,RND_NODES):
+        darkie = Darkie(distribution[idx], strategy=random_strategy(EPOCH_LENGTH))
+        dt.add_darkie(darkie)
+    acc, apy, reward, stake_ratio, apr = dt.background(rand_running_time, hp)
+    return acc, apy, reward, stake_ratio, apr
+
+def multi_trial_exp(kp, ki, distribution = [], hp=True):
+    global highest_apr
+    global highest_acc
+    global highest_staked
+    global highest_gain
+    global lowest_apr2target_diff
+    new_record=False
+    accs = []
+    aprs = []
+    rewards = []
+    stakes_ratios = []
+    aprs = []
+    for i in range(0, AVG_LEN):
+        acc, apy, reward, stake_ratio, apr = experiment(CONTROLLER_TYPE_DISCRETE, rkp=kp, rki=ki, distribution=distribution, hp=hp)
+        accs += [acc]
+        rewards += [reward]
+        aprs += [apr]
+        stakes_ratios += [stake_ratio]
+    avg_acc = float(sum(accs))/AVG_LEN
+    avg_reward = float(sum(rewards))/AVG_LEN
+    avg_staked = float(sum(stakes_ratios))/AVG_LEN
+    avg_apr = float(sum(aprs))/AVG_LEN
+    buff = 'avg(acc): {}, avg(apr): {}, avg(reward): {}, avg(stake ratio): {}, kp: {}, ki:{}, '.format(avg_acc, avg_apr, avg_reward, avg_staked, kp, ki)
+    if avg_apr > 0:
+        gain = (kp, ki)
+        acc_gain = (avg_apr, gain)
+        apr2target_diff = math.fabs(avg_apr - float(TARGET_APR))
+        #if  avg_acc > highest_acc and apr2target_diff < 0.08:
+        if  avg_acc > highest_acc:
+            new_record = True
+            highest_apr = avg_apr
+            highest_acc = avg_acc
+            highest_staked = avg_staked
+            highest_gain = (kp, ki)
+            lowest_apr2target_diff = apr2target_diff
+            with open('log'+os.sep+"highest_gain.txt", 'w') as f:
+                f.write(buff)
+    return buff, new_record
+
+def crawler(crawl, range_multiplier, step=0.1):
+    start = None
+    if crawl==KP:
+        start = highest_gain[0]
+    elif crawl==KI:
+        start = highest_gain[1]
+
+    range_start = (start*range_multiplier if start <=0 else -1*start)
+    range_end = (-1*start if start<=0 else range_multiplier*start)
+    # if number of steps under 10 step resize the step to 50
+    while (range_end-range_start)/step < 10:
+        range_start -= SHIFTING
+        range_end += SHIFTING
+        step /= 10
+
+    while True:
+        try:
+            crawl_range = np.arange(range_start, range_end, step)
+            break
+        except Exception as e:
+            print('start: {}, end: {}, step: {}, exp: {}'.format(range_start, rang_end, step, e))
+            step*=10
+    np.random.shuffle(crawl_range)
+    crawl_range = tqdm(crawl_range)
+    distribution = [random.gauss(ERC20DRK/NODES, ERC20DRK/NODES*0.1) for i in range(NODES)]
+    for i in crawl_range:
+        kp = i if crawl==KP else highest_gain[0]
+        ki = i if crawl==KI else highest_gain[1]
+        buff, new_record = multi_trial_exp(kp, ki, distribution, hp=high_precision)
+        crawl_range.set_description('highest:{} / {}'.format(highest_acc, buff))
+        if new_record:
+            break
+
+while True:
+    prev_highest_gain = highest_gain
+    # kp crawl
+    crawler(KP, KP_RANGE_MULTIPLIER, KP_STEP)
+    if highest_gain[0] == prev_highest_gain[0]:
+        KP_RANGE_MULTIPLIER+=1
+        KP_STEP/=10
+    else:
+        start = highest_gain[0]
+        range_start = (start*KP_RANGE_MULTIPLIER if start <=0 else -1*start) - SHIFTING
+        range_end = (-1*start if start<=0 else KP_RANGE_MULTIPLIER*start) + SHIFTING
+        while (range_end - range_start)/KP_STEP >500:
+            #if KP_STEP < 0.1:
+            KP_STEP*=2
+            KP_RANGE_MULTIPLIER-=1
+            #TODO (res) shouldn't the range also shrink?
+            # not always true.
+            # how to distinguish between thrinking range, and large step?
+            # good strategy is step shoudn't > 0.1
+            # range also should be > 0.8
+            # what about range multiplier?
+
+    # ki crawl
+    crawler(KI, KI_RANGE_MULTIPLIER, KI_STEP)
+    if highest_gain[1] == prev_highest_gain[1]:
+        KI_RANGE_MULTIPLIER+=1
+        KI_STEP/=10
+    else:
+        start = highest_gain[1]
+        range_start = (start*KI_RANGE_MULTIPLIER if start <=0 else -1*start) - SHIFTING
+        range_end = (-1*start if start<=0 else KI_RANGE_MULTIPLIER*start) + SHIFTING
+        while (range_end - range_start)/KI_STEP >500:
+            #print('range_end: {}, range_start: {}, ki_step: {}'.format(range_end, range_start, KI_STEP))
+            #if KP_STEP < 1:
+            KI_STEP*=2
+            KI_RANGE_MULTIPLIER-=1

+ 2 - 1
script/research/lotterysim/reports/cascade.aux

@@ -15,5 +15,6 @@
 \@writefile{lof}{\contentsline {figure}{\numberline {5}{\ignorespaces cascade PID diagram}}{7}{}\protected@file@percent }
 \@writefile{lof}{\contentsline {figure}{\numberline {5}{\ignorespaces cascade PID diagram}}{7}{}\protected@file@percent }
 \@writefile{lof}{\contentsline {figure}{\numberline {6}{\ignorespaces with accuracy set point to primary controller}}{8}{}\protected@file@percent }
 \@writefile{lof}{\contentsline {figure}{\numberline {6}{\ignorespaces with accuracy set point to primary controller}}{8}{}\protected@file@percent }
 \@writefile{lof}{\contentsline {figure}{\numberline {7}{\ignorespaces relation between staked DRK ratio and accuracy of secondary controller, with accuracy as set point to primary controller}}{9}{}\protected@file@percent }
 \@writefile{lof}{\contentsline {figure}{\numberline {7}{\ignorespaces relation between staked DRK ratio and accuracy of secondary controller, with accuracy as set point to primary controller}}{9}{}\protected@file@percent }
-\@writefile{toc}{\contentsline {section}{\numberline {5}conclusion}{10}{}\protected@file@percent }
+\@writefile{toc}{\contentsline {section}{\numberline {5}PI, or PID \dots  }{9}{}\protected@file@percent }
+\@writefile{toc}{\contentsline {section}{\numberline {6}conclusion}{10}{}\protected@file@percent }
 \gdef \@abspage@last{10}
 \gdef \@abspage@last{10}

+ 36 - 19
script/research/lotterysim/reports/cascade.log

@@ -1,4 +1,4 @@
-This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2022/dev/Debian) (preloaded format=pdflatex 2023.6.2)  22 JUN 2023 02:56
+This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2022/dev/Debian) (preloaded format=pdflatex 2023.6.2)  4 JUL 2023 22:04
 entering extended mode
 entering extended mode
  restricted \write18 enabled.
  restricted \write18 enabled.
  %&-line parsing enabled.
  %&-line parsing enabled.
@@ -546,36 +546,53 @@ Overfull \hbox (117.52686pt too wide) in paragraph at lines 181--182
 [][] 
 [][] 
  []
  []
 
 
-<acc_stake_1000nodes_10kslots_with_cascade.png, id=44, 462.528pt x 346.896pt>
+
+! LaTeX Error: Lonely \item--perhaps a missing list environment.
+
+See the LaTeX manual or LaTeX Companion for explanation.
+Type  H <return>  for immediate help.
+ ...                                              
+                                                  
+l.187 \item e
+             xperiments done on accuracy of cascade control, and relation be...
+
+? 
+
+Underfull \hbox (badness 10000) detected at line 187
+[][]
+ []
+
+[8 <./acc_setpoint_quarterstake.png>]
+<acc_stake_1000nodes_10kslots_with_cascade.png, id=48, 462.528pt x 346.896pt>
 File: acc_stake_1000nodes_10kslots_with_cascade.png Graphic file (type png)
 File: acc_stake_1000nodes_10kslots_with_cascade.png Graphic file (type png)
 <use acc_stake_1000nodes_10kslots_with_cascade.png>
 <use acc_stake_1000nodes_10kslots_with_cascade.png>
 Package pdftex.def Info: acc_stake_1000nodes_10kslots_with_cascade.png  used on
 Package pdftex.def Info: acc_stake_1000nodes_10kslots_with_cascade.png  used on
- input line 189.
+ input line 193.
 (pdftex.def)             Requested size: 462.52686pt x 346.89514pt.
 (pdftex.def)             Requested size: 462.52686pt x 346.89514pt.
 
 
-Overfull \hbox (117.52686pt too wide) in paragraph at lines 189--190
+Overfull \hbox (117.52686pt too wide) in paragraph at lines 193--194
 [][] 
 [][] 
  []
  []
 
 
-[8 <./acc_setpoint_quarterstake.png>] [9 <./acc_stake_1000nodes_10kslots_with_c
-ascade.png>] [10] (./cascade.aux) ) 
+[9 <./acc_stake_1000nodes_10kslots_with_cascade.png>] [10] (./cascade.aux) ) 
 Here is how much of TeX's memory you used:
 Here is how much of TeX's memory you used:
- 13365 strings out of 480171
- 274482 string characters out of 5894490
- 534248 words of memory out of 5000000
- 31079 multiletter control sequences out of 15000+600000
+ 13366 strings out of 480171
+ 274498 string characters out of 5894490
+ 534249 words of memory out of 5000000
+ 31080 multiletter control sequences out of 15000+600000
  474642 words of font info for 47 fonts, out of 8000000 for 9000
  474642 words of font info for 47 fonts, out of 8000000 for 9000
  59 hyphenation exceptions out of 8191
  59 hyphenation exceptions out of 8191
  100i,8n,104p,461b,754s stack positions out of 5000i,500n,10000p,200000b,80000s
  100i,8n,104p,461b,754s stack positions out of 5000i,500n,10000p,200000b,80000s
-</usr/share/texlive/texmf-dist/fonts/type1/p
-ublic/amsfonts/cm/cmbx12.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/
-amsfonts/cm/cmmi10.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/amsfon
-ts/cm/cmmi7.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/c
-mr10.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr12.pf
-b></usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr17.pfb></usr
-/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb></usr/share/t
-exlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb>
-Output written on cascade.pdf (10 pages, 188489 bytes).
+</u
+sr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx12.pfb></usr/sha
+re/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb></usr/share/tex
+live/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi7.pfb></usr/share/texlive/te
+xmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb></usr/share/texlive/texmf-dis
+t/fonts/type1/public/amsfonts/cm/cmr12.pfb></usr/share/texlive/texmf-dist/fonts
+/type1/public/amsfonts/cm/cmr17.pfb></usr/share/texlive/texmf-dist/fonts/type1/
+public/amsfonts/cm/cmr7.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/a
+msfonts/cm/cmsy10.pfb>
+Output written on cascade.pdf (10 pages, 189605 bytes).
 PDF statistics:
 PDF statistics:
  91 PDF objects out of 1000 (max. 8388607)
  91 PDF objects out of 1000 (max. 8388607)
  52 compressed objects within 1 object stream
  52 compressed objects within 1 object stream

BIN
script/research/lotterysim/reports/cascade.pdf


+ 4 - 0
script/research/lotterysim/reports/cascade.tex

@@ -182,6 +182,10 @@ in stake.
     \caption{with accuracy set point to primary controller}
     \caption{with accuracy set point to primary controller}
 \end{figure}
 \end{figure}
 
 
+\section {PI, or PID \dots}
+
+\item experiments done on accuracy of cascade control, and relation between staked ratio, and accuracy of finalization using PI, PID controllers, using same parameters in experiments above, with $kd=0$ in primary, and secondary controller, although secondary controller exhibit high oscillation, but overall accuracy doesn't change if derivative term is dropped.
+
 \begin{description}
 \begin{description}
 \item figure 7 shows the relation between staked DRK tokens ratio, and accuracy
 \item figure 7 shows the relation between staked DRK tokens ratio, and accuracy
 \end{description}
 \end{description}

BIN
script/research/lotterysim/reports/stake_pi.png


+ 7 - 7
script/research/lotterysim/secondary_discrete_auto_crawler.py

@@ -8,17 +8,17 @@ import os
 
 
 AVG_LEN = 5
 AVG_LEN = 5
 
 
-KP_STEP=0.3
-KP_SEARCH= 0.03
+KP_STEP=0.01
+KP_SEARCH= -0.01
 
 
-KI_STEP=0.3
-KI_SEARCH=1.95
+KI_STEP=0.01
+KI_SEARCH=-0.036
 
 
-KD_STEP=0.3
-KD_SEARCH=-0.95
+KD_STEP=0.01
+KD_SEARCH=0.0384
 
 
 EPSILON=0.0001
 EPSILON=0.0001
-RUNNING_TIME=1000
+RUNNING_TIME=10000
 NODES = 1000
 NODES = 1000
 
 
 highest_acc = 0
 highest_acc = 0

+ 136 - 0
script/research/lotterysim/secondary_discrete_auto_crawler_pi.py

@@ -0,0 +1,136 @@
+from argparse import ArgumentParser
+from core.lottery import DarkfiTable
+from core.utils import *
+from core.darkie import Darkie
+from tqdm import tqdm
+from core.strategy import SigmoidStrategy
+import os
+
+AVG_LEN = 5
+
+KP_STEP=0.01
+KP_SEARCH= -0.01
+
+KI_STEP=0.01
+KI_SEARCH=-0.036
+
+EPSILON=0.0001
+RUNNING_TIME=1000
+NODES = 1000
+
+highest_acc = 0
+
+KP='kp'
+KI='ki'
+
+KP_RANGE_MULTIPLIER = 2
+KI_RANGE_MULTIPLIER = 2
+
+highest_gain = (KP_SEARCH, KI_SEARCH)
+
+parser = ArgumentParser()
+parser.add_argument('-p', '--high-precision', action='store_true')
+parser.add_argument('-r', '--randomize-nodes', action='store_false')
+parser.add_argument('-t', '--rand-running-time', action='store_false')
+parser.add_argument('-d', '--debug', action='store_false')
+args = parser.parse_args()
+high_precision = args.high_precision
+randomize_nodes = args.randomize_nodes
+rand_running_time = args.rand_running_time
+debug = args.debug
+
+def experiment(controller_type=CONTROLLER_TYPE_DISCRETE, kp=0, ki=0, distribution=[], hp=True):
+    dt = DarkfiTable(ERC20DRK, RUNNING_TIME, controller_type, kp=kp, ki=ki, kd=0)
+    RND_NODES = random.randint(5, NODES) if randomize_nodes else NODES
+    for idx in range(0,RND_NODES):
+        darkie = Darkie(distribution[idx], strategy=SigmoidStrategy(EPOCH_LENGTH))
+        dt.add_darkie(darkie)
+    acc, apy, reward, stake_ratio, apr = dt.background(rand_running_time, hp)
+    return acc
+
+def multi_trial_exp(kp, ki, distribution = [], hp=True):
+    global highest_acc
+    global highest_gain
+    new_record=False
+    exp_threads = []
+    accs = []
+    for i in range(0, AVG_LEN):
+        acc = experiment(CONTROLLER_TYPE_DISCRETE, kp=kp, ki=ki, distribution=distribution, hp=hp)
+        accs += [acc]
+    avg_acc = sum(accs)/float(AVG_LEN)
+    buff = 'accuracy:{}, kp: {}, ki:{}'.format(avg_acc, kp, ki)
+    if avg_acc > 0:
+        gain = (kp, ki)
+        acc_gain = (avg_acc, gain)
+        if avg_acc > highest_acc:
+            new_record = True
+            highest_acc = avg_acc
+            highest_gain = (kp, ki)
+            with open('log'+os.sep+"highest_gain.txt", 'w') as f:
+                f.write(buff)
+    return buff, new_record
+
+SHIFTING = 0.05
+
+def crawler(crawl, range_multiplier, step=0.1):
+    start = None
+    if crawl==KP:
+        start = highest_gain[0]
+    elif crawl==KI:
+        start = highest_gain[1]
+
+    range_start = (start*range_multiplier if start <=0 else -1*start)
+    range_end = (-1*start if start<=0 else range_multiplier*start)
+    # if number of steps under 10 step resize the step to 50
+    while (range_end-range_start)/step < 10:
+        range_start -= SHIFTING
+        range_end += SHIFTING
+        step /= 10
+
+    crawl_range = np.arange(range_start, range_end, step)
+    np.random.shuffle(crawl_range)
+    crawl_range = tqdm(crawl_range)
+    distribution = [random.gauss(ERC20DRK/NODES, ERC20DRK/NODES*0.1) for i in range(NODES)]
+    for i in crawl_range:
+        kp = i if crawl==KP else highest_gain[0]
+        ki = i if crawl==KI else highest_gain[1]
+        buff, new_record = multi_trial_exp(kp, ki, distribution, hp=high_precision)
+        crawl_range.set_description('highest:{} / {}'.format(highest_acc, buff))
+        if new_record:
+            break
+
+while True:
+    prev_highest_gain = highest_gain
+    # kp crawl
+    crawler(KP, KP_RANGE_MULTIPLIER, KP_STEP)
+    if highest_gain[0] == prev_highest_gain[0]:
+        KP_RANGE_MULTIPLIER+=1
+        KP_STEP/=10
+    else:
+        start = highest_gain[0]
+        range_start = (start*KP_RANGE_MULTIPLIER if start <=0 else -1*start) - SHIFTING
+        range_end = (-1*start if start<=0 else KP_RANGE_MULTIPLIER*start) + SHIFTING
+        while (range_end - range_start)/KP_STEP >500:
+            if KP_STEP < 0.1:
+                KP_STEP*=10
+            KP_RANGE_MULTIPLIER-=1
+            #TODO (res) shouldn't the range also shrink?
+            # not always true.
+            # how to distinguish between thrinking range, and large step?
+            # good strategy is step shoudn't > 0.1
+            # range also should be > 0.8
+            # what about range multiplier?
+
+    # ki crawl
+    crawler(KI, KI_RANGE_MULTIPLIER, KI_STEP)
+    if highest_gain[1] == prev_highest_gain[1]:
+        KI_RANGE_MULTIPLIER+=1
+        KI_STEP/=10
+    else:
+        start = highest_gain[1]
+        range_start = (start*KI_RANGE_MULTIPLIER if start <=0 else -1*start) - SHIFTING
+        range_end = (-1*start if start<=0 else KI_RANGE_MULTIPLIER*start) + SHIFTING
+        while (range_end - range_start)/KI_STEP >500:
+            if KP_STEP < 0.1:
+                KI_STEP*=10
+            KI_RANGE_MULTIPLIER-=1