From 3e70083b35de741823d549a71b66a363d2b16f9d Mon Sep 17 00:00:00 2001
From: Eugene Gusarov <eugene.gusarov@devfactory.com>
Date: Tue, 12 Mar 2019 11:46:27 +0300
Subject: [PATCH 1/2] added an extension method for saving statically typed
 model (#1286)

---
 .../ModelOperationsCatalog.cs                 | 23 +++++++++++++++++++
 .../Api/CookbookSamples/CookbookSamples.cs    |  2 +-
 2 files changed, 24 insertions(+), 1 deletion(-)
 create mode 100644 src/Microsoft.ML.StaticPipe/ModelOperationsCatalog.cs

diff --git a/src/Microsoft.ML.StaticPipe/ModelOperationsCatalog.cs b/src/Microsoft.ML.StaticPipe/ModelOperationsCatalog.cs
new file mode 100644
index 0000000000..4481b80abe
--- /dev/null
+++ b/src/Microsoft.ML.StaticPipe/ModelOperationsCatalog.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO;
+
+namespace Microsoft.ML.StaticPipe
+{
+    public static class ModelOperationsCatalog
+    {
+        /// <summary>
+        /// Save statically typed model to the stream.
+        /// </summary>
+        /// <param name="catalog">The model explainability operations catalog.</param>
+        /// <param name="model">The trained model to be saved.</param>
+        /// <param name="stream">A writeable, seekable stream to save to.</param>
+        public static void Save<TInShape, TOutShape, TTransformer>(this ML.ModelOperationsCatalog catalog, Transformer<TInShape, TOutShape, TTransformer> model, Stream stream)
+            where TTransformer : class, ITransformer
+        {
+            catalog.Save(model.AsDynamic, stream);
+        }
+    }
+}
diff --git a/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamples.cs b/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamples.cs
index b60afc07f2..4bbf27b1b5 100644
--- a/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamples.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamples.cs
@@ -149,7 +149,7 @@ private void TrainRegression(string trainDataPath, string testDataPath, string m
             using (var stream = File.Create(modelPath))
             {
                 // Saving and loading happens to 'dynamic' models, so the static typing is lost in the process.
-                mlContext.Model.Save(model.AsDynamic, stream);
+                mlContext.Model.Save(model, stream);
             }
 
             // Potentially, the lines below can be in a different process altogether.

From b929023c814a9b1ed94a842c25d38a02fe6bd574 Mon Sep 17 00:00:00 2001
From: Eugene Gusarov <gusarov.email@gmail.com>
Date: Sat, 30 Mar 2019 12:52:15 +0300
Subject: [PATCH 2/2] added an extension method for saving statically typed
 model - adjust api changes & documentation example

---
 .../experimental/MlNetCookBookStaticApi.md    |  2 +-
 .../ModelOperationsCatalog.cs                 | 23 ----------
 .../ModelOperationsCatalogExtensions.cs       | 42 +++++++++++++++++++
 .../Api/CookbookSamples/CookbookSamples.cs    |  2 +-
 4 files changed, 44 insertions(+), 25 deletions(-)
 delete mode 100644 src/Microsoft.ML.StaticPipe/ModelOperationsCatalog.cs
 create mode 100644 src/Microsoft.ML.StaticPipe/ModelOperationsCatalogExtensions.cs

diff --git a/docs/code/experimental/MlNetCookBookStaticApi.md b/docs/code/experimental/MlNetCookBookStaticApi.md
index 086e3b8e3b..99ce4a8d3c 100644
--- a/docs/code/experimental/MlNetCookBookStaticApi.md
+++ b/docs/code/experimental/MlNetCookBookStaticApi.md
@@ -397,7 +397,7 @@ Here's what you do to save the model to a file, and reload it (potentially in a
 
 ```csharp
 // Saving and loading happens to 'dynamic' models, so the static typing is lost in the process.
-mlContext.Model.Save(model.AsDynamic, trainData.AsDynamic.Schema, modelPath);
+mlContext.Model.Save(model, trainData, modelPath);
 
 // Potentially, the lines below can be in a different process altogether.
 
diff --git a/src/Microsoft.ML.StaticPipe/ModelOperationsCatalog.cs b/src/Microsoft.ML.StaticPipe/ModelOperationsCatalog.cs
deleted file mode 100644
index 4481b80abe..0000000000
--- a/src/Microsoft.ML.StaticPipe/ModelOperationsCatalog.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.IO;
-
-namespace Microsoft.ML.StaticPipe
-{
-    public static class ModelOperationsCatalog
-    {
-        /// <summary>
-        /// Save statically typed model to the stream.
-        /// </summary>
-        /// <param name="catalog">The model explainability operations catalog.</param>
-        /// <param name="model">The trained model to be saved.</param>
-        /// <param name="stream">A writeable, seekable stream to save to.</param>
-        public static void Save<TInShape, TOutShape, TTransformer>(this ML.ModelOperationsCatalog catalog, Transformer<TInShape, TOutShape, TTransformer> model, Stream stream)
-            where TTransformer : class, ITransformer
-        {
-            catalog.Save(model.AsDynamic, stream);
-        }
-    }
-}
diff --git a/src/Microsoft.ML.StaticPipe/ModelOperationsCatalogExtensions.cs b/src/Microsoft.ML.StaticPipe/ModelOperationsCatalogExtensions.cs
new file mode 100644
index 0000000000..c42b983edc
--- /dev/null
+++ b/src/Microsoft.ML.StaticPipe/ModelOperationsCatalogExtensions.cs
@@ -0,0 +1,42 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO;
+using Microsoft.ML.Data;
+
+namespace Microsoft.ML.StaticPipe
+{
+    public static class ModelOperationsCatalogExtensions
+    {
+        /// <summary>
+        /// Save statically typed model to the stream.
+        /// </summary>
+        /// <param name="catalog">The model explainability operations catalog.</param>
+        /// <param name="model">The trained model to be saved. Note that this can be <see langword="null"/>, as a shorthand
+        /// for an empty transformer chain. Upon loading with <see cref="ML.ModelOperationsCatalog.Load(Stream, out DataViewSchema)"/> the returned value will
+        /// be an empty <see cref="TransformerChain{TLastTransformer}"/>.</param>
+        /// <param name="dataView">The data view with the schema of the input to the transformer. This can be <see langword="null"/>.</param>
+        /// <param name="stream">A writeable, seekable stream to save to.</param>
+        public static void Save<TInShape, TOutShape, TTransformer>(this ML.ModelOperationsCatalog catalog, Transformer<TInShape, TOutShape, TTransformer> model, DataView<TInShape> dataView, Stream stream)
+            where TTransformer : class, ITransformer
+        {
+            catalog.Save(model?.AsDynamic, dataView?.AsDynamic.Schema, stream);
+        }
+
+        /// <summary>
+        /// Save statically typed model to the stream.
+        /// </summary>
+        /// <param name="catalog">The model explainability operations catalog.</param>
+        /// <param name="model">The trained model to be saved. Note that this can be <see langword="null"/>, as a shorthand
+        /// for an empty transformer chain. Upon loading with <see cref="ML.ModelOperationsCatalog.Load(Stream, out DataViewSchema)"/> the returned value will
+        /// be an empty <see cref="TransformerChain{TLastTransformer}"/>.</param>
+        /// <param name="dataView">The data view with the schema of the input to the transformer. This can be <see langword="null"/>.</param>
+        /// <param name="filePath">Path where model should be saved.</param>
+        public static void Save<TInShape, TOutShape, TTransformer>(this ML.ModelOperationsCatalog catalog, Transformer<TInShape, TOutShape, TTransformer> model, DataView<TInShape> dataView, string filePath)
+            where TTransformer : class, ITransformer
+        {
+            catalog.Save(model?.AsDynamic, dataView?.AsDynamic.Schema, filePath);
+        }
+    }
+}
diff --git a/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamples.cs b/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamples.cs
index cce1de27b5..dc66dd570d 100644
--- a/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamples.cs
+++ b/test/Microsoft.ML.Tests/Scenarios/Api/CookbookSamples/CookbookSamples.cs
@@ -147,7 +147,7 @@ private void TrainRegression(string trainDataPath, string testDataPath, string m
             var metrics = mlContext.Regression.Evaluate(model.Transform(testData), label: r => r.Target, score: r => r.Prediction);
 
             // Saving and loading happens to 'dynamic' models, so the static typing is lost in the process.
-            mlContext.Model.Save(model.AsDynamic, trainData.AsDynamic.Schema, modelPath);
+            mlContext.Model.Save(model, trainData, modelPath);
 
             // Potentially, the lines below can be in a different process altogether.