Najlepszy sposób konstruowania funkcji z pamięcią

Dzień dobry,

Mam jakąś bardzo słabą i skomplikowaną funkcję, powiedzmy f[x,y]. I muszę skonstruować z tego szczegółową ContourPlot. Ponadto funkcja f[x,y] czasami zawodzi z powodu braku fizycznej pamięci. W takich przypadkach muszę przerwać ewaluację i sam zbadać problem w punkcie {x, y}. Następnie powinienem dodać element {x, y, f [x, y]} do listy obliczonych wartości f[x,y] (powiedzmy "cache") i ponownie uruchomić ocenę ContourPlot. ContourPlot musi przyjmować wszystkie już obliczone wartości f z pamięci podręcznej. Wolałbym przechowywać taką listę w jakimś pliku, aby móc ją później ponownie wykorzystać. I prawdopodobnie prostsze jest ręczne dodawanie problematycznych punktów do tego pliku.

Jaki jest najszybszy sposób wdrożenia tego, jeśli lista obliczonych wartości {[6] } może zawierać 10000-50000 punktów?

Author: Alexey Popkov, 2011-03-13

2 answers

Załóżmy, że nasza funkcja slow ma sygnaturę f[x, y].

Podejście czyste w pamięci

Jeśli jesteś zadowolony z pamięci podręcznej w pamięci, najprostszą rzeczą do zrobienia byłoby użycie memoizacji: {]}

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

To dodaje definicję do siebie za każdym razem, gdy jest wywoływana z kombinacją argumentów, których wcześniej nie widział.

Podejście oparte na plikach w pamięci

Jednakże, jeśli kończy ci się pamięć lub cierpisz na awaria podczas długich obliczeń, będziesz chciał cofnąć tę pamięć podręczną z pewnym uporem. Najprościej byłoby zachować uruchomiony plik dziennika:

$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 jest tym samym co fmem, z tą różnicą, że zapisuje również wpis do dziennika, który może być użyty do przywrócenia buforowanej definicji w późniejszej sesji. Ostatnie wyrażenie przeładowuje te definicje, gdy znajdzie istniejący plik dziennika(lub utworzy plik, jeśli nie istnieje).

Tekstowy charakter plik dziennika jest wygodny, gdy wymagana jest ręczna interwencja. Należy pamiętać, że tekstowa reprezentacja liczb zmiennoprzecinkowych wprowadza nieuniknione błędy zaokrąglania, więc możesz uzyskać nieco inne wyniki po przeładowaniu wartości z pliku dziennika. Jeśli jest to bardzo niepokojące, możesz rozważyć użycie funkcji binary DumpSave, chociaż szczegóły tego podejścia zostawię czytelnikowi, ponieważ nie jest to tak wygodne do prowadzenia dziennika przyrostowego.

SQL Podejście

Jeśli pamięć jest naprawdę napięta i chcesz uniknąć posiadania dużej pamięci podręcznej w pamięci, aby zrobić miejsce na inne obliczenia, poprzednia strategia może nie być odpowiednia. W takim przypadku można rozważyć użycie wbudowanej bazy danych SQL Mathematica do przechowywania pamięci podręcznej całkowicie Na Zewnątrz: [23]}

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

Definiuję loadCachedValue i saveCachedValue poniżej. Podstawową ideą jest stworzenie tabeli SQL, w której każdy wiersz zawiera x, y, f potrójnie. Tabela SQL jest pytana co czas potrzebna jest wartość. Zauważ, że to podejście jest zasadniczo wolniejsze niż pamięć podręczna w pamięci podręcznej, więc ma to największy sens, gdy obliczanie f trwa znacznie dłużej niż czas dostępu do SQL. Podejście SQL nie cierpi z powodu błędów zaokrąglania, które dotknęły podejście do plików dziennika tekstowego.

Definicje loadCachedValue i saveCachedValue są teraz następujące, wraz z innymi przydatnymi funkcjami pomocniczymi:

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

Ten kod SQL wykorzystuje wartości zmiennoprzecinkowe jako podstawowe klucze. Jest to zwykle wątpliwa praktyka w SQL, ale działa dobrze w obecnym kontekście.

Musisz wywołać openCacheConnection[] przed próbą użycia którejkolwiek z tych funkcji. Powinieneś zadzwonić closeCacheConnection[] po zakończeniu. Tylko jeden raz, musisz wywołać createCache[], aby zainicjować bazę danych SQL. replaceCachedValue, clearCache i showCache są przewidziane do interwencji ręcznych.

 29
Author: WReach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-13 18:02:52

Najprostszym i prawdopodobnie najbardziej efektywnym sposobem na to jest skonfigurowanie wartości buforowanych jako specjalnych definicji przypadków dla funkcji. Wyszukiwanie jest dość szybkie dzięki hashowaniu.

Funkcja:

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

Które punkty są używane podczas konturówki?

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

Ustaw wersję f z wstępnie obliczonymi wartościami dla 10000 ocen:

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

W powyższym przykładzie użyłbyś czegoś w rodzaju precomputedf[x, y] = z zamiast precomputed[x, y] = f[x, y], gdzie z jest twoją wstępnie obliczoną wartością, którą zostały zapisane w zewnętrznym pliku.

Oto przypadek "else", który po prostu ocenia f:

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

Porównanie czasów:

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}

Mała różnica w czasie, ponieważ w tym przykładzie f nie jest funkcją kosztowną.

Osobna uwaga dla konkretnej aplikacji: być może mógłbyś użyć ListContourPlot . Następnie możesz wybrać dokładnie, które punkty są oceniane.

 7
Author: Andrew Moylan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-13 08:32:04