Undocumented Matlab
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT

Afterthoughts on implicit expansion

November 30, 2016 12 Comments

Matlab release R2016b introduced implicit arithmetic expansion, which is a great and long-awaited natural expansion of Matlab’s arithmetic syntax (if you are still unaware of this or what it means, now would be a good time to read about it). This is a well-documented new feature. The reason for today’s post is that this new feature contains an undocumented aspect that should very well have been documented and even highlighted.
The undocumented aspect that I’m referring to is the fact that code that until R2016a produced an error, in R2016b produces a valid result:

% R2016a
>> [1:5] + [1:3]'
Error using  +
Matrix dimensions must agree.
 
% R2016b
>> [1:5] + [1:3]'
ans =
     2     3     4     5     6
     3     4     5     6     7
     4     5     6     7     8

This incompatibility is indeed documented, but not where it matters most (read on).
I first discovered this feature by chance when trying to track down a very strange phenomenon with client code that produced different numeric results on R2015b and earlier, compared to R2016a Pre-release. After some debugging the problem was traced to a code snippet in the client’s code that looked something like this (simplified):

% Ensure compatible input data
try
    dataA + dataB;  % this will (?) error if dataA, dataB are incompatible
catch
    dataB = dataB';
end

% Ensure compatible input data try dataA + dataB; % this will (?) error if dataA, dataB are incompatible catch dataB = dataB'; end


The code snippet relied on the fact that incompatible data (row vs. col) would error when combined, as it did up to R2015b. But in R2016a Pre-release it just gave a valid numeric matrix, which caused numerically incorrect results downstream in the code. The program never crashed, so everything appeared to be in order, it just gave different numeric results. I looked at the release notes and none of the mentioned release incompatibilities appeared relevant. It took me quite some time, using side-by-side step-by-step debugging on two separate instances of Matlab (R2015b and R2016aPR) to trace the problem to this new feature.
This implicit expansion feature was removed from the official R2016a release for performance reasons. This was apparently fixed in time for R2016b’s release.
I’m totally in favor of this great new feature, don’t get me wrong. I’ve been an ardent user of bsxfun for many years and (unlike many) have even grown fond of it, but I still find the new feature to be better. I use it wherever there is no significant performance penalty, a need to support older Matlab releases, or a possibility of incorrect results due to dimensional mismatch.

So what’s my point?

What I am concerned about is that I have not seen the new feature highlighted as a potential backward compatibility issue in the documentation or the release notes. Issues of far lesser importance are clearly marked for their backward incompatibility in the release notes, but not this important major change. A simple marking of the new feature with the warning icon () and in the “Functionality being removed or changed” section would have saved my client and me a lot of time and frustration.
MathWorks are definitely aware of the potential problems that the new feature might cause in rare use cases such as this. As Steve Eddins recently noted, there were plenty of internal discussions about this very thing. MathWorks were careful to ensure that the feature’s benefits far outweigh its risks (and I concur). But this also highlights the fact that MathWorks were fully aware that in some rare cases it might indeed break existing code. For those cases, I believe that they should have clearly marked the incompatibility implications in the release notes and elsewhere.
I have several clients who scour Matlab’s release notes before each release, trying to determine the operational risk of a Matlab upgrade. Having a program that returns different results in R2016b compared to R2016a, without being aware of this risk, is simply unacceptable to them, and leaves users with a disinclination to upgrade Matlab, to MathWorks’ detriment.
MathWorks in general are taking a very serious methodical approach to compatibility issues, and are clearly investing a lot of energy in this (a recent example). It’s too bad that sometimes this chain is broken. I find it a pity, and think that this can still be corrected in the online doc pages. If and when this is fixed, I’ll be happy to post an addendum here.
In my humble opinion from the backbenches, increasing the transparency on compatibility issues and open bugs will increase user confidence and result in greater adoption and upgrades of Matlab. Just my 2 cents…

Addendum December 27, 2016:

Today MathWorks added the following compatibility warning to the release notes (R2016b, Mathematics section, first item) – thanks for listening MathWorks 🙂

MathWorks compatibility warning

Related posts:

  1. Additional license data – Additional meta-data about installed toolboxes can be retrieved in Matlab. ...
  2. Setting class property types – take 2 – R2016a saw the addition of class property types. However, a better alternative has existed for many years. ...
  3. Viewing saved profiling results – We can easily use saved profiling results to analyze, view and compare profiling results of multiple runs. ...
  4. Customizing axes part 5 – origin crossover and labels – The axes rulers (axles) can be made to cross-over at any x,y location within the chart. ...
  5. Preallocation performance – Preallocation is a standard Matlab speedup technique. Still, it has several undocumented aspects. ...
  6. Working with non-standard DPI displays – Matlab uses logical (scaled) pixel positions. With high-DPI displays, this might cause problems, but it is easy to retrieve the physical (unscaled) pixel values. ...
