How to Contribute

You can easily contribute a new ML model to PosDefManifoldML.jl package following these steps:

Let's say you want to contribute an ML model named ABC.

If the model act on the manifold of positive definite matrices (PSD), use as template the mdm.jl unit. If it acts on the tangent space, use as template the svm.jl unit (you can also check the enlr.jl unit).

Save the template unit as unit abc.jl in the same directory where the template file is.

Implementing your ABC model entails the following five steps:

1) Declare an abstract type for the model

if your model acts on the manifold of PSD matrices, this will be

abstract type ABCmodel<:PDmodel end

if your model act on the tangent space, this will be

abstract type ABCmodel<:TSmodel end

2) Declare the struct to hold the model and its default creator

This will look like:

mutable struct ABC <: ABCmodel
		metric        :: Metric
		defaultkwarg1 :: Type_of_defaultkwarg1
		defaultkwarg2 :: Type_of_defaultkwarg2
		...
		voidkwarg1
		voidkwarg2
		...
		function ABC(metric :: Metric=Fisher;
				defaultkwarg1 :: Type_of_defaultkwarg1 = default_value,
				defaultkwarg2 :: Type_of_defaultkwarg2 = default_value,
				...
				voidkwarg1 = nothing,
				voidkwarg2 = nothing,
				...)
			new(metric, defaultkwarg1, defaultkwarg1,...,
				voidkwarg1, voidkwarg2,...)
		end
end

In the above example defaultkwarg are essential parameters that should be set by default upon creation. Use as few as those as needed to obtain a working ML model when the user does not pass any argument.

The voidkwarg arguments are arguments that you wish be accessible to the user in the structure once the model has been fitted. Include here as few of them as possible.

3) write the fit function

This function will fit the model. Its default behavior should fit the model and tune hyperparameters in order to find the best model by cross-validation if the ABC model has hyperparameters.

Your fit function declaration will look like:

function fit(   model :: ABCmodel,
			    𝐏Tr :: ℍVector,
				# if the model acts on the tangent space use:
				# 𝐏Tr :: Union{ℍVector, Matrix{Float64}}
				yTr :: Vector;
				verbose :: Bool = true
				kwarg1  :: type-of-kwarg1 = default-value,
				kwarg2  :: type-of-kwarg2 = default-value,
				...)
end

Here you can use as many kwarg arguments as you wish. Currently, all ML models have a verbose argument. Your fit function should starts with:

⌚=now() # time in milliseconds
ℳ=deepcopy(model) # output model

and ends with

verbose && println(defaultFont, "Done in ", now()-⌚,".")
return ℳ

In between these two blocks you will fit the model and write into the voidkwarg arguments you have declared in the ABC struct (see above).

Keep in mind that if the model acts in the tangent space you will need to project the data therein. For doing so you can use the _getFeat_fit! internal function (declared in unit tools.jl), as it is done for the ENLR and SVM models. This entails using some standard arguments for tangent space projection, which should be given as options to the user, as done for these models. See how this is done in the fit function of the ENLR and SVM models (unit enlr.jl and svm.jl, respectively).

Once you have finished, a call such as

m1 = fit(ABC(), PTr, yTr)

for some data PTr(a matrix or ℍVector type) and labels yTr (see fit) should fit the model in such a way that it is ready to allow a call to the predict function and return the model.

4) write the predict function

Your predict function declaration will look like:

function predict(model   :: ABCmodel,
				𝐏Te :: Union{ℍVector, Matrix{Float64}},
				# if the model acts on the tangent space use:
				# 𝐏Tr :: Union{ℍVector, Matrix{Float64}}
				what :: Symbol = :labels;
				kwarg1 :: type_of_kwarg1 = default_value
				kwarg2 :: type_of_kwarg2 = default_value
				...
				verbose  :: Bool = true)

where in general here you will not need kwarg arguments.

Your predict function should starts with:

⌚=now() # time in milliseconds

and ends with

verbose && println(defaultFont, "Done in ", now()-⌚,".")
verbose && println(titleFont, "\nPredicted ",_what2Str(what),":", defaultFont)
return 🃏

where in between these two blocks variable 🃏 has been filled with the prediction.

To use this template code, in the declaration of the _what2Str function (declared in unit tools.jl), add a line to allow returning the full name of your model as a string.

As for the fit function, if the model acts in the tangent space you will need to project the data therein. For doing so you can use here the _getFeat_Predict! internal function (declared in unit tools.jl), as it is done for the ENLR and SVM models. This entails using some standard arguments for tangent space projection, which should be given as options to the user, as done for these models. Note that _getFeat_Predict! is similar, but not the same as _getFeat_fit! function.

All ML models implemented so far allow three types of prediction, depending on the symbol passed by the user with argument what. See the documentation of the predict function for the ENLR model to see what these three types of predictions are and the code of the function in unit enlr.jl for an example on how to compute them. Note that the returned type of the predict function, the variable 🃏, depends on what is predicted.

5) Allow the cvAcc function to support your model properly

If you have been following these guidelines so far, the cvAcc function (declared in unit cv.jl) will be able to perform k-fold cross-validation on data using your ABC model.

This function allows the user to pass an arbitrary number of optional keyword arguments, so the user will be able to pass here the kwarg arguments you have declared in your fit function for the ABC model and those will be passed to the fit function when fitting the model at each fold.

This will be done automatically, however it is necessary to prevent the user from passing here optional keyword arguments that should not be used in a k-fold cross-validation setting (if in your fit function you have declared such arguments).

Also, it is necessary here to overwrite into the ABC model that is passed by the user as argument to the cvAcc function the defaultkwarg1 fields of the ABC struct you have declared, if the user can request different values for those fields using optional keyword arguments passed to the cvAcc function. For example, consider the SVM model; suppose the user pass to the cvAcc function a default SVM model. For such a model the kernel is a radial basis kernel. Suppose the user has passed to the cvAcc function argument kernel=linear. If you don't overwrite the kernel into the model, the radianl basis kernel will be used instead of the desired linear kernel.

You can do so easily using the _rmArgs and _getArgValue internal functions (declared in unit tools.jl), as done for the SVM and ENLR model (see the code of the cvAcc function).