{"id":2984,"date":"2018-07-05T08:58:33","date_gmt":"2018-07-05T13:58:33","guid":{"rendered":"https:\/\/blogs.mathworks.com\/loren\/?p=2984"},"modified":"2018-07-05T08:58:33","modified_gmt":"2018-07-05T13:58:33","slug":"memoize-functions-in-matlab","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/loren\/2018\/07\/05\/memoize-functions-in-matlab\/","title":{"rendered":"Memoize Functions in MATLAB"},"content":{"rendered":"<p>Very early on in this blog (2006!), I wrote a post on <a href=\"https:\/\/blogs.mathworks.com\/loren\/2006\/02\/08\/use-nested-functions-to-memoize-costly-functions\/\">memoizing functions<\/a>, i.e., caching results so outputs that have already been calculated don't need to be calculated again (code included at the end of this post). Memoization can provide a significant performance boost especially if the function in question is expensive to calculate, and is likely to have inputs repeated. <\/p>\r\n      <p>It wasn't until recently that I realized this functionality (<a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/memoize.html\">memoize<\/a>) was added to MATLAB in R2017a. Needless to say, the shipping function is different than the solution I presented over 10 years ago. And without the limitations that mine had (limited to elementwise functions that had a single input). <\/p>\r\n      <p><h3>Table of Contents<\/h3><\/p>\r\n      <p><a href=\"internal:H_86BA8ECE\">What is Memoization? <\/a><br><a href=\"internal:H_82ECEEEA\">Let's Try It <\/a><br><a href=\"internal:H_8A30C91E\">Is That All? <\/a><br><a href=\"internal:H_B5C71415\">Do You Use Memoization? <\/a><br><a href=\"internal:H_1C75B18A\">From Reference from My 2006 Post<\/a><br><\/p>\r\n      <h3>What is Memoization?<\/h3>\r\n      <p>The idea of memoization is to cache function results from specific inputs so if these same inputs are used again, the function can simply return the values computed earlier, without rerunning the computation. This can be useful if you have a function that is very expensive to compute. <\/p>\r\n      <p>Of course, if you run the memoized function a lot, it will take up increasing amounts of memory as unique inputs get added to the list, unless we do something to limit the cache size. That's what MATLAB does now with the function <inline style=\"font-family: monospace, monospace; font-size: inherit;\">memoize<\/inline>. <\/p>\r\n      <h3>Let's Try It<\/h3>\r\n      <p>As in my earlier post, let's try something simple, the function <inline style=\"font-family: monospace, monospace; font-size: inherit;\">sin<\/inline>. <\/p><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\">fmem = memoize(@sin)\r\n<\/pre><pre class=\"output\" style=\"font-family:monospace;border:none;background-color:white;color:rgba(64, 64, 64, 1);\">fmem = \r\n  MemoizedFunction with properties:\r\n\r\n     Function: @sin\r\n      Enabled: 1\r\n    CacheSize: 10\r\n<\/pre><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\">y = fmem(pi.\/(1:5)')\r\n<\/pre><pre class=\"output\" style=\"font-family:monospace;border:none;background-color:white;color:rgba(64, 64, 64, 1);\">y = <em>5\u00d71<\/em>\r\n\r\n   1.2246e-16\r\n            1\r\n      0.86603br\r\n      0.70711\r\n      0.58779\r\n\r\n<\/pre><p>So, we still get the answers we expect.<\/p>\r\n      <p>Now let's compute some more values, some already in the cache and others not.<\/p><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\">ymore = fmem(pi.\/(1:10)')\r\n<\/pre><pre class=\"output\" style=\"font-family:monospace;border:none;background-color:white;color:rgba(64, 64, 64, 1);\">ymore = <em>10\u00d71<\/em>\r\n\r\n   1.2246e-16\r\n            1\r\n      0.86603\r\n      0.70711\r\n      0.58779\r\n          0.5\r\n      0.43388\r\n      0.38268\r\n      0.34202\r\n      0.30902\r\n\r\n<\/pre><p>Again, no surprises on the out. The values are the ones we expect. I am not doing enough computation here for you to see the benefit of reduced time from caching, however. <\/p>\r\n      <h3>Is That All?<\/h3>\r\n      <p>Of course not! There are a bunch of choices you can use to control how much information gets cached, etc. Here's some links for more information. <\/p>\r\n      <ul type=\"square\" style=\"list-style-type:square\">\r\n         <li><a href=\"https:\/\/www.mathworks.com\/help\/matlab\/performance-and-memory.html\">general performance and memory information<\/a><\/li>\r\n         <li><a title=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/memoizedfunction.html (link no longer works)\">memoized function<\/a><\/li>\r\n         <li><a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/clearallmemoizedcaches.html\">clear memoized caches<\/a><\/li>\r\n      <\/ul>\r\n      <p>Now let's see how this works. First, what is <inline style=\"font-family: monospace, monospace; font-size: inherit;\">fmem<\/inline>? <\/p><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\">fmem\r\n<\/pre><pre class=\"output\" style=\"font-family:monospace;border:none;background-color:white;color:rgba(64, 64, 64, 1);\">fmem = \r\n  MemoizedFunction with properties:\r\n\r\n     Function: @sin\r\n      Enabled: 1\r\n    CacheSize: 10\r\n<\/pre><p>We see what function is being memoized, that caching is enabled, and how many distinct inputs are being cached. Since the inputs are consider collectively and I have called <inline style=\"font-family: monospace, monospace; font-size: inherit;\">fmem<\/inline> 3 time so far with 3 different inputs (never mind that some values are shared), I should have 3 \"elements\" in the cache. <\/p>\r\n      <p>Let's see what's been cached.<\/p><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\">s = stats(fmem)\r\n<\/pre><pre class=\"output\" style=\"font-family:monospace;border:none;background-color:white;color:rgba(64, 64, 64, 1);\">s = <em>struct with fields:<\/em>\r\n                    Cache: [1\u00d71 struct]\r\n       MostHitCachedInput: [1\u00d71 struct]\r\n      CacheHitRatePercent: 77.778\r\n    CacheOccupancyPercent: 40\r\n<\/pre><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\">s.Cache\r\n<\/pre><pre class=\"output\" style=\"font-family:monospace;border:none;background-color:white;color:rgba(64, 64, 64, 1);\">ans = <em>struct with fields:<\/em>\r\n         Inputs: {{1\u00d71 cell}  {1\u00d71 cell}  {1\u00d71 cell}  {1\u00d71 cell}}\r\n        Nargout: [1 1 1 1]\r\n        Outputs: {{1\u00d71 cell}  {1\u00d71 cell}  {1\u00d71 cell}  {1\u00d71 cell}}\r\n       HitCount: [4 9 1 0]\r\n      TotalHits: 14\r\n    TotalMisses: 4\r\n<\/pre><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\"><\/pre><p>And now let's use another input.<\/p><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\">ysomemore = fmem(pi.\/-(1:12)')\r\n<\/pre><pre class=\"output\" style=\"font-family:monospace;border:none;background-color:white;color:rgba(64, 64, 64, 1);\">ysomemore = <em>12\u00d71<\/em>\r\n\r\n  -1.2246e-16\r\n           -1\r\n     -0.86603\r\n     -0.70711\r\n     -0.58779\r\n         -0.5\r\n     -0.43388\r\n     -0.38268\r\n     -0.34202\r\n     -0.30902\r\n      \u22ee\r\n\r\n<\/pre><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\">snew = stats(fmem)\r\n<\/pre><pre class=\"output\" style=\"font-family:monospace;border:none;background-color:white;color:rgba(64, 64, 64, 1);\">snew = <em>struct with fields:<\/em>\r\n                    Cache: [1\u00d71 struct]\r\n       MostHitCachedInput: [1\u00d71 struct]\r\n      CacheHitRatePercent: 78.947\r\n    CacheOccupancyPercent: 40\r\n<\/pre><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\">snew.Cache\r\n<\/pre><pre class=\"output\" style=\"font-family:monospace;border:none;background-color:white;color:rgba(64, 64, 64, 1);\">ans = <em>struct with fields:<\/em>\r\n         Inputs: {{1\u00d71 cell}  {1\u00d71 cell}  {1\u00d71 cell}  {1\u00d71 cell}}\r\n        Nargout: [1 1 1 1]\r\n        Outputs: {{1\u00d71 cell}  {1\u00d71 cell}  {1\u00d71 cell}  {1\u00d71 cell}}\r\n       HitCount: [4 9 1 1]\r\n      TotalHits: 15\r\n    TotalMisses: 4\r\n<\/pre><p>Now see what happens to the cached if we repeat an input.<\/p><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\">yrepeat = fmem(pi.\/(1:10)')\r\n<\/pre><pre class=\"output\" style=\"font-family:monospace;border:none;background-color:white;color:rgba(64, 64, 64, 1);\">yrepeat = <em>10\u00d71<\/em>\r\n\r\n   1.2246e-16\r\n            1\r\n      0.86603\r\n      0.70711\r\n      0.58779\r\n          0.5\r\n      0.43388\r\n      0.38268\r\n      0.34202\r\n      0.30902\r\n\r\n<\/pre><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\">srepeat = stats(fmem)\r\n<\/pre><pre class=\"output\" style=\"font-family:monospace;border:none;background-color:white;color:rgba(64, 64, 64, 1);\">srepeat = <em>struct with fields:<\/em>\r\n                    Cache: [1\u00d71 struct]\r\n       MostHitCachedInput: [1\u00d71 struct]\r\n      CacheHitRatePercent: 80\r\n    CacheOccupancyPercent: 40\r\n<\/pre><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\">srepeat.Cache\r\n<\/pre><pre class=\"output\" style=\"font-family:monospace;border:none;background-color:white;color:rgba(64, 64, 64, 1);\">ans = <em>struct with fields:<\/em>\r\n         Inputs: {{1\u00d71 cell}  {1\u00d71 cell}  {1\u00d71 cell}  {1\u00d71 cell}}\r\n        Nargout: [1 1 1 1]\r\n        Outputs: {{1\u00d71 cell}  {1\u00d71 cell}  {1\u00d71 cell}  {1\u00d71 cell}}\r\n       HitCount: [4 10 1 1]\r\n      TotalHits: 16\r\n    TotalMisses: 4\r\n<\/pre><p>I can also clear the cache for a particular function or clear the caches for all memoized functions: <\/p>\r\n      <ul type=\"square\" style=\"list-style-type:square\">\r\n         <li><a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/clearallmemoizedcaches.html\">clearAllMemoizedCaches<\/a><\/li>\r\n         <li><a title=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/clearcache.html (link no longer works)\">clearCache<\/a><\/li>\r\n      <\/ul>\r\n      <h3>Do You Use Memoization?<\/h3>\r\n      <p>Do you ever use memoization in your code, with or without the MATLAB functions? Let us know how you do this <a href=\"https:\/\/blogs.mathworks.com\/loren\/?p=2984#respond\">here<\/a>. <\/p>\r\n      <h3>From Reference from My 2006 Post<\/h3><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\"><span style=\"color:rgb(0, 0, 255);\">function<\/span> f = memoize2(F)\r\n<span style=\"color:rgb(34, 139, 34);\">% one-arg F, inputs testable with ==<\/span>\r\n<span style=\"color:rgb(34, 139, 34);\">% allow nonscalar input.<\/span>\r\nx = [];\r\ny = [];\r\nf = @inner;\r\n    <span style=\"color:rgb(0, 0, 255);\">function<\/span> out = inner(in)\r\n        out = zeros(size(in));  <span style=\"color:rgb(34, 139, 34);\">% preallocate output<\/span>\r\n        [tf,loc] = ismember(in,x);  <span style=\"color:rgb(34, 139, 34);\">% find which in's already computed in x<\/span>\r\n        ft = ~tf;  <span style=\"color:rgb(34, 139, 34);\">% ones to be computed<\/span>\r\n        out(ft) = F(in(ft));  <span style=\"color:rgb(34, 139, 34);\">% get output values for ones not already in<\/span>\r\n        <span style=\"color:rgb(34, 139, 34);\">% place new values in storage<\/span>\r\n        x = [x in(ft(:).')];\r\n        y = [y reshape(out(ft),1,[])];\r\n        out(tf) = y(loc(tf));  <span style=\"color:rgb(34, 139, 34);\">% fill in the rest of the output values<\/span>\r\n    <span style=\"color:rgb(0, 0, 255);\">end<\/span>\r\n<span style=\"color:rgb(0, 0, 255);\">end<\/span>\r\n<\/pre><p>and<\/p><pre class=\"matlab-code\" id=\"matlabcode\" style=\"background-color: #F7F7F7;font-family: monospace;font-weight:normal;border-style: solid; border-width: 1px ;border-color:#E9E9E9;padding-top:5px;padding-bottom:5px;line-height:150%;\"><span style=\"color:rgb(0, 0, 255);\">function<\/span> f = memoize1(F)\r\n<span style=\"color:rgb(34, 139, 34);\">% one-arg F, inputs testable with ==<\/span>\r\nx = [];\r\ny = [];\r\nf = @inner;\r\n    <span style=\"color:rgb(0, 0, 255);\">function<\/span> out = inner(in)\r\n        ind = find(in == x);\r\n        <span style=\"color:rgb(0, 0, 255);\">if<\/span> isempty(ind)\r\n            out = F(in);\r\n            x(end+1) = in;\r\n            y(end+1) = out;\r\n        <span style=\"color:rgb(0, 0, 255);\">else<\/span>\r\n            out = y(ind);\r\n        <span style=\"color:rgb(0, 0, 255);\">end<\/span>\r\n    <span style=\"color:rgb(0, 0, 255);\">end<\/span>\r\n<span style=\"color:rgb(0, 0, 255);\">end<\/span>\r\n<\/pre>","protected":false},"excerpt":{"rendered":"<p>Very early on in this blog (2006!), I wrote a post on memoizing functions, i.e., caching results so outputs that have already been calculated don't need to be calculated again (code included at the... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/loren\/2018\/07\/05\/memoize-functions-in-matlab\/\">read more >><\/a><\/p>","protected":false},"author":39,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[3,6],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/2984"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/users\/39"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/comments?post=2984"}],"version-history":[{"count":4,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/2984\/revisions"}],"predecessor-version":[{"id":2994,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/2984\/revisions\/2994"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/media?parent=2984"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/categories?post=2984"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/tags?post=2984"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}