<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jaakko Kangasharju</title>
    <description>The latest articles on DEV Community by Jaakko Kangasharju (@vorahsa).</description>
    <link>https://dev.to/vorahsa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F10220%2Ffacce13711d98254d9837c7464536731.png</url>
      <title>DEV Community: Jaakko Kangasharju</title>
      <link>https://dev.to/vorahsa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vorahsa"/>
    <language>en</language>
    <item>
      <title>I had a bug with ForEach and Range in my SwiftUI code</title>
      <dc:creator>Jaakko Kangasharju</dc:creator>
      <pubDate>Wed, 29 Apr 2020 18:56:29 +0000</pubDate>
      <link>https://dev.to/vorahsa/i-had-a-bug-with-foreach-and-range-in-my-swiftui-code-32cm</link>
      <guid>https://dev.to/vorahsa/i-had-a-bug-with-foreach-and-range-in-my-swiftui-code-32cm</guid>
      <description>&lt;p&gt;I have a side project for iOS, and since it's a side project, I thought to try &lt;a href="https://developer.apple.com/xcode/swiftui/"&gt;SwiftUI&lt;/a&gt; for the UI. There's some complexity because the UI isn't exactly traditional, but overall, I've been happy with SwiftUI.&lt;/p&gt;

&lt;p&gt;But when Xcode 11.4 was released, the app wouldn't work properly. Some, but not all, parts of the UI weren't updating anymore when state changed. For a while, I stayed on 11.3 so I could work on the functionality, but as time passed, it became evident that this wasn't a problem with Xcode or iOS, it was something in my code.&lt;/p&gt;

&lt;p&gt;Mostly, I thought that I had messed up with the bindings so that the UI wasn't observing my presentation layer properly. No luck there, nothing I tried would work. After a lot of frantic Web searching, I eventually found the answer in the &lt;a href="https://swiftui-lab.com/frame-behaviors/"&gt;comments to an unrelated blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My code looked something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;MainView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;@Binding&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?]]&lt;/span&gt;

  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;VStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kt"&gt;ForEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;nameIndex&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="kt"&gt;NamesView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;nameIndex&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The problem is that &lt;code&gt;ForEach&lt;/code&gt; has three &lt;code&gt;init&lt;/code&gt; methods,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Range&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;KeyPath&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and using it like I did picks the second one, with the &lt;code&gt;Range&lt;/code&gt; parameter. This method assumes that the range doesn't change, which is true in my case, but it also seems to assume that the content views don't change, at least from Xcode 11.4 onward. Maybe this is a bug, maybe it's intended behavior, I can't tell from the documentation.&lt;/p&gt;

&lt;p&gt;When I had finally discovered the problem, the fix was simple enough. I changed the &lt;code&gt;ForEach&lt;/code&gt; to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;      &lt;span class="kt"&gt;ForEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;nameIndex&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="kt"&gt;NamesView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;nameIndex&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;which picks the third &lt;code&gt;init&lt;/code&gt; method of &lt;code&gt;ForEach&lt;/code&gt;, and allows updates of the views.&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>swiftui</category>
    </item>
    <item>
      <title>Detecting Cute Animals on iOS: A Continuation</title>
      <dc:creator>Jaakko Kangasharju</dc:creator>
      <pubDate>Mon, 21 Oct 2019 07:43:19 +0000</pubDate>
      <link>https://dev.to/vorahsa/detecting-cute-animals-on-ios-a-continuation-4a2e</link>
      <guid>https://dev.to/vorahsa/detecting-cute-animals-on-ios-a-continuation-4a2e</guid>
      <description>&lt;p&gt;Earlier, I wrote about &lt;a href="https://dev.to/vorahsa/detecting-cute-animals-with-machine-learning-df2"&gt;making a cute animal detector with machine learning for a mobile phone&lt;/a&gt;. Back then, I had to develop only for Android, since the iOS tools were incompatible with what I was using to train my model. But now the iOS tools are updated, and I had some time, so here is what I did to implement my cute animal detector on iOS.&lt;/p&gt;

&lt;p&gt;The earlier article explains all the model training that needs to be done. Here, I talk about only the additions that needed to be made to make the app for iOS. The &lt;a href="https://github.com/asharov/cute-animal-detector"&gt;repository&lt;/a&gt; has been updated with the latest code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Converting the Model
&lt;/h3&gt;

&lt;p&gt;On iOS, CoreML is the format to use for machine learning models. Apple provides &lt;a href="https://apple.github.io/coremltools/generated/coremltools.converters.keras.convert.html"&gt;tools that are able to convert the Keras model I have into CoreML&lt;/a&gt; in a straightforward manner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;coremltools&lt;/span&gt;

&lt;span class="n"&gt;coreml_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coremltools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keras&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'iscute.h5'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;image_input_names&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'input1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;input_name_shape_dict&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'input1'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;299&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;299&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
    &lt;span class="n"&gt;class_labels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'cute'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'notcute'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;coreml_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'iscute.mlmodel'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As in the Android case, the converter requires the input shape to be provided. In this case, however, the first dimension in the input tensor had to be left to &lt;code&gt;None&lt;/code&gt;, not set to 1. The &lt;code&gt;image_input_names&lt;/code&gt; and &lt;code&gt;class_labels&lt;/code&gt; are needed so that the iOS app knows that the model 1) processes images, and 2) is a classifier. This makes writing the app code easier, as special-purpose APIs are available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reading the Camera on iOS
&lt;/h3&gt;

&lt;p&gt;The app needs to do the same things on iOS as on Android:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Show the camera preview on screen&lt;/li&gt;
&lt;li&gt;Let the user press a button to capture the current image&lt;/li&gt;
&lt;li&gt;Feed the captured image to the classifier&lt;/li&gt;
&lt;li&gt;Display the result after the classification is complete&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Android implementation of image capture did not actually use the camera. Instead, when the user pressed the button, it grabbed the preview contents as a bitmap and generated the image to classify from that. On iOS I did not find a way to extract the preview contents as a bitmap, so I went with the route of instructing the camera to take a proper picture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YJpcjiPJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/z8fryqytjehyau68w7v4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YJpcjiPJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/z8fryqytjehyau68w7v4.jpg" alt="Alt Text"&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@spdumb2025"&gt;Amy Chen&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/cute-animal"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before the app can access the camera, the user must grant it permission. There are other possible authorization statuses, and the user might deny permission when asked, but for a sample app that is only about using the camera, I consider it fine to assume that the permission is or will be granted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="kt"&gt;AVCaptureDevice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authorizationStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;authorized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setupCaptureSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;notDetermined&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kt"&gt;AVCaptureDevice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;granted&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;granted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setupCaptureSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For custom use of the camera, as in this app, AVKit is the platform library to use. A camera session needs to be created and started. In a session, there are inputs and outputs, with inputs connecting to outputs. For this case, the input is set to video input from the back camera, so that the screen can show a live preview, and the output is a photo, since the app processes a single photo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;captureSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AVCaptureSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;captureSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginConfiguration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;backCamera&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AVCaptureDevice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;builtInWideAngleCamera&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;backCameraInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kt"&gt;AVCaptureDeviceInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;device&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;backCamera&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;captureSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;canAddInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backCameraInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;captureSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backCameraInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cameraOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AVCapturePhotoOutput&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="n"&gt;captureSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;canAddOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cameraOutput&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;captureSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cameraOutput&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;previewView&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="n"&gt;previewLayer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;captureSession&lt;/span&gt;
&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;previewView&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="n"&gt;previewLayer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;videoOrientation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;portrait&lt;/span&gt;
&lt;span class="n"&gt;captureSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commitConfiguration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;captureSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startRunning&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The photo output is what is used to take the picture. The target method of the evaluate button creates a settings object, where the default settings are fine for this use, and instructs the photo to be captured.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@objc&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;pictureSettings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AVCapturePhotoSettings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cameraOutput&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="nf"&gt;capturePhoto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pictureSettings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;delegate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Cropping the Photo
&lt;/h3&gt;

&lt;p&gt;In the Android app the app was processing a bitmap, which made cropping and scaling easy. On iOS, AVKit will produce an &lt;code&gt;AVCapturePhoto&lt;/code&gt; object, which needs a bit more work to match with the screen, since the screen resolution is different from the photo resolution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;cropImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AVCapturePhoto&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;UIImage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;photoOrientation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CGImagePropertyOrientation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;rawValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kCGImagePropertyOrientation&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;as!&lt;/span&gt; &lt;span class="kt"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;fullCgImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cgImageRepresentation&lt;/span&gt;&lt;span class="p"&gt;()?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;takeUnretainedValue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;fullCgImageSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CGSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fullCgImage&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fullCgImage&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;orientedBounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;photoOrientation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fullCgImageSize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;orientedBounds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;frameSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CGSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;299.0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;299.0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;center&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CGPoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fullCgImage&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fullCgImage&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CGRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;frameSize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;frameSize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;frameSize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;frameSize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;croppedCgImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fullCgImage&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="nf"&gt;cropping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;imageOrienation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;imageOrientation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;photoOrientation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;croppedImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UIImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;cgImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;croppedCgImage&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIScreen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;orientation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;imageOrienation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;croppedImage&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The best image type in this case that supports cropping is &lt;code&gt;CGImage&lt;/code&gt;. Unlike &lt;code&gt;UIImage&lt;/code&gt; that would be used for on-screen display, a &lt;code&gt;CGImage&lt;/code&gt; does not know its orientation, so that needs to be extracted from the &lt;code&gt;AVCapturePhoto&lt;/code&gt; object. The orientation is used to make sure that the screen bounds are oriented in the same way as the photo, so that the scaling factor can be correctly computed. (The width and height scales were different; using height worked correctly but I’m not sure why.)&lt;/p&gt;

&lt;p&gt;The picture to extract for classification is a 299x299 area in the center of the photo. So the code scales a 299x299 rectangle to the photo resolution, centers it, and does the cropping. Finally, a &lt;code&gt;UIImage&lt;/code&gt; with the appropriate screen scaling factor and orientation is returned, since working with a &lt;code&gt;UIImage&lt;/code&gt; is handier than with a &lt;code&gt;CGImage&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OT3UkCAZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5f1r1ik6w8iziuu9gdqt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OT3UkCAZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5f1r1ik6w8iziuu9gdqt.jpg" alt="Alt Text"&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@lilissa"&gt;Liliya Lisa&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/cute-fox"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the Classifier
&lt;/h3&gt;

&lt;p&gt;Creating the model is best done at startup since it is not going to change while the app is running, and it takes a while to create. The created model type is from the Vision framework, which is a nice library to use for image classification purposes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;?(&lt;/span&gt;&lt;span class="nv"&gt;coder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSCoder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;modelConfiguration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;MLModelConfiguration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;modelConfiguration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;computeUnits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpuAndGPU&lt;/span&gt;
  &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;isCuteModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="nf"&gt;iscute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;modelConfiguration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="kt"&gt;VNCoreMLModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;isCuteModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;coder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;coder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When trying to run the model with the default configuration, I encountered an Overflow Error that &lt;a href="https://stackoverflow.com/questions/54773171/espresso-aneruntimeengine-program-inference-overflow"&gt;apparently means the model is too big for the neural engine&lt;/a&gt;. So that is why it is limited to CPU and GPU only.&lt;/p&gt;

&lt;p&gt;Starting the classification with the Vision framework is straightforward. Create a classification request with the model and a callback method, create a handler from the (cropped) image, and call the handler with the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cropImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;photoOrientation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;photoOrientation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;VNCoreMLRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pictureClassified&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;VNImageRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;cgImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cgImage&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;orientation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;photoOrientation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[:])&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When the classifier has finished, the completion method will be called with the results. The method verifies that the results are classification results. Since the model was converted as an image classifier with the appropriate labels for the outputs, the code can look for the identifier &lt;code&gt;cute&lt;/code&gt; instead of needing to know which index corresponds to which class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;pictureClassified&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;VNRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;VNClassificationObservation&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Fail &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;describing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;classification&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;classification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;identifier&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"cute"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;showResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Building the iOS app was somewhat less work than building the Android app, in part because I already had some experience from the Android app. The biggest hurdle was the image processing that needed to be done on the captured photo, since I was not that familiar with the intricacies of &lt;code&gt;UIImage&lt;/code&gt;, &lt;code&gt;CGImage&lt;/code&gt;, and &lt;code&gt;AVCapturePhoto&lt;/code&gt;. As after making the Android app, I am now more confident in my ability to apply machine learning in a real iOS app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aQMw9w-R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hr73pv4lv6xcclu9t894.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aQMw9w-R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hr73pv4lv6xcclu9t894.PNG" alt="Alt Text"&gt;&lt;/a&gt;Screenshot of the app evaluating a dog&lt;/p&gt;

