# # Collective Knowledge: compiler flags crowdtuning (crowdsource autotuning via spare computers such as mobile devices) # # See CK LICENSE.txt for licensing details # See CK COPYRIGHT.txt for copyright details # # Developer: Grigori Fursin, Grigori.Fursin@cTuning.org, http://fursin.net # cfg={} # Will be updated by CK (meta description of this module) work={} # Will be updated by CK (temporal data) ck=None # Will be updated by CK (initialized CK kernel) # Local settings compiler_choices='#choices#compiler_flags#' line='================================================================' fsummary='summary.json' fclassification='classification.json' fgraph='tmp-reactions-graph.json' ############################################################################## # Initialize module def init(i): """ Input: {} Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 } """ return {'return':0} ############################################################################## # show results def html_viewer(i): """ Input: { TBD (interactive_report) - if 'yes' output keys for interactive report (minimal) - if 'yes', minimal html } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 html (style) - styles - useful for plotting JavaScript-based graphs (predicted_opt) - if MILEPOST prediction is used } """ import os global cfg, work orig_module_uid=work['self_module_uid'] features=i.get('features',{}) xfeatures={} if len(features)>0: for k in features: xfeatures[k[2:]]=features[k] mcfg=i.get('module_cfg',{}) if len(mcfg)>0: cfg=mcfg mwork=i.get('module_work',{}) if len(mwork)>0: work=mwork ir=i.get('interactive_report','') mn=i.get('minimal','') st='' if i.get('force_url','')=='': url0=i['url_base'] else: url0=i['force_url'] ap=i.get('all_params',{}) ruoa=i.get('repo_uoa','') eruoa=i.get('experiment_repo_uoa','') # for interactive articles if eruoa!='': ruoa=eruoa muoa=work['self_module_uoa'] muid=work['self_module_uid'] duoa=i.get('data_uoa','') ik=cfg['improvements_keys'] # Load program optimization entry rx=ck.access({'action':'load', 'module_uoa':cfg['module_deps']['module'], 'data_uoa':cfg['module_deps']['program.optimization']}) if rx['return']>0: return rx urld=rx['dict'].get('url_discuss','') # Load Entry r=ck.access({'action':'load', 'repo_uoa':ruoa, 'module_uoa':muoa, 'data_uoa':duoa}) if r['return']>0: return {'return':0, 'html':'CK error: '+r['error']+'!'} p=r['path'] d=r['dict'] duid=r['data_uid'] # Load program module to get desc keys r=ck.access({'action':'load', 'module_uoa':cfg['module_deps']['module'], 'data_uoa':cfg['replay_desc']['module_uoa']}) if r['return']>0: return r pdesc=r.get('desc',{}) xxkey=cfg['replay_desc'].get('desc_key','') if xxkey!='': pdesc=pdesc.get(xxkey,{}) h='
\n' h+='\n\n\n\n' h+='

Distinct solutions after online classification ('+cfg['desc']+')

\n' h+='
\n' h+='

