{"id":6633,"date":"2020-01-06T05:46:15","date_gmt":"2020-01-06T04:46:15","guid":{"rendered":"http:\/\/www.walkingrandomly.com\/?p=6633"},"modified":"2020-01-06T06:11:54","modified_gmt":"2020-01-06T05:11:54","slug":"hypot-a-story-of-a-simple-function","status":"publish","type":"post","link":"https:\/\/walkingrandomly.com\/?p=6633","title":{"rendered":"Hypot &#8211; A story of a  &#8216;simple&#8217; function"},"content":{"rendered":"<p>My stepchildren are pretty good at mathematics for their age and have recently learned about <a href=\"https:\/\/en.wikipedia.org\/wiki\/Pythagorean_theorem\">Pythagora&#8217;s theorem<\/a><\/p>\n<p>$c=\\sqrt{a^2+b^2}$<\/p>\n<p>The fact that they have learned about this so early in their mathematical lives is testament to its importance. Pythagoras is everywhere in computational science and it may well be the case that you&#8217;ll need to compute the hypotenuse to a triangle some day.<\/p>\n<p>Fortunately for you, this important computation is implemented in every computational environment I can think of!<br \/>\nIt&#8217;s almost always called <a href=\"https:\/\/uk.mathworks.com\/help\/matlab\/ref\/hypot.html\">hypot<\/a> so it will be easy to find.<br \/>\nHere it is in action using Python&#8217;s numpy module<\/p>\n<pre>import numpy as np\r\na = 3\r\nb = 4\r\nnp.hypot(3,4)\r\n\r\n5\r\n<\/pre>\n<p>When I&#8217;m out and about giving talks and tutorials about <a href=\"https:\/\/mikecroucher.github.io\/why_rse\/\">Research Software Engineering<\/a>, <a href=\"https:\/\/mikecroucher.github.io\/HPC_for_everyone\/\">High Performance Computing<\/a> and <a href=\"https:\/\/mikecroucher.github.io\/NAG_IYRSC\/\">so on<\/a>, I often get the chance to mention the hypot function and it turns out that fewer people know about this routine than you might expect.<\/p>\n<h3>Trivial Calculation? Do it Yourself!<\/h3>\n<p>Such a trivial calculation, so easy to code up yourself! Here&#8217;s a one-line implementation<\/p>\n<pre>def mike_hypot(a,b):\r\n    return(np.sqrt(a*a+b*b))\r\n<\/pre>\n<p>In use it looks fine<\/p>\n<pre>mike_hypot(3,4)\r\n\r\n5.0\r\n<\/pre>\n<h3>Overflow and Underflow<\/h3>\n<p>I could probably work for quite some time before I found that my implementation was flawed in several places. Here&#8217;s one<\/p>\n<pre>mike_hypot(1e154,1e154)\r\n\r\ninf\r\n<\/pre>\n<p>You would, of course, expect the result to be large but not infinity. Numpy doesn&#8217;t have this problem<\/p>\n<pre>np.hypot(1e154,1e154)\r\n\r\n1.414213562373095e+154\r\n<\/pre>\n<p>My function also doesn&#8217;t do well when things are small.<\/p>\n<pre>a = mike_hypot(1e-200,1e-200)\r\n\r\n0.0\r\n<\/pre>\n<p>but again, the more carefully implemented hypot function in numpy does fine.<\/p>\n<pre>np.hypot(1e-200,1e-200)\r\n\r\n1.414213562373095e-200\r\n<\/pre>\n<h3>Standards Compliance<\/h3>\n<p>Next up &#8212; standards compliance. It turns out that there is a an official standard for how hypot implementations should behave in certain edge cases. The IEEE-754 standard for floating point arithmetic has something to say about how any implementation of hypot handles NaNs (Not a Number) and inf (Infinity).<\/p>\n<p>It states that any implementation of hypot should behave as follows (Here&#8217;s a human readable summary <a href=\"https:\/\/www.agner.org\/optimize\/nan_propagation.pdf\">https:\/\/www.agner.org\/optimize\/nan_propagation.pdf)<\/a><\/p>\n<pre>hypot(nan,inf) = hypot(inf,nan) = inf\r\n<\/pre>\n<p>numpy behaves well!<\/p>\n<pre>np.hypot(np.nan,np.inf)\r\n\r\ninf\r\n\r\nnp.hypot(np.inf,np.nan)\r\n\r\ninf\r\n<\/pre>\n<p>My implementation does not<\/p>\n<pre>mike_hypot(np.inf,np.nan)\r\n\r\nnan\r\n<\/pre>\n<p>So in summary, my implementation is<\/p>\n<ul>\n<li>Wrong for very large numbers<\/li>\n<li>Wrong for very small numbers<\/li>\n<li>Not standards compliant<\/li>\n<\/ul>\n<p>That&#8217;s a lot of mistakes for one line of code! Of course, we can do better with a small number of extra lines of code as John D Cook demonstrates in the blog post <a href=\"https:\/\/www.johndcook.com\/blog\/2010\/06\/02\/whats-so-hard-about-finding-a-hypotenuse\/\">What&#8217;s so hard about finding a hypotenuse?<\/a><\/p>\n<h3>Hypot implementations in production<\/h3>\n<p>Production versions of the hypot function, however, are much more complex than you might imagine. The source code for the implementation used in openlibm (used by Julia for example) was 132 lines long last time I checked. Here&#8217;s a screenshot of part of the implementation I saw for prosterity. At the time of writing the code is at <a href=\"https:\/\/github.com\/JuliaMath\/openlibm\/blob\/master\/src\/e_hypot.c\">https:\/\/github.com\/JuliaMath\/openlibm\/blob\/master\/src\/e_hypot.c<\/a><\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"https:\/\/www.walkingrandomly.com\/wp-content\/uploads\/2020\/01\/openlibm_hypot.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-6639\" src=\"https:\/\/www.walkingrandomly.com\/wp-content\/uploads\/2020\/01\/openlibm_hypot-758x1024.png\" alt=\"openlibm_hypot\" width=\"758\" height=\"1024\" srcset=\"https:\/\/walkingrandomly.com\/wp-content\/uploads\/2020\/01\/openlibm_hypot-758x1024.png 758w, https:\/\/walkingrandomly.com\/wp-content\/uploads\/2020\/01\/openlibm_hypot-222x300.png 222w, https:\/\/walkingrandomly.com\/wp-content\/uploads\/2020\/01\/openlibm_hypot-768x1037.png 768w, https:\/\/walkingrandomly.com\/wp-content\/uploads\/2020\/01\/openlibm_hypot.png 1294w\" sizes=\"auto, (max-width: 758px) 100vw, 758px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>That&#8217;s what bullet-proof, bug checked, has been compiled on every platform you can imagine and survived code looks like.<\/p>\n<p>There&#8217;s more!<\/p>\n<h3>Active Research<\/h3>\n<p>When I learned how complex production versions of hypot could be, I shouted out about it on twitter and learned that the story of hypot was far from over!<\/p>\n<p><strong>The implementation of the hypot function is still a matter of active research! <\/strong>See the paper here <a href=\"https:\/\/arxiv.org\/abs\/1904.09481\">https:\/\/arxiv.org\/abs\/1904.09481<\/a><\/p>\n<p><a href=\"https:\/\/twitter.com\/walkingrandomly\/status\/1126059146703511559\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-6641 size-large\" src=\"https:\/\/www.walkingrandomly.com\/wp-content\/uploads\/2020\/01\/hypot_twitter-1024x553.png\" alt=\"hypot_twitter\" width=\"512\" height=\"276\" srcset=\"https:\/\/walkingrandomly.com\/wp-content\/uploads\/2020\/01\/hypot_twitter-1024x553.png 1024w, https:\/\/walkingrandomly.com\/wp-content\/uploads\/2020\/01\/hypot_twitter-300x162.png 300w, https:\/\/walkingrandomly.com\/wp-content\/uploads\/2020\/01\/hypot_twitter-768x415.png 768w, https:\/\/walkingrandomly.com\/wp-content\/uploads\/2020\/01\/hypot_twitter.png 1197w\" sizes=\"auto, (max-width: 512px) 100vw, 512px\" \/><\/a><\/p>\n<h3>Is Your Research Software Correct?<\/h3>\n<p>Given that such a &#8216;simple&#8217; computation is so complicated to implement well, consider your own code and ask <a href=\"https:\/\/mikecroucher.github.io\/NAG_IYRSC\/\">Is Your Research Software Correct?<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>My stepchildren are pretty good at mathematics for their age and have recently learned about Pythagora&#8217;s theorem $c=\\sqrt{a^2+b^2}$ The fact that they have learned about this so early in their mathematical lives is testament to its importance. Pythagoras is everywhere in computational science and it may well be the case that you&#8217;ll need to compute [&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":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[59,4,11,32,7,31,80,67,84,42],"tags":[],"class_list":["post-6633","post","type-post","status-publish","format-standard","hentry","category-julia","category-math-software","category-matlab","category-open-source","category-programming","category-python","category-rse","category-scientific-software","category-software-errors-in-research","category-tutorials"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3swhs-1IZ","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts\/6633","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=6633"}],"version-history":[{"count":16,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts\/6633\/revisions"}],"predecessor-version":[{"id":6652,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts\/6633\/revisions\/6652"}],"wp:attachment":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6633"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=6633"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=6633"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}