├── .gitignore ├── .DS_Store ├── lam_fit ├── .DS_Store ├── 4pt_exp.py ├── lam_fit_integrals.py └── lam_fit.py ├── generate_models.py ├── README.md ├── rg.py ├── free_theory.py ├── six_pt_prediction.py ├── six_pt_connected.py └── lib.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/lib.cpython-38.pyc 2 | *.pickle -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keeganstoner/nn-qft/HEAD/.DS_Store -------------------------------------------------------------------------------- /lam_fit/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keeganstoner/nn-qft/HEAD/lam_fit/.DS_Store -------------------------------------------------------------------------------- /generate_models.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("./") 3 | sys.path.append("..") 4 | from lib import * 5 | import itertools 6 | import numpy as np 7 | 8 | def Print(*s): 9 | print(s) 10 | sys.stdout.flush() 11 | 12 | if __name__ == "__main__": 13 | 14 | parser = argparse.ArgumentParser() 15 | parser.add_argument("--activation", type=str, default = "GaussNet") 16 | parser.add_argument('--exp', type=str, default = None) 17 | parser.add_argument("--width", type=int, default = 100) 18 | parser.add_argument("--n-inputs", type = int, default = 6) 19 | parser.add_argument("--n-models", type = int, default = 10**3) 20 | parser.add_argument("--d-in", type = int, default = 1) 21 | parser.add_argument("--d-out", type = int, default = 1) 22 | parser.add_argument("--sb", type = float, default = 1.0) 23 | parser.add_argument("--sw", type = float, default = 1.0) 24 | parser.add_argument("--mb", type = float, default = 0.0) 25 | parser.add_argument("--mw", type = float, default = 0.0) 26 | parser.add_argument("--cuda", action = 'store_true', default = False) 27 | 28 | args = parser.parse_args() 29 | 30 | widths = [2, 3, 4, 5, 10, 20, 50, 100, 500, 1000] # ten 31 | 32 | runs = 1 # runs per width, usually set to 10 or 1 33 | 34 | # # parallelize via MPI # 35 | # from mpi4py import MPI 36 | # comm = MPI.COMM_WORLD 37 | # size = comm.Get_size() 38 | # rank = comm.Get_rank() 39 | # widths = [widths[rank]] 40 | 41 | if args.d_in == 1: 42 | if args.activation == "Erf": 43 | xs = torch.tensor([[-1],[-0.6],[-0.2],[0.2],[0.6], [1.0]]) 44 | xset = "xset1" 45 | if args.activation == "GaussNet": 46 | xs = 0.01*torch.tensor([[-1],[-0.6],[-0.2],[0.2],[0.6], [1.0]]) 47 | xset = "xset2" 48 | if args.activation == "ReLU": 49 | xs = torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 50 | xset = "xset1A" 51 | 52 | if args.d_in == 2: 53 | xs = torch.tensor([-1.0, 1.0]) 54 | xs = torch.cartesian_prod(xs, xs) 55 | xset = "xset3" 56 | if args.activation == "GaussNet": 57 | xs = 0.01*xs 58 | xset = "xset4" 59 | if args.activation == "ReLU": 60 | xs = torch.tensor([0.5, 1.0]) 61 | xs = torch.cartesian_prod(xs, xs) 62 | xset = "xset3A" 63 | 64 | if args.d_in == 3: 65 | xs = torch.tensor([[-1., -1., -1.],[ 1., 1., -1.],[-1., 1., 1.],[ 1., -1., 1.]]) 66 | xset = "xset5" 67 | if args.activation == "GaussNet": 68 | xs = 0.01*xs 69 | xset = "xset6" 70 | if args.activation == "ReLU": 71 | xs = torch.tensor([[0.2, 0.2, 0.2],[ 1., 1., 0.2],[0.2, 1., 1.],[ 1., 0.2, 1.]]) 72 | xset = "xset5A" 73 | 74 | args.n_inputs = len(xs) 75 | 76 | 77 | for args.width in widths: 78 | for run in range(runs): 79 | print("Generating networks for "+args.activation+" at width "+str(args.width), " - run ", run+1, " of ", runs) 80 | fss = create_networks(xs, args) 81 | #print("Pickling: "+args.activation+" at width "+str(args.width)) 82 | pickle.dump(fss, open("run"+str(run+1)+"_din="+str(args.d_in)+"_"+args.activation+"_1e"+str(int(np.log10(args.n_models)))+"models_"+str(args.width)+"width_"+xset+".pickle",'wb')) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Neural Networks and Quantum Field Theory 2 | 3 | Implementation of 4 | 5 | https://arxiv.org/abs/2008.08601 6 | 7 | and 8 | 9 | https://iopscience.iop.org/article/10.1088/2632-2153/abeca3 10 | 11 | by James Halverson, Anindita Maiti, and Keegan Stoner 12 | 13 | ## Abstract 14 | 15 | We propose a theoretical understanding of neural networks in terms of Wilsonian effective field theory. The correspondence relies on the fact that many asymptotic neural networks are drawn from Gaussian processes (GPs), the analog of non-interacting field theories. Moving away from the asymptotic limit yields a non-Gaussian process (NGP) and corresponds to turning on particle interactions, allowing for the computation of correlation functions of neural network outputs with Feynman diagrams. Minimal NGP likelihoods are determined by the most relevant non-Gaussian terms, according to the flow in their coefficients induced by the Wilsonian renormalization group. This yields a direct connection between overparameterization and simplicity of neural network likelihoods. Whether the coefficients are constants or functions may be understood in terms of GP limit symmetries, as expected from 't Hooft's technical naturalness. General theoretical calculations are matched to neural network experiments in the simplest class of models allowing the correspondence. Our formalism is valid for any of the many architectures that becomes a GP in an asymptotic limit, a property preserved under certain types of training. 16 | 17 | 18 | ## Using this code 19 | 20 | ```python script.py --activation=ReLU --d-in=1 --n-models=10**6``` 21 | 22 | runs script.py with 10**6 models using the ReLU activation for the 1D inputs listed in Section 2. 23 | 24 | ### 1. ```generate_models.py``` 25 | 26 | This generates models for a given activation function and input dimension and saves the outputs in a pickle file. These can be used in the other scripts where the analysis is done. 27 | 28 | This generates the models and outputs them as pickle files that are used in the other three scripts. 29 | 30 | The options are: 31 | 32 | - ```--activation``` - activation function defining network architecture 33 | - ```ReLU, Erf, GaussNet``` 34 | - ```--d-in``` - input dimension of xs 35 | - ```1, 2, 3``` 36 | - ```--n-models``` - total number of models created/used in each experiment 37 | - ```--mw``` - mean of W distribution (default = 0.0) 38 | - ```--mb``` - mean of b distribution (default = 0.0) 39 | - ```--sw``` - standard deviation of W distribution (default = 1.0) 40 | - ```--sb```- standard deviation of b distribution (default = 1.0, except ReLU sets to 0.0) 41 | 42 | 43 | ### 2. ```free_theory.py``` 44 | 45 | This has the same options from the previous code but adds 46 | - ```--n-pt```- plots either the 2-, 4-, or 6-pt function for ```activation``` 47 | - ```2, 4, 6``` 48 | 49 | Uses the models to show falloff of 4-pt and 6-pt signals to their GP predictions as a function of width, as well as 2-pt signals below background noise level as shown in Section 2. 50 | 51 | ### 3. ```rg.py``` 52 | 53 | Uses the models for ReLU-net to show the 4-pt coupling lambda change with varying cutoff scale in accordance with the RG equations in Section 4. Also pickles the lambda tensor at each cutoff for use in ```six_pt_prediction.py```. 54 | 55 | ### 4. ```six_pt_prediction.py``` 56 | 57 | Uses the models and the lambda tensor from ```rg.py``` to predict the 6-pt function for ReLU-net at the NGP width used in the paper. 58 | 59 | 60 | ### 5. ```lam_fit.py``` 61 | 62 | Requires ```lam_fit_integrals.py``` and ```4pt_exp.py``` for the theoretical and experimental predictions, respectively. This matches the EFT predictions (via integrals using the kernels) to the experimental 4-pt function using an optimizer. The learning rate (```args.lr```) must be adjusted for a given fit. 63 | 64 | ### 6. ```six_pt_connected.py``` 65 | 66 | Requires ```free_theory.py``` to be run first with ```n = 4, 6```. Produces the plots for the connected piece of the 6-pt function (excluding contributions from the 2-pt and 4-pt functions) for any of the three architectures, along with background (statistical fluctuation) levels across widths. 67 | 68 | ## Contact 69 | 70 | **Code authors:** Keegan Stoner, Anindita Maiti ([aninditamaiti](https://github.com/aninditamaiti))

71 | 72 | **Issues and questions:** @keeganstoner, stoner.ke@northeastern.edu

