Can I pass a type as a parameter to this function?

280 Views Asked by At

The following code, mostly copied from

http://accord-framework.net/docs/html/T_Accord_MachineLearning_VectorMachines_Learning_SequentialMinimalOptimization.htm

works fine.

module SVMModule

open Accord.MachineLearning
open Accord.MachineLearning.VectorMachines
open Accord.MachineLearning.VectorMachines.Learning
open Accord.Statistics.Kernels
open Accord.Math.Optimization.Losses

// open MathNet.Numerics.LinearAlgebra.Matrix

let inputs = [| [| 0.; 0. |]; [| 0.; 1. |]; [| 1.; 0. |]; [| 1.; 1. |] |]
let xor = [| 0; 1; 1; 0 |]
/// Creates and trains a Support Vector Machine given inputs and outputs.
/// The kernel can be Linear, Gaussian, or Polynomial.
/// The default tolerance is 1e-2.
let train (C: float) (tol: float) (inputs: float [] []) =
    let learn = SequentialMinimalOptimization<Gaussian>()

    learn.UseComplexityHeuristic <- true
    learn.UseKernelEstimation <- true
    if C >= 0. then learn.Complexity <- C
    if tol > 0. then learn.Tolerance <- tol

    let svm = learn.Learn(inputs, xor)
    svm

let svm = train 0.5 1e-2 inputs
let prediction = svm.Decide inputs

printfn "SVM_0 Prediction: %A" prediction

I would like to implement a polymorphic version of train, something like

let train (kernel: string) (C: float) (tol: float) (inputs: float [] []) =
    let learn =
        if kernel = "Gaussian" then 
           SequentialMinimalOptimization<Gaussian>()
        else
           SequentialMinimalOptimization<Linear>()
    // More code

This does not work because an if expression must return objects of the same type in all its branches.

I wonder if there is a way to pass Linear or Gaussian as a type to train (these are indeed types) so that I do not have to write one train function for each type (trainGaussian and trainLinear). Akso, even if I took the trouble of writing these separate functions, I guess it would be difficult to call them at runtime depending on a user choice, as the same problem with an if statement would rear its ugly head.

I have implemented polymorphism in F# using interfaces, but with classes I built myself. These classes are in Accord.NET and even though they inherit from a base class I was not able to handle the type issue and implement polymorphism.

Thanks for any suggestions.

1

There are 1 best solutions below

3
On BEST ANSWER

It should be straightforward to simply replace the concrete type Gaussian with a type parameter like 't (and, optionally, add it as an explicit type parameter to train). I've cleaned up your existing code very slightly while doing so:

let train<'t> (C: float) (tol: float) (inputs: float [] []) =
    let learn = SequentialMinimalOptimization<'t>(UseComplexityHeuristic = true, UseKernelEstimation = true)
    if C >= 0. then learn.Complexity <- C
    if tol > 0. then learn.Tolerance <- tol

    learn.Learn(inputs, xor)

Then at the call-site, there will need to be some way for the compiler to know what type to use, either by passing it in explicitly:

let svm = train<Gaussian> 0.5 1e-2 inputs

or by relying on type inference to flow the types from another part of your program:

let svm:Gaussian = train 0.5 1e-2 inputs