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

美好的一天,

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

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


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

纯内存方法

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

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

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

文件支持的内存中方法

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

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

flog相同fmem ,但它也写的进入,可以用来恢复缓存的定义在以后的会话中运行日志。 最后一个表达式在找到现有日志文件时重新加载这些定义(或者如果该文件不存在,则创建该文件)。

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

SQL方法

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

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

我在下面定义了loadCachedValuesaveCachedValue 。 基本思想是创建一个SQL表,其中每行包含xyf三元组。 每次需要值时查询SQL表。 请注意,这种方法比内存中缓存要慢很多,因此当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中通常是一个值得怀疑的做法,但在当前上下文中工作正常。

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


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

功能:

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不是一个昂贵的功能。

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

链接地址: http://www.djcxy.com/p/35525.html

上一篇: The best way to construct a function with memory

下一篇: Implementing a Quadtree in Mathematica