{"id":4776,"date":"2013-01-30T16:16:49","date_gmt":"2013-01-30T15:16:49","guid":{"rendered":"http:\/\/www.walkingrandomly.com\/?p=4776"},"modified":"2013-01-30T16:51:51","modified_gmt":"2013-01-30T15:51:51","slug":"explorations-in-overriding-matlab-functions","status":"publish","type":"post","link":"https:\/\/walkingrandomly.com\/?p=4776","title":{"rendered":"Explorations in overriding MATLAB functions"},"content":{"rendered":"<p>In a recent blog post, I demonstrated how to use the MATLAB 2012a Symbolic Toolbox to perform <a href=\"https:\/\/www.walkingrandomly.com\/?p=4462\">Variable precision QR decomposition in MATLAB<\/a>.\u00a0 The result was a function called <a href=\"https:\/\/www.walkingrandomly.com\/images\/matlab\/vpa_qr.m\"><strong>vpa_qr<\/strong><\/a> which did the necessary work.<\/p>\n<pre>&gt;&gt; a=vpa([2 1 3;-1 0 7; 0 -1 -1]);\r\n&gt;&gt; [Q R]=vpa_qr(a);<\/pre>\n<p>I&#8217;ve suppressed the output because it\u2019s so large but it definitely works. When I triumphantly presented this function to the user who requested it he was almost completely happy.\u00a0 What he really wanted, however, was for this to work:<\/p>\n<pre>&gt;&gt; a=vpa([2 1 3;-1 0 7; 0 -1 -1]);\r\n&gt;&gt; [Q R]=qr(a);<\/pre>\n<p>In other words he wants to override the <strong>qr<\/strong> function such that it accepts variable precision types. MATLAB 2012a does not allow this:<\/p>\n<pre>&gt;&gt; a=vpa([2 1 3;-1 0 7; 0 -1 -1]);\r\n&gt;&gt; [Q R]=qr(a)\r\nUndefined function 'qr' for input arguments of type 'sym'.<\/pre>\n<p>I put something together that did the job for him but felt that it was unsatisfactory.\u00a0 So, I sent my code to The MathWorks and asked them if what I had done was sensible and if there were any better options.\u00a0 A MathWorks engineer called <strong>Hugo Carr<\/strong> sent me such a great, detailed reply that I asked if I could write it up as a blog post.\u00a0 Here is the result:<\/p>\n<p><strong>Approach 1:<\/strong>\u00a0 Define a new qr function, with a different name (such as vpa_qr).\u00a0 This is probably the safest and simplest option and was the method <a href=\"https:\/\/www.walkingrandomly.com\/?p=4462\">I used in the original blog post<\/a>.<\/p>\n<ul>\n<li>Pros: The new function will not interfere with your MATLAB namespace<\/li>\n<li>Cons: MATLAB will only use this function if you explicitly define that you wish to use it in a given function.\u00a0 You would have to find all prior references to the qr algorithm and make a decision about which to use.<\/li>\n<\/ul>\n<p><strong>Approach 2:<\/strong> Define a new qr function and use the \u2018isa\u2019 function to catch instances of \u2018sym\u2019. This is the approach I took in the code I sent to The MathWorks.<\/p>\n<pre>function varargout = qr( varargin )\r\n\r\nif nargin == 1 &amp;&amp; isa( varargin{1}, 'sym' )\r\n    [varargout{1:nargout}] = vpa_qr( varargin{:} );\r\nelse\r\n    [varargout{1:nargout}] = builtin( 'qr', varargin{:} );\r\nend<\/pre>\n<ul>\n<li>Pros: qr will always select the correct code when executed on sym objects<\/li>\n<li>Cons: This code only works for shadowing built-ins and will produce a warning reminding you of this fact. If you wish to extend this pattern for other class types, you\u2019ll require a switch statement (or nested if-then-else block), which could lead to a complex comparison each time qr is invoked (and subsequent performance hit). Note that switch statements in conjunction with calls to \u2018isa\u2019 are usually indicators that an object oriented approach is a better way forward.<\/li>\n<\/ul>\n<p><strong>Approach 3:<\/strong> The MathWorks do not\u00a0recommend that you modify your MATLAB install. However for completeness, it is possible to add a new \u2018method\u2019 to the sym class by dropping your function into the sym class folder.\u00a0 For MATLAB 2012a on Windows, this folder is at<\/p>\n<p><strong>C:\\Program Files\\MATLAB\\R2012a\\toolbox\\symbolic\\symbolic\\@sym<\/strong><\/p>\n<p>For the sake of illustration, here is a simplified implementation. Call it qr.m<\/p>\n<pre>function result = qr( this )\r\n  result = feval(symengine,'linalg::factorQR', this);\r\nend<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone\" src=\"https:\/\/www.walkingrandomly.com\/images\/matlab\/qr\/qr_overload1.png\" alt=\"\" width=\"752\" height=\"486\" \/><\/p>\n<p><strong>Pros:<\/strong> Functions saved to a class folder take precedence over built in functionality, which means that MATLAB will always use your qr method for sym objects.<\/p>\n<p><strong>Cons:<\/strong> If you share code which uses this functionality, it won\u2019t run on someone\u2019s computer unless they update their sym class folder with your qr code. Additionally, if a new method is added to a class it may shadow the behaviour of other MATLAB functionality and lead to unexpected behaviour in Symbolic Toolbox.<\/p>\n<p><strong>Approach 4:<\/strong> For more of an object-oriented approach it is possible to sub-class the sym class, and add a new qr method.<\/p>\n<pre>classdef mySym &lt; sym\r\n\r\n    methods\r\n        function this = mySym(arg)\r\n            this = this@sym(arg);\r\n        end\r\n\r\n        function result = qr( this )\r\n            result = feval(symengine,'linalg::factorQR', this);\r\n        end\r\n    end\r\n\r\nend<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone\" src=\"https:\/\/www.walkingrandomly.com\/images\/matlab\/qr\/qr_overload2.png\" alt=\"\" width=\"752\" height=\"545\" \/><\/p>\n<p><strong>Pros:<\/strong> Your change can be shipped with your code and it will work on a client\u2019s computer without having to change the sym class.<\/p>\n<p><strong>Cons:<\/strong> When calling superclass methods on your mySym objects (such as sin(mySym1)), the result will be returned as the superclass unless you explicitly redefine the method to return the subclass.<\/p>\n<p>N.B. There is a lot of literature which discusses why inheritance (subclassing) to augment a class\u2019s behaviour is a bad idea. For example, if Symbolic Toolbox developers decide to add their own qr method to the sym API, overriding that function with your own code could break the system. You would need to update your subclass every time the superclass is updated. This violates encapsulation, as the subclass implementation depends on the superclass. You can avoid problems like these by using composition instead of inheritance.<\/p>\n<p><strong>Approach 5:<\/strong> You can create a new sym class by using composition, but it takes a little longer than the other approaches. Essentially, this involves creating a wrapper which provides the functionality of the original class, as well as any new functions you are interested in.<\/p>\n<pre>classdef mySymComp\r\n\r\n    properties\r\n        SymProp\r\n    end\r\n\r\n    methods\r\n        function this = mySymComp(symInput)\r\n            this.SymProp = symInput;\r\n        end\r\n\r\n        function result = qr( this )\r\n            result = feval(symengine,'linalg::factorQR', this.SymProp);\r\n        end\r\n    end\r\n\r\nend<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone\" src=\"https:\/\/www.walkingrandomly.com\/images\/matlab\/qr\/qr_overload3.png\" alt=\"\" width=\"752\" height=\"545\" \/><br \/>\nNote that in this example we did not add any of the original sym functions to the mySymComp class, however this can be done for as many as you like. For example, I might like to use the sin method from the original sym class, so I can just delegate to the methods of the sym object that I passed in during construction:<\/p>\n<pre>classdef mySymComp\r\n\r\n    properties\r\n        SymProp\r\n    end\r\n\r\n    methods\r\n        function this = mySymComp(symInput)\r\n            this.SymProp = symInput;\r\n        end\r\n\r\n        function result = qr( this )\r\n            result = feval(symengine,'linalg::factorQR', this.SymProp);\r\n        end\r\n\r\n        function G = sin(this)\r\n            G = mySymComp(sin(this.SymProp));\r\n        end\r\n    end\r\n\r\nend<\/pre>\n<p><strong>Pros:<\/strong> The change is totally encapsulated, and cannot be broken save for a significant change to the sym api (for example, the MathWorks adding a qr method to sym would not break your code).<\/p>\n<p><strong>Cons:<\/strong> The wrapper can be time consuming to write, and the resulting object is not a \u2018sym\u2019, meaning that if you pass a mySymComp object \u2018a\u2019 into the following code:<\/p>\n<pre>isa(a, 'sym')<\/pre>\n<p>MATLAB will return \u2018false\u2019 by default.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In a recent blog post, I demonstrated how to use the MATLAB 2012a Symbolic Toolbox to perform Variable precision QR decomposition in MATLAB.\u00a0 The result was a function called vpa_qr which did the necessary work. &gt;&gt; a=vpa([2 1 3;-1 0 7; 0 -1 -1]); &gt;&gt; [Q R]=vpa_qr(a); I&#8217;ve suppressed the output because it\u2019s so large [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[4,11,7],"tags":[],"class_list":["post-4776","post","type-post","status-publish","format-standard","hentry","category-math-software","category-matlab","category-programming"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3swhs-1f2","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts\/4776","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4776"}],"version-history":[{"count":8,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts\/4776\/revisions"}],"predecessor-version":[{"id":4787,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts\/4776\/revisions\/4787"}],"wp:attachment":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4776"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4776"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4776"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}