&lt;p&gt;Yep, it still works.&lt;/p&gt;

</description>
      <category>cute</category>
      <category>ios</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Programming with State: Concurrency</title>
      <dc:creator>Jaakko Kangasharju</dc:creator>
      <pubDate>Tue, 27 Aug 2019 10:58:11 +0000</pubDate>
      <link>https://dev.to/vorahsa/programming-with-state-concurrency-3a76</link>
      <guid>https://dev.to/vorahsa/programming-with-state-concurrency-3a76</guid>
      <description>&lt;p&gt;Programming with mutable state is already difficult in a sequential execution model where only one part of the program is executing at one time and functions complete execution without being interrupted. The context of our applications is not so accommodating, though, requiring programs to respond to various asynchronous events. And while some languages, like Javascript, adopt only asynchrony, many languages bring in also concurrency, where many parts of the program might be (conceptually at least) executing at the same time.&lt;/p&gt;

&lt;p&gt;Threads are a common abstraction for concurrency. Since each thread has access to the same program memory, the problems of mutable state get exacerbated. Not only can state now change between function calls, it can be changed by another thread while a function is executing. This problem is typically handled by using locks to prevent multiple threads from operating on the same piece of state at the same time, but locks can be difficult to get right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Invariants
&lt;/h2&gt;

&lt;p&gt;Looking at the value-entity model from the &lt;a href="https://dev.to/vorahsa/programming-with-state-values-and-entities-1hcp"&gt;first post of this series&lt;/a&gt;, there are additional problems with multithreading. I mentioned invariants as a useful tool to keep the state of an entity manageable. But since an invariant can be temporarily violated while an entity is modifying itself, a concurrent execution of a state query might observe the inconsistent state. And this leads back to all the problems with mutable state that we were trying to avoid by adopting the value-entity model.&lt;/p&gt;

&lt;p&gt;Locks internal to an entity can of course be used to ensure that other threads do not see temporary invariant violations. An additional problem to the usual ones in the entity model is that often we want to allow concurrent invocations on an entity that do not conflict with each other, and designing locking to support this can get complicated. Furthermore, a lock-based system only supports those operations that were thought of by the entity implementor. For instance, a common operation needed for an associative array is insert-if-not-present, but if the array only implements operations for checking the presence and unconditional insertion, this combined operation cannot be safely performed by the caller.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://channel9.msdn.com/Shows/Going+Deep/Programming-in-the-Age-of-Concurrency-Software-Transactional-Memory"&gt;Software Transactional Memory (STM)&lt;/a&gt; is an interesting concept that solves many of the problems in lock-based entity-style programming. The optimistic execution makes non-conflicting operations succeed. The ability to wrap atomic operations into larger atomic operations solves the composability of operations. And finally, when programming with the observable-state model that emits modification events, STM prevents such an event from being emitted before all state modification has completed. STM is not very mainstream, though, so even though libraries exist for many programming languages, it would be difficult to justify its adoption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entities and Messages
&lt;/h2&gt;

&lt;p&gt;From the word “entity”, one might expect a different kind of execution. An entity brings to mind something autonomous and separate, but this is not how threaded execution sees things. There are no enforced entity boundaries. Any thread can just come and execute an entity’s code at any time. What was architecturally good encapsulation in a single-threaded system completely disappears in a multi-threaded system.&lt;/p&gt;

&lt;p&gt;My preferred system following this thinking would have each entity “living” in a single thread. All of the code belonging to an entity would always be executed on that entity’s thread. This is similar to many UI frameworks that require UI component manipulation to happen in the UI thread, except that I would prefer the system to take care of thread switching instead of having to do it manually. Such a system would make programming much more asynchronous, since each entity invocation could involve a thread switch, but I have written code in this style, and it is not too difficult with, say, &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/"&gt;the async/await mechanism&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This kind of view of objects as independent entities that communicate with each other is not new. It resembles &lt;a href="http://www.purl.org/stefan_ram/pub/doc_kay_oop_en"&gt;Alan Kay’s definition of object-oriented programming&lt;/a&gt;, which is focused on messages between objects. When looking from the concurrency perspective, &lt;a href="http://ulf.wiger.net/weblog/2008/02/06/what-is-erlang-style-concurrency/"&gt;processes in Erlang&lt;/a&gt; function semantically as independent entities with internal state. To me, it is more difficult to conceptualize observable entity state in the message passing model, especially the &lt;a href="https://dev.to/vorahsa/programming-with-state-values-and-entities-1hcp"&gt;dependency network of derived state as described in the first post&lt;/a&gt;. Still, when concurrency gets involved, I find seeing entity invocation as message passing a better way of comprehending the system in a way that helps make it work correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mitigation in Practice
&lt;/h2&gt;

&lt;p&gt;There are some patterns and ways of thinking that can mitigate the problems of concurrency with mutable state. Unlike in the single-threaded case, these tend to be more difficult to set up so that they would work automatically. Therefore more discipline and careful documentation of the used patterns is needed, since the programming environment cannot always enforce it for us.&lt;/p&gt;

&lt;p&gt;One way to avoid the issues with other threads observing temporarily-broken invariants is to postpone internal state modification. Instead of directly modifying an entity’s state during function execution, use local variables to capture all the modified pieces of state, and only at the end of execution set the entity’s state. This also helps when an invocation depends on operations that can fail, since no actual modifications have yet been made so the execution can simply be abandoned instead of having to implement complex rollback logic.&lt;/p&gt;

&lt;p&gt;This style of state update becomes interesting when the state to update is some form of collection, like an associative array. Fully copying a big structure for local modifications may be too inefficient (but it might not; remember to measure before assuming where your bottlenecks are). A good option would be to use a &lt;a href="https://en.wikipedia.org/wiki/Persistent_data_structure"&gt;persistent data structure&lt;/a&gt;, which allows the modifications to be made locally in a natural way, swapping the original for the updated one at the end, but persistent data structures are not commonly used in many programming languages. The ultimate expression of this style would be to keep all (mutable) state of an entity in a single persistent data structure, which would eliminate any issues with breaking invariants, since the state would then only get modified all at once in a single operation.&lt;/p&gt;

&lt;p&gt;When an entity gets more complex, it can be helpful to structure its internals as a &lt;a href="https://skorks.com/2011/09/why-developers-never-use-state-machines/"&gt;state machine&lt;/a&gt;, with the state and allowed transitions defining which invocations are permissible. This can also help in concurrent and asynchronous situations. Namely, when the state machine implementation is thread-safe, the check on permitted state transitions can take care that conflicting operations are not executed, even when requested concurrently.&lt;/p&gt;

&lt;p&gt;Surprisingly, I’ve found that automated tests can be helpful. In general, concurrency issues are non-deterministic, depending on specific timing conditions that won’t be reproduced exactly. What I do, when applicable, is to run a huge number of threads in the automated test, all hammering on the entity that is supposed to be thread-safe, and verify that the results are consistent with some correct execution. This is not always possible to do, usually the expected result is not as clear-cut as with a traditional test, and the test may not always catch failures. But if the expectations can be defined, I’ve found that running enough threads flushes out many problems reliably enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Dealing with mutable state in a concurrent program can get very difficult. Concurrency makes it ever more important to architect the program so that state mutation happens only in specific, controlled ways. Building the program directly on low-level concurrency abstractions, as often provided by popular programming platforms, will usually lead to trouble. Whenever possible, try to extract higher-level concepts into reusable components, such as &lt;a href="https://winterbe.com/posts/2015/04/07/java8-concurrency-tutorial-thread-executor-examples/"&gt;task executors&lt;/a&gt; or &lt;a href="https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationObjects/OperationObjects.html"&gt;operation queues&lt;/a&gt;. Be even stricter in controlling how state is mutated. And when it is not possible to ensure correct operation through language-enforced mechanisms, be very disciplined and document carefully what is being done and why.&lt;/p&gt;

</description>
      <category>state</category>
      <category>architecture</category>
      <category>concurrency</category>
    </item>
    <item>
      <title>Programming with State: Architectural Roles</title>
      <dc:creator>Jaakko Kangasharju</dc:creator>
      <pubDate>Tue, 20 Aug 2019 12:06:06 +0000</pubDate>
      <link>https://dev.to/vorahsa/programming-with-state-architectural-roles-ni</link>
      <guid>https://dev.to/vorahsa/programming-with-state-architectural-roles-ni</guid>
      <description>&lt;p&gt;To me, application architecture has one primary question to answer: Where does this functionality belong? An application I’m working on is going to have features, each feature consisting of several pieces of interacting functionality. And I want to have a clear view of where each piece of functionality should go, to make sure the components stay comprehensible. The &lt;a href="https://dev.to/vorahsa/programming-with-state-values-and-entities-1hcp"&gt;first part of this series&lt;/a&gt; described an application as a group of interconnected entities. Now we will look at which entities to create and when.&lt;/p&gt;

&lt;p&gt;There exist a number of MV* architectures, MVC, MVP, MVVM, … What these have in common is that the M (model) and V (view) have very clear and limited responsibilities and then everything else apparently goes into the * part. Display formatting of information, accessing the backend over the network, navigating through the application, you name it, the * handles it. And that’s just not enough for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture with No Pronounceable Acronym
&lt;/h2&gt;

&lt;p&gt;Over the years, I have come up with an architecture that works for me. The way that things are split into components makes sense, and I always know how to split up a feature and where each piece of needed functionality goes. I have not tried to force it to form any specific acronym, so it’s really just my architecture, with components created where they make sense.&lt;/p&gt;

&lt;p&gt;I also like to separate the connections between components into two different kinds: A &lt;em&gt;dependency&lt;/em&gt; means that a component has a reference to another component. And &lt;em&gt;data flow&lt;/em&gt; means that a component passes data to another component. Sometimes data flow follows a dependency, when data is passed in to a component whose functionality is invoked. And other times data flow goes in the inverse direction, when a component observes another component’s state.&lt;/p&gt;

&lt;p&gt;The diagram shows the useful architectural concepts that I have identified, which I call &lt;em&gt;roles&lt;/em&gt;. The data flow between them is shown with arrows pointing in the direction of where the data flows. I have found that mostly, unidirectional data flow works well and makes things easier to understand, since it is always known from where changes can be triggered. Dependencies in the diagram go from top to bottom: something higher up can depend on anything lower down, but not vice versa.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LJHxL9qa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ss1tabx2avdfbih1tex9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LJHxL9qa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ss1tabx2avdfbih1tex9.png" alt="Architecture Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of Roles
&lt;/h2&gt;

&lt;p&gt;It is of course necessary to have good definitions of the roles, to serve the goal of knowing where each piece of functionality belongs. View and Resource are the roles that interact with the outside world. A View is whatever draws the user interface on the screen and recognizes user interactions. A single screen often contains multiple Views, usually arranged hierarchically in containment relationships. A Resource is anything interacting with the external world outside of the user interface, from communicating with a backend to file I/O and even to timers.&lt;/p&gt;

