Ale*_*kov 19 wolfram-mathematica
美好的一天,
我说,我有一些非常懒散和复杂的功能f[x,y]
.我需要详细ContourPlot
介绍它.此外,f[x,y]
由于缺乏物理内存,该功能有时会失败.在这种情况下,我必须自己停止评估并调查点{x,y}的问题情况.然后我应该将元素{x,y,f [x,y]}添加到计算值列表f[x,y]
(比如"缓存")并重新开始评估ContourPlot
.ContourPlot
必须f
从缓存中获取所有已计算的值.我宁愿将这样的列表存储在某个文件中,以便以后能够重用它.并且手动向该文件添加有问题的点可能更简单.
如果计算值列表f
可能包含10000-50000个点,那么实现此方法的最快方法是什么?
WRe*_*ach 29
让我们假设我们的慢函数有签名f[x, y]
.
纯粹的记忆内方法
如果您对内存缓存感到满意,最简单的方法是使用memoization:
Clear@fmem
fmem[x_, y_] := fmem[x, y] = f[x, y]
Run Code Online (Sandbox Code Playgroud)
每次使用前面没有见过的参数组合调用时,它都会为自己添加一个定义.
文件支持的内存中方法
但是,如果在长时间计算期间内存不足或内核崩溃,则需要使用某种持久性来备份此缓存.最简单的方法是保持一个正在运行的日志文件:
$runningLogFile = "/some/directory/runningLog.txt";
Clear@flog
flog[x_, y_] := flog[x, y] = f[x, y] /.
v_ :> (PutAppend[Unevaluated[flog[x, y] = v;], $runningLogFile]; v)
If[FileExistsQ[$runningLogFile]
, Get[$runningLogFile]
, Export[$runningLogFile, "", "Text"];
]
Run Code Online (Sandbox Code Playgroud)
flog
是一样的fmem
,不同的是它也写的进入,可以用来恢复缓存的定义在以后的会话中运行日志.最后一个表达式在找到现有日志文件时重新加载这些定义(或者如果文件不存在则创建该文件).
当需要手动干预时,日志文件的文本性质很方便.请注意,浮点数的文本表示会引入不可避免的舍入错误,因此从日志文件重新加载值后,您可能会得到稍微不同的结果.如果这非常令人担忧,您可以考虑使用二进制DumpSave
功能,尽管我会将该方法的详细信息留给读者,因为它不太方便保留增量日志.
SQL方法
如果内存非常紧张,并且您希望避免使用大型内存缓存为其他计算腾出空间,则之前的策略可能不合适.在这种情况下,您可以考虑使用Mathematica的内置SQL数据库来完全外部存储缓存:
fsql[x_, y_] :=
loadCachedValue[x, y] /. $Failed :> saveCachedValue[x, y, f[x, y]]
Run Code Online (Sandbox Code Playgroud)
我定义loadCachedValue
和saveCachedValue
下面.其基本思想是要创造一个每行持有的SQL表x
,y
,f
三倍.每次需要值时都会查询SQL表.请注意,这种做法实质上比内存缓存慢,所以它最有意义时的计算f
,除SQL访问时间长得多.SQL方法不会受到影响文本日志文件方法的舍入错误的影响.
现在跟随的定义loadCachedValue
以及saveCachedValue
其他一些有用的辅助函数:
Needs["DatabaseLink`"]
$cacheFile = "/some/directory/cache.hsqldb";
openCacheConnection[] :=
$cache = OpenSQLConnection[JDBC["HSQL(Standalone)", $cacheFile]]
closeCacheConnection[] :=
CloseSQLConnection[$cache]
createCache[] :=
SQLExecute[$cache,
"CREATE TABLE cached_values (x float, y float, f float)
ALTER TABLE cached_values ADD CONSTRAINT pk_cached_values PRIMARY KEY (x, y)"
]
saveCachedValue[x_, y_, value_] :=
( SQLExecute[$cache,
"INSERT INTO cached_values (x, y, f) VALUES (?, ?, ?)", {x, y, value}
]
; value
)
loadCachedValue[x_, y_] :=
SQLExecute[$cache,
"SELECT f FROM cached_values WHERE x = ? AND y = ?", {x, y}
] /. {{{v_}} :> v, {} :> $Failed}
replaceCachedValue[x_, y_, value_] :=
SQLExecute[$cache,
"UPDATE cached_values SET f = ? WHERE x = ? AND y = ?", {value, x, y}
]
clearCache[] :=
SQLExecute[$cache,
"DELETE FROM cached_values"
]
showCache[minX_, maxX_, minY_, maxY_] :=
SQLExecute[$cache,
"SELECT *
FROM cached_values
WHERE x BETWEEN ? AND ?
AND y BETWEEN ? AND ?
ORDER BY x, y"
, {minX, maxX, minY, maxY}
, "ShowColumnHeadings" -> True
] // TableForm
Run Code Online (Sandbox Code Playgroud)
此SQL代码使用浮点值作为主键.这在SQL中通常是一个值得怀疑的做法,但在目前的上下文中工作正常.
您必须openCacheConnection[]
在尝试使用任何这些功能之前调用.closeCacheConnection[]
完成后你应该打电话.只有一次,您必须调用createCache[]
初始化SQL数据库. replaceCachedValue
,clearCache
并showCache
提供手动干预.
最简单且可能最有效的方法是将缓存值设置为函数的特殊情况定义.由于散列,查找相当快.
功能:
In[1]:= f[x_, y_] := Cos[x] + Cos[y]
Run Code Online (Sandbox Code Playgroud)
在ContourPlot中使用哪些点?
In[2]:= points = Last[
Last[Reap[
ContourPlot[f[x, y], {x, 0, 4 Pi}, {y, 0, 4 Pi},
EvaluationMonitor :> Sow[{x, y}]]]]];
In[3]:= Length[points]
Out[3]= 10417
Run Code Online (Sandbox Code Playgroud)
为10000个评估设置具有预先计算值的f版本:
In[4]:= Do[With[{x = First[p], y = Last[p]}, precomputedf[x, y] = f[x, y];], {p,
Take[points, 10000]}];
Run Code Online (Sandbox Code Playgroud)
在上面,您将使用类似的东西,precomputedf[x, y] = z
而不是precomputed[x, y] = f[x, y]
,其中z是您存储在外部文件中的预先计算的值.
这是仅仅评估f的"else"情况:
In[5]:= precomputedf[x_, y_] := f[x, y]
Run Code Online (Sandbox Code Playgroud)
比较时间:
In[6]:= ContourPlot[f[x, y], {x, 0, 4 Pi}, {y, 0, 4 Pi}]; // Timing
Out[6]= {0.453539, Null}
In[7]:= ContourPlot[precomputedf[x, y], {x, 0, 4 Pi}, {y, 0, 4 Pi}]; // Timing
Out[7]= {0.440996, Null}
Run Code Online (Sandbox Code Playgroud)
时间差别不大,因为在这个例子中f不是一个昂贵的功能.
针对您的特定应用程序的单独注释:也许您可以使用ListContourPlot.然后,您可以准确选择要评估的点.