Skip to content

Commit e590f3f

Browse files
authored
Add files via upload
1 parent 8362f46 commit e590f3f

File tree

4 files changed

+271
-0
lines changed

4 files changed

+271
-0
lines changed

Demo.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import numpy as np
2+
import Precon_Conjugate_Gradient as PCG
3+
import Other_functions as wt
4+
import random
5+
6+
7+
#this demo version illustrates the increasing effectiveness of our preconditioner as the dimension of the underlying data grows.
8+
#for our demo, we use the Quadratic Inverse RBF and draw from the multidimensional normal distribution, scaled appropriately.
9+
10+
n=1000
11+
12+
for d in[2,3,6,10,25,100,158,1000,3981,10000,25118,63095]:
13+
x_i = wt.points_normal(n,d)/(np.sqrt(2*d-1))
14+
15+
f_i = [random.random() for _ in range(n)]
16+
17+
#Generating the interpolation matrix, phi.
18+
phi = wt.interpolation_matrix_generator('Quadratic_inv',x_i)
19+
20+
#generating our preconditioner
21+
precon_inv, const_1, const_2 = wt.precon_generator(n,1,'Quadratic_inv','')
22+
23+
#preconditioned solution
24+
solution, count = PCG.untransformed_Pc_GC(phi,f_i,precon_inv,1e-10,const_1,const_2)
25+
26+
#unpreconditioned solution
27+
solution2,count2 = PCG.CG_no_precon(phi,f_i,1e-10)
28+
29+
print('dimension:',d,'Preconditioned Iteration count:',count, 'unpreconditioned iteration count:',count2)

Full_implementation.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import Precon_Conjugate_Gradient as PCG
2+
import Other_functions as wt
3+
4+
5+
#this takes data centers, values at those points, the kind of RBF you wish to use and a value, x, at which you wish your interpolant to be evaluated.
6+
#this returns - iteration count for convergence of the CG algorithm, iterpolant coefficients and the interpolant evaluated at x.
7+
#it is suggested that you scale your data so that |x_i|<1 to prevent the RBFs from being too flat.
8+
#where appropriate, we use a shape parameter of 1 in the RBFs.
9+
10+
def interpolant_generator_evaluator(data_centers, data_values, RBF,x):
11+
x_i = [i for i in data_centers]
12+
13+
n = len(x_i)
14+
15+
f_i = [i for i in data_values]
16+
17+
if n != len(f_i):
18+
print('data_centers and data_values different length')
19+
return
20+
21+
else:
22+
23+
#Generating the interpolation matrix, phi.
24+
phi = wt.interpolation_matrix_generator(RBF,x_i)
25+
26+
#generating our preconditioner
27+
precon_inv, const_1, const_2 = wt.precon_generator(n,1,'Q','')
28+
29+
#generating our interpolation coefficients
30+
solution, count = PCG.untransformed_Pc_GC(phi,f_i,precon_inv,1e-10,const_1,const_2)
31+
32+
#evaluating our interpolant at x
33+
interpolant_eval = wt.interp(solution,x_i,x,RBF)
34+
35+
return count, solution, interpolant_eval
36+

Other_functions.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import numpy as np
2+
3+
#Preconditioner generator - returns our preconditioner and its coefficients (the preconditioner is of the form aI + b e^Te)
4+
#n = number of data points
5+
#c = shape parameter for the RBFs - we are safe to use 1 here, but feel free to adjust to your liking.
6+
#distribution - if your data is uniformly distributed in the unit hypercube/unit sphere, select accordingly. Otherwise we take k=1.
7+
8+
def precon_generator(n,c,RBF,distribution):
9+
if distribution == 'cube':
10+
k=1
11+
if distribution =='ball':
12+
k=np.sqrt(2)
13+
else:
14+
k=1
15+
16+
if RBF == 'Gaussian':
17+
phi_rt2 = Gaussian(k,c)
18+
if RBF == 'MQ':
19+
phi_rt2 = MQ(k,c)
20+
if RBF == 'MQ_inv':
21+
phi_rt2 = MQ_inv(k,c)
22+
if RBF == 'Quadratic_inv':
23+
phi_rt2 = Quadratic_inv(k,c)
24+
const_1 = phi_rt2 / ((phi_rt2-c)*(c-phi_rt2+n*phi_rt2))
25+
const_2 = 1 / (phi_rt2-c)
26+
27+
preconditioner = const_1 * np.ones((n,n)) - const_2 * np.eye(n)
28+
29+
return preconditioner, const_1, const_2
30+
31+
#A function that evaluates our interpolant using
32+
#coefficients - generated from the PCG algorithm output
33+
#centers - the same centers we used to generate the coefficients
34+
#x_value - the point we want to evaluate our interpolant at
35+
#RBF_type - make sure to use the same RBF type as the one used to generate the interpolant.
36+
37+
#returns the value of the interpolant.
38+
39+
def interp(coefficients, centers, x_value, RBF_type):
40+
if RBF_type == 'Gaussian':
41+
center_vector = [Gaussian(x_value - center,1) for center in centers]
42+
if RBF_type == 'MQ':
43+
center_vector = [MQ(x_value - center,1) for center in centers]
44+
if RBF_type == 'MQ_inv':
45+
center_vector = [MQ_inv(x_value - center,1) for center in centers]
46+
if RBF_type == 'Quadratic_inv':
47+
center_vector = [Quadratic_inv(x_value - center,1) for center in centers]
48+
49+
return coefficients.T @ center_vector
50+
51+
52+
#RBF functions - note that our algorithm is not certain to converge for the multiquadric (MQ) as the interpolation matrix generated is not positive definite.
53+
def MQ(point,c):
54+
return np.sqrt(c**2+np.linalg.norm(point))
55+
56+
def Gaussian(point, c):
57+
return np.exp(-c * np.linalg.norm(point))
58+
59+
def MQ_inv(point,c):
60+
return 1/np.sqrt(c**2+np.linalg.norm(point))
61+
62+
def Quadratic_inv(point,c):
63+
return 1/(c**2+np.linalg.norm(point))
64+
65+
#interpolation matrix generator
66+
def interpolation_matrix_generator(RBF_type, data):
67+
phi = np.ones((len(data),len(data)))
68+
for i, point in enumerate(data):
69+
for j, center in enumerate(data):
70+
phi[i,j] = np.linalg.norm(point-center)
71+
72+
if RBF_type == 'Gaussian':
73+
vectorized_function = np.vectorize(Gaussian)
74+
if RBF_type == 'MQ':
75+
vectorized_function = np.vectorize(MQ)
76+
if RBF_type == 'MQ_inv':
77+
vectorized_function = np.vectorize(MQ_inv)
78+
if RBF_type == 'Quadratic_inv':
79+
vectorized_function = np.vectorize(Quadratic_inv)
80+
81+
return vectorized_function(phi,1)
82+
83+
84+
#data generator for a range of underlying distributions.
85+
def points_in_unit_ball(n,d):
86+
cube = np.random.standard_normal(size=(n, d))
87+
norms = np.linalg.norm(cube,axis=1)
88+
surface_sphere = cube/norms[:,np.newaxis]
89+
scales = np.random.uniform(0,1, size= n)
90+
points = surface_sphere * (scales[:, np.newaxis])**(1/d)
91+
return points
92+
93+
def points_integer_grid(n,d, s_min, s_max):
94+
points = np.random.randint(s_min,s_max, size = (n,d))
95+
return points
96+
97+
def points_in_cube(n,d,normalised):
98+
points = np.random.uniform(0,1,size=(n,d))
99+
if normalised == 'no':
100+
return points
101+
if normalised == 'yes':
102+
return points/np.sqrt(d/6-17/120)
103+
104+
def points_normal(n,d):
105+
points = np.random.normal(0,1,size=(n,d))
106+
return points
107+