&lt;p&gt;Presentation and Control are both related to the user interface. In the diagram they are marked as view-local, whereas other roles are global. This means that usually each View has its own components to perform these roles, and they are not typically shared between different Views. The reason for splitting these roles is to make the data flow clearer (and unidirectional): Presentation is only concerned with how data is displayed on screen, so the data flow is unidirectional from Store through Presentation to View. And in the other direction, Control only receives user interaction triggers from the View and triggers actions in Store or Agent.&lt;/p&gt;

&lt;p&gt;The division between Model and Store comes from how state is represented according to the &lt;a href="https://dev.to/vorahsa/programming-with-state-values-and-entities-1hcp"&gt;previous post in this series&lt;/a&gt;. Models are values, and Stores are entities whose state consists of Model objects. All base state in the application is state of a Store. The Store can acquire this state either from data that flows into it from above, or by fetching it from a Resource, such as a backend or database. The data that flows out of a Store are the Model objects that comprise its state.&lt;/p&gt;

&lt;p&gt;The Agent role is perhaps the most unusual and also the rarest. The idea behind Agent is that other roles are purely reactive; they do something only in response to data flowing into them, whereas an Agent is allowed to be active, to initiate an action (in a way, the View can also be seen as active, since user actions are definitely not initiated as a direct response to data flow, but user activity is not modelled here). For instance, a periodic synchronization of data with a backend would be an Agent activity, and separating the periodic triggering of the action by the Agent from the actual communication performed by the Store makes the whole system easier to comprehend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roles instead of Components
&lt;/h2&gt;

&lt;p&gt;The key point in the diagram is that these are not architectural components, they are roles. The difference being, a component may assume multiple roles. When a Model object is shown in the user interface by simply displaying some of its properties as they are, I don’t bother to create a completely new Model Presentation component but simply let the View directly use the Model object that it acquires from a Store. But when the needed presentation becomes more complex, as it often does, the established separation of roles brings easily to mind the solution of creating a new component for the Presentation role.&lt;/p&gt;

&lt;p&gt;I place great importance on being able to unit-test as much of an application as possible, and that in part has guided the architecture. The two roles marked as not unit-testable in the diagram are always separate components to facilitate this. For Resources, a &lt;a href="https://martinfowler.com/bliki/TestDouble.html"&gt;test double&lt;/a&gt; is always used. Sometimes the double is a stub when the Resource interaction in the test is simple, sometimes a fake to allow more precise control over its behavior. Views are not considered in unit tests at all. All the logic determining what to show is in a Presentation, and all action code is in a Control, so there is no unit-testable logic in a View, only the actual displaying on screen and mapping user interactions to Control triggers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Architectures
&lt;/h2&gt;

&lt;p&gt;The architecture described here owes a lot to my experiences building mobile applications, and especially to using the popular architectures and trying to fill their “deficiencies”. For instance, the view-local part of the architecture comes quite directly from MVVM, where the View Model fulfils the roles of Presentation and Control. Experience with MVVM has taught me that these two roles are typically very distinct in a View Model, and for me separating them as concepts makes the whole architecture clearer.&lt;/p&gt;

&lt;p&gt;Traditionally, the whole functionality contained in the Model, Store, and Resource roles would be contained in the Model part in an MV* architecture. The &lt;a href="https://dev.to/vorahsa/programming-with-state-values-and-entities-1hcp"&gt;previous post on values and entities&lt;/a&gt; establishes why I like the Model-Store separation, and it is also partly inspired by the &lt;a href="https://www.amazon.com/iOS-Programming-Ranch-Guide-Guides-ebook/dp/B00IG8TGZ8/ref=mt_kindle"&gt;Model-View-Controller-Store architecture&lt;/a&gt;. The separation of Store and Resource is purely to help with unit testing. By separating the part that in a real application requires interaction with the external world into a thin stubbable layer allows unit tests to cover a lot more of the application than if all the Resource use was contained inside the Store.&lt;/p&gt;

&lt;p&gt;Navigation is one of the most difficult architectural problems in mobile application development. It is always very simple at the beginning, and can be handled by the Views using the platform native navigation patterns. But too often the navigation patterns of an application become more complex, and they typically do this a bit at a time, never allowing the moment where it’s clear that the navigation needs to be rethought. The &lt;a href="https://www.objc.io/issues/13-architecture/viper/"&gt;VIPER architecture&lt;/a&gt; is the only one I know that explicitly includes navigation, but even there it appears to be only a way to separate the too-simple platform-provided navigation into its own components. The kinds of complex navigation that I have encountered require a more global approach, usually requiring a single component that is responsible for the navigation needs of the whole application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenarios
&lt;/h2&gt;

&lt;p&gt;I began with the purpose of the architecture being to determine where each piece of functionality belongs. Thus, the validation of an architecture is performed by observing how well this purpose is fulfilled in development situations. The following two examples show how the functionality in an application screen would be split, and how the role-based thinking allows the implemented architecture to remain simple when applicable and to evolve into something more complex when needed.&lt;/p&gt;

&lt;p&gt;The component architecture of a very basic screen could look like the User Info Screen example shown below. This is a component diagram, with the roles that each component fulfils shown on the top-right corner of the component. The User Store holds the User model object. The screen shows directly the information in this model object without any additional presentation logic, so the User object can act also in the Presentation role.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N7u4C2za--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0awhsjfsdpzlxzxvvji5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N7u4C2za--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0awhsjfsdpzlxzxvvji5.png" alt="Basic Application Components"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screen allows editing the user information. In this case no special interaction logic is needed, so the User Store can act in the Control role for the View. Finally, the User Store is responsible for local and remote persistence of the user information, and it uses two Resources for this, one for the local database and one to communicate with the backend.&lt;/p&gt;

&lt;p&gt;A more complex screen, with more components and more separated roles, is provided by a chat application. The Chat Store is in a way the center point of the application, as it stores all the chats that the user participates in. The Model objects are the messages in the chat, but in this case, a separate Presentation component for messages is needed; for instance, the Model object is likely to store the precise timestamp of the message but the screen should display a more human-friendly representation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xDpZbTVK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/sr9ikizfywsj4np4ewyd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xDpZbTVK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/sr9ikizfywsj4np4ewyd.png" alt="Chat Application Components"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depending on how message sending is implemented, there might be a Message Sender component in the Control role that both requests the Chat Store to send a new message from the user and immediately calls the Presentation to update the screen for a better user experience. An alternative would be for the representation of a chat provided by the Chat Store to also provide sufficient information for any pending messages. In general, a separate Control component is useful when a user action requires data to flow into several different components, but it could also be used when the Control functionality is complex enough to warrant testing but is not considered to be the responsibility of a Store.&lt;/p&gt;

&lt;p&gt;The chat application also provides an example of the Agent role. The Chat Updater is an independent component that could be triggered by a timer, or by external notifications, either one of which would be a Resource. When triggered, the Chat Updater will tell the Chat Store to update a specific chat or all the chats using the Resource for communicating with the chat backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;To summarize the above: I split an application architecture in as many concepts as I feel are necessary. I don’t feel the need to force them into a cutesy acronym. I use components only when they actually have some necessary function instead of being just a pass-through layer for data, and that’s why I think in terms of roles instead of components. I find &lt;a href="https://www.futurice.com/blog/empirical-methods-of-debugging"&gt;thinking in terms of data flow&lt;/a&gt; very useful, so data flow between components is an important part of the architecture. And finally, I’m not afraid to go against my principles when needed. Not everything fits into neat little boxes, and sometimes it’s better to just acknowledge that than to try to squeeze a square peg into a round hole. In the end, software architecture should provide guidance, not act as a straitjacket.&lt;/p&gt;

</description>
      <category>state</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Programming with State: Values and Entities</title>
      <dc:creator>Jaakko Kangasharju</dc:creator>
      <pubDate>Wed, 14 Aug 2019 14:38:25 +0000</pubDate>
      <link>https://dev.to/vorahsa/programming-with-state-values-and-entities-1hcp</link>
      <guid>https://dev.to/vorahsa/programming-with-state-values-and-entities-1hcp</guid>
      <description>&lt;p&gt;Developers know well the perils of mutable state in a program. A value changes from underneath you, messing up a computation in progress. Two different places on the screen, ostensibly based on the same information, are not consistent with each other. The application crashes because a value was not changed at an appropriate time.&lt;/p&gt;

&lt;p&gt;The functional style of programming, which is based on immutable values and functions that build new values instead of mutating existing ones, has become very popular due to the complexity of managing state when everything is mutable.  But let’s say that we find programming with mutable state useful. For instance, my experience is primarily in mobile app development with the native tools, where the whole platform is built around mutable state, and it is a rare team or company that is willing to go for a completely different app architecture or programming style than is practiced by the majority of developers. The main question then becomes how do we manage that state to keep our programs comprehensible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Values and Entities
&lt;/h2&gt;

&lt;p&gt;A view that I’ve found useful is to divide the objects manipulated by the program into two kinds, &lt;em&gt;values&lt;/em&gt; and &lt;em&gt;entities&lt;/em&gt;. A value is conceptually similar to a primitive type, an immutable object, with a structured value simply a sum of its parts with no hidden content. In contrast, each entity is a distinct object, even from entities with the exact same state. And as an entity has a distinct identity, its state can be mutable.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Entity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;State-based equality&lt;/td&gt;
&lt;td&gt;Distinct identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Immutable&lt;/td&gt;
&lt;td&gt;Internal mutable state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;External observable state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;“Functional”&lt;/td&gt;
&lt;td&gt;“Object-oriented”&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This view conflates equality with mutability, but in my opinion the other possibilities are not needed. An immutable object with a distinct identity can simply be a value that includes a unique identifier. And a mutable object with state-based equality is in my experience just asking for trouble.&lt;/p&gt;

&lt;p&gt;Languages like C# and Swift have some of this separation at the language level, with the separation of types into structs (values) and classes (entities). But they don’t go all the way, as both also include an overridable equality for their entity types, allowing a non-identity-based equality to be defined. I feel like this is a holdover from languages where “Everything is an entity” and has no place in a proper value-entity-separating language. The &lt;a href="https://clojure.org/about/state"&gt;“identity has a state” approach of Clojure&lt;/a&gt; is much better but we can’t always program in Clojure.&lt;/p&gt;

&lt;p&gt;I’ve used the terms “Functional” and “Object-oriented” in the table above, as this is how a programming style primarily based on one kind feels to me. A functional style is based on values, building new values out of pure computations on existing values, whereas an object-oriented style is based on distinct entities having internal state modified by their external interfaces. The rest of this post will be focused on the entity-based object-oriented style relying on mutable state, so I won’t say more about the pure functional style.&lt;/p&gt;

&lt;h2&gt;
  
  
  State
&lt;/h2&gt;

&lt;p&gt;Now that the big beast of mutable state has been pushed into these things I call entities, let’s look at how to manage it. I’ve split state in an entity into two forms, internal and external. It doesn’t matter much how internal state is managed, as that is local to the entity and can be contained in a small enough area to usually be understandable.&lt;/p&gt;

&lt;p&gt;For external state, it should make sense as something that is exposed by the entity. Most often, it is better not to simply expose a piece of internal state directly as external state, but provide something more meaningful to the outside world. Or even expose only invocations that can be performed on the entity, whose implementation depends on the internal state but no specific state is ever exposed.&lt;/p&gt;

&lt;p&gt;Thinking in terms of &lt;em&gt;invariants&lt;/em&gt; helps keep the state of an entity manageable. An invariant is simply a property that holds among different parts of an entity’s state. For instance, in an entity representing a shopping cart, the total price should be the sum of the prices of the individual items minus any applied discounts. While an entity is modifying itself, an invariant can temporarily be violated, but an external observer should always see the invariant to hold.&lt;/p&gt;

