Создание копии класса внутри класса
Я хочу цитонизировать код на python, чтобы ускорить код. Ниже вы можете увидеть мою попытку сделать мой класс python понятным для cython:
import numpy as np
cimport numpy as np
ctypedef np.double_t DTYPE_T
cpdef double std_G,v_c
std_G=4.3e-9 # Newton's const in Mpc (km/s)^2 M_sol^{-1}
v_c = 299792.458 #km/s
cdef extern from "math.h":
double log(double) nogil
double sqrt(double) nogil
cdef extern from "gsl/gsl_math.h":
ctypedef struct gsl_function:
double (* function) (double x, void * params)
void * params
cdef extern from "gsl/gsl_integration.h":
ctypedef struct gsl_integration_workspace
gsl_integration_workspace * gsl_integration_workspace_alloc(size_t n)
void gsl_integration_workspace_free(gsl_integration_workspace * w)
int gsl_integration_qags(const gsl_function * f, double a, double b, double epsabs, double epsrel, size_t limit, gsl_integration_workspace * workspace, double *result, double *abserr)
cdef double do_callback(double x, void* params):
return (<ComovingDistMemoization>params).eval(x)
cdef class ComovingDistMemoization(object):
cdef list _memotable
cdef cosmolgy
def __cinit__(self, cosmology, memotable = None):
if memotable is None:
self._memotable = []
self._memotable = memotable
self.cosmology = cosmology
def __call__(self, double z):
if z in self._memotable:
return self._memotable[z]
def eval(z):
return 1./sqrt(self.cosmology.hubble2(z))
cdef gsl_integration_workspace* w =gsl_integration_workspace_alloc(1000)
cdef gsl_function F
F.function = &do_callback
F.params = <void*>self
cdef double result = 3, error = 5
cdef double y, err, dist
gsl_integration_qags (&F, 0, z, 0, 1e-7, 1000, w, &result, &error)
y, err = result, error
gsl_integration_workspace_free(w)
dist = self.cosmology.v_c * y #to get proper units, ie to put in the hubble length
self._memotable[z] = dist
return dist
cdef class cosmology(object):
cdef comovingdist
cdef public double omega_m, omega_l, h, w, omega_r, G, v_c
cdef double H0, hubble_length
def __init__(self, omega_m = 0.3, omega_l = 0.7, h = 0.7, w = -1, omega_r = 0., G = std_G):
self.omega_m = omega_m
self.omega_l = omega_l
self.omega_r = omega_r
self.h = h
self.w = w
self.G = G
self.v_c = v_c
self.comovingdist = ComovingDistMemoization(self)
def __copy__(self):
return cosmology(omega_m = self.omega_m, omega_l = self.omega_l, h = self.h, w = self.w, omega_r = self.omega_r, G = self.G)
property H0:
def __get__(self):
return 100*self.h #km/s/MPC
def hubble2(self, double z):
cdef double inv_a
inv_a = 1.+z
return (self.omega_r*inv_a**4 + self.omega_m*inv_a**3 +
self.omega_l*(inv_a**(3*(1+self.w))) + (1 - self.omega_m - self.omega_l - self.omega_r)*inv_a**2)*self.H0**2
property hubble_length:
def __get__(self):
return self.v_c / self.H0
def rho_crit(self, double z):
return 3.*self.hubble2(z)/(8*np.pi*self.G)
def angulardist(self, double z, double z2 = None):
if z2 is None:
return self.comovingdist(z) / (1+z)
return (self.comovingdist(z2) - self.comovingdist(z)) / (1+z2)
Однако одно из мест, которое вызывает ошибку, и я не смог найти до сих пор никакой замены, и мое исследование просто дошло до того, что функция __copy__(self)
не поддерживается cython.
Первый вопрос: Как я могу сделать копию одного метода class
в cython
?
Я читал, что с помощью pickle.Pickler
можно обеспечить подстановка для экземпляра класса, но я не знаю, как он должен производить copy
класса внутри класса??
Обновление:
Правильно ли заменить __copy__
следующим фрагментом кода:
def __reduce__(self):
return (self.__class__, (), self.__getstate__())
def __getstate__(self):
return (self.omega_m, self.omega_l, self.omega_r, self.h, self.w, self.G, self.v_c)
def __setstate__(self, data):
(self.omega_m, self.omega_l, self.omega_r, self.h, self.w, self.G, self.v_c) = data
Мой второй вопрос касается property
, определил ли я свойство в cython или мне также нужно иметь функцию __set__
?
cosmology
с помощью ComovingDistMemoization
, они возвращают ноль. Я задаюсь вопросом, является ли путь, который я призвал экземпляр cosmology
класса в ComovingDistMemoization
классе и наоборот вызвал проблему и не может передать информацию между ними ?2 ответа:
Отвечая на ваш второй вопрос, в вашем случае вам не нужна функция
__set__
, и, похоже, есть ошибка в том, как вы используете концепцию__get__
. Получаемое имя атрибута должно быть задано следующим образом:def get_H0(self): return 100*self.h #km/s/MPC H0 = property(fget=get_H0) def get_hubble_length(self): return self.v_c / self.H0 hubble_length = property(fget=get_hubble_length)
EDIT: я не знал, что
@property
нельзя использовать с Cython
Лучший способ-объединить класс
cosmology
с классомComovingDistMemoization
и записать второй класс в качестве метода для первого. Я переименовал этот класс в функциюcomovingdist
для классаcosmology
.cdef extern from "gsl/gsl_math.h": ctypedef struct gsl_function: double (* function) (double x, void * params) void * params cdef extern from "gsl/gsl_integration.h": ctypedef struct gsl_integration_workspace gsl_integration_workspace * gsl_integration_workspace_alloc(size_t n) void gsl_integration_workspace_free(gsl_integration_workspace * w) int gsl_integration_qags(const gsl_function * f, double a, double b, double epsabs, double epsrel, size_t limit, gsl_integration_workspace * workspace, double *result, double *abserr) cdef double do_callback(double x, void* params): return (<cosmology>params).eval(x) cdef class cosmology(object): cdef public double omega_m, omega_l, h, w, omega_r, G, v_c def __init__(self,double omega_m = 0.3, double omega_l = 0.7, double h = 0.7, double w = -1, double omega_r = 0., double G = std_G): self.omega_m = omega_m self.omega_l = omega_l self.omega_r = omega_r self.h = h self.w = w self.G = G self.v_c = v_c def __copy__(self): return cosmology(omega_m = self.omega_m, omega_l = self.omega_l, h = self.h, w = self.w, omega_r = self.omega_r, G = self.G) property H0: def __get__(self): return 100*self.h #km/s/MPC def hubble2(self, double z): cdef double inv_a inv_a = 1.+z return (self.omega_r*inv_a**4 + self.omega_m*inv_a**3 + \ self.omega_l*(inv_a**(3*(1+self.w))) + (1 - self.omega_m - self.omega_l - self.omega_r)*inv_a**2)*self.H0**2 property hubble_length: def __get__(self): return self.v_c / self.H0 cpdef double eval(self, double z): return 1./sqrt(self.hubble2(z)) def comovingdist(self, double z): cdef gsl_integration_workspace* w =gsl_integration_workspace_alloc(1000) cdef gsl_function F F.function = &do_callback F.params = <void*>self cdef double result = 3, error = 5 cdef double y, err, dist gsl_integration_qags(&F, 0, z, 0, 1e-7, 1000, w, &result, &error) y, err = result, error gsl_integration_workspace_free(w) dist = self.v_c * y #to get proper units, ie to put in the hubble length return dist def rho_crit(self, double z): return 3.*self.hubble2(z)/(8*np.pi*self.G) def angulardist(self, z, z2=None): if z2 is None: return self.comovingdist(z) / (1+z) return (self.comovingdist(z2) - self.comovingdist(z)) / (1+z2) def beta(self, z, zcluster): Ds = np.array([self.angulardist(zi) for zi in z]) Dls = np.array([self.angulardist(zcluster, zi) for zi in z]) Dls_over_Ds = np.zeros_like(Dls) Dls_over_Ds[Ds > 0] = Dls[Ds > 0] / Ds[Ds > 0] Dls_over_Ds[Dls <= 0] = 0 return Dls_over_Ds def beta_s(self, z, zcluster): betainf = self.beta([1e6], zcluster) beta_s = self.beta(z, zcluster) / betainf return beta_s def RhoCrit_over_SigmaC(self,z, zcluster): Dl = self.angulardist(zcluster) r= (4*np.pi*self.G)*self.rho_crit(zcluster)*self.beta( z, zcluster )*Dl/self.v_c**2 return r