Performance Pure Matlab Undocumented feature
Print Print
« Previous
Next »
12 Responses
  1. Steve Eddins November 30, 2016 at 23:55 Reply

    I am looking into the possibility of adding a “compatibility consideration” to the release note.

    Implicit expansion was pulled from the final release of R2016a for performance reasons. There were no other factors involved in the decision.

    • Yair Altman November 30, 2016 at 23:59 Reply

      @Steve – thanks on both accounts. I amended the text accordingly.

  2. TheBlackCat December 1, 2016 at 02:30 Reply

    Your client is lucky the problem got caught. A lot of such issues will likely go unnoticed. For example, basically anything with a mathematical operation followed by the use of linear indexing (such as in a for loop) will seem to work fine but will give mathematically incorrect results. I know you ideally shouldn’t be doing this, and I assume MATLAB internal code doesn’t do it very much, but I see code like that all the time from people with less of a programming background, and sometimes your algorithm requires it.

    I had always assumed the reason MATLAB hadn’t implemented this feature over the last 15 years or so was that it was too big of a backwards-compatibility break.

  3. David B December 1, 2016 at 22:20 Reply

    If nothing else I hope that the exercise of debugging this proved as a rude wake-up call for the author of that original and terrible code in the try/catch. They should be ashamed.

    • Yair Altman December 1, 2016 at 22:36 Reply

      @David – while I fully agree with you that it’s not good coding style/practice, I’ve seen much worse client codes. Most Matlab users don’t have a degree in computer science, and sadly enough even CS grads often exhibit deplorable coding. In fact, my personal experience has been that only a minority of Matlab users have high-quality code. Most Matlab users use Matlab as an engineering tool and not as an end to itself: as long as something works, they don’t mind if it’s nice-looking – they just move on to solving the next problem. In this sense, the snippet above is beautiful in its simplicity, and to hell with the CS purists…

    • TheBlackCat December 2, 2016 at 00:28 Reply

      What would your alternative be? The equivalent one I can think of would be:

      if numel(dataA)~=1 && numel(dataB)~=1 && any(size(dataA)~=size(dataB))
          dataB = dataB';
      end

      if numel(dataA)~=1 && numel(dataB)~=1 && any(size(dataA)~=size(dataB)) dataB = dataB'; end

      A more strict test would be:

      if numel(dataA)~=1 && numel(dataB)~=1 && ndim(dataA)==2 && ndim(dataB)==2 && any(size(dataA)~=size(dataB)) && all(fliplr(size(dataA))==size(dataB))
          dataB = dataB';
      end

      if numel(dataA)~=1 && numel(dataB)~=1 && ndim(dataA)==2 && ndim(dataB)==2 && any(size(dataA)~=size(dataB)) && all(fliplr(size(dataA))==size(dataB)) dataB = dataB'; end

      So yes, probably from a code correctness standpoint you are probably right. However, from a readability and maintainability standpoint their solution is pretty elegant. Will it be slower? Yes, but in many cases not enough to make a difference. Does it have corner cases that it doesn’t handle? Yes, although those may not be relevant or may get caught later. But even without a comment I can tell in an instant what their code is doing, while it would take me some time to figure out what either of the two examples I posted did.

      If you have another approach that is as simple and easy-to-read as the above case then of course I will retract that. But otherwise, the best algorithm from a CS standpoint isn’t necessarily the best approach once you have to start involving humans and want to be able to figure out what your code is doing 3 years down the road.

    • David B December 3, 2016 at 12:33 Reply

      @TheBlackCat From the limited information we have available we are assuming the data is a vector. If that is the case then I think something like this code snippet would work nicely and is perfectly human readable.

      if isrow(a) && ~isrow(b) || iscolumn(a) && ~iscolumn(b)
          b = b';
      end

      if isrow(a) && ~isrow(b) || iscolumn(a) && ~iscolumn(b) b = b'; end

    • Marshall December 12, 2016 at 21:04 Reply

      @DavidB if we know we are receiving vectors, and they may be oriented differently, the following would be more elegant compared to any requiring any logical branches:

      dataA(:)+dataB(:);

      dataA(:)+dataB(:);

  4. Guillaume December 2, 2016 at 18:05 Reply

    Well, presumably, the code is operating on vectors, so the alternative could be

       dataA(:) + dataB(:)

    dataA(:) + dataB(:)

    But really, the proper alternative would have been to find out why the data does not come with the expected shape rather than take a gamble and flip it. I’m with David B, the code is a strong indication that something is very wrong in the algorithm somewhere and that one day, given some particular input, it’s going to break in even more unpleasant ways.

    • TheBlackCat December 2, 2016 at 19:19 Reply

      It is hard to say without seeing more code. It may very well be that the data can only come in a few formats, so transposing it is the correct thing to do.

    • Yair Altman December 3, 2016 at 19:37 Reply

      @DavidB + @Guillaume + @TheBlackCat – if I remember correctly, my client wanted the code to continue processing only when the 2 inputs were both vectors, although possibly of different dimensionality. So, [1,2,3] should be combinable with [3;4;5] but not with [3;4] (which would error out downstream). In such cases the try-catch block gives the expected results, but a(:)+b(:) would not have.

      As I said in the post, the code snippet is just a simplified version and the actual coding details don’t really matter. The important thing in my opinion is that for this specific use-case, a functional change in Matlab R2016b caused fully-legitimate code to return different results, and since the functional change was not documented as having a compatibility aspect, this caused an operational problem that was unacceptable. In this respect, it really does not matter whether the client’s code was due to bad coding or to explicit design.

  5. Sue Ann Koay August 4, 2017 at 04:37 Reply

    Having only recently installed Matlab 2017a, I almost immediately “discovered” this feature and am compelled to say that implicit expansion gives me the creeps. As an outside example: why do some languages like C++ impose type safety? It’s not because programmers can’t write their own type checking code. For a 10-line function I can easily write 50 lines of code to perform every possible check I can think of to ensure that the inputs are of the correct type and sizes and ranges and whatnot. Will this make me a better coder? Maybe. Will chances be high that I’ll do it for every function? If I existed solely for such a purpose, maybe. Will this help others read and modify my code down the line? Well…

    As a person from a joint CS/science background, I’m certainly not advocating sloppy coding practices. I think implicit expansion (and yes also lack of type safety) actually goes towards encouraging sloppy coding practices. Now instead of a self-documenting piece of code where one uses bsxfun(), if I read the line a .* b somewhere I have to wonder if it is the outer product of vectors or an element-wise product or anything goes. The best designed systems limit the amount of mistakes that humans can make, and allowing the syntax itself to be sloppy doesn’t seem very encouraging.