&lt;p&gt;I used the term “observable” in the table above for external state. This, to me, is necessary to manage the application state properly. It must be possible for other entities to observe changes in an entity’s state and to react to those changes. In a stateful application, pieces of state are never fully independent of each other, so keeping the complete application state consistent requires modifying many pieces of it simultaneously.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependencies
&lt;/h2&gt;

&lt;p&gt;Observable state and reacting to its changes introduces the notion of an entity, and its state, depending on the state of another entity. Internally, entities can contain references to other entities that they depend on. And their state can depend on the state of other entities. So there are two kinds of state: &lt;em&gt;Base state&lt;/em&gt; and &lt;em&gt;derived state&lt;/em&gt;, where derived state is a composition of other state.&lt;/p&gt;

&lt;p&gt;I find that a good way to manage this is to represent the observable state of an entity as values. Following this, derived state can be defined as a pure function of its dependent state. This is essentially how spreadsheets work, and that is a very useful conceptual model for understanding the complete application state as consisting of independent pieces of base state and dependent pieces of derived state. Entities may contain other entities as internal state but these contained entities are never exposed as external state.&lt;/p&gt;

&lt;p&gt;To do this properly, the programming language or environment needs to support some form of data binding. Derived state is defined by binding to all its dependencies, with a pure function to compose it. This way the system guarantees that all the state throughout the application is always consistent. This can in theory be also done with the Observer pattern, but as the Observer pattern splits the definition of the state from the update, in practice it can be too easy to forget some necessary update to derived state when the relationships get complex.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://redux.js.org/introduction/three-principles"&gt;Redux architecture&lt;/a&gt; shows similar thinking. State changes are performed with pure functions that take the existing state and a performed state-changing action. Redux essentially has only one entity, the store, which simplifies derived-state management. When combined with something like React, where &lt;a href="https://reactjs.org/docs/components-and-props.html"&gt;the UI is a pure function of application state&lt;/a&gt;, the complexity of state management is significantly reduced.&lt;/p&gt;

&lt;p&gt;In platforms that encourage the multiple-entity style, derived-state management is not really supported all that well in languages themselves or their libraries. Data binding is usually treated through modification events, which do not function well in, say, the diamond dependency scenario: The state of entity Top depends on the states of entities Middle1 and Middle2, both of which depend on the state of entity Bottom. The correct order to modify the states is Bottom -&amp;gt; (Middle1, Middle2) -&amp;gt; Top, which is followed by any spreadsheet, but a system based on modification events will do Bottom -&amp;gt; Middle1 -&amp;gt; Top, where the new state of Top is computed based on the new state of Middle1 but the old state of Middle2, leading to an inconsistency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8WdEnqWo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xjzuglbr2qtuh9tiqtxq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8WdEnqWo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xjzuglbr2qtuh9tiqtxq.png" alt="Illustration of the diamond dependency scenario"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For large pieces of state, it is not really the best idea to rebuild all depending state from scratch when something little changes. For instance, if the state represents the whole product catalog of a shop service, adding one more product is a very small change that should not trigger massive recomputations. In such situations, I usually include an additional stream of change events. A change event is essentially just the difference between the previous state and the current state, and it has enough information so that any derived state can be modified to match the new dependent state. So when first creating an entity with derived state, a full computation is performed, but afterwards, the derived state is modified in place to reflect changes communicated through the change events.&lt;/p&gt;

</description>
      <category>state</category>
      <category>architecture</category>
    </item>
    <item>
      <title>I Am Unhireable</title>
      <dc:creator>Jaakko Kangasharju</dc:creator>
      <pubDate>Tue, 09 Jul 2019 12:28:12 +0000</pubDate>
      <link>https://dev.to/vorahsa/i-am-unhireable-541d</link>
      <guid>https://dev.to/vorahsa/i-am-unhireable-541d</guid>
      <description>&lt;p&gt;Following discussions around programmer recruitment, I often come across people who talk about simple criteria they have for who could even get an interview. And when thinking about such criteria, I typically discover that I don't fit them. I guess all this makes me unhireable?&lt;/p&gt;

&lt;p&gt;(Phrases in quotes are paraphrases of the common attitudes I see, not quoting any specific people.)&lt;/p&gt;

&lt;h2&gt;
  
  
  I am in my mid-40's
&lt;/h2&gt;

&lt;p&gt;"Programming is for young people." I didn't even become a professional developer until my mid-30's, though my work before that did involve coding. Granted, I don't think my mind is as quick as it was, but that also helps me avoid making my code too complex. And I do have quite a bit of experience, and even experience in now-obsolete platforms can turn out to be useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  I've worked in the same place for 10 years
&lt;/h2&gt;

&lt;p&gt;"I am suspicious of anyone who's been in the same place too long." "2-3 years is the optimal length of one job." I don't really see any point in making a change just for the sake of making a change. I like my job, I like the company, and I've had the opportunity to develop myself in interesting and challenging directions.&lt;/p&gt;

&lt;h2&gt;
  
  
  I learned to program in the university
&lt;/h2&gt;

&lt;p&gt;"All good developers started young, playing with their childhood computer." We did have a computer when I was a child, but I mostly used it to play games. I knew some BASIC but whatever I did with it wasn't much help later on. I didn't even get the point of subroutines. Sure, it probably helped me become comfortable with computers in general, but it's not like I did anything of the sort that people tend to expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Github is small and irrelevant
&lt;/h2&gt;

&lt;p&gt;"I always look through a candidate's Github and expect to find a professional portfolio." Here are my Github contributions&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcn82v7orpplntqbec0jn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcn82v7orpplntqbec0jn.png"&gt;&lt;/a&gt;&lt;br&gt;
In fact, the period shown here has been exceptionally busy for my Github.&lt;br&gt;
Furthermore, practically everything in my Github is small side projects, not showing anything of my actual professional work. Anyone going through my Github is going to have a very distorted view of my skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  I don't have a "passion" for coding
&lt;/h2&gt;

&lt;p&gt;"Good developers spend their free time coding, they cannot stop." I never feel like coding just for coding's sake. I occasionally have side projects, but those are for real problems that I want to solve where the solution involves coding something. I do like coding, which is good since it's my job, but when I finish at work, I tend to do something else on my free time.&lt;/p&gt;

&lt;h2&gt;
  
  
  I have a PhD
&lt;/h2&gt;

&lt;p&gt;"I want to hire practical people, not ivory tower academics." This is perhaps not a bad thing everywhere. But in Finland, my native country, it definitely is. Fellow students of mine who worked in the industry hid their PhDs at work to avoid being pigeonholed. And recently I've been getting a feeling that there is beginning to be more sentiment against more academic backgrounds in general in software development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nevertheless...
&lt;/h2&gt;

&lt;p&gt;Despite all these ways in which I am "unhireable", I'm still employed, still well-respected at work, and consistently get great feedback on both my programming and social skills. So maybe making snap judgments on one characteristic is not the best way?&lt;/p&gt;

&lt;p&gt;It does make me sad when my colleagues talk about using some of these criteria or similar ones. I've gotten into the habit of saying "Doing that would mean you wouldn't hire me. Is that what you want?" but, as one could expect, it doesn't really cause them to re-evaluate their thinking all that much.&lt;/p&gt;

&lt;p&gt;Finally, of course all that is irrelevant since I possess the most sought-after skill of a developer: I am excellent at coding algorithms on a whiteboard.&lt;/p&gt;

</description>
      <category>career</category>
      <category>hiring</category>
      <category>developer</category>
    </item>
    <item>
      <title>Tennis and Amplifying Small Differences</title>
      <dc:creator>Jaakko Kangasharju</dc:creator>
      <pubDate>Tue, 04 Jun 2019 13:19:32 +0000</pubDate>
      <link>https://dev.to/vorahsa/tennis-and-amplifying-small-differences-3icl</link>
      <guid>https://dev.to/vorahsa/tennis-and-amplifying-small-differences-3icl</guid>
      <description>&lt;p&gt;A while ago there was a nice post by &lt;a class="mentioned-user" href="https://dev.to/teckert"&gt;@teckert&lt;/a&gt; on probabilities in a basketball game &lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/teckert" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F154986%2F9eda4235-05fd-459e-92ae-d59ac3a16b73.jpeg" alt="teckert"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/teckert/basketball-for-nerds-5bf0" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Basketball for Nerds&lt;/h2&gt;
      &lt;h3&gt;Thomas Eckert ・ Apr 10 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#math&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#probability&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#interviewquestions&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Generalization
&lt;/h2&gt;

&lt;p&gt;Thinking about the two situations in the post I came up with the following generalization: You have an odd number of chances and you have to make more than half the shots. This fits both the "1 out of 1" and "2 out of 3" cases that were in the post. And it lets us visualize the effect that &lt;a class="mentioned-user" href="https://dev.to/walker"&gt;@walker&lt;/a&gt; pointed out in a comment, that the more chances you have, the more the final result of the game is determined by your individual shot probability.&lt;/p&gt;

&lt;p&gt;In the original post, &lt;a class="mentioned-user" href="https://dev.to/teckert"&gt;@teckert&lt;/a&gt; computed the probability in the "2 out of 3" game by hand. But if we want to generalize to really long games, the formulas would be really unwieldy to derive and handle. We could also write a loop that would generate &lt;a class="mentioned-user" href="https://dev.to/teckert"&gt;@teckert&lt;/a&gt;'s table, letting the computer handle the heavy lifting. Is there a better way?&lt;/p&gt;

&lt;p&gt;Turns out, there is. We have a situation where we have &lt;code&gt;n&lt;/code&gt; chances and probability &lt;code&gt;p&lt;/code&gt; of making each of them, independently of how the others have gone. This means that the number of successes follows the &lt;em&gt;binomial distribution&lt;/em&gt;. And SciPy has a &lt;a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.binom.html" rel="noopener noreferrer"&gt;ready-made function&lt;/a&gt; for computing this.&lt;/p&gt;

