【发布时间】:2015-04-29 18:51:00
【问题描述】:
我在一个类属性中求解一个由两个方程组成的系统,它返回解——两个变量的值。我希望这两个值都是类的属性——如何在不解决系统两次的情况下实现这一点?这是一个例子
#!/usr/bin/python3
class Test(object):
pass
def ab(self):
print("Calc!")
a = 1
b = 2
return [a,b]
@property
def a(self):
return self.ab()[0]
@property
def b(self):
return self.ab()[1]
test = Test()
print(test.a)
print(test.b)
它输出:
Calc!
1
Calc!
2
所以它实际上两次“解决”了方程组(ab 属性)。如果它已经解决了一次,那么输出将是:
Calc!
1
2
我如何做到这一点?
编辑
系统示例:
#!/usr/bin/python3
import scipy
from scipy.optimize import fsolve
class Test(object):
def __init__(self, c, d):
self.c = c
self.d = d
def ab(self):
print("Calc!")
result = fsolve(lambda x: [
x[0] + 2*x[1] + self.c
, 3*x[0] - x[1] + self.d
], [1,1])
return result
@property
def a(self):
return self.ab()[0]
@property
def b(self):
return self.ab()[1]
test = Test(-5,2)
print(test.a)
print(test.b)
给予:
Calc!
0.142857142857
Calc!
2.42857142857
我希望它只解决一个系统一次:
Calc!
0.142857142857
2.42857142857
编辑 2
真实代码:
#!/usr/bin/env python3
import argparse, os, sys
# ==============
## parsing args:
parser = argparse.ArgumentParser()
argsLip = parser.add_argument_group('Properties of lipid:')
argsLip.add_argument('-A', '--area',
help = "incompressible area, Ų (default to %(default)s)",
dest = 'A_n',
action = 'store',
type = float,
default = 20.0,
)
argsLip.add_argument('-L',
help = "basic length in Å (default to %(default)s)",
dest = 'L',
action = 'store',
type = float,
default = 15.0,
)
argsLip.add_argument('-K', '--K-coef',
help = "bending rigidity in kTL (default to %(default)s)",
dest = 'K_f_coef',
action = 'store',
type = float,
default = 0.33,
)
argsMem = parser.add_argument_group('Properties of membrane:')
argsMem.add_argument('-g', '--gamma',
help = "surface tension, erg/cm² (default to %(default)s)",
dest = 'γ',
action = 'store',
type = float,
default = 30.0,
)
argsEnv = parser.add_argument_group('Properties of environment:')
argsEnv.add_argument('-T', '--temperature',
help = "temperature, K (default to %(default)s)",
dest = 'T',
action = 'store',
type = float,
default = 323.0,
)
argsCalc = parser.add_argument_group('Calc options:')
argsCalc.add_argument('-a', '--a-trial',
help = "trial value of a to be used in nsolve (default to %(default)s)",
dest = 'a_trial',
action = 'store',
type = float,
default = 2.0,
)
args = parser.parse_args()
# =========
## imports:
# symbolic:
import sympy
from sympy import symbols, solve, nsolve, pprint, diff, S, Function
from sympy import sqrt as Sqrt
sympy.init_printing(pretty_print=True, use_unicode=True, wrap_line=False, no_global=True)
# numeric:
import scipy
from scipy import sqrt
from scipy.optimize import fsolve
# constants:
from scipy import pi as π
from scipy.constants import k as k_SI
k = k_SI * 10**7 # J/K → erg/K
# =========
## program:
class MonoFlexible_symbolic(object):
"This class initiates common symbolic expressions to be used in all MonoFlexible classes."
def __init__(self):
a, l = symbols("a l", real=True, positive=True)
b = Function('b')(a, l)
ν = Function('ν')(l)
equation = (
3 / (4 * b)
+ 1 / ( 2 * Sqrt(2) * b**(S(3)/4) )
- ν * ( Sqrt(a) - 1 )**2
)
equation_diff_a = equation.diff(a)
equation_diff_a2 = equation_diff_a.diff(a)
equation_diff_l = equation.diff(l) .subs(ν.diff(l) , -3*ν)
equation_diff_l2 = equation_diff_l.diff(l) .subs(ν.diff(l,2), 12*ν)
equation_diff_al = equation_diff_a.diff(l) .subs(ν.diff(l) , -3*ν)
db_da = solve( equation_diff_a , b.diff(a) )[0]
d2b_da2 = solve( equation_diff_a2 , b.diff(a,2) )[0]
db_dl = solve( equation_diff_l , b.diff(l) )[0]
d2b_d2l = solve( equation_diff_l2 , b.diff(l,2) )[0]
d2b_dadl = solve( equation_diff_al , b.diff(a).diff(l) )[0]
# print("db_da =")
# pprint(
# db_da
# )
# print("d2b_da2 =")
# pprint("d2b_da2 =",
# d2b_da2
# )
# print("db_dl =")
# pprint(
# db_dl
# )
# print("d2b_dl2 =")
# pprint(
# d2b_d2l
# )
# print("d2b_dadl =")
# pprint(
# cancel(d2b_dadl[0])
# )
self.db_da_func = lambda aa, bb, νν: db_da.subs({
a: aa
, b: bb
, ν: νν
}).evalf()
self.d2b_da2_func = lambda aa, bb, νν: d2b_da2.subs({
a: aa
, b: bb
, ν: νν
}).evalf()
self.db_dl_func = lambda aa, bb, νν: db_dl.subs({
a: aa
, b: bb
, ν: νν
}).evalf()
self.d2b_d2l_func = lambda aa, bb, νν: d2b_dl2.subs({
a: aa
, b: bb
, ν: νν
}).evalf()
self.d2b_dadl_func = lambda aa, bb, νν: d2b_dadl.subs({
a: aa
, b: bb
, ν: νν
}).evalf()
class MonoFlexible(MonoFlexible_symbolic):
def __init__(self,
γ : "Surface tension of the membrane, erg/cm²",
T : "Temperature, K",
L : "Length of the hydrocarbon chain, Å",
A_n : "Non-compressible area of the lipid, Ų",
a_trial : "Initial value for fsolve, default to 2.0" = None,
K_f_coef : "K_f = k T L * K_f_coef, default to 1/3" = None,
) -> "Calculates thermodynamic properties of flexible string":
super().__init__()
self.__γ = γ
self.__T = T
self.__L = L
self.__A_n = A_n
self.__a_trial = a_trial
self.__K_f_coef = K_f_coef
@property
def A_n_Å2(self):
return self.__A_n
@property
def A_n(self):
return self.__A_n * 10**(-16) # Ų → cm²
@property
def L_Å(self):
return self.__L
@property
def L(self):
return self.__L * 10**(-8) # Å → cm
@property
def γ(self):
return self.__γ
@property
def T(self):
return self.__T
@property
def a_trial(self):
"Initial value for numerical equation solving function to find area per lipid."
a_trial = self.__a_trial or 2.0
return a_trial
@property
def K_f_coef(self):
K_f_coef = self.__K_f_coef or 1/3
return K_f_coef
@property
def K_f(self):
"Rigidity of the string."
return k * self.T * self.L * self.K_f_coef
@property
def ν(self):
return self.K_f * self.A_n / (
π * k * self.T * self.L**3
)
@property
def ab(self):
print("Calc!")
ab = fsolve( lambda x: [
3 / ( 4 * x[1] )
+ 1 / ( 2 * sqrt(2) * x[1]**(3/4) )
- self.ν * (sqrt(x[0]) - 1)**2
,
- k * self.T / self.A_n * self.db_da_func(x[0], x[1], self.ν) * self.ν * (sqrt(x[0]) - 1)**2
- self.γ
]
, [2., 300.] )
return ab
@property
def a(self):
return self.ab[0]
@property
def b(self):
return self.ab[1]
# ======
## calc:
def main():
flexible_kwargs = {
"γ" : args.γ,
"T" : args.T,
"L" : args.L,
"A_n" : args.A_n,
"a_trial" : args.a_trial,
"K_f_coef" : args.K_f_coef,
}
flexible = MonoFlexible(**flexible_kwargs)
print( "ν = {ν:.5f}".format(ν=flexible.ν) )
print( "a = {a:.2f}".format(a=flexible.a) )
print( "b = {b:.2f}".format(b=flexible.b) )
# python code run faster in a function:
if __name__ == "__main__":
main()
使用默认参数,所以为了测试它——只需运行它。
【问题讨论】:
-
您的示例与实际使用相去甚远,因此很难确定问题的约束条件。对于给定的实例,是否只有一个解决方案?为什么这些属性而不是普通的属性或方法?你要解决的真正问题是什么?
-
@MikeGraham:但它说明了一切......我解决了两个方程组,只有一个解决方案,我使用 scipy 的 fsolve,它将解决方案作为列表(或数组)返回.但我希望这两种解决方案都是实例的属性,所以我正在寻找一种方法将类方法返回的列表重新绑定为两个属性。
-
@MikeGraham:我添加了一个带有虚拟方程组的示例。