\n' cid=muid+':'+duid h+='\n' x=muid if muoa!=muid: x+=' ('+muoa+')' h+='\n' h+='\n' h+='\n' url5=ck.cfg.get('wiki_data_web','') if url5!='' or urld!='': if url5!='': x='GitHub wiki' if urld!='': if x!='': x+=', ' x+='Google group' h+='\n' h+='\n' urlx=url0+'action=get&cid='+cfg['module_deps']['program.optimization']+':'+duid+'&scenario_module_uoa='+muid+'&out=json' urls=url0+'action=pull&common_action=yes&cid='+muid+':'+duid+'&filename=summary.json' urlc=url0+'action=pull&common_action=yes&cid='+muid+':'+duid+'&filename=classification.json' x='' if urls!='': x+='[ All solutions in JSON ]' if urlc!='': if x!='': x+=', ' x+='[ Solutions\' classification in JSON ]' if x!='': h+='\n' h+='\n' h+='\n' pr=cfg.get('prune_results',[]) mm=d.get('meta',{}) em=d.get('extra_meta',{}) obj=mm.get('objective','') for k in pr: qd=k.get('desc','') qi=k.get('id','') qr=k.get('ref_uid','') qm=k.get('ref_module_uoa','') x=mm.get(qi,'') if x!='' and qm!='' and qr!='': xuid=mm.get(qr,'') if xuid!='': x=''+x+'' h+='\n' h+='\n' h+='\n' kk=0 for kx in range(0, len(ik)): k=ik[kx] k1=k.replace('$#obj#$',obj) ik[kx]=k1 if pdesc.get(k1,{}).get('desc','')!='': k1=pdesc[k1]['desc'] kk+=1 h+='\n' ik0=ik[0] # first key to sort h+='
Scenario UID'+x+'
Data UID'+duid+'
Discuss (optimizations to improve compilers,
semantic/data set/hardware features
to improve predictions
, etc):
'+x+'
Download:'+x+'
Reproduce all (with reactions):ck replay '+cid+'
'+qd+''+x+'
Objective'+obj+'
Improvement key IK'+str(kk)+''+k1+'
\n' h+='

\n' h+='

\n' bgraph={"0":[], "1":[]} # graph with highest improvements # Load summary sols=[] psum=os.path.join(p, fsummary) if os.path.isfile(psum): rx=ck.load_json_file({'json_file':psum}) if rx['return']>0: return rx sols=rx['dict'] # Load classification file classification={} pcl=os.path.join(p, fclassification) if os.path.isfile(pcl): rx=ck.load_json_file({'json_file':pcl}) if rx['return']>0: return rx classification=rx['dict'] # If features, update similarity and find min/max predicted_opt='' if len(features)>0: dist_min=None dist_max=None for q in sols: em=q.get('extra_meta',{}) suid=q['solution_uid'] dv=em.get('flat',{}).get('##characteristics#compile#joined_compiler_flags#min','') cls=classification.get(suid,{}) if len(cls)>0: wl_best=len(cls.get('best',[])) wl_worst=len(cls.get('worst',[])) wl_best_prog_uoa='' wl_best_cmd_key='' if wl_best>0: wl_best_prog_uoa=cls['best'][0].get('workload',{}).get('program_uoa','') # for now only for the 1st program # howelever later should group all programs with best opt! wl_best_cmd_key=cls['best'][0].get('workload',{}).get('cmd_key','') if wl_best_prog_uoa!='' and wl_best_cmd_key!='': # Try to load program static features ra=ck.access({'action':'load', 'module_uoa':cfg['module_deps']['program.static.features'], 'data_uoa':wl_best_prog_uoa}) if ra['return']==0: dfeat=ra['dict'].get('features',{}).get('program_static_milepost_features',{}) # Load program to get hot kernel (for features) ra=ck.access({'action':'load', 'module_uoa':cfg['module_deps']['program'], 'data_uoa':wl_best_prog_uoa}) if ra['return']==0: func=ra['dict'].get('run_cmds',{}).get(wl_best_cmd_key,{}).get('hot_functions',[]) if len(func)>0: dft=dfeat.get(func[0]['name'],{}) # # Calculate similarity r=ck.access({'action':'calculate_similarity', 'module_uoa':cfg['module_deps']['program.static.features'], 'features1':xfeatures, 'features2':dft}) if r['return']==0: dist=r['distance'] if dist!=None: cls['distance']=dist if dist_max==None or dist>dist_max: dist_max=dist if dist_min==None or dist0: h+=' \n' h+=' \n' h+=' Improvements (<4% variation)\n' h+=' \n' h+=' \n' h+=' Distinct workload for highest improvement\n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' #\n' h+=' \n' if len(features)>0: h+=' \n' h+=' MILEPOST features and NN distance (red - most close)\n' h+=' \n' h+=' \n' h+=' Solution UID\n' h+=' \n' for k in range(0, len(ik)): h+=' \n' h+=' IK'+str(k+1)+'\n' h+=' \n' h+=' \n' h+=' New distinct optimization choices\n' h+=' \n' h+=' \n' h+=' Ref\n' h+=' \n' h+=' \n' h+=' Best species\n' h+=' \n' h+=' \n' h+=' Worst species\n' h+=' \n' h+=' \n' h+=' Touched\n' h+=' \n' h+=' \n' h+=' Iters\n' h+=' \n' h+=' \n' h+=' Program\n' h+=' \n' h+=' \n' h+=' CMD\n' h+=' \n' h+=' \n' h+=' Dataset\n' h+=' \n' h+=' \n' h+=' Dataset file\n' h+=' \n' h+=' \n' h+=' CPU freq (MHz)\n' h+=' \n' h+=' \n' h+=' Cores\n' h+=' \n' h+=' \n' h+=' Platform\n' h+=' \n' h+=' \n' h+=' OS\n' h+=' \n' h+=' \n' h+=' Replay\n' h+=' \n' h+=' \n' # List num=0 iq=-1 iq1=0 res={} sres=[] ires=0 em={} cls={} tbl=[] while iq10: return rx d1=rx['dict'] px=os.path.join(p1,'ckp-'+uid+'.features_flat.json') if rx['return']>0: return rx d2=rx['dict'] x={'flat':d1, 'features_flat':d2} px=os.path.join(p1, 'ckp-'+uid+'.features.json') rx=ck.load_json_file({'json_file':px}) if rx['return']>0: return rx dx=rx['dict'] if dx.get('permanent','')=='yes': ref_res==x else: res[uid]=x rr=list(res.keys()) sres=sorted(rr, key=lambda v: (float(res[v].get('flat',{}).get(ik0,0.0))), reverse=True) rr={} if ires0: if dist!=None and dist_min!=None and dist_max!=None: if dist==dist_min: predicted_opt=flags xdist="%.3f" % dist col='FFFFFF' if dist<=1: col1=int(55+((dist-dist_min)/(1-dist_min))*200) col2=hex(col1)[2:] if (col1<16): col2='0'+col2 col='FF'+col2+col2 else: col1=int(55+(1-((dist-1)/(dist_max-1)))*200) col2=hex(col1)[2:] if (col1<16): col2='0'+col2 col=col2+col2+'FF' h+=' '+xdist+'\n' else: h+=' \n' h+=' \n' h+=' \n' if ires<2 and urlx!='': h+=' '+suid+'\n' h+=' \n' xtbl['solution_uid']=suid for k in range(0, len(ik)): h+=' \n' # dv=rr.get('flat',{}).get(ik[k],'') dv='' dvw='' points=q.get('points',[]) iresx=ires-1 # if iresx1.0: y=''+y+'' elif dv!=0: y=''+y+'' h+=str(y)+'\n' h+=' \n' xtbl['best_flags']=flags h+=' \n' h+=' '+flags+'\n' h+=' \n' h+=' \n' if ires<2: # Ideally should add pipeline description somewhere # to properly recreate flags. However since it is most of the time -Ox # we don't need to make it complex at the moment ry=rebuild_cmd({'choices':ref_sol, 'choices_order':ref_sol_order, 'choices_desc':{}}) if ry['return']>0: return ry ref=ry['cmd'] h+=' '+ref+'\n' xtbl['ref_flags']=ref h+=' \n' h+=' \n' h+=' \n' if ires<2: h+=' '+str(wl_best)+'\n' xtbl['best_species']=wl_best h+=' \n' h+=' \n' if ires<2: h+=' '+str(wl_worst)+'\n' xtbl['worst_species']=wl_worst h+=' \n' h+=' \n' if ires<2: h+=' '+str(touched)+'\n' h+=' \n' h+=' \n' if ires<2: h+=' '+str(iterations)+'\n' h+=' \n' h+=' \n' if ires<2: h+=' '+program_uoa+'\n' h+=' \n' h+=' \n' if ires<2: h+=' '+cmd+'\n' h+=' \n' h+=' \n' if ires<2: h+=' '+dataset_uoa+'\n' h+=' \n' h+=' \n' if ires<2: h+=' '+dataset_file+'\n' h+=' \n' # h+=' \n' # if ires<2: # h+=' '+str(em.get('kernel_repetitions',-1))+'\n' # h+=' \n' h+=' \n' if ires<2: x='' qq=em.get('cpu_cur_freq',[]) for q in qq: xq=qq[q] if x!='': x+=', ' x+=str(xq) h+=' '+x+'\n' h+=' \n' h+=' \n' if ires<2: qq=em.get('cpu_num_proc',1) h+=' '+str(qq)+'\n' h+=' \n' h+=' \n' if ires<2: h+=' '+str(em.get('platform_name',''))+'\n' h+=' \n' h+=' \n' if ires<2: h+=' '+str(em.get('os_name',''))+'\n' h+=' \n' x='ck replay '+cid+' --solution_uid='+suid y=ck.cfg.get('add_extra_to_replay','') if y!='': x+=' '+y xtbl['replay']=x h+='
\n' h+=' \n' h+=' \n' else: iq1+=1 if len(xtbl)>0: tbl.append(xtbl) if ir=='yes': rrr['table']=tbl h+='\n' if predicted_opt!='': h+='

Using machine learning to predict optimizations:

'+predicted_opt+'

\n' h+='
\n' h+='
\n' h+='

\n' rx=ck.access({'action':'links', 'module_uoa':cfg['module_deps']['program.optimization']}) if rx['return']>0: return rx h+=rx['html'] # Plot graph hg='' ftmp='' d3_div='ck_interactive' if i.get('graph_d3_div','')!='': d3_div=i['graph_d3_div'] if len(bgraph['0'])>0: ii={'action':'plot', 'module_uoa':cfg['module_deps']['graph'], "table":bgraph, "h_lines":[1.0], "ymin":0, "ignore_point_if_none":"yes", "plot_type":"d3_2d_bars", "display_y_error_bar":"no", "title":"Powered by Collective Knowledge", "axis_x_desc":"Distinct optimization solutions (highest improvement vs highest degradation)", "axis_y_desc":"Max improvement ( IK1 = Ref / Solution )", "plot_grid":"yes", "d3_div":d3_div, "image_width":"900", "image_height":"400", "wfe_url":url0} # Trick to save to file (for interactive/live articles) if ir=='yes': import copy rrr['graph_dict']=copy.deepcopy(ii) if ap.get('fgg_save_graph_to_file','')=='yes': import copy iii=copy.deepcopy(ii) iii["substitute_x_with_loop"]="yes" iii["plot_type"]="mpl_2d_bars" if 'ymin' in iii: del(iii['ymin']) if 'ymax' in iii: del(iii['ymax']) # Prepare batch file rx=ck.gen_tmp_file({'prefix':'tmp-', 'suffix':'.json'}) if rx['return']>0: return rx ftmp=rx['file_name'] rx=ck.save_json_to_file({'json_file':ftmp, 'dict':iii, 'sort_keys':'yes'}) if rx['return']>0: return rx r=ck.access(ii) if r['return']==0: x=r.get('html','') if x!='': st=r.get('style','') hg='
\n' if ftmp!='': hg+='
Note: graph info has been saved to file '+ftmp+' for interactive publications
' hg+='
\n' hg+=x+'\n' hg+='
\n' hg+='
\n' if mn=='yes': h=hg else: h=h.replace('$#graph#$', hg) rrr['html']=h rrr['style']=st rrr['predicted_opt']=predicted_opt return rrr ############################################################################## # crowdsource these experiments def crowdsource(i): """ Input: { See 'crowdsource program.optimization' (compiler_env_uoa) - fix compiler environment } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 } """ global cfg, work import copy mcfg=i.get('module_cfg',{}) if len(mcfg)>0: cfg=mcfg mwork=i.get('module_work',{}) if len(mwork)>0: work=mwork # Setting output o=i.get('out','') oo='' if o=='con': oo='con' quiet=i.get('quiet','') er=i.get('exchange_repo','') if er=='': er=ck.cfg['default_exchange_repo_uoa'] esr=i.get('exchange_subrepo','') if esr=='': esr=ck.cfg['default_exchange_subrepo_uoa'] if i.get('local','')=='yes': er='local' esr='' la=i.get('local_autotuning','') # Get user user='' mcfg={} ii={'action':'load', 'module_uoa':'module', 'data_uoa':cfg['module_deps']['program.optimization']} r=ck.access(ii) if r['return']==0: mcfg=r['dict'] dcfg={} ii={'action':'load', 'module_uoa':mcfg['module_deps']['cfg'], 'data_uoa':mcfg['cfg_uoa']} r=ck.access(ii) if r['return']>0 and r['return']!=16: return r if r['return']!=16: dcfg=r['dict'] user=dcfg.get('user_email','') ceuoa=i.get('compiler_env_uoa', '') if ceuoa!='': rx=ck.access({'action':'load', 'module_uoa':cfg['module_deps']['env'], 'data_uoa':ceuoa}) if rx['return']>0: return rx ceuoa=rx['data_uid'] # Initialize local environment for program optimization *********************************************************** pi=i.get('platform_info',{}) if len(pi)==0: ii=copy.deepcopy(i) ii['action']='initialize' ii['module_uoa']=cfg['module_deps']['program.optimization'] ii['exchange_repo']=er ii['exchange_subrepo']=esr r=ck.access(ii) if r['return']>0: return r pi=r['platform_info'] user=r.get('user','') hos=pi['host_os_uoa'] hosd=pi['host_os_dict'] tos=pi['os_uoa'] tosd=pi['os_dict'] tbits=tosd.get('bits','') remote=tosd.get('remote','') tdid=pi['device_id'] program_tags=i.get('program_tags','') if program_tags=='' and i.get('local_autotuning','')!='yes' and i.get('data_uoa','')=='': program_tags=cfg['program_tags'] # Check that has minimal dependencies for this scenario *********************************************************** sdeps=i.get('dependencies',{}) # useful to preset inside crowd-tuning if len(sdeps)==0: sdeps=copy.deepcopy(cfg['deps']) if len(sdeps)>0: if o=='con': ck.out(line) ck.out('Resolving software dependencies required for this scenario ...') ck.out('') if ceuoa!='': x=sdeps.get('compiler',{}) if len(x)>0: if 'cus' in x: del(x['cus']) if 'deps' in x: del(x['deps']) x['uoa']=ceuoa sdeps['compiler']=x ii={'action':'resolve', 'module_uoa':cfg['module_deps']['env'], 'host_os':hos, 'target_os':tos, 'device_id':tdid, 'deps':sdeps, 'add_customize':'yes'} if quiet=='yes': ii['random']='yes' else: ii['out']=oo rx=ck.access(ii) if rx['return']>0: return rx sdeps=rx['deps'] # Update deps (add UOA) cpu_name=pi.get('features',{}).get('cpu',{}).get('name','') compiler_soft_uoa=sdeps.get('compiler',{}).get('dict',{}).get('soft_uoa','') compiler_env=sdeps.get('compiler',{}).get('bat','') compiler_tool=sdeps.get('compiler',{}).get('dict',{}).get('env',{}).get('CK_CC','') plat_extra={} pft=pi.get('features',{}) for q in pft: if q.endswith('_uid'): plat_extra[q]=pft[q] elif type(pft[q])==dict and pft[q].get('name','')!='': plat_extra[q+'_name']=pft[q]['name'] # Detect real compiler version *********************************************************** if o=='con': ck.out(line) ck.out('Detecting compiler version ...') ii={'action':'internal_detect', 'module_uoa':cfg['module_deps']['soft'], 'data_uoa':compiler_soft_uoa, 'host_os':hos, 'target_os':tos, 'target_device_id':tdid, 'env':compiler_env, 'tool':compiler_tool} r=ck.access(ii) if r['return']>0: return r compiler_version=r['version_str'] compiler=cfg.get('compiler_name','')+' '+compiler_version if o=='con': ck.out('') ck.out('* Compiler: '+compiler) ck.out('* CPU: '+cpu_name) # Start preparing input to run program.optimization ii=copy.deepcopy(i) ii['action']='run' ii['module_uoa']=cfg['module_deps']['program.optimization'] ii['host_os']=hos ii['target_os']=tos ii['target_device_id']=tdid ii['dependencies']=sdeps ii['scenario_cfg']=cfg ii['platform_info']=pi ii['program_tags']=program_tags ii['scenario_module_uoa']=work['self_module_uid'] ii['experiment_meta']={'cpu_name':cpu_name, 'compiler':compiler} ii['experiment_meta_extra']=plat_extra ii['exchange_repo']=er ii['exchange_subrepo']=esr ii['user']=user # Select sub-scenario ******************************************************************** from random import randint ss=1 # num of scenarios sx=randint(1,ss) rr={'return':0} if sx==1 or la=='yes': # **************************************************************** explore random program/dataset sdesc='explore random program/cmd/data set' if o=='con': ck.out('') ck.out(' ****** Sub-scenario: '+sdesc+' ******') ii['subscenario_desc']=sdesc rr=ck.access(ii) if rr['return']>0: return rr rr['platform_info']=pi return rr ############################################################################## # rebuild compiler cmd from choices def rebuild_cmd(i): """ Input: { choices - dict of choices choices_order - choices order choices_desc - dict of choices desc } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 cmd - compiler command line pruned_choices - leave only compiler flags } """ cmd='' choices=i.get('choices',{}) corder=i.get('choices_order',[]) cdesc=i.get('choices_desc',{}) for q in sorted(corder): v=choices.get(q, None) d=cdesc.get(q, None) if v!=None: if cmd!='': cmd+=' ' cmd+=v return {'return':0, 'cmd':cmd} ############################################################################## # replay optimization def replay(i): """ See 'replay program.optimization' """ i['module_uoa']=cfg['module_deps']['program.optimization'] i['module_ref_uoa']=work['self_module_uid'] i['module_cfg']=copy.deepcopy(cfg) i['module_work']=copy.deepcopy(work) return ck.access(i) ############################################################################## # prune compiler flags to find minimal set of choices def prune(i): """ See 'replay program.optimization' """ i['module_uoa']=cfg['module_deps']['program.optimization'] i['module_ref_uoa']=work['self_module_uid'] i['module_cfg']=copy.deepcopy(cfg) i['module_work']=copy.deepcopy(work) return ck.access(i) ############################################################################## # prepare graph for interactive reports def show(i): """ Input: { (from_repo) - change repository (useful for remote-ck) (change_module_uoa) - change module_uoa (to select scenario module) (force_url) - useful to redirect interactive graphs to external repo (save_to_file) - output to file (for auto-generated LaTex and interactive graphs via CK) (minimal) - if 'yes', return minimal html } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 } """ import os i['action']='process_interactive_graph' i['interactive_report']='yes' i['out']='' from_repo=i.get('from_repo','') if from_repo!='': i['repo_uoa']=from_repo rr=ck.access(i) if rr['return']>0: return rr if i.get('minimal','')=='yes': rr['html']=rr.get('graph_html','') stf=i.get('save_to_file','') stf0='' # Checks to avoid hacking if stf!='': stf=os.path.basename(stf) if not stf.endswith('.png') and not stf.endswith('.pdf'): stf='' if stf!='': stf0=os.path.splitext(stf)[0] # Save graph iii=rr.get('graph_dict',{}) iii["substitute_x_with_loop"]="yes" iii["plot_type"]="mpl_2d_bars" if 'ymin' in iii: del(iii['ymin']) if 'ymax' in iii: del(iii['ymax']) iii['out_to_file']=stf if i.get("save_graph_title",'')!='': iii['title']=i['save_graph_title'] if i.get("save_graph_axis_x_desc",'')!='': iii['axis_x_desc']=i['save_graph_axis_x_desc'] if i.get("save_graph_axis_y_desc",'')!='': iii['axis_y_desc']=i['save_graph_axis_y_desc'] if i.get("save_graph_axis_x_rotation",'')!='': iii['axis_x_rotation']=i['save_graph_axis_x_rotation'] if i.get("save_font_size",'')!='': iii['font_size']=i['save_font_size'] r=ck.access(iii) if r['return']>0: return r h ='\n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' t =" \\begin{tabular}{|r|p{4.5in}|p{0.5in}|p{0.5in}|}\n" t+=" \\hline\n" t+=" \\textbf{Solution} & \\textbf{Pruned flags (complexity reduction)} & \\textbf{Best species} & \\textbf{Worst species} \\\\ \n" t+=" \\hline\n" tbl=rr.get('table',[]) for q in tbl: sn=q['solution_num'] bf=q['best_flags'] bs=q.get('best_species', 0) ws=q.get('worst_species', 0) t+=" "+str(sn)+" & "+bf+" & "+str(bs)+" & "+str(ws)+" \\\\\n" t+=" \\hline\n" h+=' ' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' ' t+=" \\end{tabular}" h+='
SolutionPruned flags (complexity reduction)Best speciesWorst species
'+str(sn)+''+bf+''+str(bs)+''+str(ws)+'
\n' # Save tex file r=ck.save_text_file({'text_file':stf0+'.solutions.tex', 'string':t}) if r['return']>0: return r # Save html file r=ck.save_text_file({'text_file':stf0+'.solutions.html', 'string':h}) if r['return']>0: return r return rr ############################################################################## # prepare graph for interactive reports def process_interactive_graph(i): """ Input: { (change_module_uoa) } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 } """ i['action']='html_viewer' i['out']='' change_module_uoa=i.get('change_module_uoa','') if change_module_uoa!='': i['module_uoa']=change_module_uoa r=ck.access(i) if 'html' in r: r['graph_html']=r['html'] del(r['html']) return r