Init
This commit is contained in:
parent
2a1b1fef27
commit
c41e4890e7
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,99 @@
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0,'..')
|
||||||
|
import numpy as np
|
||||||
|
from numpy.random import default_rng
|
||||||
|
import math
|
||||||
|
from time import time
|
||||||
|
from functions import test_funcs
|
||||||
|
from FMM_LU import FMM_LU as fmm_lu
|
||||||
|
from problem_tools import problem
|
||||||
|
|
||||||
|
import fmm3dbie as h3
|
||||||
|
import fmm3dpy as fmm3d
|
||||||
|
|
||||||
|
verbose = 1
|
||||||
|
np.random.seed(0)
|
||||||
|
bs = 64
|
||||||
|
func = test_funcs.double_layer
|
||||||
|
close_r = 1.1
|
||||||
|
num_child_tree = 'hyper'
|
||||||
|
point_based_tree = 0
|
||||||
|
eps = 0.51e-4
|
||||||
|
zk = 1.0 + 1j*0
|
||||||
|
alpha = 0.0
|
||||||
|
beta = 1.0
|
||||||
|
proxy_p = (2, 200)
|
||||||
|
proxy_r = 1.
|
||||||
|
csc_fun = 0
|
||||||
|
symmetric_fun = 0
|
||||||
|
half_sym = 1
|
||||||
|
t0 = time()
|
||||||
|
order = 3
|
||||||
|
nu = 20
|
||||||
|
q_fun = 1
|
||||||
|
|
||||||
|
pr = fmm_lu.build_problem(geom_type='wtorus', block_size=bs, func=func,
|
||||||
|
point_based_tree=point_based_tree, close_r=close_r,
|
||||||
|
num_child_tree=num_child_tree, eps=eps,
|
||||||
|
zk=zk, alpha=alpha, beta=beta, wtd_T=1, half_sym=half_sym,
|
||||||
|
csc_fun=csc_fun, q_fun=q_fun, nu=nu, order=order)
|
||||||
|
|
||||||
|
pr.coef = coef = -1
|
||||||
|
print(f'n = {pr.shape[0]}\n')
|
||||||
|
print(f'problem-build time: {time() - t0}\n')
|
||||||
|
|
||||||
|
# FMM-LU solver
|
||||||
|
|
||||||
|
xyz_out = np.array([[31.17,-0.03,3.15],[6.13,-4.1,22.2]]).transpose()
|
||||||
|
xyz_in = np.array([[0.11,-2.13,0.05],[0.13,2.1,-0.01]]).transpose()
|
||||||
|
#coef = -1
|
||||||
|
|
||||||
|
# comparizon to FMM
|
||||||
|
|
||||||
|
c = np.array([1 + 1j*0,1+1.1j])
|
||||||
|
out = fmm3d.h3ddir(zk=zk, sources=xyz_out, targets=pr.srcvals[0:3,:], charges=c, pgt=1)
|
||||||
|
rhs = out.pottarg
|
||||||
|
|
||||||
|
sigma, factor, tf, ts = fmm_lu.fmm_lu_solve(pr, eps, rhs, proxy_p, proxy_r, verbose=verbose)
|
||||||
|
|
||||||
|
ntarg = np.shape(xyz_in)[1]
|
||||||
|
|
||||||
|
ipatch_id = coef*np.ones(2)
|
||||||
|
uvs_targ = np.zeros((2,ntarg))
|
||||||
|
|
||||||
|
norders = pr.order*np.ones(pr.npatches)
|
||||||
|
iptype = np.ones(pr.npatches)
|
||||||
|
|
||||||
|
pot_comp = h3.lpcomp_helm_comb_dir(norders, pr.ixyzs, iptype, pr.srccoefs, pr.srcvals,
|
||||||
|
xyz_in, ipatch_id, uvs_targ, eps, pr.zpars, sigma)
|
||||||
|
|
||||||
|
out = fmm3d.h3ddir(zk=zk, sources=xyz_out, targets=xyz_in, charges=c,pgt=1)
|
||||||
|
pot_ex = out.pottarg
|
||||||
|
erra = np.linalg.norm(pot_ex-pot_comp)
|
||||||
|
print(f'Fact time: {tf}\n')
|
||||||
|
print(f'Sol time: {ts}\n')
|
||||||
|
print(f"error in solution = {erra}\n")
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
tree = pr.row_tree
|
||||||
|
level_count = len(tree.level) - 2
|
||||||
|
print(f'Compression on levels. \nlevel_count: {level_count-2}')
|
||||||
|
for l in range(level_count-1, factor.tail_lvl-1, -1):
|
||||||
|
job = [j for j in
|
||||||
|
range(tree.level[l], tree.level[l+1])]
|
||||||
|
print(f'Level: {l}')
|
||||||
|
proc = 0
|
||||||
|
mean_b = 0
|
||||||
|
mean_ind = 0
|
||||||
|
nindl = 0
|
||||||
|
mean_other_lvl_close = 0
|
||||||
|
for i in job:
|
||||||
|
mean_other_lvl_close +=len(pr.other_lvl_close[i])
|
||||||
|
if factor.index_lvl[i].shape[0] != 0:
|
||||||
|
proc += factor.basis[i].shape[0]/factor.index_lvl[i].shape[0]*100
|
||||||
|
nindl += 1
|
||||||
|
mean_b += factor.basis[i].shape[0]
|
||||||
|
mean_ind += factor.index_lvl[i].shape[0]
|
||||||
|
print(f' Mean other lvl close: {mean_other_lvl_close/nindl}')
|
||||||
|
print(f' Mean compression: {proc/nindl:.2f}%, mean basis: {mean_b/nindl:.2f}, mean index: {mean_ind/nindl:.2f}')
|
||||||
|
|
||||||
|
|
@ -0,0 +1,240 @@
|
||||||
|
from __future__ import print_function, absolute_import, division
|
||||||
|
|
||||||
|
__all__ = ['Particles', 'inv_distance', 'log_distance']
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from time import time
|
||||||
|
from numba import jit
|
||||||
|
import math
|
||||||
|
import cmath
|
||||||
|
from scipy import integrate as intg
|
||||||
|
|
||||||
|
|
||||||
|
def log_dist_int(x,y):
|
||||||
|
return -1 / (2 * np.pi) * np.log(np.sqrt(x ** 2 + y ** 2))
|
||||||
|
def log_dist_2d(xd,yd):
|
||||||
|
return -1 / (2 * np.pi) * np.log(np.sqrt((xd[0] - yd[0]) ** 2 + (xd[1] - yd[1]) ** 2))
|
||||||
|
def log_distance(data1, list1, data2, list2):
|
||||||
|
ans = np.ndarray((list1.size, list2.size), dtype=np.float64)
|
||||||
|
vertex1 = data1.vertex
|
||||||
|
vertex2 = data2.vertex
|
||||||
|
n = list1.size
|
||||||
|
m = list2.size
|
||||||
|
N = data1.vertex.shape[1]
|
||||||
|
for i in range(n):
|
||||||
|
for j in range(m):
|
||||||
|
if (vertex1[:,list1[i]] == vertex2[:,list2[j]]).all():
|
||||||
|
ans[i, j] = intg.dblquad(log_dist_int,0,1/(2*np.sqrt(N)),lambda x: 0, lambda x: 1/(2*np.sqrt(N)))[0]*4
|
||||||
|
else:
|
||||||
|
ans[i, j] = log_dist_2d(vertex1[:,list1[i]],vertex2[:,list2[j]])/N
|
||||||
|
return ans
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
### interactions for Particles ###
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
def inv_distance(data1, list1, data2, list2):
|
||||||
|
"""
|
||||||
|
Returns 1/r for each pair of particles from two sets.
|
||||||
|
|
||||||
|
Function 1/r is used as interaction between two particles.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
data1 : Python object
|
||||||
|
Destination of interactions
|
||||||
|
list1 : array
|
||||||
|
Indices of particles from `data1` to compute interactions
|
||||||
|
data2 : Python object
|
||||||
|
Source of interactions
|
||||||
|
list2 : array
|
||||||
|
Indices of particles from `data1` to compute interactions
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
numpy.ndarray(ndim=2)
|
||||||
|
Array of interactions of corresponding particles.
|
||||||
|
"""
|
||||||
|
ans = np.ndarray((list1.size, list2.size), dtype=np.float64)
|
||||||
|
return inv_distance_numba(data1.ndim, data1.vertex, list1, data2.vertex,
|
||||||
|
list2, ans)
|
||||||
|
|
||||||
|
@jit(nopython=True, parallel=True)
|
||||||
|
def inv_distance_numba(ndim, vertex1, list1, vertex2, list2, ans):
|
||||||
|
n = list1.size
|
||||||
|
m = list2.size
|
||||||
|
for i in range(n):
|
||||||
|
for j in range(m):
|
||||||
|
tmp_l = 0.0
|
||||||
|
for k in range(ndim):
|
||||||
|
tmp_v = vertex1[k, list1[i]]-vertex2[k, list2[j]]
|
||||||
|
tmp_l += tmp_v*tmp_v
|
||||||
|
if tmp_l <= 0:
|
||||||
|
ans[i, j] = 0
|
||||||
|
else:
|
||||||
|
ans[i, j] = 1./math.sqrt(tmp_l)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def log_distance_h2t(data1, list1, data2, list2):
|
||||||
|
"""
|
||||||
|
Returns -log(r) for each pair of particles from two sets.
|
||||||
|
|
||||||
|
Function -log(r) is used as interaction between two particles.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
data1 : Python object
|
||||||
|
Destination of interactions
|
||||||
|
list1 : array
|
||||||
|
Indices of particles from `data1` to compute interactions
|
||||||
|
data2 : Python object
|
||||||
|
Source of interactions
|
||||||
|
list2 : array
|
||||||
|
Indices of particles from `data1` to compute interactions
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
numpy.ndarray(ndim=2)
|
||||||
|
Array of interactions of corresponding particles.
|
||||||
|
"""
|
||||||
|
ans = np.ndarray((list1.size, list2.size), dtype=np.float64)
|
||||||
|
return log_distance_numba(data1.ndim, data1.vertex, list1, data2.vertex,
|
||||||
|
list2, ans)
|
||||||
|
|
||||||
|
@jit(nopython=True)
|
||||||
|
def log_distance_numba(ndim, vertex1, list1, vertex2, list2, ans):
|
||||||
|
n = list1.size
|
||||||
|
m = list2.size
|
||||||
|
for i in range(n):
|
||||||
|
for j in range(m):
|
||||||
|
tmp_l = 0.0
|
||||||
|
for k in range(ndim):
|
||||||
|
tmp_v = vertex1[k, list1[i]]-vertex2[k, list2[j]]
|
||||||
|
tmp_l += tmp_v*tmp_v
|
||||||
|
if tmp_l <= 0:
|
||||||
|
ans[i, j] = 0
|
||||||
|
else:
|
||||||
|
ans[i, j] = -0.5*math.log(tmp_l)
|
||||||
|
if list1[i] == list2[j]:
|
||||||
|
ans[i, j] = 15
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def exp_distance_h2t(data1, list1, data2, list2):
|
||||||
|
ans = np.ndarray((list1.size, list2.size), dtype=np.cdouble)
|
||||||
|
return exp_distance_numba(data1.ndim, data1.vertex, list1, data2.vertex,
|
||||||
|
list2, data1.k, ans)
|
||||||
|
|
||||||
|
@jit(nopython=True)
|
||||||
|
def exp_distance_numba(ndim, vertex1, list1, vertex2, list2, kz, ans):
|
||||||
|
n = list1.size
|
||||||
|
m = list2.size
|
||||||
|
for i in range(n):
|
||||||
|
for j in range(m):
|
||||||
|
tmp_l = 0.0
|
||||||
|
for k in range(ndim):
|
||||||
|
tmp_v = vertex1[k, list1[i]] - vertex2[k, list2[j]]
|
||||||
|
tmp_l += tmp_v*tmp_v
|
||||||
|
if tmp_l <= 0:
|
||||||
|
ans[i, j] = 0
|
||||||
|
else:
|
||||||
|
r = math.sqrt(tmp_l)
|
||||||
|
ans[i, j] = cmath.exp(1j * kz * r)/ r
|
||||||
|
if list1[i] == list2[j]:
|
||||||
|
ans[i, j] = 6 + 1j*0
|
||||||
|
return ans
|
||||||
|
|
||||||
|
# def test_fun(data1, list1, data2, list2):
|
||||||
|
# ans = np.ndarray((list1.size, list2.size), dtype=np.float64)
|
||||||
|
# # ans = np.ndarray((list1.size, list2.size), dtype=np.float64)
|
||||||
|
# return test_fun_numba(data1.ndim, data1.vertex, list1, data2.vertex,
|
||||||
|
# list2, ans)
|
||||||
|
|
||||||
|
# @jit(nopython=True)
|
||||||
|
# def test_fun_numba(ndim, vertex1, list1, vertex2, list2, ans):
|
||||||
|
# n = list1.size
|
||||||
|
# m = list2.size
|
||||||
|
# for i in range(n):
|
||||||
|
# for j in range(m):
|
||||||
|
# tmp_l = 0.0
|
||||||
|
# for k in range(ndim):
|
||||||
|
# tmp_v = vertex1[k, list1[i]]-vertex2[k, list2[j]]
|
||||||
|
# tmp_l += tmp_v*tmp_v
|
||||||
|
# if tmp_l <= 0:
|
||||||
|
# ans[i, j] = 0
|
||||||
|
# else:
|
||||||
|
# # r = math.sqrt(tmp_l)
|
||||||
|
# ans[i, j] = 1./ (tmp_l)
|
||||||
|
# if list1[i] == list2[j]:
|
||||||
|
# ans[i, j] = 1000
|
||||||
|
# return ans
|
||||||
|
|
||||||
|
|
||||||
|
def double_layer(data1, list1, data2, list2):
|
||||||
|
ans = np.ndarray((list1.size, list2.size), dtype=np.cdouble)
|
||||||
|
return double_layer_numba(data1.ndim, data1.vertex, list1, data2.vertex,
|
||||||
|
list2, data1.k, data1.norms, ans)
|
||||||
|
|
||||||
|
@jit(nopython=True)
|
||||||
|
def double_layer_numba(ndim, vertex1, list1, vertex2, list2, kz, norms, ans):
|
||||||
|
n = list1.size
|
||||||
|
m = list2.size
|
||||||
|
for i in range(n):
|
||||||
|
for j in range(m):
|
||||||
|
tmp_l = 0.0
|
||||||
|
tetha = 0.0
|
||||||
|
len_norm = 0.0
|
||||||
|
for k in range(ndim):
|
||||||
|
tmp_v = vertex1[k, list1[i]] - vertex2[k, list2[j]]
|
||||||
|
tmp_l += tmp_v * tmp_v
|
||||||
|
tetha += tmp_v * norms[k,list1[i]]
|
||||||
|
len_norm += norms[k,list1[i]] * norms[k,list1[i]]
|
||||||
|
# print (tetha)
|
||||||
|
if tmp_l <= 0:
|
||||||
|
ans[i, j] = 0
|
||||||
|
else:
|
||||||
|
r = math.sqrt(tmp_l)
|
||||||
|
len_norm = math.sqrt(len_norm)
|
||||||
|
tetha = tetha / (r * len_norm)
|
||||||
|
ans[i, j] = (cmath.exp(1j * kz * r)/ r) * (1j * kz - 1/r)* math.cos(tetha)
|
||||||
|
if list1[i] == list2[j]:
|
||||||
|
ans[i, j] = 6 + 1j*0
|
||||||
|
return ans
|
||||||
|
|
||||||
|
@jit(nopython=True)
|
||||||
|
def comp_sph_numba(ndim, vertex1, list1, vertex2, list2, ans):
|
||||||
|
n = list1.size
|
||||||
|
m = list2.size
|
||||||
|
for i in range(n):
|
||||||
|
for j in range(m):
|
||||||
|
tmp_l = 0.0
|
||||||
|
for k in range(ndim):
|
||||||
|
tmp_v = vertex1[k, list1[i]]-vertex2[k, list2[j]]
|
||||||
|
tmp_l += tmp_v*tmp_v
|
||||||
|
if tmp_l <= 0:
|
||||||
|
ans[i, j] = 0
|
||||||
|
else:
|
||||||
|
ans[i, j] = 1/(4 * np.pi * math.sqrt(tmp_l))
|
||||||
|
if list1[i] == list2[j]:
|
||||||
|
ans[i, j] = 150
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def comp_sph(data1, list1, data2, list2):
|
||||||
|
ans = np.ndarray((list1.size, list2.size))
|
||||||
|
return comp_sph_numba(data1.ndim, data1.vertex, list1, data2.vertex,
|
||||||
|
list2, ans)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,297 @@
|
||||||
|
import numpy as np
|
||||||
|
from numba import jit
|
||||||
|
# from h2tools.cluster_tree import SmartIndex
|
||||||
|
from copy import deepcopy as dc
|
||||||
|
|
||||||
|
class SmartIndex(object):
|
||||||
|
"""
|
||||||
|
Stores only view to index and information about each node.
|
||||||
|
|
||||||
|
It is only used in `ClusterTree` class for convenient work with
|
||||||
|
indexes. Main reason this is implemented separately from
|
||||||
|
`ClusterTree` is easily readable syntax: `index[key]` returns view
|
||||||
|
to subarray of array `index`, corresponding to indices of node
|
||||||
|
`key`.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
size : integer
|
||||||
|
Number of objects in cluster
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
index: 1-dimensional array
|
||||||
|
Permutation array such, that indexes of objects, corresponding
|
||||||
|
to the same subcluster, are located one after each other.
|
||||||
|
node: list of tuples
|
||||||
|
Indexes of `i`-th node of cluster tree are
|
||||||
|
`index[node[i][0]:node[i][1]]`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, size):
|
||||||
|
self.index = np.arange(size, dtype=np.uint64)
|
||||||
|
self.node = [(0, size)]
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
"""Get indices for cluster `key`."""
|
||||||
|
return self.index[slice(*self.node[key])]
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
"""
|
||||||
|
Set indices for cluster `key`.
|
||||||
|
|
||||||
|
Changes only main index array.
|
||||||
|
"""
|
||||||
|
self.index[slice(*self.node[key])] = value
|
||||||
|
|
||||||
|
def add_node(self, parent, node):
|
||||||
|
"""Add node, that corresponds to `index[node[0]:node[1]]`."""
|
||||||
|
start = self.node[parent][0]+node[0]
|
||||||
|
stop = self.node[parent][0]+node[1]
|
||||||
|
self.node.append((start, stop))
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.node)
|
||||||
|
class Data(object):
|
||||||
|
def __init__(self, ndim, count, vertex, close_r='1box'):
|
||||||
|
self.ndim = ndim
|
||||||
|
self.count = count
|
||||||
|
self.vertex = vertex
|
||||||
|
self.close_r = close_r
|
||||||
|
def check_far(self, self_aux, other_aux):
|
||||||
|
return Data.fast_check_far_ndim(self_aux, other_aux, self.ndim, self.close_r)
|
||||||
|
def fast_check_far_ndim(self_aux, other_aux, ndim, close_r):
|
||||||
|
if close_r == '1box':
|
||||||
|
if ndim == 2:
|
||||||
|
corners_self = [np.array([self_aux[0,0], self_aux[0,1]]),
|
||||||
|
np.array([self_aux[1,0], self_aux[0,1]]),
|
||||||
|
np.array([self_aux[0,0], self_aux[1,1]]),
|
||||||
|
np.array([self_aux[1,0], self_aux[1,1]])]
|
||||||
|
corners_other = [np.array([other_aux[0,0], other_aux[0,1]]),
|
||||||
|
np.array([other_aux[1,0], other_aux[0,1]]),
|
||||||
|
np.array([other_aux[0,0], other_aux[1,1]]),
|
||||||
|
np.array([other_aux[1,0], other_aux[1,1]])]
|
||||||
|
for i in corners_self:
|
||||||
|
for j in corners_other:
|
||||||
|
if np.array_equal(i,j):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
elif ndim == 3:
|
||||||
|
corners_self = [np.array([self_aux[0,0], self_aux[0,1], self_aux[0,2]]),
|
||||||
|
np.array([self_aux[0,0], self_aux[0,1], self_aux[1,2]]),
|
||||||
|
np.array([self_aux[0,0], self_aux[1,1], self_aux[0,2]]),
|
||||||
|
np.array([self_aux[0,0], self_aux[1,1], self_aux[1,2]]),
|
||||||
|
np.array([self_aux[1,0], self_aux[0,1], self_aux[0,2]]),
|
||||||
|
np.array([self_aux[1,0], self_aux[0,1], self_aux[1,2]]),
|
||||||
|
np.array([self_aux[1,0], self_aux[1,1], self_aux[0,2]]),
|
||||||
|
np.array([self_aux[1,0], self_aux[1,1], self_aux[1,2]])]
|
||||||
|
corners_other = [np.array([other_aux[0,0], other_aux[0,1], other_aux[0,2]]),
|
||||||
|
np.array([other_aux[0,0], other_aux[0,1], other_aux[1,2]]),
|
||||||
|
np.array([other_aux[0,0], other_aux[1,1], other_aux[0,2]]),
|
||||||
|
np.array([other_aux[0,0], other_aux[1,1], other_aux[1,2]]),
|
||||||
|
np.array([other_aux[1,0], other_aux[0,1], other_aux[0,2]]),
|
||||||
|
np.array([other_aux[1,0], other_aux[0,1], other_aux[1,2]]),
|
||||||
|
np.array([other_aux[1,0], other_aux[1,1], other_aux[0,2]]),
|
||||||
|
np.array([other_aux[1,0], other_aux[1,1], other_aux[1,2]])]
|
||||||
|
for i in corners_self:
|
||||||
|
for j in corners_other:
|
||||||
|
if np.allclose(i,j):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
elif type(close_r) == float:
|
||||||
|
diam0 = 0.
|
||||||
|
diam1 = 0.
|
||||||
|
dist = 0.
|
||||||
|
for i in range(ndim):
|
||||||
|
tmp = self_aux[0, i]-self_aux[1, i]
|
||||||
|
diam0 += tmp*tmp
|
||||||
|
tmp = other_aux[0, i]-other_aux[1, i]
|
||||||
|
diam1 += tmp*tmp
|
||||||
|
tmp = self_aux[0, i]+self_aux[1, i]-other_aux[0, i]-other_aux[1, i]
|
||||||
|
dist += tmp*tmp
|
||||||
|
dist *= 0.25
|
||||||
|
return dist > diam0 * close_r and dist > diam1*close_r
|
||||||
|
else:
|
||||||
|
raise NameError('Wrong close_r')
|
||||||
|
def compute_aux(self, index):
|
||||||
|
tmp_particles = self.vertex[:,index]
|
||||||
|
|
||||||
|
xyzmin = np.min(tmp_particles,axis=1)
|
||||||
|
xyzmax = np.max(tmp_particles,axis=1)
|
||||||
|
bs = xyzmax-xyzmin
|
||||||
|
bs = np.max(bs)*1.01 # Fudge factor to account for some rounding issue
|
||||||
|
xyzmaxuse = xyzmin + bs
|
||||||
|
return np.array([xyzmin,xyzmaxuse])
|
||||||
|
def divide(self, index):
|
||||||
|
vertex = self.vertex[:, index]
|
||||||
|
center = vertex.mean(axis=1)
|
||||||
|
vertex -= center.reshape(-1, 1)
|
||||||
|
normal = np.linalg.svd(vertex, full_matrices=0)[0][:,0]
|
||||||
|
scal_dot = normal.dot(vertex)
|
||||||
|
scal_sorted = scal_dot.argsort()
|
||||||
|
scal_dot = scal_dot[scal_sorted]
|
||||||
|
k = scal_dot.searchsorted(0)
|
||||||
|
return scal_sorted, [0, k, scal_sorted.size]
|
||||||
|
def half_box(self, index, ax, mid_point):
|
||||||
|
ndim = self.ndim
|
||||||
|
vertex = self.vertex[:, index]
|
||||||
|
center = mid_point#vertex.mean(axis=1)
|
||||||
|
vertex -= center.reshape(-1, 1)
|
||||||
|
normal = np.zeros(ndim)
|
||||||
|
normal[ax] = 1.
|
||||||
|
scal_dot = normal.dot(vertex)
|
||||||
|
scal_sorted = scal_dot.argsort()
|
||||||
|
scal_dot = scal_dot[scal_sorted]
|
||||||
|
k = scal_dot.searchsorted(0)
|
||||||
|
return scal_sorted, [0, k, scal_sorted.size]
|
||||||
|
def __len__(self):
|
||||||
|
return self.count
|
||||||
|
class Tree(object):
|
||||||
|
def __init__(self, data, block_size, point_based_tree = True, num_child_tree = 'hyper'):
|
||||||
|
self.block_size = block_size
|
||||||
|
self.data = data
|
||||||
|
self.index = SmartIndex(len(data))
|
||||||
|
self.parent = [-1]
|
||||||
|
self.child = [[]]
|
||||||
|
self.leaf = [0]
|
||||||
|
self.level = [0, 1]
|
||||||
|
self.num_levels = 0
|
||||||
|
self.num_leaves = 1
|
||||||
|
self.num_nodes = 1
|
||||||
|
self.point_based_tree = point_based_tree
|
||||||
|
self.num_child_tree = num_child_tree
|
||||||
|
if num_child_tree == 'hyper':
|
||||||
|
self.nchild = 2 ** data.ndim
|
||||||
|
elif num_child_tree == 2:
|
||||||
|
self.nchild = num_child_tree
|
||||||
|
else:
|
||||||
|
print(f'Number of children = {num_child_tree} is not suported, # children changed to 2')
|
||||||
|
self.nchild = 2
|
||||||
|
def divide_space(self, key):
|
||||||
|
ndim = self.data.ndim
|
||||||
|
index = self.index[key]
|
||||||
|
box_list = []
|
||||||
|
for i in range(self.nchild):
|
||||||
|
box_list.append(dc(self.aux[key]))
|
||||||
|
if self.num_child_tree == 'hyper':
|
||||||
|
for i in range(ndim):
|
||||||
|
mid_point = (self.aux[key][0, i] + self.aux[key][1, i]) / 2
|
||||||
|
for ii in range(len(box_list)):
|
||||||
|
if self.check(ii, i, ndim):
|
||||||
|
box_list[ii][1,i] = mid_point
|
||||||
|
else:
|
||||||
|
box_list[ii][0,i] = mid_point
|
||||||
|
index_list_old = []
|
||||||
|
for i in range(2**ndim):
|
||||||
|
self.aux.append(box_list[i])
|
||||||
|
index_list_old.append([])
|
||||||
|
vertex = self.data.vertex[:, index]
|
||||||
|
for i_v in range(vertex.shape[1]):
|
||||||
|
v_in_b = 0
|
||||||
|
for i_aux in range(2**ndim):
|
||||||
|
vertex_in_box = 1
|
||||||
|
for nd in range(ndim):
|
||||||
|
vertex_in_box = vertex_in_box and (vertex[nd,i_v] >= box_list[i_aux][0,nd]) and (vertex[nd,i_v] <= box_list[i_aux][1,nd])
|
||||||
|
if vertex_in_box and v_in_b == 0:
|
||||||
|
index_list_old[i_aux].append(index[i_v])
|
||||||
|
v_in_b += 1
|
||||||
|
if v_in_b != 1:
|
||||||
|
for i_aux in range(2**ndim):
|
||||||
|
print(f'n box: {i_aux}, box: {box_list[i_aux]} \nlast ind: {index_list_old[i_aux]}')
|
||||||
|
raise NameError(f'{i_v, v_in_b}, v:{vertex[:,i_v]}')
|
||||||
|
|
||||||
|
|
||||||
|
index_res = np.array([])
|
||||||
|
list_k = [0]
|
||||||
|
for local_index in index_list_old:
|
||||||
|
local_index = np.array(local_index)
|
||||||
|
list_k.append(list_k[-1]+local_index.shape[0])
|
||||||
|
index_res = np.hstack((index_res,local_index))
|
||||||
|
return index_res.astype(int), list_k
|
||||||
|
else:
|
||||||
|
ax = len(self.level)%2
|
||||||
|
mid_point = (self.aux[key][0, ax] + self.aux[key][1, ax]) / 2
|
||||||
|
box_list[0][1,ax] = mid_point
|
||||||
|
box_list[1][0,ax] = mid_point
|
||||||
|
for i in range(2):
|
||||||
|
self.aux.append(box_list[i])
|
||||||
|
l = len(self.level)
|
||||||
|
new_index, subclusters = self.data.half_box(index, l%2, mid_point)
|
||||||
|
new_index = index[new_index]
|
||||||
|
return new_index, subclusters
|
||||||
|
def divide_point(self, key):
|
||||||
|
ndim = self.data.ndim
|
||||||
|
index = self.index[key]
|
||||||
|
if self.num_child_tree == 'hyper':
|
||||||
|
index_list_old = [self.index[key]]
|
||||||
|
list_k = [0]
|
||||||
|
ndim = self.data.ndim
|
||||||
|
for i in range(ndim):
|
||||||
|
index_list_new = []
|
||||||
|
for index in index_list_old:
|
||||||
|
new_index, subclusters = self.data.divide(index)
|
||||||
|
new_index = index[new_index]
|
||||||
|
index_list_new.append(new_index[:subclusters[1]])
|
||||||
|
index_list_new.append(new_index[subclusters[1]:])
|
||||||
|
index_list_old = dc(index_list_new)
|
||||||
|
index_res = np.array([])
|
||||||
|
for local_index in index_list_old:
|
||||||
|
list_k.append(list_k[-1]+np.array(local_index).shape[0])
|
||||||
|
index_res = np.hstack((index_res,local_index))
|
||||||
|
new_index = dc(index_res.astype(int))
|
||||||
|
subclusters = dc(list_k)
|
||||||
|
else:
|
||||||
|
index = self.index[key]
|
||||||
|
new_index, subclusters = self.data.divide(index)
|
||||||
|
new_index = index[new_index]
|
||||||
|
|
||||||
|
last_ind = subclusters[0]
|
||||||
|
for i in range(len(subclusters)-1):
|
||||||
|
next_ind = subclusters[i+1]
|
||||||
|
self.aux.append(self.data.compute_aux(new_index[last_ind:next_ind]))
|
||||||
|
last_ind = next_ind
|
||||||
|
return new_index, subclusters
|
||||||
|
def check(self, n, dim, ndim):
|
||||||
|
for _ in range(ndim - dim):
|
||||||
|
res = n % 2
|
||||||
|
n = n // 2
|
||||||
|
return res==0
|
||||||
|
def divide(self, key):
|
||||||
|
ndim = self.data.ndim
|
||||||
|
index = self.index[key]
|
||||||
|
# d = 1/0
|
||||||
|
if self.point_based_tree:
|
||||||
|
new_index, subclusters = self.divide_point(key)
|
||||||
|
else:
|
||||||
|
new_index, subclusters = self.divide_space(key)
|
||||||
|
test_index = new_index.copy()
|
||||||
|
test_index.sort()
|
||||||
|
last_ind = subclusters[0]
|
||||||
|
for i in range(len(subclusters)-1):
|
||||||
|
next_ind = subclusters[i+1]
|
||||||
|
if next_ind < last_ind:
|
||||||
|
raise NameError("children indices must be one after other")
|
||||||
|
self.index.add_node(key, (last_ind, next_ind))
|
||||||
|
last = len(self.parent)
|
||||||
|
self.parent.append(key)
|
||||||
|
if self.child[key]:
|
||||||
|
self.num_leaves += 1
|
||||||
|
self.num_nodes += 1
|
||||||
|
self.child[key].append(last)
|
||||||
|
self.child.append([])
|
||||||
|
|
||||||
|
last_ind = next_ind
|
||||||
|
if next_ind != test_index.size:
|
||||||
|
raise Error("Sum of sizes of children must be the same as"
|
||||||
|
" size of the parent")
|
||||||
|
self.index[key] = new_index
|
||||||
|
def is_far(self, i, other_tree, j):
|
||||||
|
if i <= j:
|
||||||
|
result = self.data.check_far(self.aux[i], other_tree.aux[j])
|
||||||
|
else:
|
||||||
|
result = other_tree.data.check_far(other_tree.aux[j], self.aux[i])
|
||||||
|
return result
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.parent)
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
import numpy as np
|
||||||
|
from copy import deepcopy as dc
|
||||||
|
from collections import defaultdict
|
||||||
|
from itertools import product
|
||||||
|
from numba import jit
|
||||||
|
class Problem(object):
|
||||||
|
def __init__(self, func, row_tree, col_tree, symmetric, verbose=False):
|
||||||
|
self._func = func
|
||||||
|
if symmetric and row_tree is not col_tree:
|
||||||
|
raise ValueError("row_tree and col_tree parameters must be the "
|
||||||
|
"same (as Python objects) if flag symmetric is `True`")
|
||||||
|
self.symmetric = symmetric
|
||||||
|
self.row_tree = row_tree
|
||||||
|
self.col_tree = col_tree
|
||||||
|
self.row_data = row_tree.data
|
||||||
|
self.col_data = col_tree.data
|
||||||
|
self.shape = (len(row_tree.data), len(col_tree.data))
|
||||||
|
l = np.arange(1, dtype=np.uint64)
|
||||||
|
tmp = self.func(l, l)
|
||||||
|
self.func_shape = tmp.shape[1:-1]
|
||||||
|
self.dtype = tmp.dtype
|
||||||
|
self._build(verbose)
|
||||||
|
def _build(self, verbose=False):
|
||||||
|
row_check = [[0]]
|
||||||
|
self.row_far = []
|
||||||
|
self.row_close = []
|
||||||
|
self.row_notransition = []
|
||||||
|
self.col_far = self.row_far
|
||||||
|
self.col_close = self.row_close
|
||||||
|
self.col_notransition = self.row_notransition
|
||||||
|
self.row_tree.aux =[self.row_data.compute_aux(self.row_tree.index[0])]
|
||||||
|
print(self.row_tree.aux)
|
||||||
|
cur_level = 0
|
||||||
|
while (self.row_tree.level[cur_level] < self.row_tree.level[cur_level+1]):
|
||||||
|
# print (f' level {cur_level}')
|
||||||
|
for i in range(self.row_tree.level[cur_level],self.row_tree.level[cur_level+1]):
|
||||||
|
self.row_far.append([])
|
||||||
|
self.row_close.append([])
|
||||||
|
for i in range(self.row_tree.level[cur_level],self.row_tree.level[cur_level+1]):
|
||||||
|
for j in row_check[i]:
|
||||||
|
if self.row_tree.is_far(i, self.col_tree, j):
|
||||||
|
self.row_far[i].append(j)
|
||||||
|
if self.row_tree is not self.col_tree:
|
||||||
|
self.col_far[j].append(i)
|
||||||
|
else:
|
||||||
|
self.row_close[i].append(j)
|
||||||
|
if self.row_tree is not self.col_tree:
|
||||||
|
self.col_close[j].append(i)
|
||||||
|
# print (i, self.row_close[i])
|
||||||
|
for i in range(self.row_tree.level[cur_level],self.row_tree.level[cur_level+1]):
|
||||||
|
if i == 0:
|
||||||
|
self.row_notransition.append(not self.row_far[i])
|
||||||
|
else:
|
||||||
|
self.row_notransition.append(not(self.row_far[i] or
|
||||||
|
not self.row_notransition[self.row_tree.parent[i]]))
|
||||||
|
for i in range(self.row_tree.level[cur_level],self.row_tree.level[cur_level+1]):
|
||||||
|
if(cur_level == 1):
|
||||||
|
self.row_tree.divide(i)
|
||||||
|
else:
|
||||||
|
if (self.row_close[i] and not self.row_tree.child[i] and
|
||||||
|
self.row_tree.index[i].size >
|
||||||
|
self.row_tree.block_size):
|
||||||
|
nonzero_close = False
|
||||||
|
for j in self.row_close[i]:
|
||||||
|
if (self.col_tree.index[j].size >
|
||||||
|
self.col_tree.block_size):
|
||||||
|
nonzero_close = True
|
||||||
|
break
|
||||||
|
if nonzero_close:
|
||||||
|
self.row_tree.divide(i)
|
||||||
|
for i in range(self.row_tree.level[cur_level],self.row_tree.level[cur_level+1]):
|
||||||
|
whom_to_check = []
|
||||||
|
for j in self.row_close[i]:
|
||||||
|
whom_to_check.extend(self.col_tree.child[j])
|
||||||
|
for j in self.row_tree.child[i]:
|
||||||
|
row_check.append(whom_to_check)
|
||||||
|
# print (f' 3:')
|
||||||
|
# for i in range(self.row_tree.level[cur_level],self.row_tree.level[cur_level+1]):
|
||||||
|
# print (i, self.row_close[i])
|
||||||
|
# for i in range(self.row_tree.level[cur_level],self.row_tree.level[cur_level+1]):
|
||||||
|
# tmp_close = []
|
||||||
|
# if self.row_tree.child[i]:
|
||||||
|
# for j in self.row_close[i]:
|
||||||
|
# if not self.col_tree.child[j]:
|
||||||
|
# tmp_close.append(j)
|
||||||
|
# self.row_close[i] = tmp_close
|
||||||
|
self.row_tree.level.append(len(self.row_tree))
|
||||||
|
# print (f' End:')
|
||||||
|
# for i in range(self.row_tree.level[cur_level],self.row_tree.level[cur_level+1]):
|
||||||
|
# print (i, self.row_close[i])
|
||||||
|
cur_level += 1
|
||||||
|
# update number of levels
|
||||||
|
self.num_levels = len(self.row_tree.level)-1
|
||||||
|
self.row_tree.num_levels = self.num_levels
|
||||||
|
self.col_tree.num_levels = self.num_levels
|
||||||
|
def func(self, row, col):
|
||||||
|
return self._func(self.row_data, row, self.col_data, col)
|
||||||
|
def multilevel_close(self):
|
||||||
|
self.lvl_close = dc(self.row_close)
|
||||||
|
|
||||||
|
tree = self.row_tree
|
||||||
|
close = self.row_close
|
||||||
|
level_count = len(tree.level)-2
|
||||||
|
row_size = tree.level[-1]
|
||||||
|
self.other_lvl_close = [[] for i in range(row_size)]
|
||||||
|
if self.add_up_level_close:
|
||||||
|
for i in range(level_count-1, 0, -1):
|
||||||
|
job = [j for j in range(tree.level[i], tree.level[i+1])]
|
||||||
|
for ind in job:
|
||||||
|
if tree.child[ind] == []:
|
||||||
|
for cl in close[ind]:
|
||||||
|
for ch_cl in tree.child[cl]:
|
||||||
|
if not tree.is_far(ch_cl, tree, ind):
|
||||||
|
self.add_child_to_close(ind, ch_cl)
|
||||||
|
def add_child_to_close(self, main_node, node):
|
||||||
|
close = self.row_close
|
||||||
|
tree = self.row_tree
|
||||||
|
self.other_lvl_close[node].append(main_node)
|
||||||
|
if not tree.child[node] == [] :
|
||||||
|
for ch_cl in tree.child[node]:
|
||||||
|
if not tree.is_far(ch_cl, tree, main_node):
|
||||||
|
self.add_child_to_close(main_node, ch_cl)
|
||||||
|
def schur_precompute(self):
|
||||||
|
row_tree = self.row_tree
|
||||||
|
N = row_tree.level[-1]
|
||||||
|
|
||||||
|
self.schur_list = [set() for i in range(N)] # list of sets
|
||||||
|
tmp_schur_dict = defaultdict(list) # like a dict, but if element is not exist, empty list are genereted
|
||||||
|
|
||||||
|
for ind in range(N):
|
||||||
|
close = self.lvl_close[ind]
|
||||||
|
other_close = self.other_lvl_close[ind]
|
||||||
|
# if self.symmetric:
|
||||||
|
# col_close = self.lvl_close[ind]
|
||||||
|
# else:
|
||||||
|
# col_close = self.col_lvl_close[ind]
|
||||||
|
for c1, c2 in product(close, close):
|
||||||
|
self.schur_list[c1].add(c2)
|
||||||
|
tmp_schur_dict[c1, c2].append(ind)
|
||||||
|
for c1,c2 in product(close, other_close):
|
||||||
|
self.schur_list[c1].add(c2)
|
||||||
|
tmp_schur_dict[c1, c2].append(ind)
|
||||||
|
|
||||||
|
self.schur_dict = dict(tmp_schur_dict)
|
||||||
Loading…
Reference in New Issue