Precon_Conjugate_Gradient.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import numpy as np
2+
3+
#Setup for 'Untransformed Preconditioned Conjugate Gradient Method' to solve
4+
#Ax=b with a preconditioner M, which gives us
5+
#inv(M)Ax = inv(M)b
6+
7+
8+
#A is the interpolation matrix
9+
#x is the solution
10+
#M is the preconditioner
11+
#precon_inv can be generated from Other_functions.precon_generator, or feel free to use your own - you will need to adjust the code for the action of your preconditioner on a vector.
12+
13+
#See README for full description of the algorithm.
14+
15+
#Returns the coefficients of the interpolant and the iteration count.
16+
17+
def untransformed_Pc_GC(A,b,precon_inv,tolerance,c1,c2):
18+
19+
#Setup
20+
n=np.size(b)
21+
initial_guess = np.zeros(n)
22+
residual = b - A@initial_guess
23+
24+
#efficient calculation of our preconditioner's action on a vector
25+
def precon_action_on_vector(const_1,const_2,vector):
26+
vector_of_ones = np.ones(len(vector))
27+
return const_1*np.sum(vector) * vector_of_ones - const_2 * vector
28+
29+
search_direction = precon_action_on_vector(c1,c2,residual)
30+
31+
#x will be our solution
32+
x = initial_guess
33+
34+
dummy_variable = residual.T @ search_direction
35+
36+
#iteration count
37+
k=0
38+
#Implementation
39+
while np.linalg.norm(residual) > tolerance:
40+
k+=1
41+
#dummy variable is used twice to avoid recalculation
42+
43+
step_size = dummy_variable / (search_direction.T @ A @ search_direction)
44+
x += step_size * search_direction
45+
46+
residual -= step_size * A@search_direction
47+
48+
new_dummy = residual.T @ precon_action_on_vector(c1,c2,residual)
49+
50+
scale_factor = new_dummy / dummy_variable
51+
52+
dummy_variable = new_dummy
53+
54+
search_direction = precon_action_on_vector(c1,c2,residual)+ scale_factor*search_direction
55+
56+
if k> 1000:
57+
break
58+
59+
return x,k
60+
61+
#Unpreconditioned algorithm for comparison
62+
63+
def CG_no_precon(A,b,tolerance):
64+
65+
#Setup
66+
n=np.size(b)
67+
initial_guess = np.zeros(n)
68+
residual = b - A@initial_guess
69+
search_direction = residual
70+
71+
#x will be our solution
72+
x = initial_guess
73+
74+
dummy_variable = residual.T @ residual
75+
76+
#iteration coun
77+
k=0
78+
#Implementation
79+
while np.linalg.norm(residual) > tolerance:
80+
k+=1
81+
#dummy variable is used twice to avoid recalculation
82+
83+
step_size = dummy_variable / (search_direction.T @ A @ search_direction)
84+
x += step_size * search_direction
85+
86+
residual -= step_size * A@search_direction
87+
88+
new_dummy = residual.T @ residual
89+
90+
scale_factor = new_dummy / dummy_variable
91+
92+
dummy_variable = new_dummy
93+
94+
search_direction = residual + scale_factor*search_direction
95+
96+
if k>1000:
97+
break
98+
99+
return x,k

0 commit comments

Comments
 (0)