Leave a Reply
HTML tags such as <b> or <i> are accepted.
Wrap code fragments inside <pre lang="matlab"> tags, like this:
<pre lang="matlab">
a = magic(3);
disp(sum(a))
</pre>
I reserve the right to edit/delete comments (read the site policies).
Not all comments will be answered. You can always email me (altmany at gmail) for private consulting.

Click here to cancel reply.

Useful links
  •  Email Yair Altman
  •  Subscribe to new posts (feed)
  •  Subscribe to new posts (reader)
  •  Subscribe to comments (feed)
 
Accelerating MATLAB Performance book
Recent Posts

Speeding-up builtin Matlab functions – part 3

Improving graphics interactivity

Interesting Matlab puzzle – analysis

Interesting Matlab puzzle

Undocumented plot marker types

Matlab toolstrip – part 9 (popup figures)

Matlab toolstrip – part 8 (galleries)

Matlab toolstrip – part 7 (selection controls)

Matlab toolstrip – part 6 (complex controls)

Matlab toolstrip – part 5 (icons)

Matlab toolstrip – part 4 (control customization)

Reverting axes controls in figure toolbar

Matlab toolstrip – part 3 (basic customization)

Matlab toolstrip – part 2 (ToolGroup App)

Matlab toolstrip – part 1

Categories
  • Desktop (45)
  • Figure window (59)
  • Guest bloggers (65)
  • GUI (165)
  • Handle graphics (84)
  • Hidden property (42)
  • Icons (15)
  • Java (174)
  • Listeners (22)
  • Memory (16)
  • Mex (13)
  • Presumed future risk (394)
    • High risk of breaking in future versions (100)
    • Low risk of breaking in future versions (160)
    • Medium risk of breaking in future versions (136)
  • Public presentation (6)
  • Semi-documented feature (10)
  • Semi-documented function (35)
  • Stock Matlab function (140)
  • Toolbox (10)
  • UI controls (52)
  • Uncategorized (13)
  • Undocumented feature (217)
  • Undocumented function (37)
Tags
AppDesigner (9) Callbacks (31) Compiler (10) Desktop (38) Donn Shull (10) Editor (8) Figure (19) FindJObj (27) GUI (141) GUIDE (8) Handle graphics (78) HG2 (34) Hidden property (51) HTML (26) Icons (9) Internal component (39) Java (178) JavaFrame (20) JIDE (19) JMI (8) Listener (17) Malcolm Lidierth (8) MCOS (11) Memory (13) Menubar (9) Mex (14) Optical illusion (11) Performance (78) Profiler (9) Pure Matlab (187) schema (7) schema.class (8) schema.prop (18) Semi-documented feature (6) Semi-documented function (33) Toolbar (14) Toolstrip (13) uicontrol (37) uifigure (8) UIInspect (12) uitable (6) uitools (20) Undocumented feature (187) Undocumented function (37) Undocumented property (20)
Recent Comments
Contact us
Captcha image for Custom Contact Forms plugin. You must type the numbers shown in the image
Undocumented Matlab © 2009 - Yair Altman
This website and Octahedron Ltd. are not affiliated with The MathWorks Inc.; MATLAB® is a registered trademark of The MathWorks Inc.
Scroll to top