73 | 74 | 75 | ## BibTeX Citation 76 | ``` 77 | @misc{halverson2020neural, 78 | title={Neural Networks and Quantum Field Theory}, 79 | author={James Halverson and Anindita Maiti and Keegan Stoner}, 80 | year={2020}, 81 | eprint={2008.08601}, 82 | archivePrefix={arXiv}, 83 | primaryClass={cs.LG} 84 | } 85 | ``` 86 | 87 | 88 | and 89 | 90 | ``` 91 | @article{Halverson_2021, 92 | doi = {10.1088/2632-2153/abeca3}, 93 | url = {https://doi.org/10.1088/2632-2153/abeca3}, 94 | year = 2021, 95 | month = {mar}, 96 | publisher = {{IOP} Publishing}, 97 | author = {James Halverson and Anindita Maiti and Keegan Stoner}, 98 | title = {Neural Networks and Quantum Field Theory}, 99 | journal = {Machine Learning: Science and Technology}} 100 | } 101 | ``` 102 | -------------------------------------------------------------------------------- /lam_fit/4pt_exp.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("./") 3 | sys.path.append("..") 4 | from lib import * 5 | 6 | if __name__ == "__main__": 7 | 8 | parser = argparse.ArgumentParser() 9 | parser.add_argument("--activation", type=str, default = "GaussNet") 10 | parser.add_argument('--exp', type=str, default = None) 11 | parser.add_argument("--width", type=int, default = 100) 12 | parser.add_argument("--n-inputs", type = int, default = 6) 13 | parser.add_argument("--n-models", type = int, default = 10**3) 14 | parser.add_argument("--d-in", type = int, default = 1) 15 | parser.add_argument("--d-out", type = int, default = 1) 16 | parser.add_argument("--sb", type = float, default = 1.0) 17 | parser.add_argument("--sw", type = float, default = 1.0) 18 | parser.add_argument("--mb", type = float, default = 0.0) 19 | parser.add_argument("--mw", type = float, default = 0.0) 20 | parser.add_argument("--cuda", action = 'store_true', default = False) 21 | parser.add_argument("--n-pt", type = int, default = 4) 22 | parser.add_argument("--inputset", type = int, default = 1) 23 | 24 | args = parser.parse_args() 25 | n = args.n_pt 26 | 27 | runs = 10 28 | 29 | for inputset in [1,2]: # for the test/train inputs 30 | args.inputset = inputset 31 | 32 | if args.activation == "Erf": 33 | if args.inputset == 1: 34 | xs = (np.sqrt(2)/2)*0.01*torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 35 | xset = "xsetEt" 36 | 37 | if args.inputset == 2: 38 | xs = 0.01*torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 39 | xset = "xsetE" 40 | 41 | widths = [5] # defines NGP 42 | cutoffs = [100] 43 | 44 | 45 | if args.activation == "GaussNet": 46 | if args.inputset == 1: 47 | xs = (np.sqrt(2)/2)*0.01*torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 48 | xset = "xsetGt" 49 | 50 | if args.inputset == 2: 51 | xs = 0.01*torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 52 | xset = "xsetG" 53 | 54 | widths = [1000] # defines NGP 55 | cutoffs = [np.inf] 56 | 57 | if args.activation == "ReLU": 58 | if args.inputset == 1: 59 | xs = (np.sqrt(2)/2)*torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 60 | xset = "xsetRt" 61 | 62 | if args.inputset == 2: 63 | xs = torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 64 | xset = "xsetR" 65 | 66 | widths = [20] # defines NGP 67 | cutoffs = [100] 68 | 69 | args.sb = 10**-100 70 | 71 | args.n_inputs = len(xs) 72 | 73 | for args.width in widths: 74 | for run in range(runs): 75 | print("Generating networks for "+args.activation+" with xset ", xset, " at width "+str(args.width), " - run ", run+1, " of ", runs) 76 | fss = create_networks(xs, args) 77 | #print("Pickling: "+args.activation+" at width "+str(args.width)) 78 | pickle.dump(fss, open("run"+str(run+1)+"_din="+str(args.d_in)+"_"+args.activation+"_1e"+str(int(np.log10(args.n_models)))+"models_"+str(args.width)+"width_"+xset+".pickle",'wb')) 79 | 80 | 81 | fss = {} # dictionary for storing outputs after importing 82 | # keys are widths 83 | 84 | print("Computing npt functions with activation ", args.activation, " and xset ", xset) 85 | for width in widths: 86 | print("Unpickling width "+str(width)) 87 | args.width = width 88 | for run in range(runs): 89 | with open("run"+str(run+1)+"_din="+str(args.d_in)+"_"+args.activation+"_1e"+str(int(np.log10(args.n_models)))+"models_"+str(args.width)+"width_"+xset+".pickle",'rb') as handle: 90 | if run == 0: 91 | fss[width] = pickle.load(handle) 92 | else: 93 | fss[width] = torch.cat((fss[width], pickle.load(handle))) 94 | 95 | 96 | print("Computing "+str(n)+"-pt function for activation "+args.activation) 97 | if n == 4: 98 | n_thy = four_pt_tensor(xs, args) 99 | 100 | fss_chunk = {} 101 | # split data into k chunks so background level can be plotted 102 | k = 10 103 | chunk = len(fss[widths[0]])//k 104 | print("Models in each chunk: ", chunk) 105 | 106 | widths_list, n_diff_full, backgrounds, n_exp = [], [], [], [0. for _ in range(10)] 107 | for width in widths: 108 | for chunk_num in range(10): 109 | # this is a dictionary (with keys = widths) for a single chunk 110 | fss_chunk[width] = fss[width].narrow_copy(0,chunk_num*chunk,chunk) 111 | 112 | # computes the experimental n-pt function and averages over models elementwise 113 | n_tensor = torch.mean(n_point(fss_chunk[width], n), dim=0) 114 | assert(args.d_out == 1) # this code is written for d_out = 1 115 | n_tensor = n_tensor.view(n_tensor.shape[0:n]) 116 | n_exp[chunk_num] = n_tensor.tolist() 117 | 118 | n_diff = np.nanmean(n_exp, axis = 0) - np.array(n_thy) 119 | pickle.dump(n_exp, open("four_pt_exp_"+args.activation+"_width"+str(width)+"_din"+str(args.d_in)+"_"+xset+".pickle",'wb')) 120 | 121 | 122 | -------------------------------------------------------------------------------- /rg.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("./") 3 | sys.path.append("..") 4 | from lib import * 5 | 6 | if __name__ == "__main__": 7 | 8 | parser = argparse.ArgumentParser() 9 | parser.add_argument("--activation", type=str, default = "ReLU") 10 | parser.add_argument('--exp', type=str, default = None) 11 | parser.add_argument("--width", type=int, default = 100) 12 | parser.add_argument("--n-inputs", type = int, default = 6) 13 | parser.add_argument("--n-models", type = int, default = 10**3) 14 | parser.add_argument("--d-in", type = int, default = 1) 15 | parser.add_argument("--d-out", type = int, default = 1) 16 | parser.add_argument("--sb", type = float, default = 1.0) 17 | parser.add_argument("--sw", type = float, default = 1.0) 18 | parser.add_argument("--mb", type = float, default = 0.0) 19 | parser.add_argument("--mw", type = float, default = 0.0) 20 | parser.add_argument("--cuda", action = 'store_true', default = False) 21 | 22 | args = parser.parse_args() 23 | n = 4 24 | 25 | runs = 1 26 | if args.d_in == 1: 27 | if args.activation == "ReLU": 28 | xs = torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 29 | xset = "xset1A" 30 | 31 | if args.d_in == 2: 32 | if args.activation == "ReLU": 33 | xs = torch.tensor([0.5, 1.0]) 34 | xs = torch.cartesian_prod(xs, xs) 35 | xset = "xset3A" 36 | 37 | if args.d_in == 3: 38 | if args.activation == "ReLU": 39 | xs = torch.tensor([[0.2, 0.2, 0.2],[ 1., 1., 0.2],[0.2, 1., 1.],[ 1., 0.2, 1.]]) 40 | xset = "xset5A" 41 | 42 | args.sb == 10**-100 43 | args.n_inputs = len(xs) 44 | width = 20 # defines NGP 45 | args.width = width 46 | 47 | 48 | fss, fss_chunk = {}, {} 49 | print("Unpickling width "+str(width)) 50 | # args.width = width 51 | for run in range(runs): 52 | with open("run"+str(run+1)+"_din="+str(args.d_in)+"_"+args.activation+"_1e"+str(int(np.log10(args.n_models)))+"models_"+str(args.width)+"width_"+xset+".pickle", 'rb') as handle: 53 | if run == 0: 54 | fss[width] = pickle.load(handle) 55 | else: 56 | fss[width] = torch.cat((fss[width], pickle.load(handle))) 57 | 58 | 59 | # chunk 60 | k = 10 61 | chunk = args.n_models*runs//k 62 | 63 | # # # 64 | n_thy = four_pt_tensor(xs, args) 65 | # # # 66 | 67 | n_exp = [0. for _ in range(10)] 68 | 69 | for chunk_num in range(10): 70 | fss_chunk[width] = fss[width].narrow_copy(0,chunk_num*chunk,chunk) 71 | 72 | n_tensor = torch.mean(n_point(fss_chunk[width], n), dim=0) 73 | 74 | # only true when d_out = 1: 75 | assert(args.d_out == 1) 76 | n_tensor = n_tensor.view(n_tensor.shape[0:n]) 77 | n_exp[chunk_num] = n_tensor.tolist() 78 | 79 | if run == 4: 80 | print("done with run ", run+1) 81 | 82 | numerator = np.nanmean(n_exp, axis = 0) - np.array(n_thy) 83 | 84 | print(np.nanmean(np.nanmean(n_exp, axis = 0))) 85 | print(np.nanmean(np.array(n_thy))) 86 | 87 | 88 | xslist = xs.tolist() 89 | logcut, loglamlist = [], [] 90 | denom = [[[[np.nan for i in range(len(xs))] for i in range(len(xs))] for i in range(len(xs))] for i in range(len(xs))] 91 | lambda_averages = {} 92 | 93 | 94 | if args.activation == "ReLU": 95 | cutoffs = [7, 10, 15, 20, 30, 40, 50, 70, 100, 200, 500, 1000, 2000, 5000, 7000, 10000, 20000, 40000, 60000, 80000, 100000] #21 96 | 97 | if args.activation == "GaussNet" or args.activation == "Erf": 98 | cutoffs = [np.inf] 99 | 100 | for cutoff in cutoffs: 101 | log_cutoff = np.inf 102 | if args.activation == "ReLU": 103 | log_cutoff = np.round(np.log10(cutoff), 2) 104 | print("Computing with cutoff ", cutoff) 105 | for i in range(len(xs)): 106 | for j in range(i, len(xs)): 107 | for k in range(j, len(xs)): 108 | for l in range(k, len(xs)): 109 | denom[i][j][k][l] = four_pt_int(xs[i], xs[j], xs[k], xs[l], cutoff, args) 110 | denom = np.array(denom) 111 | lam = (numerator/denom) 112 | print("cutoff: ", cutoff, "stdev in lam = ", np.nanstd(lam)) 113 | pickle.dump(lam, open("lam_"+str(cutoff)+"_"+args.activation+"_width_"+str(width)+"_din"+str(args.d_in)+"_"+xset+".pickle",'wb')) 114 | lambda_average = np.nanmean(lam) 115 | log_lam = [np.log10(np.abs(i)) for i in lam.flatten().tolist() if (~np.isnan(i))] 116 | loglamlist.extend(log_lam) 117 | for _ in range(len(log_lam)): 118 | logcut.append(log_cutoff) 119 | 120 | 121 | print(str(datetime.datetime.now())) 122 | 123 | import pandas as pd 124 | import seaborn as sns 125 | import matplotlib as mpl 126 | from matplotlib import pyplot as plt 127 | from matplotlib import rc 128 | from scipy import stats 129 | 130 | df_lam = pd.DataFrame({"log_lambda": loglamlist, "log_cutoff": logcut}) 131 | 132 | if args.activation == "ReLU": 133 | slope, intercept, r_value, p_value, std_err = stats.linregress(df_lam['log_cutoff'],df_lam['log_lambda']) 134 | 135 | 136 | rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']}) 137 | rc('text', usetex=True) 138 | fsize = 22 139 | plt.rc('text', usetex=True) 140 | plt.rc('text.latex', preamble=r'\usepackage{amsmath}') 141 | plt.rc('font', size=fsize) # controls default text sizes 142 | plt.rc('axes', titlesize=20) # fontsize of the axes title 143 | plt.rc('axes', labelsize=20) # fontsize of the x and y labels 144 | plt.rc('xtick', labelsize=18) # fontsize of the tick labels 145 | plt.rc('ytick', labelsize=18) # fontsize of the tick labels 146 | plt.rc('legend', fontsize=14) # legend fontsize 147 | plt.rc('figure', titlesize=fsize) # fontsize of the figure title 148 | 149 | sns.set_style("ticks", {"xtick.major.size":18, 150 | "ytick.major.size":18}) 151 | def lt(s): 152 | return (r'$\mathrm{' + s + r'}$').replace(" ", "\,\,") 153 | 154 | def lm(s): 155 | return r'$' + s + r'$' 156 | 157 | if args.activation == "GaussNet": 158 | act = "Gauss\\text{-}net" 159 | if args.activation == "Erf": 160 | act = "Erf\\text{-}net" 161 | if args.activation == "ReLU": 162 | act = "ReLU\\text{-}net" 163 | # # # 164 | # plot! 165 | # # # 166 | title_size, label_size, tick_size = fsize, fsize, fsize 167 | sns.set_style(style="darkgrid") 168 | sns.regplot(x='log_cutoff',y='log_lambda', data=df_lam, line_kws={'label':lt("slope="+str(np.round(slope, 3))+", ")+lm("R^{2}="+str(np.round(r_value**2, 5)))}) 169 | plt.tick_params(labelsize=tick_size) 170 | plt.title(lt(act+" \\lambda_{m}, ")+lm("N="+str(width))+lt(" with ")+lm("d_{in}="+str(args.d_in)),fontsize=title_size) 171 | plt.ylabel(lm("\\log_{10}\\lambda_{m}"),fontsize=label_size) 172 | plt.xlabel(lm("\\log_{10}\\Lambda"),fontsize=label_size) 173 | plt.tight_layout() 174 | plt.legend() 175 | import datetime 176 | plt.savefig("rg_"+args.activation+"_din"+str(args.d_in)+".pdf",bbox_inches='tight') 177 | plt.figure() 178 | # plt.show() -------------------------------------------------------------------------------- /lam_fit/lam_fit_integrals.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("./") 3 | sys.path.append("..") 4 | from lib import * 5 | import os 6 | import statistics 7 | import torch 8 | import torch.nn as nn 9 | 10 | if __name__ == "__main__": 11 | 12 | parser = argparse.ArgumentParser() 13 | parser.add_argument("--activation", type=str, default = "GaussNet") 14 | parser.add_argument('--exp', type=str, default = None) 15 | parser.add_argument("--width", type=int, default = 100) 16 | parser.add_argument("--n-inputs", type = int, default = 6) 17 | parser.add_argument("--n-models", type = int, default = 10**3) 18 | parser.add_argument("--d-in", type = int, default = 1) 19 | parser.add_argument("--d-out", type = int, default = 1) 20 | parser.add_argument("--sb", type = float, default = 1.0) 21 | parser.add_argument("--sw", type = float, default = 1.0) 22 | parser.add_argument("--mb", type = float, default = 0.0) 23 | parser.add_argument("--mw", type = float, default = 0.0) 24 | parser.add_argument("--cuda", action = 'store_true', default = False) 25 | parser.add_argument("--n-pt", type = int, default = 4) 26 | parser.add_argument("--parallel", type = bool, default = False) # optional arument for parallel computation 27 | parser.add_argument("--cutoff", type = float, default = 100.0) # optional arument for parallel computation 28 | parser.add_argument("--inputset", type = int, default = 1) # optional arument for parallel computation 29 | parser.add_argument("--order", type = str, default = "0") # optional arument for parallel computation 30 | 31 | args = parser.parse_args() 32 | 33 | for order in ["0", "2", "220"]: # modify or remove this loop for computations of only some integrals 34 | for inputset in [1,2]: # for the test/train inputs 35 | args.inputset = inputset 36 | 37 | if args.activation == "Erf": 38 | if args.inputset == 1: 39 | xs = (np.sqrt(2)/2)*0.01*torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 40 | xset = "xsetEt" 41 | 42 | if args.inputset == 2: 43 | xs = 0.01*torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 44 | xset = "xsetE" 45 | 46 | widths = [5] # defines NGP 47 | cutoffs = [100] 48 | 49 | 50 | if args.activation == "GaussNet": 51 | if args.inputset == 1: 52 | xs = (np.sqrt(2)/2)*0.01*torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 53 | xset = "xsetGt" 54 | 55 | if args.inputset == 2: 56 | xs = 0.01*torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 57 | xset = "xsetG" 58 | 59 | widths = [1000] # defines NGP 60 | cutoffs = [np.inf] 61 | 62 | if args.activation == "ReLU": 63 | if args.inputset == 1: 64 | xs = (np.sqrt(2)/2)*torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 65 | xset = "xsetRt" 66 | 67 | if args.inputset == 2: 68 | xs = torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 69 | xset = "xsetR" 70 | 71 | widths = [20] # defines NGP 72 | cutoffs = [100] 73 | 74 | args.sb = 10**-100 75 | 76 | 77 | # optional arguments for parallel: 78 | # order = args.order 79 | # order = "0" 80 | # order = "2" 81 | # order = "220" 82 | args.n_inputs = len(xs) 83 | 84 | params = {"0": 1, "2": 2, "220": 3} 85 | lam_array = np.ndarray((params[order])) 86 | 87 | for cutoff in cutoffs: 88 | log_cutoff = np.round(np.log10(cutoff), 2) 89 | print("Computing at cutoff ", cutoff) 90 | 91 | # this just computes all the integrals beforehand for all (unique) tensor elements 92 | local0_integral = np.array([[[[np.nan for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)]) 93 | local2_integral = np.array([[[[np.nan for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)]) # set to be the same shape array 94 | nonlocal_integral = np.array([[[[np.nan for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)]) # set to be the same shape array 95 | 96 | path_nonloc = "nonlocal_integral_"+args.activation+"_"+xset+"_"+str(cutoff)+"_"+str(args.sb)+".pickle" 97 | path_loc0 = "loc0_integral_"+args.activation+"_"+xset+"_"+str(cutoff)+"_"+str(args.sb)+".pickle" 98 | path_loc2 = "loc2_integral_"+args.activation+"_"+xset+"_"+str(cutoff)+"_"+str(args.sb)+".pickle" 99 | computed = False 100 | computed = os.path.isfile(path_nonloc) and os.path.isfile(path_loc0) and os.path.isfile(path_loc2) # boolean - is the file here? 101 | 102 | if computed: 103 | print("importing integrals") 104 | local0_integral = pickle.load(open(path_loc0, "rb")) 105 | local2_integral = pickle.load(open(path_loc2, "rb")) 106 | nonlocal_integral = pickle.load(open(path_nonloc, "rb")) 107 | 108 | if not computed: 109 | for i in range(args.n_inputs): 110 | print("computing integrals for first tensor index ", i) 111 | for j in range(args.n_inputs): 112 | for k in range(args.n_inputs): 113 | for l in range(args.n_inputs): 114 | if order == "0": 115 | local0_integral[i,j,k,l] = local0(xs[i], xs[j], xs[k], xs[l], cutoff, args) 116 | print(local0_integral[i,j,k,l]) 117 | 118 | if order == "2": 119 | local2_integral[i,j,k,l] = local2(xs[i], xs[j], xs[k], xs[l], cutoff, args) 120 | print(local2_integral[i,j,k,l]) 121 | if order == "220": 122 | 123 | print("computing nonlocal integral",i,j,k,l) 124 | nonlocal_integral[i,j,k,l] = nonlocal22(xs[i], xs[j], xs[k], xs[l], cutoff, args) 125 | print(nonlocal_integral[i,j,k,l]) 126 | 127 | # save nonlocal integral at this cutoff and activation and bias 128 | if order == "0": 129 | pickle.dump(local0_integral, open(path_loc0, "wb")) 130 | if order == "2": 131 | pickle.dump(local2_integral, open(path_loc2, "wb")) 132 | if order == "220": 133 | pickle.dump(nonlocal_integral, open(path_nonloc, "wb")) 134 | -------------------------------------------------------------------------------- /free_theory.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("./") 3 | sys.path.append("..") 4 | from lib import * 5 | 6 | if __name__ == "__main__": 7 | 8 | parser = argparse.ArgumentParser() 9 | parser.add_argument("--activation", type=str, default = "ReLU") 10 | parser.add_argument('--exp', type=str, default = None) 11 | parser.add_argument("--width", type=int, default = 100) 12 | parser.add_argument("--n-inputs", type = int, default = 6) 13 | parser.add_argument("--n-models", type = int, default = 10**3) 14 | parser.add_argument("--d-in", type = int, default = 1) 15 | parser.add_argument("--d-out", type = int, default = 1) 16 | parser.add_argument("--sb", type = float, default = 1.0) 17 | parser.add_argument("--sw", type = float, default = 1.0) 18 | parser.add_argument("--mb", type = float, default = 0.0) 19 | parser.add_argument("--mw", type = float, default = 0.0) 20 | parser.add_argument("--cuda", action = 'store_true', default = False) 21 | parser.add_argument("--n-pt", type = int, default = 4) 22 | 23 | args = parser.parse_args() 24 | 25 | # this n is the npt function being computed in this script 26 | # it can be changes directly here, or above in the defaults for --n-pt 27 | n = args.n_pt 28 | 29 | runs = 1 30 | 31 | if args.d_in == 1: 32 | if args.activation == "Erf": 33 | xs = torch.tensor([[-1],[-0.6],[-0.2],[0.2],[0.6], [1.0]]) 34 | xset = "xset1" 35 | if args.activation == "GaussNet": 36 | xs = 0.01*torch.tensor([[-1],[-0.6],[-0.2],[0.2],[0.6], [1.0]]) 37 | xset = "xset2" 38 | if args.activation == "ReLU": 39 | xs = torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 40 | xset = "xset1A" 41 | 42 | if args.d_in == 2: 43 | xs = torch.tensor([-1.0, 1.0]) 44 | xs = torch.cartesian_prod(xs, xs) 45 | xset = "xset3" 46 | if args.activation == "GaussNet": 47 | xs = 0.01*xs 48 | xset = "xset4" 49 | if args.activation == "ReLU": 50 | xs = torch.tensor([0.5, 1.0]) 51 | xs = torch.cartesian_prod(xs, xs) 52 | xset = "xset3A" 53 | 54 | if args.d_in == 3: 55 | xs = torch.tensor([[-1., -1., -1.],[ 1., 1., -1.],[-1., 1., 1.],[ 1., -1., 1.]]) 56 | xset = "xset5" 57 | if args.activation == "GaussNet": 58 | xs = 0.01*xs 59 | xset = "xset6" 60 | if args.activation == "ReLU": 61 | xs = torch.tensor([[0.2, 0.2, 0.2],[ 1., 1., 0.2],[0.2, 1., 1.],[ 1., 0.2, 1.]]) 62 | xset = "xset5A" 63 | 64 | if args.activation == "ReLU": 65 | args.sb == 10**-100 66 | 67 | args.n_inputs = len(xs) 68 | widths = [2, 3, 4, 5, 10, 20, 50, 100, 500, 1000] #ten 69 | 70 | fss = {} # dictionary for storing outputs after importing 71 | # keys are widths 72 | 73 | for width in widths: 74 | print("Unpickling width "+str(width)) 75 | args.width = width 76 | for run in range(runs): 77 | with open("run"+str(run+1)+"_din="+str(args.d_in)+"_"+args.activation+"_1e"+str(int(np.log10(args.n_models)))+"models_"+str(args.width)+"width_"+xset+".pickle",'rb') as handle: 78 | if run == 0: 79 | fss[width] = pickle.load(handle) 80 | else: 81 | fss[width] = torch.cat((fss[width], pickle.load(handle))) 82 | 83 | 84 | print("Computing "+str(n)+"-pt function for activation "+args.activation) 85 | if n == 2: 86 | n_thy = kernel_tensor(xs, args) 87 | if n == 4: 88 | n_thy = four_pt_tensor(xs, args) 89 | if n == 6: 90 | n_thy = six_pt_tensor(xs, args) 91 | #store the GP 6pt function for later 92 | pickle.dump(n_thy, open("six_pt_tensor_"+args.activation+"_din"+str(args.d_in)+"_"+xset+".pickle",'wb')) 93 | if n not in [2, 4, 6]: 94 | print("Not a 2, 4, or 6pt function") 95 | exit() 96 | 97 | fss_chunk = {} 98 | # split data into k chunks so background level can be plotted 99 | k = 10 100 | chunk = len(fss[widths[0]])//k 101 | print("Models in each chunk: ", chunk) 102 | 103 | widths_list, n_diff_full, backgrounds, n_exp = [], [], [], [0. for _ in range(10)] 104 | for width in widths: 105 | for chunk_num in range(10): 106 | # this is a dictionary (with keys = widths) for a single chunk 107 | fss_chunk[width] = fss[width].narrow_copy(0,chunk_num*chunk,chunk) 108 | 109 | # computes the experimental n-pt function and averages over models elementwise 110 | n_tensor = torch.mean(n_point(fss_chunk[width], n), dim=0) 111 | assert(args.d_out == 1) # this code is written for d_out = 1 112 | n_tensor = n_tensor.view(n_tensor.shape[0:n]) 113 | n_exp[chunk_num] = n_tensor.tolist() 114 | 115 | n_diff = np.abs(np.nanmean(n_exp, axis = 0) - np.array(n_thy))/np.array(n_thy) 116 | 117 | if n == 6: 118 | pickle.dump(n_exp, open("six_pt_exp_"+args.activation+"_width"+str(width)+"_din"+str(args.d_in)+"_"+xset+".pickle",'wb')) 119 | if n == 4: 120 | pickle.dump(n_exp, open("four_pt_exp_"+args.activation+"_width"+str(width)+"_din"+str(args.d_in)+"_"+xset+".pickle",'wb')) 121 | 122 | # computes elementwise standard deviation among chunks 123 | n_diff_std = np.nanstd(n_exp, axis = 0)/np.array(n_thy) 124 | n_diff = [i for i in n_diff.flatten().tolist() if (~np.isnan(i))] 125 | n_diff_std = [i for i in n_diff_std.flatten().tolist() if (~np.isnan(i))] 126 | 127 | n_diff_full.extend(n_diff) 128 | 129 | mean1 = np.mean(n_diff) 130 | background1 = np.mean(n_diff_std) 131 | 132 | for i in range(len(n_diff)): 133 | widths_list.append(width) 134 | backgrounds.append(background1) 135 | 136 | 137 | df = pd.DataFrame({"width": widths_list, "n_point": n_diff_full, "background": backgrounds}) 138 | df['log10width'] = np.log10(df['width']) 139 | df['log10n_point'] = np.log10(df['n_point']) 140 | df['log10background'] = np.log10(df['background']) 141 | 142 | import seaborn as sns 143 | import matplotlib as mpl 144 | import numpy as np 145 | from matplotlib import pyplot as plt 146 | from matplotlib import rc 147 | 148 | rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']}) 149 | rc('text', usetex=True) 150 | fsize = 24 151 | plt.rc('text', usetex=True) 152 | plt.rc('text.latex', preamble=r'\usepackage{amsmath}') 153 | plt.rc('font', size=fsize) # controls default text sizes 154 | plt.rc('axes', titlesize=20) # fontsize of the axes title 155 | plt.rc('axes', labelsize=20) # fontsize of the x and y labels 156 | plt.rc('xtick', labelsize=18) # fontsize of the tick labels 157 | plt.rc('ytick', labelsize=18) # fontsize of the tick labels 158 | plt.rc('legend', fontsize=14) # legend fontsize 159 | plt.rc('figure', titlesize=fsize) # fontsize of the figure title 160 | 161 | sns.set_style("ticks", {"xtick.major.size":18, 162 | "ytick.major.size":18}) 163 | def lt(s): 164 | return (r'$\mathrm{' + s + r'}$').replace(" ", "\,\,") 165 | 166 | def lm(s): 167 | return r'$' + s + r'$' 168 | 169 | ### 170 | # plot! 171 | if args.activation == "GaussNet": 172 | act = "Gauss\\text{-}net" 173 | if args.activation == "Erf": 174 | act = "Erf\\text{-}net" 175 | if args.activation == "ReLU": 176 | act = "ReLU\\text{-}net" 177 | title_size, label_size, tick_size = fsize, fsize, fsize 178 | sns.set_style(style="darkgrid") 179 | sns.lineplot(data=df,x='log10width',y='log10n_point', label = lt(str(n)+"\\text{-}pt signal")) 180 | sns.lineplot(data=df,x='log10width',y='log10background', label = lt("background")) 181 | plt.tick_params(labelsize=tick_size) 182 | plt.title(lt(act+" "+str(n)+"\\text{-}pt Deviation, ")+lm("d_{in}="+str(args.d_in)),fontsize=title_size) 183 | plt.ylabel(lm("\\log_{10} m_{"+str(n)+"}"),fontsize=label_size) 184 | plt.xlabel(lm("\\log_{10} N"),fontsize=label_size) 185 | plt.tight_layout() 186 | b, t = plt.ylim() # discover the values for bottom and top 187 | b -= 0.01 # aesthetics 188 | t += 0.01 189 | plt.ylim(b, t) 190 | plt.margins(0,0) # aesthetics 191 | plt.savefig("gp_"+args.activation+str(n)+".pdf",bbox_inches='tight') 192 | plt.legend() 193 | plt.figure() 194 | # plt.show() -------------------------------------------------------------------------------- /lam_fit/lam_fit.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("./") 3 | sys.path.append("..") 4 | from lib import * 5 | import os 6 | import statistics 7 | import torch 8 | import torch.nn as nn 9 | 10 | from scipy.optimize import least_squares 11 | from scipy.optimize import minimize 12 | from scipy.optimize import minimize_scalar 13 | 14 | if __name__ == "__main__": 15 | 16 | parser = argparse.ArgumentParser() 17 | parser.add_argument("--activation", type=str, default = "GaussNet") 18 | parser.add_argument('--exp', type=str, default = None) 19 | parser.add_argument("--width", type=int, default = 100) 20 | parser.add_argument("--n-inputs", type = int, default = 6) 21 | parser.add_argument("--n-models", type = int, default = 10**3) 22 | parser.add_argument("--d-in", type = int, default = 1) 23 | parser.add_argument("--d-out", type = int, default = 1) 24 | parser.add_argument("--sb", type = float, default = 1.0) 25 | parser.add_argument("--sw", type = float, default = 1.0) 26 | parser.add_argument("--mb", type = float, default = 0.0) 27 | parser.add_argument("--mw", type = float, default = 0.0) 28 | parser.add_argument("--cuda", action = 'store_true', default = False) 29 | parser.add_argument("--n-pt", type = int, default = 4) 30 | parser.add_argument("--parallel", type = bool, default = False) 31 | 32 | args = parser.parse_args() 33 | n = args.n_pt 34 | 35 | 36 | if args.activation == "Erf": 37 | width = 5 38 | cutoffs = [100] 39 | xset = "xsetE" 40 | 41 | 42 | if args.activation == "GaussNet": 43 | xset = "xsetG" 44 | width = 1000 45 | cutoffs = [np.inf] 46 | 47 | 48 | if args.activation == "ReLU": 49 | xset = "xsetR" 50 | width = 20 51 | cutoffs = [100] 52 | args.sb = 10**-100 53 | 54 | path_LHS_test = "four_pt_exp_"+args.activation+"_width"+str(width)+"_din"+str(args.d_in)+"_"+xset+".pickle" 55 | path_LHS_train = "four_pt_exp_"+args.activation+"_width"+str(width)+"_din"+str(args.d_in)+"_"+xset+"t.pickle" 56 | 57 | deltaG4test = np.nanmean(pickle.load(open(path_LHS_test, "rb")), axis = 0) 58 | deltaG4train = np.nanmean(pickle.load(open(path_LHS_train, "rb")), axis = 0) 59 | 60 | deltaG4train_flat = np.array([i for i in deltaG4train.flatten().tolist() if (~np.isnan(i))]) 61 | deltaG4test_flat = np.array([i for i in deltaG4test.flatten().tolist() if (~np.isnan(i))]) 62 | 63 | dG4 = torch.DoubleTensor(deltaG4train_flat) 64 | dG4_test = torch.DoubleTensor(deltaG4test_flat) 65 | 66 | 67 | for cutoff in cutoffs: 68 | log_cutoff = np.round(np.log10(cutoff), 2) 69 | print("Computing at cutoff ", cutoff) 70 | 71 | # this just computes all the integrals beforehand for all (unique) tensor elements 72 | local0_integral = np.array([[[[np.nan for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)]) 73 | local2_integral = np.array([[[[np.nan for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)]) # set to be the same shape array 74 | nonlocal_integral = np.array([[[[np.nan for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)]) # set to be the same shape array 75 | 76 | path_nonloc = "nonlocal_integral_"+args.activation+"_"+xset+"_"+str(cutoff)+"_"+str(args.sb)+".pickle" 77 | path_loc0 = "loc0_integral_"+args.activation+"_"+xset+"_"+str(cutoff)+"_"+str(args.sb)+".pickle" 78 | path_loc2 = "loc2_integral_"+args.activation+"_"+xset+"_"+str(cutoff)+"_"+str(args.sb)+".pickle" 79 | 80 | path_nonloct = "nonlocal_integral_"+args.activation+"_"+xset+"t_"+str(cutoff)+"_"+str(args.sb)+".pickle" 81 | path_loc0t = "loc0_integral_"+args.activation+"_"+xset+"t_"+str(cutoff)+"_"+str(args.sb)+".pickle" 82 | path_loc2t = "loc2_integral_"+args.activation+"_"+xset+"t_"+str(cutoff)+"_"+str(args.sb)+".pickle" 83 | computed = os.path.isfile(path_nonloc) and os.path.isfile(path_loc0) and os.path.isfile(path_loc2) 84 | 85 | if computed: 86 | print("importing integrals") 87 | 88 | local0_integral = pickle.load(open(path_loc0, "rb")) 89 | local2_integral = pickle.load(open(path_loc2, "rb")) 90 | nonlocal_integral = pickle.load(open(path_nonloc, "rb")) 91 | 92 | local0_integralt = pickle.load(open(path_loc0t, "rb")) 93 | local2_integralt = pickle.load(open(path_loc2t, "rb")) 94 | nonlocal_integralt = pickle.load(open(path_nonloct, "rb")) 95 | 96 | if not computed: 97 | print("Please run lam_fit_integrals.py first.") 98 | quit() 99 | 100 | 101 | local0_integral_train = local0_integral 102 | local2_integral_train = local2_integral 103 | nonlocal_integral_train = nonlocal_integral 104 | 105 | # change this part because integrals are indexed differently 106 | local0_integral_test = local0_integralt 107 | local2_integral_test = local2_integralt 108 | nonlocal_integral_test = nonlocal_integralt 109 | 110 | 111 | local0_integral_flat = np.array([i for i in local0_integral_train.flatten().tolist() if (~np.isnan(i))]) 112 | local2_integral_flat = np.array([i for i in local2_integral_train.flatten().tolist() if (~np.isnan(i))]) 113 | nonlocal_integral_flat = np.array([i for i in nonlocal_integral_train.flatten().tolist() if (~np.isnan(i))]) 114 | 115 | local0_integral_flat_test = np.array([i for i in local0_integral_test.flatten().tolist() if (~np.isnan(i))]) 116 | local2_integral_flat_test = np.array([i for i in local2_integral_test.flatten().tolist() if (~np.isnan(i))]) 117 | nonlocal_integral_flat_test = np.array([i for i in nonlocal_integral_test.flatten().tolist() if (~np.isnan(i))]) 118 | 119 | 120 | T0, T2, TNL = torch.DoubleTensor(local0_integral_flat), torch.DoubleTensor(local2_integral_flat), torch.DoubleTensor(nonlocal_integral_flat) 121 | T0_test, T2_test, TNL_test = torch.DoubleTensor(local0_integral_flat_test), torch.DoubleTensor(local2_integral_flat_test), torch.DoubleTensor(nonlocal_integral_flat_test) 122 | 123 | l0, l2, lNL = torch.tensor(0.0,requires_grad=True), torch.tensor(0.0,requires_grad=True), torch.tensor(0.0,requires_grad=True) 124 | 125 | def MAPE_out(A,P): 126 | return torch.mean(100.0*torch.abs((A-P)/A)), torch.max(100.0*torch.abs((A-P)/A)) 127 | def MAPE(A,P): 128 | return torch.mean(100.0*torch.abs((A-P)/A)) 129 | 130 | # LEARNING RATE - needs to be played with 131 | lr = 1e-6 132 | 133 | 134 | optimizer = torch.optim.SGD([l0,l2,lNL],lr=lr) 135 | 136 | # can decide which criterion to optimize with 137 | criterion = nn.MSELoss() 138 | # criterion = MAPE 139 | 140 | def converged(recent_losses,thresh=1): # default within 1% 141 | m1, m2, m = max(recent_losses), min(recent_losses), sum(recent_losses)/len(recent_losses) 142 | if 100.0*(m1-m2)/m <= thresh: 143 | return True 144 | return False 145 | 146 | 147 | last_losses = [] 148 | print("BEGIN MSE", criterion(dG4, l0*T0+ l2*T2 + lNL*TNL).detach().cpu().numpy()) 149 | print("\n") 150 | for epoch in range(500000): 151 | #print(l0,l2) 152 | # loss = criterion(dG4, l0*T0) # this is the only time we have to change the lambda expressions 153 | # loss = criterion(dG4, l0*T0 + l2*T2) 154 | loss = criterion(dG4, l0*T0 + l2*T2 + lNL*TNL) 155 | if epoch%1000 == 0: print("epoch, l0, l2, lNL, loss",epoch,float(l0.detach().cpu().numpy()),float(l2.detach().cpu().numpy()),float(lNL.detach().cpu().numpy()),float(loss.detach().cpu().numpy())) 156 | # if epoch%20 == 19: quit() 157 | optimizer.zero_grad() 158 | loss.backward() 159 | optimizer.step() 160 | if len(last_losses) <= 50: 161 | last_losses.append(float(loss.detach().cpu().numpy())) 162 | else: 163 | last_losses = last_losses[1:] 164 | last_losses.append(float(loss.detach().cpu().numpy())) 165 | if converged(last_losses,10**-9): 166 | print("CONVERGED EARLY: ", epoch, float(l0.detach().cpu().numpy()),float(l2.detach().cpu().numpy()),float(lNL.detach().cpu().numpy()),float(loss.detach().cpu().numpy())) 167 | break 168 | 169 | 170 | print("epoch",epoch, "\nl0 = ", float(l0.detach().cpu().numpy())," \nl2 = ", float(l2.detach().cpu().numpy()), " \nlNL = ", float(lNL.detach().cpu().numpy())) 171 | # print(max(last_losses)-min(last_losses)) 172 | print("\nMAPE: ", MAPE_out(dG4, l0*T0+ l2*T2 + lNL*TNL)[0].item(), "% \nMAX APE: ", MAPE_out(dG4, l0*T0+ l2*T2 + lNL*TNL)[1].item(), "% \nMSE: ", criterion(dG4, l0*T0+ l2*T2 + lNL*TNL).item()) 173 | 174 | print("\ntestset MAPE: ", MAPE_out(dG4_test,l0*T0_test + l2*T2_test + lNL*TNL_test )[0].item(), "% \ntestset MAX APE: ", MAPE_out(dG4_test,l0*T0_test + l2*T2_test + lNL*TNL_test )[1].item(), "% \ntestset MSE: ", criterion(dG4_test,l0*T0_test + l2*T2_test + lNL*TNL_test ).item()) 175 | print("\nlr = ", lr) 176 | -------------------------------------------------------------------------------- /six_pt_prediction.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("./") 3 | sys.path.append("..") 4 | from lib import * 5 | import statistics 6 | 7 | 8 | if __name__ == "__main__": 9 | 10 | parser = argparse.ArgumentParser() 11 | parser.add_argument("--activation", type=str, default = "ReLU") 12 | parser.add_argument('--exp', type=str, default = None) 13 | parser.add_argument("--width", type=int, default = 100) 14 | parser.add_argument("--n-inputs", type = int, default = 6) 15 | parser.add_argument("--n-models", type = int, default = 10**3) 16 | parser.add_argument("--d-in", type = int, default = 1) 17 | parser.add_argument("--d-out", type = int, default = 1) 18 | parser.add_argument("--sb", type = float, default = 1.0) 19 | parser.add_argument("--sw", type = float, default = 1.0) 20 | parser.add_argument("--mb", type = float, default = 0.0) 21 | parser.add_argument("--mw", type = float, default = 0.0) 22 | parser.add_argument("--cuda", action = 'store_true', default = False) 23 | parser.add_argument("--n-pt", type = int, default = 6) 24 | 25 | args = parser.parse_args() 26 | n = args.n_pt 27 | 28 | runs = 1 29 | 30 | if args.d_in == 1: 31 | if args.activation == "Erf": 32 | xs = torch.tensor([[-1],[-0.6],[-0.2],[0.2],[0.6], [1.0]]) 33 | xset = "xset1" 34 | if args.activation == "GaussNet": 35 | xs = 0.01*torch.tensor([[-1],[-0.6],[-0.2],[0.2],[0.6], [1.0]]) 36 | xset = "xset2" 37 | if args.activation == "ReLU": 38 | xs = torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 39 | xset = "xset1A" 40 | args.sb == 10**-100 41 | 42 | args.n_inputs = len(xs) 43 | 44 | if args.activation == "ReLU": 45 | width = 20 46 | if args.activation == "GaussNet": 47 | width = 100 48 | if args.activation == "Erf": 49 | width = 5 50 | args.width = width 51 | 52 | 53 | fss, fss_chunk = {}, {} 54 | print("Unpickling width "+str(width)) 55 | # args.width = width 56 | for run in range(runs): 57 | with open("run"+str(run+1)+"_din="+str(args.d_in)+"_"+args.activation+"_1e"+str(int(np.log10(args.n_models)))+"models_"+str(args.width)+"width_"+xset+".pickle", 'rb') as handle: 58 | if run == 0: 59 | fss[width] = pickle.load(handle) 60 | else: 61 | fss[width] = torch.cat((fss[width], pickle.load(handle))) 62 | 63 | # chunk 64 | k = 10 65 | chunk = args.n_models*runs//k 66 | 67 | # GP 6-pt function 68 | if path.exists("six_pt_tensor_"+args.activation+"_din"+str(args.d_in)+"_"+xset+".pickle"): 69 | n_thy = pickle.load(open("six_pt_tensor_"+args.activation+"_din"+str(args.d_in)+"_"+xset+".pickle",'rb')) 70 | GP_array = np.array(n_thy) 71 | else: 72 | GP_array = np.array(six_pt_tensor(xs, args)) 73 | 74 | # experimental 6-pt function 75 | n_exp, n_exp4 = [0. for _ in range(k)], [0. for _ in range(k)] 76 | 77 | # GP 6-pt function 78 | if path.exists("six_pt_exp_"+args.activation+"_width"+str(width)+"_din"+str(args.d_in)+"_"+xset+".pickle"): 79 | exp_array = pickle.load(open("six_pt_exp_"+args.activation+"_width"+str(width)+"_din"+str(args.d_in)+"_"+xset+".pickle",'rb')) 80 | exp_array = np.array(exp_array) 81 | else: 82 | for chunk_num in range(k): 83 | fss_chunk[width] = fss[width].narrow_copy(0,chunk_num*chunk,chunk) 84 | 85 | n_tensor = torch.mean(n_point(fss_chunk[width], n), dim=0) 86 | n_tensor4 = torch.mean(n_point(fss_chunk[width], 4), dim=0) 87 | # only true when d_out = 1: 88 | assert(args.d_out == 1) 89 | n_tensor = n_tensor.view(n_tensor.shape[0:n]) 90 | n_exp[chunk_num] = n_tensor.tolist() 91 | n_exp4[chunk_num] = n_tensor4.tolist() 92 | 93 | if run == 4: 94 | print("done with run ", run+1) 95 | 96 | exp_array_full = np.nanmean(n_exp, axis = 0) 97 | exp_array = trim_sym_tensor(exp_array_full, args) 98 | expt4 = trim_sym_tensor4(np.nanmean(n_exp4, axis = 0), args) 99 | 100 | if args.activation == "ReLU": 101 | cutoffs = [7, 10, 15, 20, 30, 40, 50, 70, 100, 200, 500, 1000, 2000, 5000, 7000, 10000, 20000, 40000, 60000, 80000, 100000] #21 102 | 103 | if args.activation == "GaussNet" or args.activation == "Erf": 104 | cutoffs = [np.inf] 105 | 106 | logcut, GP_list, GP_lam_list = [], [], [] 107 | 108 | GP = GP_array/exp_array 109 | 110 | fourptcontribution = [[[[[[0. for i in range(len(xs))] for i in range(len(xs))] for i in range(len(xs))] for i in range(len(xs))] for i in range(len(xs))] for i in range(len(xs))] 111 | xslist = xs.tolist() 112 | 113 | 114 | def calc_lam(xs): 115 | denom = [[[[np.nan for i in range(len(xs))] for i in range(len(xs))] for i in range(len(xs))] for i in range(len(xs))] 116 | denom = np.array(denom) 117 | numerator = np.array(four_pt_tensor(xs, args)) - expt4 118 | for i in range(len(xs)): 119 | for j in range(i, len(xs)): 120 | for k in range(j, len(xs)): 121 | for l in range(k, len(xs)): 122 | denom[i][j][k][l] = four_pt_int(xs[i], xs[j], xs[k], xs[l], cutoff, args) 123 | denom = np.array(denom) 124 | lam = (numerator/denom) 125 | print("cutoff: ", cutoff, "stdev in lam = ", np.nanstd(lam)) 126 | return lam 127 | 128 | 129 | # # parallelize the cutoff integrals 130 | # from mpi4py import MPI 131 | # comm = MPI.COMM_WORLD 132 | # size = comm.Get_size() 133 | # rank = comm.Get_rank() 134 | # cutoffs = [cutoffs[rank]] 135 | 136 | 137 | for cutoff in cutoffs: 138 | print("Running cutoff ", cutoff) 139 | for x1 in xs: 140 | for x2 in xs[1:]: 141 | for x3 in xs[2:]: 142 | for x4 in xs[3:]: 143 | for x5 in xs[4:]: 144 | for x6 in xs[5:]: 145 | num = 0. 146 | # all possible 6pt diagrams containing a 4pt vertex (lambda) 147 | num += intkappa(x5, x6, x3, x4, x1, x2, cutoff, args) 148 | num += intkappa(x6, x2, x5, x5, x1, x3, cutoff, args) 149 | num += intkappa(x6, x2, x5, x3, x1, x4, cutoff, args) 150 | num += intkappa(x6, x2, x3, x4, x1, x5, cutoff, args) 151 | num += intkappa(x5, x2, x3, x4, x1, x6, cutoff, args) 152 | num += intkappa(x1, x4, x6, x5, x2, x3, cutoff, args) 153 | num += intkappa(x1, x6, x5, x3, x2, x4, cutoff, args) 154 | num += intkappa(x1, x3, x6, x4, x2, x5, cutoff, args) 155 | num += intkappa(x1, x5, x3, x4, x2, x6, cutoff, args) 156 | num += intkappa(x1, x2, x5, x6, x3, x4, cutoff, args) 157 | num += intkappa(x1, x2, x4, x6, x3, x5, cutoff, args) 158 | num += intkappa(x4, x2, x5, x1, x3, x6, cutoff, args) 159 | num += intkappa(x2, x1, x3, x6, x4, x5, cutoff, args) 160 | num += intkappa(x5, x2, x1, x3, x4, x6, cutoff, args) 161 | num += intkappa(x4, x2, x1, x3, x5, x6, cutoff, args) 162 | # total lambda contribution to the 6pt function 163 | fourptcontribution[xslist.index([x1])][xslist.index([x2])][xslist.index([x3])][xslist.index([x4])][xslist.index([x5])][xslist.index([x6])] = num 164 | if path.exists("lam_"+str(cutoff)+"_"+args.activation+"_width_"+str(width)+"_din"+str(args.d_in)+"_"+xset+".pickle"): 165 | lambda_tensor = pickle.load(open("lam_"+str(cutoff)+"_"+args.activation+"_width_"+str(width)+"_din"+str(args.d_in)+"_"+xset+".pickle",'rb')) 166 | print("Loaded lambda tensor from rg analysis") 167 | else: 168 | lambda_tensor = calc_lam(xs) 169 | 170 | lambda_average = np.nanmean(lambda_tensor) 171 | # computation of lambda term 172 | lambda_term = 24.*lambda_average*np.array(fourptcontribution) 173 | # normalized GP + lambda theory prediction 174 | GP_lam = (GP_array + lambda_term)/exp_array 175 | 176 | print(cutoff, "kernel = ", np.nanmean(GP_array)) 177 | print("lam term ", np.nanmean(lambda_term)) 178 | print("exp array = ", np.nanmean(exp_array)) 179 | print("delta = ", np.nanmean(GP_lam)) 180 | 181 | # flatten everything for pandas 182 | GPflat = [i for i in GP.flatten().tolist() if (~np.isnan(i))] 183 | GP_lamflat = [i for i in GP_lam.flatten().tolist() if (~np.isnan(i))] 184 | GP_list.extend(GPflat) 185 | GP_lam_list.extend(GP_lamflat) 186 | for i in range(len(GP_lamflat)): 187 | logcut.append(np.round(np.log10(cutoff), 2)) 188 | 189 | import pandas as pd 190 | df = pd.DataFrame({"log_cutoff": logcut}) 191 | df["GP"] = GP_list 192 | df["GP_lam"] = GP_lam_list 193 | 194 | 195 | import seaborn as sns 196 | import matplotlib as mpl 197 | from matplotlib import pyplot as plt 198 | from matplotlib import rc 199 | 200 | #pickle.dump(df, open("delta_add_"+args.activation+".pickle", "wb")) 201 | #df = pickle.load(open("delta_add_"+args.activation+".pickle", "rb")) 202 | 203 | rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']}) 204 | rc('text', usetex=True) 205 | fsize = 22 206 | plt.rc('text', usetex=True) 207 | plt.rc('text.latex', preamble=r'\usepackage{amsmath}') 208 | plt.rc('font', size=fsize) # controls default text sizes 209 | plt.rc('axes', titlesize=10) # fontsize of the axes title 210 | plt.rc('axes', labelsize=10) # fontsize of the x and y labels 211 | plt.rc('xtick', labelsize=8) # fontsize of the tick labels 212 | plt.rc('ytick', labelsize=8) # fontsize of the tick labels 213 | plt.rc('legend', fontsize=12) # legend fontsize 214 | plt.rc('figure', titlesize=fsize) # fontsize of the figure title 215 | 216 | sns.set_style("ticks", {"xtick.major.size":18, 217 | "ytick.major.size":18}) 218 | def lt(s): 219 | return (r'$\mathrm{' + s + r'}$').replace(" ", "\,\,") 220 | 221 | def lm(s): 222 | return r'$' + s + r'$' 223 | 224 | if args.activation == "GaussNet": 225 | act = "Gauss\\text{-}net" 226 | if args.activation == "Erf": 227 | act = "Erf\\text{-}net" 228 | if args.activation == "ReLU": 229 | act = "ReLU\\text{-}net" 230 | title_size, label_size, tick_size = fsize, fsize, fsize 231 | sns.set_style(style="darkgrid") 232 | sns.lineplot(x='log_cutoff',y='GP', data=df, label = lm("G^{(6)}_{GP}/G^{(6)}")) 233 | sns.lineplot(x='log_cutoff',y='GP_lam', data=df, label = lm("(G^{(6)}_{GP} + \\bar\\lambda")+lt(" contribution)")+lm("/G^{(6)}")) 234 | plt.tick_params(labelsize=tick_size) 235 | plt.title(lt(act+" ")+lm("G^{(6)}")+lt(" prediction, ")+lm("N="+str(width)),fontsize=title_size) 236 | plt.ylabel(lt("Normalized contributions to ")+lm("G^{6}"),fontsize=0.8*label_size) 237 | plt.ylim(0, 1.2) 238 | plt.xlabel(lm("\\log_{10}\\Lambda"),fontsize=label_size) 239 | plt.tight_layout() 240 | plt.savefig("six_pt_pred_"+args.activation+".pdf",bbox_inches='tight') 241 | plt.legend() 242 | plt.figure() 243 | # plt.show() -------------------------------------------------------------------------------- /six_pt_connected.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("./") 3 | sys.path.append("..") 4 | from lib import * 5 | import itertools 6 | import numpy as np 7 | 8 | import seaborn as sns 9 | import matplotlib as mpl 10 | import numpy as np 11 | from matplotlib import pyplot as plt 12 | from matplotlib import rc 13 | 14 | 15 | 16 | if __name__ == "__main__": 17 | 18 | parser = argparse.ArgumentParser() 19 | parser.add_argument("--activation", type=str, default = "ReLU") # changes. should be provided 20 | parser.add_argument('--exp', type=str, default = None) # does not change 21 | parser.add_argument("--width", type=int, default = 100) # changes. should be provided 22 | parser.add_argument("--n-inputs", type = int, default = 6) # does not change 23 | parser.add_argument("--n-models", type = int, default = 10**3) # each run has 10^6 models 24 | parser.add_argument("--d-in", type = int, default = 1) # does not change 25 | parser.add_argument("--d-out", type = int, default = 1) # does not change 26 | parser.add_argument("--sb", type = float, default = 1.0) # changes. 1 for Gauss, Erf, 0 for ReLU 27 | parser.add_argument("--sw", type = float, default = 1.0) # does not change 28 | parser.add_argument("--mb", type = float, default = 0.0) # does not change 29 | parser.add_argument("--mw", type = float, default = 0.0) # does not change 30 | parser.add_argument("--cuda", action = 'store_true', default = False) # does not change 31 | parser.add_argument("--n-pt", type = int, default = 6) 32 | 33 | args = parser.parse_args() 34 | 35 | runs = 1 36 | n = args.n_pt 37 | 38 | if args.d_in == 1: 39 | if args.activation == "Erf": 40 | xs = torch.tensor([[-1],[-0.6],[-0.2],[0.2],[0.6], [1.0]]) 41 | xset = "xset1" 42 | if args.activation == "GaussNet": 43 | xs = 0.01*torch.tensor([[-1],[-0.6],[-0.2],[0.2],[0.6], [1.0]]) 44 | xset = "xset2" 45 | if args.activation == "ReLU": 46 | xs = torch.tensor([[0.2],[0.4],[0.6],[0.8],[1.0],[1.2]]) 47 | xset = "xset1A" 48 | args.sb = 10**-100 49 | 50 | args.n_inputs = len(xs) 51 | widths = [2, 3, 4, 5, 10, 20, 50, 100, 500, 1000] #ten 52 | xslist = xs.tolist() 53 | 54 | 55 | if path.exists("six_pt_tensor_"+args.activation+"_din"+str(args.d_in)+"_"+xset+".pickle"): 56 | six_pt_thy = np.array(pickle.load(open("six_pt_tensor_"+args.activation+"_din"+str(args.d_in)+"_"+xset+".pickle",'rb'))) 57 | else: 58 | six_pt_thy = np.array(six_pt_tensor(xs, args)) 59 | 60 | if path.exists("four_pt_tensor_"+args.activation+"_din"+str(args.d_in)+"_"+xset+".pickle"): 61 | four_pt_thy = np.array(pickle.load(open("four_pt_tensor_"+args.activation+"_din"+str(args.d_in)+"_"+xset+".pickle",'rb'))) 62 | else: 63 | four_pt_thy = np.array(four_pt_tensor(xs, args)) 64 | 65 | 66 | six_diff_full = [] # list for plotting 6pt connected piece to see 1/N^2 dependence 67 | backgrounds = [] 68 | widths_list = [] 69 | background_per_width = [] # normalized width-dependent background 70 | six_diff_full_unnormalized = [] # list for plotting unnormalized 6pt connected to see 1/N^2 dependence 71 | background_per_width_unnormalized = [] # unnormalized width-dependent background 72 | 73 | 74 | for i in range(len(widths)) : 75 | args.width = widths[i] 76 | width = widths[i] 77 | 78 | six_pt_expt_list = pickle.load(open("six_pt_exp_"+args.activation+"_width"+str(width)+"_din"+str(args.d_in)+"_"+xset+".pickle",'rb')) 79 | four_pt_expt_list = pickle.load(open("four_pt_exp_"+args.activation+"_width"+str(width)+"_din"+str(args.d_in)+"_"+xset+".pickle",'rb')) 80 | 81 | 82 | # 6pt connected term, according to QFT definition : G^6 + 15 combination(2*G2*G2*G2 - G4*G2) = G^6 + 2*G^6_GP - 15 combo G4*G2 83 | sixptdev_O1 = np.nanmean(six_pt_expt_list, axis = 0) + 2*np.array(six_pt_thy) 84 | 85 | fourptexpt = np.nanmean(four_pt_expt_list, axis = 0) # with modified definition of 6-pt connected from this is what we need 86 | 87 | six_pt_fluctuations = np.nanstd(six_pt_expt_list, axis = 0) # an array with elementwise 6pt fluctuations at tree level 88 | four_pt_fluctuations = np.nanstd(four_pt_expt_list, axis = 0) # an array with elementwise 4pt fluctuations at tree level 89 | 90 | six_pt_fluctuations_O1 = [[[[[[0. for q in range(len(xs))] for j in range(len(xs))] for k in range(len(xs))] for l in range(len(xs))] for m in range(len(xs))] for p in range(len(xs))] # an array to contain elementwise fluctuation to 6pt at O(1/N) 91 | 92 | sixptdev_O2 = [] 93 | sixptdev_O2_unnormalized = [] 94 | print("beginning 6pt calculation") 95 | for x1 in xs: 96 | for x2 in xs: 97 | for x3 in xs: 98 | for x4 in xs: 99 | for x5 in xs: 100 | for x6 in xs: 101 | G4G2_contribution = (fourptexpt[xslist.index([x1])][xslist.index([x2])][xslist.index([x3])][xslist.index([x4])])*K_int(x5, x6, args)+ (fourptexpt[xslist.index([x1])][xslist.index([x2])][xslist.index([x3])][xslist.index([x5])])*K_int(x4, x6, args)+ (fourptexpt[xslist.index([x1])][xslist.index([x2])][xslist.index([x5])][xslist.index([x4])])*K_int(x3, x6, args)+ (fourptexpt[xslist.index([x1])][xslist.index([x5])][xslist.index([x3])][xslist.index([x4])])*K_int(x2, x6, args)+ (fourptexpt[xslist.index([x5])][xslist.index([x2])][xslist.index([x3])][xslist.index([x4])])*K_int(x1, x6, args)+ (fourptexpt[xslist.index([x1])][xslist.index([x2])][xslist.index([x3])][xslist.index([x6])])*K_int(x5, x4, args)+ (fourptexpt[xslist.index([x1])][xslist.index([x2])][xslist.index([x6])][xslist.index([x4])])*K_int(x5, x3, args)+ (fourptexpt[xslist.index([x1])][xslist.index([x6])][xslist.index([x3])][xslist.index([x4])])*K_int(x5, x2, args)+ (fourptexpt[xslist.index([x6])][xslist.index([x2])][xslist.index([x3])][xslist.index([x4])])*K_int(x5, x1, args)+ (fourptexpt[xslist.index([x1])][xslist.index([x2])][xslist.index([x5])][xslist.index([x6])])*K_int(x4, x3, args)+ (fourptexpt[xslist.index([x1])][xslist.index([x5])][xslist.index([x3])][xslist.index([x6])])*K_int(x4, x2, args)+ (fourptexpt[xslist.index([x5])][xslist.index([x2])][xslist.index([x3])][xslist.index([x6])])*K_int(x4, x1, args)+ (fourptexpt[xslist.index([x1])][xslist.index([x5])][xslist.index([x6])][xslist.index([x4])])*K_int(x3, x2, args)+ (fourptexpt[xslist.index([x5])][xslist.index([x2])][xslist.index([x6])][xslist.index([x4])])*K_int(x3, x1, args)+ (fourptexpt[xslist.index([x5])][xslist.index([x6])][xslist.index([x3])][xslist.index([x4])])*K_int(x2, x1, args) 102 | 103 | a = sixptdev_O1[xslist.index([x1])][xslist.index([x2])][xslist.index([x3])][xslist.index([x4])][xslist.index([x5])][xslist.index([x6])] - G4G2_contribution # this causes G^6 + 2*G2*G2*G2 - G4*G2 104 | 105 | b = six_pt_thy[xslist.index([x1])][xslist.index([x2])][xslist.index([x3])][xslist.index([x4])][xslist.index([x5])][xslist.index([x6])] 106 | if (~np.isnan(a)) and (~np.isnan(b)): 107 | sixptdev_O2.append(np.abs(a/b)) 108 | sixptdev_O2_unnormalized.append(np.abs(a)) 109 | 110 | six_pt_fluctuations_O1[xslist.index([x1])][xslist.index([x2])][xslist.index([x3])][xslist.index([x4])][xslist.index([x5])][xslist.index([x6])] = (four_pt_fluctuations[xslist.index([x1])][xslist.index([x2])][xslist.index([x3])][xslist.index([x4])])*K_int(x5, x6, args)+ (four_pt_fluctuations[xslist.index([x1])][xslist.index([x2])][xslist.index([x3])][xslist.index([x5])])*K_int(x4, x6, args)+ (four_pt_fluctuations[xslist.index([x1])][xslist.index([x2])][xslist.index([x5])][xslist.index([x4])])*K_int(x3, x6, args)+ (four_pt_fluctuations[xslist.index([x1])][xslist.index([x5])][xslist.index([x3])][xslist.index([x4])])*K_int(x2, x6, args)+ (four_pt_fluctuations[xslist.index([x5])][xslist.index([x2])][xslist.index([x3])][xslist.index([x4])])*K_int(x1, x6, args)+ (four_pt_fluctuations[xslist.index([x1])][xslist.index([x2])][xslist.index([x3])][xslist.index([x6])])*K_int(x5, x4, args)+ (four_pt_fluctuations[xslist.index([x1])][xslist.index([x2])][xslist.index([x6])][xslist.index([x4])])*K_int(x5, x3, args)+ (four_pt_fluctuations[xslist.index([x1])][xslist.index([x6])][xslist.index([x3])][xslist.index([x4])])*K_int(x5, x2, args)+ (four_pt_fluctuations[xslist.index([x6])][xslist.index([x2])][xslist.index([x3])][xslist.index([x4])])*K_int(x5, x1, args)+ (four_pt_fluctuations[xslist.index([x1])][xslist.index([x2])][xslist.index([x5])][xslist.index([x6])])*K_int(x4, x3, args)+ (four_pt_fluctuations[xslist.index([x1])][xslist.index([x5])][xslist.index([x3])][xslist.index([x6])])*K_int(x4, x2, args)+ (four_pt_fluctuations[xslist.index([x5])][xslist.index([x2])][xslist.index([x3])][xslist.index([x6])])*K_int(x4, x1, args)+ (four_pt_fluctuations[xslist.index([x1])][xslist.index([x5])][xslist.index([x6])][xslist.index([x4])])*K_int(x3, x2, args)+ (four_pt_fluctuations[xslist.index([x5])][xslist.index([x2])][xslist.index([x6])][xslist.index([x4])])*K_int(x3, x1, args)+ (four_pt_fluctuations[xslist.index([x5])][xslist.index([x6])][xslist.index([x3])][xslist.index([x4])])*K_int(x2, x1, args) 111 | 112 | 113 | 114 | six_diff_full.extend(sixptdev_O2) 115 | six_diff_full_unnormalized.extend(sixptdev_O2_unnormalized) 116 | 117 | # this is statistical fluctuations in 6-pt connected at O(1/N^2) : STD( G^6 + 2G^6_GP + 15* 24*lambda * [XI] diagram ) = STD( G^6 + 2*G^6_GP - 15*(G^4*K) = STD(G^6) + 2*STD(G^6_GP) + + STD(15*G^4*K) = STD(G^6) + STD(15*G^4*K). So after normalization, six_diff_STD = (STD(G^6) + STD(G^4)*15*K ) / G^6_GP 118 | 119 | six_diff_std = (six_pt_fluctuations + six_pt_fluctuations_O1)/np.array(six_pt_thy) # statistical fluctuations in O(1/N^2) 6-pt connected 120 | six_diff_std = [i for i in six_diff_std.flatten().tolist() if (~np.isnan(i))] # remove zeros and turn into a list to back background data 121 | backgrounds.append(np.mean(six_diff_std)) 122 | 123 | six_diff_std_unnormalized = (six_pt_fluctuations + six_pt_fluctuations_O1) # statistical fluctuations in unnormalized O(1/N^2) 6-pt connected 124 | six_diff_std_unnormalized = [i for i in six_diff_std_unnormalized.flatten().tolist() if (~np.isnan(i))] # remove zeros and turn into a list to back background data 125 | 126 | for i in range(len(sixptdev_O2)): 127 | widths_list.append(width) 128 | background_per_width.append(np.mean(six_diff_std)) 129 | background_per_width_unnormalized.append(np.mean(six_diff_std_unnormalized)) 130 | 131 | 132 | signal = sum(backgrounds)/len(backgrounds) 133 | backgrounds = [] 134 | for i in range(len(six_diff_full)): 135 | backgrounds.append(signal) 136 | 137 | print("save dataframe") 138 | assert(len(widths_list) == len(six_diff_full)) 139 | assert(len(widths_list) ==len(backgrounds)) 140 | assert(len(widths_list) ==len(background_per_width)) 141 | 142 | dataframe_tosave = np.zeros((6,len(widths_list))) # save this dataframe for plotting locally on my computer 143 | dataframe_tosave[0,:] = widths_list # first row is the list of widths as in panda dataframe 144 | dataframe_tosave[1,:] = six_diff_full # second row is the six pt connected at O(1/N^2) 145 | dataframe_tosave[2,:] = backgrounds # third row is the background level 146 | dataframe_tosave[3,:] = background_per_width # fourth row is background which is not averaged over width 147 | dataframe_tosave[4,:] = six_diff_full_unnormalized # fifth row is the six pt connected at O(1/N^2) 148 | dataframe_tosave[5,:] = background_per_width_unnormalized # sixth row is unnormalized background which is not averaged over width 149 | 150 | pickle.dump(dataframe_tosave, open("6pt_connected.pickle",'wb')) 151 | 152 | print("onto plots") 153 | 154 | rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']}) 155 | rc('text', usetex=True) 156 | fsize = 24 157 | plt.rc('text', usetex=True) 158 | plt.rc('text.latex', preamble=r'\usepackage{amsmath}') 159 | plt.rc('font', size=fsize) # controls default text sizes 160 | plt.rc('axes', titlesize=20) # fontsize of the axes title 161 | plt.rc('axes', labelsize=20) # fontsize of the x and y labels 162 | plt.rc('xtick', labelsize=18) # fontsize of the tick labels 163 | plt.rc('ytick', labelsize=18) # fontsize of the tick labels 164 | plt.rc('legend', fontsize=14) # legend fontsize 165 | plt.rc('figure', titlesize=fsize) # fontsize of the figure title 166 | 167 | sns.set_style("ticks", {"xtick.major.size":18, 168 | "ytick.major.size":18}) 169 | def lt(s): 170 | return (r'$\mathrm{' + s + r'}$').replace(" ", "\,\,") 171 | 172 | def lm(s): 173 | return r'$' + s + r'$' 174 | 175 | 176 | df = pd.DataFrame({"width": widths_list, "6_point_dev": six_diff_full, "background": backgrounds},dtype=float) 177 | df['log10width'] = np.log10(df['width']) 178 | df['log106_point_dev'] = np.log10(df['6_point_dev']) # log of 6pt deviation at O(1/N^2) 179 | df['log10background'] = np.log10(df['background']) # log of widths 180 | 181 | z = np.polyfit(df['log10width'], df['log106_point_dev'], 1) 182 | p = np.poly1d(z) 183 | trendline_eq = str(p) 184 | print(trendline_eq, activation) 185 | 186 | if activation == "GaussNet": 187 | act = "Gauss\\text{-}net" 188 | if activation == "Erf": 189 | act = "Erf\\text{-}net" 190 | if activation == "ReLU": 191 | act = "ReLU\\text{-}net" 192 | 193 | title_size, label_size, tick_size = fsize, fsize, fsize 194 | sns.set_style(style="darkgrid") 195 | plt.figure() 196 | sns.lineplot(data=df,x='log10width',y='log106_point_dev', label = lt(str(6)+"\\text{-}pt signal")) 197 | sns.lineplot(data=df,x='log10width',y='log10background', label = lt("background")) 198 | plt.plot(df['log10width'],p(df['log10width']),linestyle=':') 199 | plt.legend() 200 | 201 | plt.tick_params(labelsize=tick_size) 202 | plt.title(lt(act+" "+str(6)+"\\text{-}pt Deviation, ")+lm("d_{in}=1"),fontsize=title_size) 203 | plt.ylabel(lm("\\log_{10} m_{"+str(6)+"}"),fontsize=label_size) 204 | plt.xlabel(lm("\\log_{10} N"),fontsize=label_size) 205 | plt.tight_layout() 206 | b, t = plt.ylim() # discover the values for bottom and top 207 | b -= 0.01 # aesthetics 208 | t += 0.01 209 | plt.ylim(b, t) 210 | plt.margins(0,0) # aesthetics 211 | plt.savefig(path + "sixpt_"+activation+"_6ptdev_v2.pdf",bbox_inches='tight') 212 | plt.legend() 213 | plt.show() 214 | 215 | 216 | 217 | -------------------------------------------------------------------------------- /lib.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch import nn 3 | import numpy as np 4 | import math 5 | from scipy import special 6 | from scipy import integrate 7 | import pickle 8 | import datetime 9 | import argparse 10 | import os 11 | import pandas as pd 12 | import datetime 13 | import os.path 14 | from os import path 15 | os.environ['KMP_DUPLICATE_LIB_OK']='True' # solves strange Mac bug 16 | 17 | import sys 18 | def Print(*s): 19 | print(s) 20 | sys.stdout.flush() 21 | 22 | import datetime 23 | 24 | # # # # # # # # 25 | # experiments # 26 | # # # # # # # # 27 | 28 | def activation(args): 29 | if args.activation == "Erf": 30 | return Erf_nobackprop() 31 | elif args.activation == "ReLU": 32 | return nn.ReLU() 33 | 34 | class Erf_nobackprop(nn.Module): 35 | def forward(self,x): 36 | n = x.detach().numpy() 37 | return torch.tensor(special.erf(n)) 38 | 39 | class expln(nn.Module): 40 | def forward(self,x): 41 | x = torch.exp(x) 42 | mean, std = torch.mean(x,dim=1), torch.std(x,dim=1) 43 | mean, std = mean.view(mean.shape[0],1), std.view(std.shape[0],1) 44 | x = (x-mean)/std 45 | return x 46 | 47 | class GaussNet(nn.Module): 48 | def __init__(self,args): 49 | super(GaussNet, self).__init__() 50 | self.args = args 51 | 52 | # create and initialize input layer 53 | self.input = nn.Linear(args.d_in,args.width) 54 | torch.nn.init.normal_(self.input.weight,mean=args.mw,std=args.sw/math.sqrt(self.input.in_features)) 55 | torch.nn.init.normal_(self.input.bias,mean=args.mb,std=args.sb) 56 | 57 | # create and initialize output layer 58 | self.output = nn.Linear(args.width,args.d_out) 59 | torch.nn.init.normal_(self.output.weight,mean=args.mw,std=args.sw/math.sqrt(self.output.in_features)) 60 | torch.nn.init.normal_(self.output.bias,mean=args.mb,std=args.sb) 61 | 62 | def forward(self,x): 63 | z = self.input(x) 64 | ez = torch.exp(z) 65 | # norm = torch.exp((4*args.sb**2+4*args.sw**2*torch.norm(x,dim=1)**2)/2.0) 66 | # fix args issue above 67 | norm = torch.exp((4+4*torch.norm(x,dim=1)**2)/(2.0*self.args.d_in)) 68 | norm = norm.view(norm.shape[0],1) 69 | norm = torch.sqrt(norm) 70 | ezonorm = ez / norm 71 | return self.output(ezonorm) 72 | 73 | 74 | def init_weights(m, args): 75 | torch.nn.init.normal_(m.weight,mean=0,std=args.sw/math.sqrt(m.in_features)) # Schoenholz et al conventions 76 | torch.nn.init.normal_(m.bias,mean=0,std=args.sb) 77 | 78 | def create_networks(xs, args): 79 | #Print(args.width) 80 | fss = None 81 | for i in range(args.n_models): 82 | #if i % (5*10**5) == 0: 83 | # Print("Finished ", i, "models", datetime.datetime.now()) 84 | if args.activation == "GaussNet": 85 | model = GaussNet(args) 86 | if args.activation == "Erf": 87 | with torch.no_grad(): 88 | model = nn.Sequential( 89 | nn.Linear(args.d_in,args.width), 90 | activation(args), 91 | nn.Linear(args.width,args.d_out) 92 | ) 93 | model.apply(lambda m: init_weights(m, args) if type(m) == nn.Linear else None) 94 | if args.activation == "ReLU": 95 | model = nn.Sequential( 96 | nn.Linear(args.d_in,args.width), 97 | activation(args), 98 | nn.Linear(args.width,args.d_out) 99 | ) 100 | model.apply(lambda m: init_weights(m, args) if type(m) == nn.Linear else None) 101 | #print(list(model.parameters())) 102 | fs = model(xs) 103 | fs = fs.view(1,fs.shape[0],fs.shape[1]) 104 | 105 | if fss is None: 106 | fss = fs 107 | else: 108 | fss = torch.cat((fss,fs)) 109 | return fss 110 | 111 | # n-point functions from network outputs 112 | 113 | def n_point(fss,n): 114 | num_nets, n_inputs, d_out = list(fss.shape) 115 | shape = [num_nets,n_inputs] 116 | while(len(shape)) < n+1: 117 | shape.append(1) 118 | shape.append(d_out) 119 | while(len(shape)) < 2*n+1: 120 | shape.append(1) 121 | fss1 = fss.view(shape) 122 | out = fss1 123 | for k in range(2,n+1): 124 | cur = torch.transpose(fss1,1,k) 125 | cur = torch.transpose(cur,1+n,k+n) 126 | out = out * cur 127 | return out 128 | 129 | 130 | # # # # # # 131 | # theory # 132 | # # # # # # 133 | 134 | def K(x,xp,args): 135 | if args.activation == "Erf": 136 | return K_erf(x,xp,args) 137 | if args.activation == "ReLU": 138 | return K_relu(x, xp, args) 139 | if args.activation == "GaussNet": 140 | return K_GaussNet(x, xp, args) 141 | 142 | def K_erf(x,xp,args): 143 | mfxxp, mfxx, mfxpxp = 2*(args.sb**2+ ((torch.dot(x,xp)*(args.sw**2))/args.d_in) ), 2*(args.sb**2+((torch.dot(x,x)*(args.sw**2))/args.d_in) ), 2*(args.sb**2+((torch.dot(xp,xp)*(args.sw**2))/args.d_in) ) 144 | den = torch.sqrt((1+mfxx)*(1+mfxpxp)) 145 | return args.sb**2 + 2*(args.sw**2/np.pi)*np.arcsin(mfxxp/den) 146 | 147 | def K_GaussNet(x,xp, args): 148 | return args.sb**2 + args.sw**2*torch.exp(-args.sw**2*(torch.norm(x-xp)**2)/(2.0*args.d_in)) 149 | 150 | def K_relu(x,xp,args): 151 | mxx = torch.sqrt(args.sb**2 + ((args.sw**2)*(torch.dot(x, x))/args.d_in) ) 152 | mxpxp = torch.sqrt(args.sb**2 + ((args.sw**2)*(torch.dot(xp, xp))/args.d_in) ) 153 | mxxp = args.sb**2 + ((args.sw**2)*(torch.dot(x, xp))/args.d_in) 154 | costheta = mxxp/(mxx*mxpxp) 155 | theta = 0.0 156 | kern = 0.0 157 | if costheta>1. : 158 | theta = np.arccos(1) 159 | kern = (mxx*mxpxp/(2.0*np.pi)) * (np.pi - theta)*costheta 160 | else : 161 | theta = np.arccos(float(costheta)) 162 | kern = (mxx*mxpxp/(2.0*np.pi)) * (torch.sqrt( 1 - costheta**2 ) + (np.pi - theta)*costheta ) 163 | return args.sb**2 + args.sw**2*kern 164 | 165 | 166 | def kernel_tensor(xs,args): 167 | k_th = [[np.nan for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] 168 | for i in range(args.n_inputs): 169 | for j in range(args.n_inputs): 170 | k_th[i][j] = G_theory([i,j], xs, args) 171 | return torch.Tensor(k_th) 172 | 173 | def four_pt_tensor(xs, args): 174 | k_four = [[[[np.nan for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] 175 | for i in range(args.n_inputs): 176 | for j in range(i, args.n_inputs): 177 | for k in range(j, args.n_inputs): 178 | for l in range(k, args.n_inputs): 179 | k_four[i][j][k][l] = G_theory([i,j,k,l], xs, args) 180 | return torch.Tensor(k_four) 181 | 182 | def six_pt_tensor(xs, args): 183 | k_six = [[[[[[np.nan for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] 184 | for i in range(args.n_inputs): 185 | for j in range(i, args.n_inputs): 186 | for k in range(j, args.n_inputs): 187 | for l in range(k, args.n_inputs): 188 | for m in range(l, args.n_inputs): 189 | for n in range(m, args.n_inputs): 190 | k_six[i][j][k][l][m][n] = G_theory([i,j,k,l,m,n], xs, args) 191 | return torch.Tensor(k_six) 192 | 193 | def G_theory(idxs, xs, args): 194 | # point_pairs = connect all point in idx in pairs 195 | point_pairs = [] 196 | 197 | if len(idxs)%2 == 0 : 198 | wick_pairs = int(len(idxs)/2) 199 | anew = list(partition(idxs)) 200 | for i in range(len(anew)): 201 | index = [0]*wick_pairs 202 | 203 | if len(anew[i]) == wick_pairs : 204 | element = anew[i] 205 | for j in range(len(element)): 206 | if len(element[j]) == 2 : 207 | index[j] = 1 208 | 209 | if sum(index) == wick_pairs : 210 | point_pairs.append(element) 211 | 212 | #print("point pairs", point_pairs) 213 | 214 | tot = 0 215 | for pairs in point_pairs: 216 | tot += K_product(xs, pairs, args) 217 | return tot 218 | 219 | def partition(collection): 220 | if len(collection) == 1: 221 | yield [ collection ] 222 | return 223 | 224 | first = collection[0] 225 | # print('f', first) 226 | for smaller in partition(collection[1:]): 227 | # insert `first` in each of the subpartition's subsets 228 | for n, subset in enumerate(smaller): 229 | #print(n, subset, 'p') 230 | yield smaller[:n] + [[ first ] + subset] + smaller[n+1:] 231 | # put `first` in its own subset 232 | yield [ [ first ] ] + smaller 233 | 234 | def K_product(xs,pairs,args): 235 | prod = 1 236 | for pair in pairs: 237 | prod = prod * K(xs[pair[0]],xs[pair[1]], args) 238 | return prod 239 | 240 | 241 | 242 | # scipy integration requires numpy, not torch 243 | 244 | def K_int(x,xp,args): 245 | if args.activation == "Erf": 246 | return K_int_erf(x,xp,args) 247 | if args.activation == "ReLU": 248 | return K_int_relu(x, xp, args) 249 | if args.activation == "GaussNet": 250 | return K_int_GaussNet(x, xp, args) 251 | 252 | def K_int_GaussNet(x,xp, args): 253 | return args.sb**2 + args.sw**2*np.exp(-args.sw**2*(np.dot(x, x)-2.*np.dot(x,xp)+np.dot(xp,xp))/2.0) 254 | 255 | def K_int_erf(x,xp,args): 256 | mfxxp, mfxx, mfxpxp = 2*(args.sb**2+ ((np.dot(x,xp)*(args.sw**2))/args.d_in) ), 2*(args.sb**2+((np.dot(x,x)*(args.sw**2))/args.d_in) ), 2*(args.sb**2+((np.dot(xp,xp)*(args.sw**2))/args.d_in) ) 257 | den = np.sqrt((1+mfxx)*(1+mfxpxp)) 258 | return args.sb**2 + 2*(args.sw**2/np.pi)*np.arcsin(mfxxp/den) 259 | 260 | 261 | def K_int_relu(x,xp,args): 262 | mxx = np.sqrt(args.sb**2 + ((args.sw**2)*(np.dot(x, x))/args.d_in) ) 263 | mxpxp = np.sqrt(args.sb**2 + ((args.sw**2)*(np.dot(xp, xp))/args.d_in) ) 264 | mxxp = args.sb**2 + ((args.sw**2)*(np.dot(x, xp))/args.d_in) 265 | costheta = mxxp/(mxx*mxpxp) 266 | theta = 0.0 267 | kern = 0.0 268 | if costheta>1. : 269 | theta = np.arccos(1) 270 | kern = (mxx*mxpxp/(2.0*np.pi)) * (np.pi - theta)*costheta 271 | else : 272 | theta = np.arccos(float(costheta)) 273 | kern = (mxx*mxpxp/(2.0*np.pi)) * (np.sqrt( 1 - costheta**2 ) + (np.pi - theta)*costheta ) 274 | return args.sb**2 + args.sw**2*kern 275 | 276 | 277 | # for trimming tensors that have duplicate components 278 | 279 | def trim_sym_tensor(a, args): 280 | k_six = [[[[[[np.nan for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] 281 | for i in range(args.n_inputs): 282 | for j in range(i, args.n_inputs): 283 | for k in range(j, args.n_inputs): 284 | for l in range(k, args.n_inputs): 285 | for m in range(l, args.n_inputs): 286 | for n in range(m, args.n_inputs): 287 | k_six[i][j][k][l][m][n] = a[i][j][k][l][m][n] 288 | return np.array(k_six) 289 | 290 | def trim_sym_tensor4(a, args): 291 | k_four = [[[[np.nan for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] for _ in range(args.n_inputs)] 292 | for i in range(args.n_inputs): 293 | for j in range(i, args.n_inputs): 294 | for k in range(j, args.n_inputs): 295 | for l in range(k, args.n_inputs): 296 | k_four[i][j][k][l] = a[i][j][k][l] 297 | return np.array(k_four) 298 | 299 | 300 | # the next two functions need to be integrated to not include the origin, since the ReLU integration will give an error otherwise 301 | 302 | def four_pt_int(x1, x2, x3, x4, cutoff, args): 303 | if args.d_in == 1: 304 | neg = integrate.quad(lambda x: (24.)*K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(x, x4.item(), args), -cutoff, 0)[0] 305 | pos = integrate.quad(lambda x: (24.)*K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(x, x4.item(), args), 0, cutoff)[0] 306 | return neg + pos 307 | if args.d_in == 2: 308 | q1 = integrate.dblquad(lambda y, x: (24.)*K_EFT((x, y), tuple(x1.tolist()), args)*K_EFT((x, y), tuple(x2.tolist()), args)*K_EFT((x, y), tuple(x3.tolist()), args)*K_EFT((x, y), tuple(x4.tolist()), args), -cutoff, 0, -cutoff, 0)[0] 309 | q2 = integrate.dblquad(lambda y, x: (24.)*K_EFT((x, y), tuple(x1.tolist()), args)*K_EFT((x, y), tuple(x2.tolist()), args)*K_EFT((x, y), tuple(x3.tolist()), args)*K_EFT((x, y), tuple(x4.tolist()), args), -cutoff, 0, 0, cutoff)[0] 310 | q3 = integrate.dblquad(lambda y, x: (24.)*K_EFT((x, y), tuple(x1.tolist()), args)*K_EFT((x, y), tuple(x2.tolist()), args)*K_EFT((x, y), tuple(x3.tolist()), args)*K_EFT((x, y), tuple(x4.tolist()), args), 0, cutoff, -cutoff, 0)[0] 311 | q4 = integrate.dblquad(lambda y, x: (24.)*K_EFT((x, y), tuple(x1.tolist()), args)*K_EFT((x, y), tuple(x2.tolist()), args)*K_EFT((x, y), tuple(x3.tolist()), args)*K_EFT((x, y), tuple(x4.tolist()), args), 0, cutoff, 0, cutoff)[0] 312 | return q1 + q2 + q3 + q4 313 | if args.d_in == 3: 314 | q1 = integrate.tplquad(lambda z, y, x: (24.)*K_EFT((x, y, z), tuple(x1.tolist()), args)*K_EFT((x, y, z), tuple(x2.tolist()), args)*K_EFT((x, y, z), tuple(x3.tolist()), args)*K_EFT((x, y, z), tuple(x4.tolist()), args), -cutoff, 0, -cutoff, 0,-cutoff, 0)[0] 315 | q2 = integrate.tplquad(lambda z, y, x: (24.)*K_EFT((x, y, z), tuple(x1.tolist()), args)*K_EFT((x, y, z), tuple(x2.tolist()), args)*K_EFT((x, y, z), tuple(x3.tolist()), args)*K_EFT((x, y, z), tuple(x4.tolist()), args), 0, cutoff, -cutoff, 0,-cutoff, 0)[0] 316 | q3 = integrate.tplquad(lambda z, y, x: (24.)*K_EFT((x, y, z), tuple(x1.tolist()), args)*K_EFT((x, y, z), tuple(x2.tolist()), args)*K_EFT((x, y, z), tuple(x3.tolist()), args)*K_EFT((x, y, z), tuple(x4.tolist()), args), -cutoff, 0, -cutoff, 0,0, cutoff)[0] 317 | q4 = integrate.tplquad(lambda z, y, x: (24.)*K_EFT((x, y, z), tuple(x1.tolist()), args)*K_EFT((x, y, z), tuple(x2.tolist()), args)*K_EFT((x, y, z), tuple(x3.tolist()), args)*K_EFT((x, y, z), tuple(x4.tolist()), args), 0, cutoff, -cutoff, 0,0, cutoff)[0] 318 | q5 = integrate.tplquad(lambda z, y, x: (24.)*K_EFT((x, y, z), tuple(x1.tolist()), args)*K_EFT((x, y, z), tuple(x2.tolist()), args)*K_EFT((x, y, z), tuple(x3.tolist()), args)*K_EFT((x, y, z), tuple(x4.tolist()), args), -cutoff, 0, 0, cutoff,-cutoff, 0)[0] 319 | q6 = integrate.tplquad(lambda z, y, x: (24.)*K_EFT((x, y, z), tuple(x1.tolist()), args)*K_EFT((x, y, z), tuple(x2.tolist()), args)*K_EFT((x, y, z), tuple(x3.tolist()), args)*K_EFT((x, y, z), tuple(x4.tolist()), args), 0, cutoff, 0, cutoff,-cutoff, 0)[0] 320 | q7 = integrate.tplquad(lambda z, y, x: (24.)*K_EFT((x, y, z), tuple(x1.tolist()), args)*K_EFT((x, y, z), tuple(x2.tolist()), args)*K_EFT((x, y, z), tuple(x3.tolist()), args)*K_EFT((x, y, z), tuple(x4.tolist()), args), -cutoff, 0, 0, cutoff ,0, cutoff)[0] 321 | q8 = integrate.tplquad(lambda z, y, x: (24.)*K_EFT((x, y, z), tuple(x1.tolist()), args)*K_EFT((x, y, z), tuple(x2.tolist()), args)*K_EFT((x, y, z), tuple(x3.tolist()), args)*K_EFT((x, y, z), tuple(x4.tolist()), args), 0, cutoff, 0, cutoff,0, cutoff)[0] 322 | return q1 + q2 + q3 + q4 + q5 + q6 + q7 + q8 323 | 324 | 325 | def intkappa(x1, x2, x3, x4, x5, x6, cutoff, args): 326 | if args.activation == "GaussNet" or args.activation == "Erf": 327 | return intkappa_infinite(x1, x2, x3, x4, x5, x6, cutoff, args) 328 | xx1 = x1.item() 329 | xx2 = x2.item() 330 | xx3 = x3.item() 331 | xx4 = x4.item() 332 | xx5 = x5.item() 333 | xx6 = x6.item() 334 | left = integrate.quad(lambda x: K_EFT(x, xx1, args)*K_EFT(x, xx2, args)*K_EFT(x, xx3, args)*K_EFT(x, xx4, args)*K_EFT(xx5, xx6, args), -cutoff, 0)[0] 335 | right = integrate.quad(lambda x: K_EFT(x, xx1, args)*K_EFT(x, xx2, args)*K_EFT(x, xx3, args)*K_EFT(x, xx4, args)*K_EFT(xx5, xx6, args), 0, cutoff)[0] 336 | return left + right 337 | 338 | 339 | def K_EFT(x,xp,args): 340 | if args.activation == "Erf": 341 | a = K_int_erf(x,xp,args) - args.sb**2 342 | return a 343 | if args.activation == "ReLU": 344 | a = K_int_relu(x, xp, args) - args.sb**2 345 | return a 346 | if args.activation == "GaussNet": 347 | a = K_int_GaussNet(x, xp, args) - args.sb**2 348 | return a 349 | 350 | def intkappa_infinite(x1, x2, x3, x4, x5, x6, cutoff, args): 351 | xx1 = x1.item() 352 | xx2 = x2.item() 353 | xx3 = x3.item() 354 | xx4 = x4.item() 355 | xx5 = x5.item() 356 | xx6 = x6.item() 357 | full = integrate.quad(lambda x: K_EFT(x, xx1, args)*K_EFT(x, xx2, args)*K_EFT(x, xx3, args)*K_EFT(x, xx4, args)*K_EFT(xx5, xx6, args), -cutoff, cutoff)[0] 358 | return full 359 | 360 | def four_pt_int_infinite(x1, x2, x3, x4, cutoff, args): 361 | if args.d_in == 1: 362 | q1 = integrate.quad(lambda x: (24.)*K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(x, x4.item(), args), -cutoff, cutoff)[0] 363 | return q1 364 | if args.d_in == 2: 365 | q1 = integrate.dblquad(lambda y, x: (24.)*K_EFT((x, y), tuple(x1.tolist()), args)*K_EFT((x, y), tuple(x2.tolist()), args)*K_EFT((x, y), tuple(x3.tolist()), args)*K_EFT((x, y), tuple(x4.tolist()), args), -cutoff, cutoff, -cutoff, cutoff)[0] 366 | return q1 367 | if args.d_in == 3: 368 | q1 = integrate.tplquad(lambda z, y, x: (24.)*K_EFT((x, y, z), tuple(x1.tolist()), args)*K_EFT((x, y, z), tuple(x2.tolist()), args)*K_EFT((x, y, z), tuple(x3.tolist()), args)*K_EFT((x, y, z), tuple(x4.tolist()), args), -cutoff, cutoff, -cutoff, cutoff,-cutoff, cutoff)[0] 369 | return q1 370 | 371 | 372 | 373 | 374 | 375 | def local0(x1, x2, x3, x4, cutoff, args): 376 | int_feyn = 0 377 | if args.activation == "ReLU" or args.activation == "Erf": 378 | int_feyn += integrate.quad(lambda x: K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(x, x4.item(), args), -cutoff, 0)[0] 379 | int_feyn += integrate.quad(lambda x: K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(x, x4.item(), args), 0, cutoff)[0] 380 | 381 | if args.activation == "GaussNet": 382 | int_feyn += integrate.quad(lambda x: K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(x, x4.item(), args), -cutoff, cutoff)[0] 383 | 384 | return 24*int_feyn # combinatorial factor 24 385 | 386 | def local2(x1, x2, x3, x4, cutoff, args): 387 | int_feyn = 0 388 | if args.activation == "ReLU" or args.activation == "Erf": 389 | int_feyn += integrate.quad(lambda x: x**2*K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(x, x4.item(), args), -cutoff, 0)[0] 390 | int_feyn += integrate.quad(lambda x: x**2*K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(x, x4.item(), args), 0, cutoff)[0] 391 | 392 | if args.activation == "GaussNet": 393 | int_feyn += integrate.quad(lambda x: x**2*K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(x, x4.item(), args), -cutoff, cutoff)[0] 394 | 395 | return 24*int_feyn # combinatorial factor 24 396 | 397 | def nonlocal22(x1, x2, x3, x4, cutoff, args): 398 | int_feyn = 0 399 | if args.activation == "ReLU" or args.activation == "Erf": 400 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(y, x3.item(), args)*K_EFT(y, x4.item(), args), -cutoff, 0, -cutoff, 0)[0] 401 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(y, x3.item(), args)*K_EFT(y, x4.item(), args), -cutoff, 0, 0, cutoff)[0] 402 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(y, x3.item(), args)*K_EFT(y, x4.item(), args), 0, cutoff, -cutoff, 0)[0] 403 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(y, x3.item(), args)*K_EFT(y, x4.item(), args), 0, cutoff, 0, cutoff)[0] 404 | 405 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(x, x1.item(), args)*K_EFT(y, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(y, x4.item(), args), -cutoff, 0, -cutoff, 0)[0] 406 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(x, x1.item(), args)*K_EFT(y, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(y, x4.item(), args), -cutoff, 0, 0, cutoff)[0] 407 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(x, x1.item(), args)*K_EFT(y, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(y, x4.item(), args), 0, cutoff, -cutoff, 0)[0] 408 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(x, x1.item(), args)*K_EFT(y, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(y, x4.item(), args), 0, cutoff, 0, cutoff)[0] 409 | 410 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(y, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(y, x4.item(), args), -cutoff, 0, -cutoff, 0)[0] 411 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(y, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(y, x4.item(), args), -cutoff, 0, 0, cutoff)[0] 412 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(y, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(y, x4.item(), args), 0, cutoff, -cutoff, 0)[0] 413 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(y, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(y, x4.item(), args), 0, cutoff, 0, cutoff)[0] 414 | 415 | if args.activation == "GaussNet": 416 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(x, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(y, x3.item(), args)*K_EFT(y, x4.item(), args), -cutoff, cutoff, -cutoff, cutoff)[0] 417 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(x, x1.item(), args)*K_EFT(y, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(y, x4.item(), args), -cutoff, cutoff, -cutoff, cutoff)[0] 418 | int_feyn += integrate.dblquad(lambda y, x: K_EFT(y, x1.item(), args)*K_EFT(x, x2.item(), args)*K_EFT(x, x3.item(), args)*K_EFT(y, x4.item(), args), -cutoff, cutoff, -cutoff, cutoff)[0] 419 | 420 | # print("doing integral nonlocal") 421 | return 8*int_feyn # to make up total 24 diagrams --------------------------------------------------------------------------------