1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
|
-----------------------------------------------------------------------------
-- Copyright 2021 Mahrud Sayrafi
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along
-- with this program. If not, see <https://www.gnu.org/licenses/>.
-----------------------------------------------------------------------------
-*
A Computation object is a container for the partial results of a computation.
Given that multiple simultaneous computations can be cached at the same time
(e.g. saturation with respect to different ideals), a Context object is
necessary to distinguish computations.
A computation cached in object X is stored in the CacheTable X.cache as:
Context{ ... } => Computation{ Result, ... }
*-
needs "classes.m2"
needs "methods.m2"
ComputationDebugLevel := new MutableHashTable from {}
ComputationCacheStats := new MutableHashTable from {}
-----------------------------------------------------------------------------
-- Local utilities
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Computation type declarations and basic constructors
-----------------------------------------------------------------------------
-- context for computation objects
Context = new SelfInitializingType of BasicList
Context.synonym = "computation context"
-- TODO: support options?
new Context from HashTable := (C, T) -> new C from {}
-- container for computation results
Computation = new Type of MutableHashTable
Computation.synonym = "computation"
new Computation from HashTable := (C, T) -> new C from { Result => null }
-----------------------------------------------------------------------------
-- fetchComputation
-----------------------------------------------------------------------------
-- TODO: not very happy with the inputs ... is there a better way?
-- if there is a compatible computation stored in T.cache,
-- returns the computation container, otherwise creates the entry:
-- Context => Computation
fetchComputation = method(Options => true)
fetchComputation(Type, HashTable, Context) := Computation => true >> opts -> (C, T, context) -> fetchComputation(C, T, T, context)
fetchComputation(Type, HashTable, Sequence, Context) :=
fetchComputation(Type, HashTable, HashTable, Context) := Computation => true >> opts -> (C, T, X, context) -> (
-- TODO: look for other compatible contexts as well
-- TODO: use https://github.com/Macaulay2/M2/issues/1596 when it is implemented
if T.cache#?context then T.cache#context
else T.cache#context = new C from X)
-----------------------------------------------------------------------------
-- isComputationDone
-----------------------------------------------------------------------------
-- determines whether further computation is necessary
isComputationDone = method(TypicalValue => Boolean, Options => true)
-- the default method only checks whether Result is non-null
isComputationDone Computation := Boolean => true >> opts -> container -> container.Result =!= null
-- TODO: use isReady?
-----------------------------------------------------------------------------
-- updateComputation
-----------------------------------------------------------------------------
-- update the computation with the given result
updateComputation = method(Options => true)
updateComputation(Computation, Thing) := true >> opts -> (container, result) -> container.Result = result
updateComputation(Computation, Nothing) := true >> opts -> (container, result) -> null
-----------------------------------------------------------------------------
-- adjustComputation
-----------------------------------------------------------------------------
-- adjust and return the cached result as appropriate based on opts
adjustComputation = method(Options => true)
adjustComputation Computation := true >> opts -> container -> container.Result
-----------------------------------------------------------------------------
-- cacheComputation
-----------------------------------------------------------------------------
-- perform the computation, or return the cached result
cacheComputation = method(TypicalValue => CacheFunction, Options => true)
-- the default method only sets the Result in the container
-- Note: this function takes advantage of FunctionClosures by modifying the container
cacheComputation Computation := CacheFunction => true >> opts -> container -> new CacheFunction from (
algorithm -> (
if isComputationDone(opts, container) then ( cacheHit container;
adjustComputation(opts, container))
else updateComputation(opts, container, algorithm(opts, container))))
-----------------------------------------------------------------------------
-- Introspective tools for Computations
-----------------------------------------------------------------------------
-- toggle printing debug information or collecting data about cache hits
debug Computation := C -> ComputationDebugLevel#(hash C) = ComputationDebugLevel#(hash C) ^^ 1
-- print the status of the computation, or cache hit statistics
status Computation := o -> C -> ComputationCacheStats#(hash C)
--
cacheHit = method()
cacheHit Computation := ZZ => C -> if debugLevel > 0 then (
if not ComputationDebugLevel#?(T := hash C)
then ComputationDebugLevel#T = ComputationCacheStats#T = 0;
if ComputationDebugLevel#T & 1 > 0 then printerr("Cache hit on a ", synonym class C, "!");
ComputationCacheStats#T = ComputationCacheStats#T + 1)
|