用内存构造函数的最好方法

美好的一天,

我有一些非常slooooow和复杂的function,说f[x,y] 。 我需要构build它的详细ContourPlot 。 此外,函数f[x,y]有时会由于缺乏物理内存而失败。 在这种情况下,我必须停止评估,并自行调查{x,y}点的问题。 然后,我可以将元素{x,y,f [x,y]}添加到f[x,y] (比如“cache”)的计算值列表中f[x,y]然后重新开始评估ContourPlotContourPlot必须从caching中取得所有已经计算ContourPlot f值。 我宁愿将这样的列表存储在某个文件中,以便稍后重用它。 手工添加问题点可能比较简单。

如果f的计算值列表可能包含10000-50000个点,那么实现这个最快的方法是什么?

假设我们的慢函数具有签名f[x, y]

纯内存方法

如果您对内存caching感到满意,最简单的方法就是使用记忆:

 Clear@fmem fmem[x_, y_] := fmem[x, y] = f[x, y] 

每次使用以前没有见过的参数组合调用它时,都会为其自身添加一个定义。

文件支持的内存中方法

但是,如果在长时间计算期间内存不足或内核崩溃,则需要使用某种持久性来支持此caching。 最简单的事情是保持一个正在运行的日志文件:

 $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"]; ] 

fmemfmem相同,除了它还将一个条目写入正在运行的日志中,该条目可用于在稍后的会话中恢复caching的定义。 最后一个expression式在find现有日志文件时重新加载这些定义(或者如果文件不存在,则创build该文件)。

当需要手动干预时,日志文件的文本性质很方便。 请注意,浮点数字的文本表示forms引入了不可避免的舍入错误,因此在重新加载日志文件中的值后,可能会得到稍微不同的结果。 如果这非常值得关注,那么可以考虑使用二进制DumpSavefunction,尽pipe我将这种方法的细节留给读者,因为保留增量日志不太方便。

SQL方法

如果内存非常紧张,并且您希望避免拥有大容量的内存caching来为其他计算腾出空间,则以前的策略可能不合适。 在这种情况下,您可能会考虑使用Mathematica的内置SQL数据库来完全在外部存储caching:

 fsql[x_, y_] := loadCachedValue[x, y] /. $Failed :> saveCachedValue[x, y, f[x, y]] 

我在下面定义了loadCachedValuesaveCachedValue 。 基本的想法是创build一个SQL表,每行保存一个xyf三元组。 每当需要值时查询SQL表。 请注意,这种方法比内存中caching要慢得多,所以当f的计算花费比SQL访问时间更长的时间时,这是最有意义的。 SQL方法不会遭受折磨文本日志文件方法的舍入错误。

下面是loadCachedValuesaveCachedValue的定义,以及其他一些有用的帮助函数:

 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 

此SQL代码使用浮点值作为主键。 这在SQL中通常是一个值得怀疑的做法,但在当前情况下工作正常。

尝试使用这些function之前,您必须调用openCacheConnection[] 。 完成后应该调用closeCacheConnection[] 。 只有一次,您必须调用createCache[]来初始化SQL数据库。 replaceCachedValueclearCacheshowCache用于手动干预。

最简单也是最有效的方法就是将caching的值设置为函数的特殊情况定义。 由于哈希,查找速度相当快。

function:

 In[1]:= f[x_, y_] := Cos[x] + Cos[y] 

在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 

设置一个预先计算的值为10000的评估版本f:

 In[4]:= Do[With[{x = First[p], y = Last[p]}, precomputedf[x, y] = f[x, y];], {p, Take[points, 10000]}]; 

在上面,你可以使用像precomputedf[x, y] = z而不是precomputed[x, y] = f[x, y] ,其中z是你存储在外部文件中的预先计算的值。

这是“否”的情况下,只是评估f:

 In[5]:= precomputedf[x_, y_] := f[x, y] 

比较时间:

 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} 

时间没有太大的差别,因为在这个例子中,f不是一个昂贵的function。

针对您的特定应用程序的单独备注:也许您可以使用ListContourPlot代替。 然后,您可以select确切评估哪些点。