我的计划是首先以符号方式计算表达式的分配,保护它们,然后在几个广泛的大都会蒙特卡罗模拟中使用它们。虽然我最初尝试了 Sympy,然后又尝试了 Symbolics,但现在我打算使用 SymEngine,因为它在所有相关步骤中似乎都快得多。当使用 SymEngine 时,用于生成函数的大部分时间实际上不是符号计算,而是羔羊化。因此我希望保存由lambdaify生成的函数而不是符号表达式。但是我似乎无法找到一种方法将它们成功加载到新会话中。
我最初尝试使用此处建议的 BSON:https://discourse.julialang.org/t/is-there-a-way-to-write-a-function-into-jld-or-other-h5-file /77718/2。但遗憾的是,重新加载只能在同一个会话中起作用。例如,可以通过执行以下操作来重现:
using SymEngine
using BSON: @save, @load
function mwe()
@vars X
f = X
return(lambdify(f,[X]))
end
f = mwe()
@save "test.bson" f
然后(在新会话中):
using SymEngine
using BSON: @save, @load
function mwe()
@vars X
f = X
return(lambdify(f,[X]))
end
@load "test.bson" f
println(f(1))
f = "nofunction"
@load "test.bson" f
println(f(1))
如果我在同一个会话中执行这两项操作,则输出为
1
1
正如预期的那样。
但是,如果我在新会话中运行第二部分,我会得到:
ERROR: UndefVarError: ###312 not defined
我还尝试了一种简单的(也许有更复杂的方法来应用这些方法?)使用序列化、FileIO 以及 JLD2 的方法。如果在同一会话中加载,所有这些都会导致类似的行为正常工作,但在不同的会话中加载时会出现错误或警告,然后出现意外行为。
有效的是使用符号
write("test.jl", string(f))
,如下所述:
https://discourse.julialang.org/t/using-serialization-to-store-then-load-a-lambdified-function/85884/2。然而,由于 Symbolics 比 SymEnginge 慢得多,这对我来说似乎并不是理想的方式。
更新:我尝试通过使用 PackageCompiler 生成 sysimage 来找到解决方法。想法是创建一个模块,在其中完成所有符号计算,然后创建一个包含所有 lamdified 表达式列表的常量。然后创建该模块的 sysimage,并希望在开始使用 sysimage 时可以在新会话中使用羔羊化表达式。 如果我不生成常量,则创建 sysimage 工作正常。但是一旦我添加了应该创建函数列表的行
const Bookofterms = generate_terms(3,3)
我收到一条有点长的错误消息,其中包含:
ERROR: The following 1 direct dependency failed to precompile:
TimeInt [14fa5fd4-ce9e-41c9-8e49-66abaaa3fbcd]
(TimeInt是自定义包)
Failed to precompile TimeInt [14fa5fd4-ce9e-41c9-8e49-66abaaa3fbcd] to C:\Users\...
ERROR: LoadError: ArgumentError: Expression does not lambdify
caused by: Evaluation into the closed module `SymEngine` breaks incremental compilation because the side effects will not be permanent. This is likely due to some other module mutating `SymEngine` with `eval` during precompilation - don't do this.
老实说我对发生的事情感到很困惑。
更新:由于几天后没有得到任何回复,我决定尝试将这篇文章复制到:https://discourse.julialang.org/t/how-to-save-a-lambdifyed-by-symengine-expression -到文件并在新会话中加载它/111165?u=zaph
在进一步调查中我发现了一些事情: 首先:保存以类似方式定义的 Julia 函数
function f(x,y,z)
do stuff
return something
end
然后在另一个会话中加载它不适用于 BSON,并且我认为它不适用于我之前尝试过的任何方法。所以不需要使用lambdaify来产生这样的错误。
但是https://discourse.julialang.org/t/is-there-a-way-to-write-a-function-into-jld-or-other-h5-file/77718/给出的代码示例2仍然有效。因此,问题似乎出在函数名称中的某个地方,因为可以保存匿名函数。
我当时尝试的是使用以下内容:
function perf_func(ex, vars = [])
body = convert(Expr, ex)
if isempty(vars)
syms = Symbol.(free_symbols(ex))
else
syms = Symbol.(vars)
end
fn = eval(:($(Expr(:tuple,syms...)) -> $body));
return(fn)
end
而不是羔羊化。
事实上,这使我能够保存生成的函数并使用 BSON 将它们加载到新会话中。
现在最后应该指出的是,我使用 BSON 加载的速度非常慢,实际上比重新计算函数还要慢。然而,这种方法还允许使用序列化,与在每个新会话中重新计算相比,这确实提供了显着的加速。
所以我的问题的答案是使用上面定义的函数而不是lambdaify,并且只是通过序列化保存和加载。