&lt;p&gt;We want to determine the total probability of all the cases where the made shots are more than half the number of chances. In probability, such a calculation can be done using the &lt;em&gt;cumulative distribution function&lt;/em&gt; (often abbreviated as CDF). The CDF gives the total probability of getting a value less than its argument, so to get the total probability of getting a value more than half, we can subtract the CDF value from 1 (note that we're assuming an odd number of chances so it is not possible to make exactly half the shots).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;scipy.stats&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;binom&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;winning_probability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number_of_chances&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shot_probability&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;binom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cdf&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;number_of_chances&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number_of_chances&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shot_probability&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Equipped with this function, we can plot the game winning probability curves for various numbers of chances.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;p_range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;linspace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;probs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;winning_probability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;p_range&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_range&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;probs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Probability of Winning Game&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Probability of Making a Single Shot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;display&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting graph shows how, the more chances we have, the more the probability approaches just 0 or 1, depending on whether we're worse or better than average.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fsxmn1nn75o3q01plm1r1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fsxmn1nn75o3q01plm1r1.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Case of Tennis
&lt;/h2&gt;

&lt;p&gt;So what does this have to do with tennis? Well, tennis shows a similar amplification of small differences. But in tennis, the effect happens on multiple levels. First, you need to win four points to win a game, you need to win six games to win a set, and finally you need to win three sets to win the match. If you lose a game or set, it doesn't matter further on how much you lost by.&lt;/p&gt;

&lt;p&gt;Tennis also presents a complication. To win a game it's not enough to win four points, you also need to be two points ahead of the opponent. So we can't use the binomial distribution anymore. But we can set up an equation for the probability and solve it. The key is noticing that, when the score is tied with two consecutive points needed to win, there are three possible situations&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You win two points in a row&lt;/li&gt;
&lt;li&gt;Your opponent wins two points in a row&lt;/li&gt;
&lt;li&gt;You both win one point of the next two&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In case 1, you win the game. In case 2, your opponent wins. And in case 3, the situation ends up back in the beginning. So we can set up a formula for the total probability, which will be a sum of two terms: the probability of case 1 and the probability of you winning in case 3. These work out to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;case_1_probability = point_probability * point_probability
case_3_probability = 2 * point_probability * (1 - point_probability) * total_probability
total_probability = case_1_probability + case_3_probability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't exactly work out as computer code because of the recursive definition (&lt;code&gt;total_probability&lt;/code&gt; depends on &lt;code&gt;case_3_probability&lt;/code&gt; which in turn depends on &lt;code&gt;total_probability&lt;/code&gt;), but we can expand the definition of &lt;code&gt;total_probability&lt;/code&gt; to make a perfectly solvable mathematical formula&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t_p = p_p * p_p + 2 * p_p * (1 - p_p) * t_p
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Standard algebraic manipulations produce the solution&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t_p = p_p * p_p / (1 - 2 * p_p * (1 - p_p)) = p_p * p_p / (1 - 2 * p_p + 2 * p_p * p_p)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which we can implement in code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;two_wins_in_a_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;point_prob&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;point_prob&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So this is the probability of winning a game when you and your opponent both have, say, 3 points, and you need to win two points in a row.&lt;/p&gt;

&lt;p&gt;Let's start putting things together for computing the probability of winning a game when we know the probability of winning a point. Conceptually, we will fill a table indexed by your and your opponent's score with your winning probability from that score. Then, the probability of you winning the game is simply the value in the table cell (0, 0).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;O\Y&lt;/th&gt;
&lt;th&gt;0&lt;/th&gt;
&lt;th&gt;1&lt;/th&gt;
&lt;th&gt;2&lt;/th&gt;
&lt;th&gt;3&lt;/th&gt;
&lt;th&gt;4&lt;/th&gt;
&lt;th&gt;5&lt;/th&gt;
&lt;th&gt;...&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;T&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;T&lt;/td&gt;
&lt;td&gt; &lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;T&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We can start by filling in the value 1 in each cell where you have 4 or more points and your opponent has at least 2 points less than you, since you've already won those. Similarly, we fill in 0 in the symmetrical cases where your opponent has already won. And finally, for 3-3 and even scores after that, we fill in T to signify the value is computed directly by calling &lt;code&gt;two_wins_in_a_row&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We want to end up with a function signature like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;game_win_prob_with_scores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which will return the value in the table cell indexed by &lt;code&gt;score_you&lt;/code&gt; and &lt;code&gt;score_opponent&lt;/code&gt;. Then the probability of you winning a game will be &lt;code&gt;game_win_prob_with_scores(point_prob, 0, 0)&lt;/code&gt;. From the table that we filled in, we can already implement some simple cases&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;two_wins_in_a_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(We noticed that in fact T is the probability already when the game is 2-2)&lt;/p&gt;

&lt;p&gt;How to handle the remaining cases? We notice that if the game is now at score &lt;code&gt;x&lt;/code&gt;-&lt;code&gt;y&lt;/code&gt;, the probability is &lt;code&gt;point_prob&lt;/code&gt; that it will next be &lt;code&gt;x+1&lt;/code&gt;-&lt;code&gt;y&lt;/code&gt;, and &lt;code&gt;1 - point_prob&lt;/code&gt; that it will be &lt;code&gt;x&lt;/code&gt;-&lt;code&gt;y+1&lt;/code&gt;. So if we know the values in the table right and below some cell, we can compute the value in that cell from those other two values. And in the table we see the "border" going from 4-0 to 4-2 to 3-3 to 2-4 to 0-4 that will let us eventually compute any value to the top and left of it.&lt;/p&gt;

&lt;p&gt;So now we can write the code for the &lt;code&gt;else&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;point_prob&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;game_win_prob_with_scores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;game_win_prob_with_scores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the border in the table is implemented by the first three conditions in the &lt;code&gt;if&lt;/code&gt;-&lt;code&gt;elif&lt;/code&gt; chain, and both recursive calls go towards the border, the recursion will eventually terminate (always make sure recursion terminates, especially in cases like this where it's not so obvious at first glance).&lt;/p&gt;

&lt;p&gt;There is still one problem with the code. Say we start from &lt;code&gt;score_you == 0&lt;/code&gt; and &lt;code&gt;score_opponent == 0&lt;/code&gt;. The left recursive call will have parameters &lt;code&gt;score_you == 1&lt;/code&gt; and &lt;code&gt;score_opponent == 0&lt;/code&gt; and the right recursive call &lt;code&gt;score_you == 0&lt;/code&gt; and &lt;code&gt;score_opponent == 1&lt;/code&gt;. The problem is that from both these calls, the function will be called with parameters &lt;code&gt;score_you == 1&lt;/code&gt; and &lt;code&gt;score_opponent == 1&lt;/code&gt;, so there will be duplicate work.&lt;/p&gt;

&lt;p&gt;This could be solved by rewriting the function to be non-recursive and to process the table cells in an order so that each cell is computed only when its dependencies to the right and down are known. A simpler way is to apply &lt;em&gt;memoization&lt;/em&gt; to the recursive version. This is especially easy in Python, since there is a decorator in &lt;code&gt;functools&lt;/code&gt; that converts any function into a memoized version. So we add&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;lru_cache&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;at the top, and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="nd"&gt;@lru_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;before the function &lt;code&gt;game_win_prob_with_scores&lt;/code&gt; and we're done.&lt;/p&gt;

&lt;p&gt;From the game-winning probability we can use the same technique to convert it into a set-winning probability. The winner of a set is the first player to reach 6 won games with the other having at most 4, or reach 7-5, or win the tie break at 6-6. The tie break is the same as a game, except it's played until 7 points.&lt;/p&gt;

&lt;p&gt;The match is easiest to handle by special-casing the three different possibilities, 3-0, 3-1, and 3-2.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;match_win_prob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;set_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set_win_prob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;three_nil_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;set_prob&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="n"&gt;three_one_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;set_prob&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;set_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;three_two_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;set_prob&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;set_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;three_nil_prob&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;three_one_prob&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;three_two_prob&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The multipliers 3 and 6 count in how many ways each type of win can happen. For instance, in a 3-1 win the 1 set won by the opponent can be the first, second, or third.&lt;/p&gt;

&lt;p&gt;And this brings us to the final graph, of showing how the rules of tennis amplify even small differences in point-winning probability to make it much more certain for even a slightly stronger player to win a match.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F960oogpx6oy35fcabn0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F960oogpx6oy35fcabn0x.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was of course somewhat simplified, since in real tennis the probability of winning a point is not constant. Especially having the serve is a major advantage. It would be possible to perform a similar analysis, considering two different probabilities, but that would be unlikely to demonstrate any additional interesting point.&lt;/p&gt;
&lt;h2&gt;
  
  
  Appendix
&lt;/h2&gt;

&lt;p&gt;Here is the full code for the tennis example, refactored to take advantage of game and tie break being exactly the same, except for a different target number of wins.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;lru_cache&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;two_wins_in_a_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;point_prob&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;point_prob&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@lru_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;simple_win_prob_with_scores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;two_wins_in_a_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;point_prob&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;simple_win_prob_with_scores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;simple_win_prob_with_scores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tie_break_win_prob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;simple_win_prob_with_scores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;game_win_prob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;simple_win_prob_with_scores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@lru_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_win_prob_with_scores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nf"&gt;elif &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;tie_break_win_prob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;game_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;game_win_prob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;game_prob&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;set_win_prob_with_scores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;game_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;set_win_prob_with_scores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_you&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score_opponent&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_win_prob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;set_win_prob_with_scores&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;match_win_prob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;set_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set_win_prob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;three_nil_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;set_prob&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="n"&gt;three_one_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;set_prob&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;set_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;three_two_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;set_prob&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;set_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;three_nil_prob&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;three_one_prob&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;three_two_prob&lt;/span&gt;

&lt;span class="n"&gt;p_range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;linspace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;probs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;match_win_prob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;p_range&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_range&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;probs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Probability of Winning Match&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Probability of Winning a Point&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;display&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>math</category>
      <category>probability</category>
      <category>python</category>
    </item>
    <item>
      <title>Detecting Cute Animals with Machine Learning</title>
      <dc:creator>Jaakko Kangasharju</dc:creator>
      <pubDate>Wed, 17 Apr 2019 00:17:39 +0000</pubDate>
      <link>https://dev.to/vorahsa/detecting-cute-animals-with-machine-learning-df2</link>
      <guid>https://dev.to/vorahsa/detecting-cute-animals-with-machine-learning-df2</guid>
      <description>&lt;h4&gt;
  
  
  Training and building a custom image classifier mobile app
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IgnFyszt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Azv7jrZ0GneRCGjEw-gb1oA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IgnFyszt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Azv7jrZ0GneRCGjEw-gb1oA.jpeg" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/photos/e9ZJpC8P0UY?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Álvaro Niño&lt;/a&gt; on &lt;a href="https://unsplash.com/search/photos/cute?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a data scientist colleague of mine recently found out I have a background in mobile app development, he asked me to show him how to use a machine learning model in a mobile app. I figured an image classification app, like the classic &lt;a href="https://medium.com/@timanglade/how-hbos-silicon-valley-built-not-hotdog-with-mobile-tensorflow-keras-react-native-ef03260747f3"&gt;Not Hotdog&lt;/a&gt;, would be a nice example, since it also requires hooking into the phone’s camera and is not just a model running on a phone instead of desktop.&lt;/p&gt;

&lt;p&gt;It took me a while to piece together everything that I needed to make a finished app. That’s why I thought writing a post would be useful, so that the full journey is documented in one place. All the code that I wrote, fetching the training images, training the model, and the app itself, is &lt;a href="https://github.com/asharov/cute-animal-detector"&gt;available on Github&lt;/a&gt;. The code is comprehensively commented, since I wanted it to be useful for both data scientists and mobile app developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to Build?
&lt;/h3&gt;

&lt;p&gt;At first I thought I’d just build a hot dog detector, and found a &lt;a href="https://towardsdatascience.com/building-the-hotdog-not-hotdog-classifier-from-hbos-silicon-valley-c0cb2317711f"&gt;post that went through the model building part&lt;/a&gt;. But I don’t have many hot dogs around, so demoing such an app would be difficult. Since I like cute animals, I decided instead to make a cuteness detector, which, as a more abstract concept, can be demoed in many everyday environments.&lt;/p&gt;

&lt;p&gt;For the source of training data, I picked &lt;a href="http://image-net.org/"&gt;ImageNet&lt;/a&gt;. I decided to take puppy and kitten pictures as my “cute” training data, and somewhat arbitrarily, creepy-crawlies and ungulates as my “not cute” training data. After filtering out non-available images, I was left with 1718 cute images and 1962 non-cute images. Verifying visually that I had good data definitely validated the choice of creepy-crawlies as “not cute”. Brrrrr…&lt;/p&gt;

&lt;h3&gt;
  
  
  Transfer Learning with Inception
&lt;/h3&gt;

&lt;p&gt;For custom image classification, &lt;a href="https://machinelearningmastery.com/transfer-learning-for-deep-learning/"&gt;transfer learning&lt;/a&gt; is the way to go. Training an image classifier from scratch would take ages and any network architecture I could come up with couldn’t be better than current state of the art. I picked InceptionV3, since I found a &lt;a href="https://medium.com/abraia/first-steps-with-transfer-learning-for-custom-image-classification-with-keras-b941601fcad5"&gt;good article with example code&lt;/a&gt; of how to make a custom image classifier with it.&lt;/p&gt;

&lt;p&gt;The key points in building the model are to take the InceptionV3 model as base, add a new classification layer, and limit the training to the new layer. Since the training data comes from ImageNet, the InceptionV3 constructor is told to use those weights. The other parameter says to exclude the top layer, which does the actual classification, since I will be using my own layer for that.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Nowadays, image classification has moved away from using pooling after each convolutional layer and to &lt;a href="https://alexisbcook.github.io/2017/global-average-pooling-layers-for-object-localization/"&gt;using global average pooling at the end&lt;/a&gt;. So the base InceptionV3 model is extended with a GAP layer, and finally a softmax classification layer with two nodes, since there are two classes to predict, cute and not cute. In the final step, all the layers from the base InceptionV3 model are marked as not trainable so that the training applies only to the added custom layers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--luYJtGx3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ADKnUmPuydSJ4wkL2A-nwgQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--luYJtGx3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ADKnUmPuydSJ4wkL2A-nwgQ.jpeg" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/photos/iJ9o00UeAWk?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Liudmyla Denysiuk&lt;/a&gt; on &lt;a href="https://unsplash.com/search/photos/cute?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Data for Training
&lt;/h3&gt;

&lt;p&gt;I had downloaded my training images, and now there is a little bit of work to prepare them for training. A good way to set up data for Keras is to make directories for each of training, validation, and testing data sets. Then, in each of these directories, create a subdirectory for each label and put the images for each label in the corresponding subdirectory. So I have directories “cute” and “notcute”. This lets Keras automatically pick up the labels during training.&lt;/p&gt;

&lt;p&gt;Augmenting the data is always a good idea when doing image classification. Keras comes with the ready-made &lt;a href="https://keras.io/preprocessing/image/"&gt;ImageDataGenerator&lt;/a&gt; that can be used to automatically augment image data with different transformations.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The ImageDataGenerator is initialized with parameters on which kinds of transformations it should perform. I didn’t pick any that would introduce distortions, since a distortion might well remove cuteness. InceptionV3 expects the pixels to be in the range [-1, 1], and it provides the function &lt;code&gt;preprocess_input&lt;/code&gt; to perform the min-max scaling to this range. This function is passed to the generator so that the generated images will be in the appropriate format.&lt;/p&gt;

&lt;p&gt;The generator is then set to produce the images from the image directory. The parameter dataset can be either “train” or “test”, depending on whether to generate training or validation data. The image size 299x299 is what is expected by InceptionV3.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Model
&lt;/h3&gt;

&lt;p&gt;Training the model with the generators is simple enough. Five epochs appears suitable for this task. In fact, I didn’t see improvement after the third epoch. With these parameters, the training on my Macbook took about 2.5 hours, which I felt was quite reasonable.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The last line above, &lt;code&gt;model.save('iscute.h5')&lt;/code&gt;, saves the model in the HDF5 format used by Keras. This is not the format used for mobile apps. For Android, I chose to use tensorflow-lite. (I wanted to make an iOS app using CoreML, but I was using Python 3.7, and the CoreML tools do not yet support that version.) Tensorflow comes with the appropriate converter, so there is not very much code here. The HDF5 file I got is 84 MB in size, while the tensorflow-lite file is 83 MB, so there was not much size reduction.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The only headache here was discovering the need for the &lt;code&gt;input_shapes&lt;/code&gt; parameter. On my initial run without that parameter, I got the error message “&lt;code&gt;ValueError: None is only supported in the 1st dimension. Tensor 'input_1' has invalid shape '[None, None, None, 3]'&lt;/code&gt;”. The required dimensions are the image dimensions 299x299, and the error message says what tensor name to use as the key in the dictionary parameter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3uZbCMJV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AW62c0mCeEeANwiYQ3xGMUA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3uZbCMJV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AW62c0mCeEeANwiYQ3xGMUA.jpeg" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/photos/adK3Vu70DEQ?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Ricky Kharawala&lt;/a&gt; on &lt;a href="https://unsplash.com/search/photos/cute?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Reading the Camera on Android
&lt;/h3&gt;

&lt;p&gt;Now I had my model ready to use in the app, so it was time to build the app. It needs to do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Show the camera preview on screen&lt;/li&gt;
&lt;li&gt;Let the user press a button to capture the current image&lt;/li&gt;
&lt;li&gt;Feed the captured image to the classifier&lt;/li&gt;
&lt;li&gt;Display the result after classification is complete&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The parts I didn’t yet know how to do were 1 and 3. Furthermore, the Android camera API had changed, and many tutorials I could find were still written for the old, deprecated version. Luckily I did find &lt;a href="https://android.jlelse.eu/the-least-you-can-do-with-camera2-api-2971c8c81b8b"&gt;one post that detailed everything that needs to be done&lt;/a&gt;. There is quite a bit of code needed to access the camera image, so I won’t show most of it here. &lt;a href="https://github.com/asharov/cute-animal-detector"&gt;Check the repository&lt;/a&gt; for the full code.&lt;/p&gt;

&lt;p&gt;To prepare for image capture requires getting a &lt;code&gt;CameraManager&lt;/code&gt;, asking the user for permission to use the camera (unless already granted), and getting a reference to a &lt;code&gt;TextureView&lt;/code&gt; that will contain the preview. A real app would need to prepare for the camera permission not being granted as well.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The camera cannot display anything until the &lt;code&gt;TextureView&lt;/code&gt; is available, so the app may have to wait for that by setting a listener. Afterwards the app needs to find an appropriate camera (the back-facing one here) and open it.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;openCamera()&lt;/code&gt; method passes in a state callback. When the camera is opened, this callback will be informed, and it can trigger creating a preview session with the camera so that the camera image is available when the user presses the evaluation button.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M0qDbhxz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AGRFRSoKKlI02naMy8GrRTg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M0qDbhxz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AGRFRSoKKlI02naMy8GrRTg.jpeg" alt=""&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/photos/sssxyuZape8?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Jairo Alzate&lt;/a&gt; on &lt;a href="https://unsplash.com/search/photos/cute?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Making Classifications on Android
&lt;/h3&gt;

&lt;p&gt;To figure out how to make the classifications, I briefly looked at the &lt;a href="https://www.tensorflow.org/lite/guide"&gt;Tensorflow-lite guide&lt;/a&gt;. While it helped to get started, it was much less help with the actual details of the code. For the details, I went through the &lt;a href="https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/java/demo"&gt;sample app that does image classification&lt;/a&gt;, and extracted out the specific bits I needed out of the very general code.&lt;/p&gt;

&lt;p&gt;Initializing the classifier means loading the model into memory from the app’s assets, and creating the Tensorflow &lt;code&gt;Interpreter&lt;/code&gt; object out of that. The input for an image classifier is usefully given as a &lt;code&gt;ByteBuffer&lt;/code&gt; and it’s so large that it makes sense to pre-allocate it during initialization.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;When running the classifier on an image, it’s important to perform it in a background thread. It takes a few seconds to run, and holding up the UI thread for that long is simply not acceptable. The first thing to do is to create the input in the correct format in the buffer: Scale the image to 299x299 and run the min-max scaling to get the pixel values to the range [-1, 1], as expected by InceptionV3.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And finally, the classifier can be run. The type of the &lt;code&gt;run()&lt;/code&gt; method is not very helpful, as it just takes an &lt;code&gt;Object&lt;/code&gt; as both input and output. As mentioned above, for image classification, &lt;code&gt;ByteBuffer&lt;/code&gt; is a good input format. The output in this classification case is a one-element array, the element of which is an array of floats whose size is the number of classes. After the run, this array of floats will be filled with the probability of each class. So in my case, it is a two-element array, with the elements being the probabilities of “cute” and “not cute”.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;I did not find a way to determine which class corresponds to which index in the array at this stage. So the class-to-index mapping would need to be extracted from the Keras model, and possibly included as data in the app, in case the mapping is not stable between different training runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;In the end, it was not too difficult to build an image classifier app. It’s cross-cutting across a few areas, so the information is not all in one place, but when you look, it’s all available somewhere. The app I built is of course only a demo, but knowing the principles and methods now, I am confident that I could add this kind of functionality in a real app too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ehcniU_V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Adz70Cd6df2tndP3pJasYaA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ehcniU_V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Adz70Cd6df2tndP3pJasYaA.png" alt=""&gt;&lt;/a&gt;Screenshot of the app evaluating a dog&lt;/p&gt;

&lt;p&gt;Yep, I think my model works well.&lt;/p&gt;




</description>
      <category>machinelearning</category>
      <category>android</category>
      <category>cute</category>
    </item>
    <item>
      <title>My Developer Interviewing Principles</title>
      <dc:creator>Jaakko Kangasharju</dc:creator>
      <pubDate>Wed, 10 Apr 2019 12:21:42 +0000</pubDate>
      <link>https://dev.to/vorahsa/my-developer-interviewing-principles-3k67</link>
      <guid>https://dev.to/vorahsa/my-developer-interviewing-principles-3k67</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A8AZBfbxYrzk8W2lcgMDLpg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A8AZBfbxYrzk8W2lcgMDLpg.jpeg"&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/photos/RDYdOvk8ats?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Kaleidico&lt;/a&gt; on &lt;a href="https://unsplash.com/search/photos/meeting?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a senior developer, a part of my job is giving technical interviews to candidates for developer positions at &lt;a href="https://futurice.com/" rel="noopener noreferrer"&gt;Futurice&lt;/a&gt;. Developer interviewing is quite a well-discussed topic around the Internet, and over time, following these discussions and thinking what I want to see, I’ve developed something of a style. For this post, I’ve attempted to distill this style into a set of principles that I try to follow whenever I’m interviewing.&lt;/p&gt;

&lt;h3&gt;
  
  
  An Interview Is Not an Exam
&lt;/h3&gt;

&lt;p&gt;In an interview, I am evaluating your developer knowledge. This is similar to an exam, which is also about evaluating your knowledge. But an exam is limited to a specific area of knowledge, which makes it possible to “cram” for it. An interview should cover a much wider area than an exam, and also be more interactive than a typical exam.&lt;/p&gt;

&lt;p&gt;In fact, I think if it’s possible or necessary to cram for an interview, it’s a bad interview. My task in the interview is to determine whether the candidate has the skills that my company needs of a developer. If I give an interview that can be crammed for and “passed” without possessing the needed skills in reality, I’m making a potentially costly mistake.&lt;/p&gt;

&lt;p&gt;This principle I followed already when I was first looking for a developer position. I never prepared for the interviews. I just went in and was myself, answering questions based on what I could think up on the spot, not giving prepared answers. And I feel like I would not want to work in a company where that method does not work.&lt;/p&gt;

&lt;h3&gt;
  
  
  An Interview Is Not an Interrogation
&lt;/h3&gt;

&lt;p&gt;Some interviewers seem to treat the interview like an interrogation. They ask questions in a rapid-fire style, expect only short answers, and work off a specific list that they have. This is not how I like to do things. I try to get you to talk most of the time, as I feel that’s how I can get the best evaluation out of you. And I try to pick up specific points from your answers and ask follow-up questions about those, making the interview more of a discussion than a Q&amp;amp;A session.&lt;/p&gt;

&lt;p&gt;That doesn’t mean I don’t have a list of prepared questions. Working off a specific list, with good ideas of what kinds of answers are expected, helps ensure that the evaluation is performed similarly for different candidates. The difference is, my questions don’t usually have simple answers, so they serve more as discussion starters, with potential for following up to whatever level you’re proficient at.&lt;/p&gt;

&lt;h3&gt;
  
  
  There Should Be No One Right Answer
&lt;/h3&gt;

&lt;p&gt;Software development is not an exact science. And even if it was, the amount of variables is so large that even very similar problems can call for very different solutions. It is very rare that a practical question could have just one answer that is correct every time. So I prefer open-ended questions with many possible solutions to questions where I would be hunting for a very specific response.&lt;/p&gt;

&lt;p&gt;Yes, this means I won’t be asking specifics about language syntax or libraries. I do expect a developer to have a working knowledge of these, but that knowledge is better demonstrated by writing code, not answering questions about them. When I care, say, about some library functionality, I prefer to introduce the general problem, discuss that, and see if you’re aware of the common libraries or methods to solve that problem. I won’t expect you to know the precise functions to call off the top of your head, let alone their syntax, but awareness of the functionality could be expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Opinions Are Not the Only Correct Ones
&lt;/h3&gt;

&lt;p&gt;Even keeping in mind the previous point, we developers do tend to pick favorites. For common problems, we have specific solutions that we tend to reach for, assuming they’re not infeasible. I acknowledge that both I and you will have these favorites, and we may have picked different ones. As long as you show familiarity with your solution, including its potential drawbacks, I won’t hold it against you if we didn’t pick the same one.&lt;/p&gt;

&lt;p&gt;Typically, you won’t even find out my opinion on whatever I asked about. Knowing what I like can easily bias you to giving that answer even if you prefer something else. And that will tell me less about how you think. So I try to keep my questions neutral, I try to be aware of the drawbacks of any of the potential solutions and ask about how you would address them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sometimes I Ask Bad Questions
&lt;/h3&gt;

&lt;p&gt;I don’t actually have a principle to ask bad questions occasionally… But we’re all human, and no matter what lofty principles I try to follow, I will make missteps. The important thing for me is to recognize I asked a bad question, put the blame on myself and not penalize you for not comprehending what I was going for, and learn from it for the future.&lt;/p&gt;

&lt;p&gt;The most important thing, when I notice I asked a bad question, is to put you back at ease. Not being able to answer an interview question can easily feel like you’ve failed. So I won’t just move on. I will try to rephrase what I was asking, clarify if needed, and if it seems appropriate, admit that it wasn’t a good question. We’re not robots, and it’s good for me as an interviewer to also admit I can make mistakes.&lt;/p&gt;

&lt;h3&gt;
  
  
  I Don’t Care How Well You Function under Pressure
&lt;/h3&gt;

&lt;p&gt;A job interview is a stressful situation. But that’s not what the work itself should be like. If you’re constantly under stress at work, there is something wrong in the environment that should be fixed. So putting you under pressure in the interview is not going to tell me anything useful about you. That means, for instance, no live coding. It wouldn’t be a typical environment for you, and I think the time can be spent better.&lt;/p&gt;

&lt;p&gt;Keeping in mind all the points made above should also help in relieving the pressure. When I treat the interview more as a discussion, ask questions prompted by what you say, and don’t make you recall minutia, that usually contributes to making you more at ease. But I still need to observe, and try to adapt if you begin to look stressed, because not everyone is the same, and not everyone has the same pressure thresholds.&lt;/p&gt;

&lt;h3&gt;
  
  
  But I Do Want to See Your Code
&lt;/h3&gt;

&lt;p&gt;To be hired as a developer, you should know how to code. As I said above, I don’t think making you code in the interview is going to show that usefully. But I still need to evaluate your coding ability. So that means, you need to provide some code of yours for me to see. And this should be done before the interview so that I can consider it properly.&lt;/p&gt;

&lt;p&gt;I don’t care so much where the code is from. It can be something you’ve already done, or it can be a coding exercise specifically for the interview made on your own time. Whatever suits you best. I won’t require you to have an extensive existing open-source portfolio ready to show, and I consider the calls to require such to be a bad idea in general.&lt;/p&gt;

&lt;p&gt;Much of the interview will be spent on looking at your code together. I will look for typical good coding practices. I will ask you to explain why you designed the code the way you did. I will ask about functionality that you might have to implement next. And I will ask about specific pieces of code where I see multiple reasonable ways to accomplish the goal.&lt;/p&gt;

&lt;p&gt;Because of what I want to ask about, my preference is for a short, 2–3-hour, specific coding exercise. Such an exercise would be defined to require functionality that we typically encounter in real projects, and I can ask about the same questions of everyone. Also, since you wrote the code shortly before the interview, it’s likely to be clearer in your mind. But as long as the code you provide has the interesting functionality, and is written to show your skills instead of being a quick hack, it should be just fine.&lt;/p&gt;

</description>
      <category>software</category>
      <category>interview</category>
      <category>developer</category>
    </item>
    <item>
      <title>Fixing Regressions with Git</title>
      <dc:creator>Jaakko Kangasharju</dc:creator>
      <pubDate>Tue, 12 Mar 2019 15:06:49 +0000</pubDate>
      <link>https://dev.to/vorahsa/fixing-regressions-with-git-4k79</link>
      <guid>https://dev.to/vorahsa/fixing-regressions-with-git-4k79</guid>
      <description>&lt;p&gt;I'm sure many of us have been there. A bug has appeared and you're sure it was working a couple of weeks ago. But it's a big project, with hundreds of commits and thousands of changes since then. Luckily Git has some tools to help track down exactly when and why that breaking change was introduced.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;git blame&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The simpler tool is &lt;code&gt;git blame&lt;/code&gt;. This can be used when you know which code is causing the problem, and you only need to find the commit to figure out why the change was made. For example, say the code in &lt;code&gt;boggles.lang&lt;/code&gt; looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;refresh_button.enable()
frequency = compute_frequency(next_time)
baffler.aeronate(frequency)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and the problem is that &lt;code&gt;refresh_button&lt;/code&gt; is disabled.&lt;/p&gt;

&lt;p&gt;Now, perhaps you're familiar with the code. Maybe you even wrote most of this functionality. You go "Of course! Aeronating the baffler starts the norigator and the refresh button is disabled whenever the norigator is running." So you know that the problem is with the line&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;baffler.aeronate(frequency)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  First Attempt
&lt;/h3&gt;

&lt;p&gt;Of course you can't just go deleting this line. Someone added it for some specific purpose. This is where &lt;code&gt;git blame&lt;/code&gt; comes in. You start with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git blame boggles.lang
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and browse to the line&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;baffler.aeronate(frequency)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It will show up as something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;30376ff1 (Tomasz 2018-10-23 14:56:11 +0100  65) baffler.aeronate(frequency)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This line has the following pieces of information, in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The commit identifier&lt;/li&gt;
&lt;li&gt;The author of the commit&lt;/li&gt;
&lt;li&gt;The date and time of the commit&lt;/li&gt;
&lt;li&gt;The line number&lt;/li&gt;
&lt;li&gt;The contents of the line itself&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What this tells you is the last commit that touched this line. That doesn't mean that it was the commit that introduced the problem. To check the commit, you would run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git show 30376ff1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;which might tell you something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit 30376ff158bd8713fbf3308efcc6a46d0b9cdf57
Author: Tomasz &amp;lt;tomasz@company.example&amp;gt;
Date:   Tue Oct 23 14:56:11 2018 +0100

    Clean up trailing whitespace
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is not very helpful...&lt;/p&gt;

&lt;h3&gt;
  
  
  Look Only at Actual Changes
&lt;/h3&gt;

&lt;p&gt;Whitespace changes are practically never what I'm looking for when trying to track down a regression, so that's why I use the &lt;code&gt;-w&lt;/code&gt; option of &lt;code&gt;git blame&lt;/code&gt; to ignore whitespace changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git blame -w boggles.lang
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now the output might look like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3f056217 (Lucia 2018-10-19 10:22:41 +0100  65) baffler.aeronate(frequency)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Again going in with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git show 3f056217
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and scrolling to the appropriate line in the diff might now reveal something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; refresh_button.enable()
-fq = compute_frequency(next_time)
-baffler.aeronate(fq)
+frequency = compute_frequency(next_time)
+baffler.aeronate(frequency)

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is a diff that shows only that the local variable &lt;code&gt;fq&lt;/code&gt; was renamed to &lt;code&gt;frequency&lt;/code&gt; but the issue-causing aeronation is still present. We must dig deeper.&lt;/p&gt;

&lt;h3&gt;
  
  
  Go Further Back in History
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;git blame&lt;/code&gt; has a nice feature where it can begin looking from any specific commit. We now know that the issue we're tracking was introduced before commit &lt;code&gt;3f056217&lt;/code&gt;, so we can run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git blame -w 3f056217^ boggles.lang
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;(The &lt;code&gt;^&lt;/code&gt; is important; it tells to start the search from the parent of 3f056217. You can also use &lt;code&gt;~&lt;/code&gt; instead of &lt;code&gt;^&lt;/code&gt; to get the parent; the differences of those two do not matter in this case.)&lt;/p&gt;

&lt;p&gt;This sequence is already familiar. Scroll to the appropriate line in the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;7e3d2394 (Peter 2018-10-17 14:02:01 +0100  65) baffler.aeronate(fq)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Check the commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git show 7e3d2394
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Scroll down to the code in the diff:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; refresh_button.enable()
+fq = compute_frequency(next_time)
+baffler.aeronate(fq)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And success! Now we have found the commit that introduced the problematic aeronation.&lt;/p&gt;

&lt;p&gt;So what next? Now I would read the commit message of &lt;code&gt;7e3d2394&lt;/code&gt;, and potentially the whole commit diff, to determine what functionality the commit introduced, and try to figure out how to reconcile those needs with the needs of the refresh button to stay enabled. If I could not figure that out, I would likely have a chat with Peter who introduced the aeronation to see if together we could come up with something. A typical case might be that the aeronation is doing too much and does not in fact need to start the norigator in all cases. This would be handled by splitting the method into two and calling only the aeronation part here.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;git bisect&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;What if you can't tell where the problematic code is? You can tell that the code has changed since it was first written, but you have no idea how aeronating the baffler could have anything to do with the refresh button, so that's not where your mind goes first. And there have been many changes here recently, so it's not that there's only one change to consider.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up Bisection
&lt;/h3&gt;

&lt;p&gt;Git itself can help you here. The &lt;code&gt;git bisect&lt;/code&gt; command is made for exactly this situation. If you know a commit that is "good" and another later commit that is "bad", you just tell &lt;code&gt;git bisect&lt;/code&gt; that, and it will let you look through commits between them until you zero in on the one that caused the change from good to bad.&lt;/p&gt;

&lt;p&gt;The way I begin this process is by first running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git log --first-parent
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and look for some commit where the feature was still working. Of course I can't tell that by just reading the log, so I mostly rely on the commit timestamps and my memory of when things were definitely fine.&lt;/p&gt;

&lt;p&gt;(The &lt;code&gt;--first-parent&lt;/code&gt; option makes &lt;code&gt;git log&lt;/code&gt; show commits only on the current branch, not commits from other branches that have been merged. It's useful for this kind of case.)&lt;/p&gt;

&lt;p&gt;Let's say I picked commit &lt;code&gt;7102f250&lt;/code&gt; as a likely one where the feature was still working. Now I will do&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout 7102f250
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and test this version. If I discover that the feature is not working, I will make a note that it was broken already on this version and go back in the logs until I find a working version.&lt;/p&gt;

&lt;p&gt;After I have a working version, it's time to start bisecting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git bisect start
git bisect bad master
git bisect good 7102f250
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;(If I discovered some earlier commit where the feature was already broken, I would place that commit's identifier in place of &lt;code&gt;master&lt;/code&gt; on the &lt;code&gt;git bisect bad master&lt;/code&gt; line.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Evaluating Commits
&lt;/h3&gt;

&lt;p&gt;After executing all these statements, there will be output something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bisecting: 105 revisions left to test after this (roughly 7 steps)
[3f056217e73576fc6ba8f0cb8c277627fd154e43] &amp;lt;Imagine useful commit message here&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This means that git has determined there are 105 possible versions that could have introduced the bug. It estimates needing 7 tries to determine the actual version (the search is binary-search-like so it needs roughly the binary logarithm of the number of possibilities). And it has checked out a suitable version to try.&lt;/p&gt;

&lt;p&gt;Now that git has checked out a version to test, you need to do exactly that. Test whether this now-checked-out version has the bug or not. If it does, tell git&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git bisect bad
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If it works correctly, tell git&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git bisect good
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And if for some reason you can't determine whether the bug is present or not, you can do&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git bisect skip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For instance, if the version has another bug that prevents you from getting to where your bug of interest is, you would need to use &lt;code&gt;skip&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Continuing with this process, testing each version that git checks out and marking it as &lt;code&gt;good&lt;/code&gt; or &lt;code&gt;bad&lt;/code&gt;, should eventually end up with something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;7e3d2394e73576fc6ba8f0cb8c277627fd154e43 is the first bad commit
commit 7e3d2394e73576fc6ba8f0cb8c277627fd154e43
Author: Peter &amp;lt;peter@company.example&amp;gt;
Date:   Wed Oct 17 14:02:01 2018 +0100

    Make sure baffler is aeronated...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we found the same commit that we found earlier with &lt;code&gt;git blame&lt;/code&gt;. The &lt;code&gt;bisect&lt;/code&gt; process is more time-consuming, requiring testing of each version, but it also requires less manual work, since git is taking care of checking out versions to test.&lt;/p&gt;

&lt;p&gt;Finally, after finding where the problem was introduced, you should make a note of the commit identifier and run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git bisect reset
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;to stop the bisection process.&lt;/p&gt;

&lt;h2&gt;
  
  
  More &lt;code&gt;git bisect&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;git bisect&lt;/code&gt; is not limited to finding regressions. It is a general tool to locate any change that happened because of a single commit. The "testing" phase doesn't need to involve running the program if the &lt;code&gt;good&lt;/code&gt;/&lt;code&gt;bad&lt;/code&gt; distinction can be made by inspecting the code. Also, when not specifically finding regressions, you can use the aliases &lt;code&gt;old&lt;/code&gt;/&lt;code&gt;new&lt;/code&gt; for &lt;code&gt;good&lt;/code&gt;/&lt;code&gt;bad&lt;/code&gt;, so that you don't get confused by the labels if, say, you're instead tracking down when a specific bug was fixed.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git blame&lt;/code&gt; is useful for finding when a specific line was added. But it cannot be used to find when a specific line was deleted. It occasionally happens that I notice a crucial line of code is no longer present, so I want to know when and why it was deleted. &lt;code&gt;git bisect&lt;/code&gt; is a good tool for this: A version containing the line is &lt;code&gt;good&lt;/code&gt; and a version not containing it is &lt;code&gt;bad&lt;/code&gt;. With this, bisecting will find the commit that deleted the line.&lt;/p&gt;

&lt;p&gt;There are even more features. You can record the bisect session with &lt;code&gt;log&lt;/code&gt; and get to the same state later with &lt;code&gt;replay&lt;/code&gt;. Editing the log in between lets you change your &lt;code&gt;bad&lt;/code&gt;/&lt;code&gt;good&lt;/code&gt; decisions, for instance if you made a mistake. It is also possible to use a script for the testing part with &lt;code&gt;run&lt;/code&gt;, which makes the whole bisection process completely automatic. I rarely find a use for this, since for me the testing is typically unique to the issue, and trying to script it would often be more work. But in some cases, e.g., failing automated tests, it can be useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bisect Stories
&lt;/h2&gt;

&lt;p&gt;It's never as simple as in a constructed example specifically made to demonstrate how &lt;code&gt;bisect&lt;/code&gt; works. In one of my projects, the result was often very unstable and didn't seem to find the actual problematic commit. Turned out, the compilation process didn't properly recompile everything that was needed after &lt;code&gt;bisect&lt;/code&gt; checked out another commit. That's when I learned to run &lt;code&gt;git clean -dxf&lt;/code&gt; between &lt;code&gt;bisect&lt;/code&gt; steps to make sure I had a clean tree. Usually it's enough to do a regular clean build and installation, though.&lt;/p&gt;

&lt;p&gt;Other times, it's happened that I haven't been able to find a working version, no matter how far back I go. That's usually caused by a toolchain update on my computer, or sometimes a dependency update that doesn't get rolled back when checking out earlier code. This can cause serious self-doubt when you first encounter it, but after a few times, you start recognizing it. It helps immensely if the dependency management system uses lock files, or if you specify precise versions for them (some teams ensure this by committing also the dependencies), so that the historical system behavior remains reproducible.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git bisect skip&lt;/code&gt; is there when some commit has other problems that make it not testable. But if you end up skipping many commits, &lt;code&gt;git bisect&lt;/code&gt; may in the end just tell you "The problem is in one of these 10 commits". One common situation where this can happen is when a feature branch has been &lt;code&gt;rebase&lt;/code&gt;d and the main branch has changed something that new functionality on the feature branch depended on, making all the older commits on the feature branch unusable.&lt;/p&gt;

&lt;p&gt;But after the obvious potential problems are dealt with, I find I can trust &lt;code&gt;git bisect&lt;/code&gt; fully. One case that has stuck with me was when &lt;code&gt;bisect&lt;/code&gt; pointed to a commit that seemed to have nothing to do with the bug. I did rerun the bisection but got the same result. After accepting that, yes, this was the commit that introduced the bug, I dug deeper and discovered that the change had in fact exposed a race condition that already existed in the code. This might have blown up in our face at any time. Being able to focus on just a small change discovered with &lt;code&gt;bisect&lt;/code&gt; greatly reduced the time to fix that bug, and perhaps we never would have figured it out without that focus.&lt;/p&gt;

</description>
      <category>git</category>
      <category>tips</category>
      <category>bug</category>
    </item>
    <item>
      <title>Introducing Git Hammer: Statistics for Git Repositories</title>
      <dc:creator>Jaakko Kangasharju</dc:creator>
      <pubDate>Mon, 21 Jan 2019 08:37:19 +0000</pubDate>
      <link>https://dev.to/vorahsa/introducing-git-hammer-statistics-for-git-repositories-3ej9</link>
      <guid>https://dev.to/vorahsa/introducing-git-hammer-statistics-for-git-repositories-3ej9</guid>
      <description>&lt;p&gt;I've been feeling that every time I'm in a longer project, I end up putting together some simple shell scripts to gather statistics from the project git repository. These were always ad-hoc, needed to be tailored to each specific project, and the only way to follow the progress of the numbers was to run them and save the output.&lt;/p&gt;

&lt;p&gt;Recently I had some free time, so I decided to finally write a proper program to do this stuff for me. It can now do everything that my ad-hoc scripts previously (actually somewhat more even), it stores whatever it computes so it takes very little time to keep up to date, and I'm happy enough with the functionality and code that I'm comfortable releasing it. So, meet &lt;a href="https://github.com/asharov/git-hammer" rel="noopener noreferrer"&gt;Git Hammer&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does It Do?
&lt;/h2&gt;

&lt;p&gt;The main thing that my scripts always had was the count of lines per person. Essentially, the script would run &lt;code&gt;git blame&lt;/code&gt; on every source file and add all the counts together. This is, in a way, the core of Git Hammer also. Another thing is to count all the tests and group those too by person. But Git Hammer knows about all the commits, so it can do many kinds of statistics based only on the commits and not their contents. At least in theory; I haven't yet implemented much.&lt;/p&gt;

&lt;p&gt;One nice feature is support for multi-repository projects. In the project where I was working when I first began to plan Git Hammer, we had the main app, but also several support libraries in other repositories. These libraries were being developed by the same team, but they were separated to allow other projects to use them too. So it makes sense to combine all these repositories under one set of statistics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graphs
&lt;/h2&gt;

&lt;p&gt;Let's take a look at some graphs that Git Hammer can draw. I'm using the &lt;a href="https://github.com/thepracticaldev/dev.to"&gt;dev.to repository&lt;/a&gt; for these. First, let's look at line counts per author:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3s6cagzx3dmpd1qxfvt3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3s6cagzx3dmpd1qxfvt3.png" alt="Graph of line counts per author over time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, that certainly displays the case where existing code was imported into a new repository. It's also not a very good graph: The legend with the author names is covering part of the data, and not nearly all authors are displayed. Running this kind of program on a repository with many many contributors can definitely uncover problems.&lt;/p&gt;

&lt;p&gt;How many tests are getting written? Let's look at just the raw test counts this time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flw8c1ylnn7p7hw2rubw6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flw8c1ylnn7p7hw2rubw6.png" alt="Graph of test counts over time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks like a nice development. New tests are being written along new code.&lt;/p&gt;

&lt;p&gt;We can also look at when the commits are happening. There is one graph for days of the week, and another for hours of the day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lvw6iyvjsoeeun0sx8o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lvw6iyvjsoeeun0sx8o.png" alt="Histogram of commits per day of week"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fiku33tgpnwl42a7b73a5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fiku33tgpnwl42a7b73a5.png" alt="Histogram of commits per hour of day"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks like primarily of a day job: The majority of commits happen Monday to Friday, roughly during business hours.&lt;/p&gt;

&lt;p&gt;By the way, this last graph uncovered a bug. I had been very happy with my graphs, but when I first saw the hour-of-day graph for dev.to, it was showing most activity happening in the night. Of course, this was a time zone issue: At some point in the processing, the commit times got converted to my local time zone (Berlin). Since most of the commits happen in New York, this pushed the times 6 hours ahead. So I did what seems to be the most common advice: I store the time zone associated with the commit explicitly in the database, and then use that when reading for display.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Long Does It Take to Run?
&lt;/h2&gt;

&lt;p&gt;Running &lt;code&gt;git blame&lt;/code&gt; on every file in the repository probably sounds like it takes a long time. And it can. The main repository of my old project requires about 4 minutes. Of course, Git Hammer doesn't run this from scratch for every commit. Rather, it uses diffs provided by git to adjust its counts only where they might have changed. Processing the dev.to repository (about 1300 commits, 70000 lines of code in the latest version) took only 6 minutes on my Macbook Pro.&lt;/p&gt;

&lt;p&gt;Larger repositories are a different case. My old project has over 33000 commits, maybe 250000 lines of code, and it takes over 12 hours to go through. Luckily, the process was using only about 20% of CPU and even towards the end well under 2 GB of memory, so I could keep working while it was running. Still, it may be that the time needed grows faster than the size of the repository, so trying a really massive repository is probably not a good idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Plans
&lt;/h2&gt;

&lt;p&gt;Git Hammer is already almost a usable library. That will likely be the next step: Fix things that don't make sense in a library, maybe add some configuration points if needed, and upload to &lt;a href="https://pypi.org/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt;. I have also a long-term hope to make a Web service that uses Git Hammer to display project statistics on the Web.&lt;/p&gt;

&lt;p&gt;Any contributions are welcome, starting from just ideas for features. The code base is also not very large, since a lot of the heavy lifting is handled by GitPython and SQLAlchemy. So it is probably comprehensible to many Python developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caution
&lt;/h2&gt;

&lt;p&gt;Deriving statistics from code is for entertainment purposes only. They have very little meaning, and none outside the specific project team, and should not be used as a basis for any decisions.&lt;/p&gt;

</description>
      <category>git</category>
      <category>tool</category>
      <category>software</category>
    </item>
    <item>
      <title>That new thing everyone is raving about? It's not so hot after all...</title>
      <dc:creator>Jaakko Kangasharju</dc:creator>
      <pubDate>Tue, 15 Jan 2019 14:51:36 +0000</pubDate>
      <link>https://dev.to/vorahsa/that-new-thing-everyone-is-raving-about-its-not-so-hot-after-all-2ho1</link>
      <guid>https://dev.to/vorahsa/that-new-thing-everyone-is-raving-about-its-not-so-hot-after-all-2ho1</guid>
      <description>&lt;p&gt;I'm sure you've heard of that new &amp;lt;best practice&amp;gt;? Everyone has been writing about it, saying how it's the best thing ever and you should immediately adopt it to remain relevant. Well, I've looked into it and I don't think it's that good. You probably shouldn't use it. Sure, sometimes it can be good but not always, so don't jump on the hype train without thinking.&lt;/p&gt;

&lt;p&gt;So how can you tell when to use &amp;lt;best practice&amp;gt;? Turns out, it's quite simple. Look at your situation. Does &amp;lt;best practice&amp;gt; make sense in your situation? If it does, use it. If it doesn't, don't use it. It's that simple! Now that you have an easy way to make that decision, hopefully the hype dies down and we can adopt the good parts of &amp;lt;best practice&amp;gt;.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
