# Creating Additional Plugins

The &kst; plugin API is currently being revised. The latest documentation about the new plugin scheme can be found at http://websvn.kde.org/trunk/extragear/graphics/kst/devel-docs/plugins/

For reference, the following sections describe the old plugin programming interface.

## Creating a Basic Plugin

A &kst; plugin consists of two files—an XML file and a shared object file.

### The XML File

The XML file provide information about the plugin and describes its inputs and outputs. The following is an example of an XML file for a &kst; plugin:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE Module SYSTEM "file:/Repository/Level2/Soft/ProC/moduledef.dtd">

    <module>

    <intro>
    <modulename name="testplugin"/>    <!-- The name of the module -->
    <author name="Rick Chern"/> <!-- The name of the author -->
    <description text="A test plugin for me"/> <!-- A description of the module -->
    <version minor="1" major="0"/>  <!-- The version number of the module -->
    <state devstate="release"/>     <!-- The development state of the module (optional)-->
    </intro>

    <interface>

    <!--inputs here-->
    <input>
    <table type="float" name="Input Vector 1" descr="The first input vector" />
    </input>

    <input>
    <float name="Input Scalar 1" descr="The first input scalar" />
    </input>

    <!--outputs here-->
    <output>
    <table type="float" name="Output Vector 1" descr="The first output vector" />
    </output>

    <output>
    <float name="Output Scalar 1" descr="The first output scalar" />
    </output>

    </interface>

    </module>

Generally, you can use the example above as a template and modify sections to fit your needs. As can be seen from the example, the XML file consists of one `module` node. The `module` node has an `intro` node and an `interface` node. You should modify the `intro` node appropriately using the comments in the above XML file as guides. It is important that `modulename` has the `name` attribute set to the same name that your shared object file will use.

The `interface` node describes the actual inputs and outputs of the plugin. Each input is described by an `input` node, and each output is described by an `output` node. Each input or output should have either a `table` or a `float` node as a child. The `type` attribute of a `table` must be set to `"float"`. Note that the order of the inputs and outputs matters—the order is used to determine the index values for each input and output array of the object file, and is the same order used to display the input and output fields in the &kst; plugin interface.

Once you have completed the XML file, save it as `[modulename].xml`, where `[modulename]` is the value of the `name` attribute of the `modulename` node.

### The Shared Object File

The shared object file contains the actual functionality of the plugin. In other words, it determines how to derive the outputs from the given inputs. The following are the requirements for the shared object file:

- The object must export the following C linkage symbol:

      int symbol(const double *const inArrays[],
                 const int inArrayLens[],
                 const double inScalars[],
                 double *outArrays[],
                 int outArrayLens[],
                 double outScalars[])

  where symbol must be the value of the `name` attribute of the `modulename` node in the XML file. This is the only function that will be called by &kst; (although you may have other functions). The following describes each argument of this function:

  `const double *const inArrays[]`

    The array of input arrays. Each input array corresponds to an input vector. The arrays are in the same order as the vectors are listed in the XML file, so `inArrays[0]` is the array representing the first input vector, `inArrays[1]` is the array representing the second input vector, and so on.

  `const int inArraysLens[]`

    The array containing array lengths for each input array. The lengths are stored in the same order as the arrays in `inArrays[]` (e.g. `inArrayLens[0]` is the length of `inArrays[0]`).

  `const double inScalars[]`

    The array of input scalars. The scalars are stored in the same order as they are listed in the XML file.

  `double *outArrays[]`

    The array of output arrays. Each output array corresponds to an output vector, and the arrays should be in the same order as the output vectors are listed in the XML file.

  `int outArrayLens[]`

    The array that should contain lengths of the output arrays. The lengths should be stored in the same order as the arrays in `outArrays[]`.

  `double outScalars[]`

    The array of output scalars. The scalars should be in the same order they are listed in the XML file.

- The function must return `0` if it executed succesfully, and `-1` otherwise.

- The code for the object file must handle unexpected inputs, such as empty input arrays (in most cases a return value of `-1` would be sufficient when such situations are encountered).

- The number and type of outputs must be exactly as specified by the XML file.

- You will probably need to resize the arrays in `outArrays[]`. To do so, use the `realloc()` function. E.g.,

      outArrays[0]=(double*)realloc(outArrays[0], 5*sizeof(double));

  will allocate space for 5 doubles for `outArrays[0]`. Do not use any memory allocator other than `realloc()`.

- The input arguments must remain constant. Do not cast them to non-constant types.

The following is an example of the shared object file source code for a simple plugin:

    #include <stdlib.h>

    extern "C" int testplugin(const double *const inArrays[], const int inArrayLens[],
                    const double is[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[]);

    int testplugin(const double *const inArrays[], const int inArrayLens[],
                    const double is[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[])

    //Accept 1 vector and 1 scalar. Multiply all elements of the vector by the
    //scalar, and output the resulting vector. Also output the original scalar.
    {
    	//Set the outputs
    	outArrayLens[0]=inArrayLens[0];

    	//resize the output arrays
    	outArrays[0]=(double*)realloc(outArrays[0], inArrayLens[0]*sizeof(double));

    	//multiply each element of the input vector
    	//by the scalar
    	for (int i=0; i<inArrayLens[0]; i++)
    	{
    		outArrays[0][i]=inArrays[0][i] * is[0];
    	}

    	//now set the output scalar
    	outScalars[0]=is[0];

    	return 0;
    }

### Compiling the Plugin

If you are using &gcc; to compile your plugin, simply compile the object file:

    cc -Wall -c -o myplugin.o myplugin.c -fPIC -DPIC

and then create the shared library:

    ld -o myplugin.so -shared myplugin.o

The resulting `*.so` file and `*.xml` file must be put in the same directory. When you use &kst;'s Plugin Manager to load the XML file, it will automatically look for the shared object file in the same directory.

## Creating Linear Fit Plugins

To create a linear fit plugin, you could implement your own fitting algorithms and output the appropriate vectors. However, &kst; already comes with header files that make it easy for you to implement linear least-squares fit plugins by just providing a few functions. This section will describe how to take advantage of these files.

### Header Files

Two header files are provided for performing linear fits, `linear.h` (for unweighted linear fits) and `linear_weighted.h` (for weighted linear fits). They are both located under `kst/plugins/fits/` in the &kst; source tarball. To use these files, include only one of them in the source code for your plugin:

    #include <../linear.h>

or

    #include <../linear_weighted.h>

(by convention, we will place the source code for the plugin one directory below where the header files are).

### Implementing Required Functions

Given a general linear model:

where `y` is a vector of `n` observations, `X` is an `n` by `p` matrix of predictor variables, and `c` is the vector of `p` best-fit parameters that are to be estimated, the header files provide functions for estimating `c` for a given `y` and `X`. To provide `X`, the following function needs to be implemented in the source code for the plugin:

    double calculate_matrix_entry( double dX, int iPos )

This function should return the value of the entry in column `iPos` of the matrix of predictor variables, using `x` value `dX`. This function will be called by linear.h or linear_weighted.h. The implementation of this function depends on the model you wish to use for the fit, and is unique to each linear fit plugin. For example, to fit to a polynomial model, `calculate_matrix_entry` could be implemented as follows:

    double calculate_matrix_entry( double dX, int iPos ) {
      double dY;
      dY = pow( dX, (double)iPos );
      return dY;
    }

### Calling the Fitting Functions

Once the appropriate header file has been included and `calculate_matrix_entry` has been implemented, call the appropriate fitting function included from the header file:

    kstfit_linear_unweighted( inArrays, inArrayLens,
                              outArrays, outArrayLens,
                              outScalars, iNumParams );

or

    kstfit_linear_weighted( inArrays, inArrayLens,
                            outArrays, outArrayLens,
                            outScalars, iNumParams );

Each function will return `0` on success, or `-1` on error, so it is a good idea to set the return value of the exported C function to be equal to the return value of the fitting function. To maintain simplicity, the code for the plugin can simply pass the arguments given to the exported C function to the fitting function. Note, however, that inArrays must be structured as follows:

- `inArrays[0]` must contain the array of x coordinates of the data points

- `inArrays[1]` must contain the array of y coordinates of the data points

- `inArrays[2]` only exists if `kstfit_linear_weighted` is being called, and must contain the array of weights to use for the fit.

The easiest way to ensure that inArrays is structured correctly is to specify the correct order of input vectors in the XML file for the plugin.

`iNumParams` is the number of parameters in the fitting model used, which should be equal to the number of columns in the matrix `X` of predictor variables. `iNumParams` must be set correctly before the fitting function is called.

After `kstfit_linear_unweighted` or `kstfit_linear_weighted` is called, `outArrays` and `outScalars` will be set as follows:

- `outArrays[0]` will contain the array of fitted y values.

- `outArrays[1]` will contain the array of residuals.

- `outArrays[2]` will contain the array of best-fit parameters that were estimated.

- `outArrays[3]` will contain the covariance matrix, returned row after row in an array.

- `outScalars[0]` will contain chi^2/nu, where chi^2 is the weighted sum of squares of the residuals, and nu is the degrees of freedom.

`outArrayLens` will be correctly set to indicate the length of each output array.

Ensure that the specified outputs in the XML file match those that the exported C function returns (which in most cases will simply be the outputs returned by the fitting function).

### Example

The following is an example of the source code for a linear fit plugin.

    /*
     *  Polynomial fitting plugin for KST.
     *  Copyright 2004, The University of British Columbia
     *  Released under the terms of the GPL.
     */

    #include "../linear.h"

    double calculate_matrix_entry( double dX, int iPos ) {
      double dY;

      dY = pow( dX, (double)iPos );

      return dY;
    }

    extern "C" int kstfit_polynomial_unweighted(
      const double *const inArrays[],
      const int inArrayLens[],
      const double inScalars[],
      double *outArrays[], int outArrayLens[],
      double outScalars[]);

    int kstfit_polynomial_unweighted(
      const double *const inArrays[],
      const int inArrayLens[],
    	const double inScalars[],
    	double *outArrays[], int outArrayLens[],
    	double outScalars[])
    {
      int iRetVal = -1;
      int iNumParams;

      iNumParams = 1 + (int)floor( inScalars[0] );
      if( iNumParams > 0 ) {
        iRetVal = kstfit_linear_unweighted( inArrays, inArrayLens,
                                            outArrays, outArrayLens,
                                            outScalars, iNumParams );
      }

      return iRetVal;
    }

## Creating Non-linear Fit Plugins

&kst; provides header files designed to simplify the creation of non-linear least-squares fit plugins. The following sections detail the use of the header files.

### Header Files and Definitions

The non-linear fit header files are located in `kst/plugins/fits_nonlinear` of the &kst; source tarball. The files are named `non_linear.h` and `non_linear_weighted.h` for unweighted and weighted fits, respectively. To use these files, include only one of them in the source code for your plugin:

    #include <../non_linear.h>

or

    #include <../non_linear_weighted.h>

(by convention, we will place the source code for the plugin one directory below where the header files are).

As non-linear fitting is an iterative process, you must also define the maximum number of iterations that should be performed. The non-linear fitting algorithm will stop when at least one of the following conditions is true:

- The maximum number of iterations has been reached.

- A precision of 10-4 has been reached.

In addition, you need to define the number of parameters in the model, as it is not passed to the fitting function explicitly. To define these two values, include the following at the top of your source code:

    #define NUM_PARAMS [num1]
    #define MAX_NUM_ITERATIONS [num2]

replacing `[num1]` with the number of parameters in the model, and `[num2]` with the maximum number of iterations to perform.

### Implementing Required Functions

To use the header files for non-linear fits, you must provide the function to use as the model, the partial derivatives of the function with respect to each parameter, and initial estimates of the best-fit parameters. To do this, three functions must be implemented. These functions are described below.

`double function_calculate( double `dX`, double* `pdParameters` )`

  This function calculates the y value of the fitting model for a given x value `dX`, using the supplied array of parameters `pdParameters`. The order of parameters in `pdParameters` is arbitrary, but should be consistent with the other two implemented functions. For example, for an exponential model, `function_calculate` could be implemented as follows:

      double function_calculate( double dX, double* pdParameters ) {
        double dScale	 = pdParameters[0];
        double dLambda = pdParameters[1];
        double dOffset = pdParameters[2];
        double dY;

        dY  = ( dScale * exp( -dLambda * dX ) ) + dOffset;

        return dY;
      }

`void function_derivative( double `dX`, double* `pdParameters`, double* `pdDerivatives` )`

  This function calculates the partial derivatives of the model function for a give value of x `dX`. The partial derivatives should be returned in `pdDerivatives`. The order of the partial derivatives in the array `pdDerivatives` should correspond to the order of the parameters in `pdParameters` (i.e. if `pdParameters[0]` contains the parameter lambda for an exponential model, `pdDerivatives[0]` should contain the derivative of the model with respect to lambda).

`void function_initial_estimate( const double* `pdX`, const double* `pdY`, int `iLength`, double* `pdParameterEstimates` )`

  This function provides an initial estimate of the best-fit parameters to the fitting function. The array of x values and y values of the data points are provided in `pdX` and `pdY` respectively, and the number of data points is provided by `iLength`. You can use any or none of these parameters at your discretion to calculate the initial estimate. The function should put the calculated initial estimates in `pdParameterEstimates`, with the order of the estimates corresponding to the order of the parameters in `pdParameters` of `function_calculate` and `function_derivative`. Keep in mind that the initial estimate is important in determining whether or not the fitting function converges to a solution.

### Calling the Fitting Functions

Once all the required functions have been implemented, the fitting function from the included header file can be called:

    kstfit_nonlinear( inArrays, inArrayLens,
    		  inScalars, outArrays,
    		  outArrayLens, outScalars );

or

    kstfit_nonlinear_weighted( inArrays, inArrayLens,
                               inScalars, outArrays,
                               outArrayLens, outScalars );

depending on whether you are implementing a non-weighted fit or a weighted fit.

The function will return `0` on success, or `-1` on error, so it is simplest to set the return value of the exported C function to be equal to the return value of the fitting function. To maintain simplicity, the code for the plugin can simply pass the arguments given to the exported C function to the fitting function. Note, however, that inArrays must be structured as follows:

- `inArrays[0]` must contain the array of x coordinates of the data points

- `inArrays[1]` must contain the array of y coordinates of the data points

- `inArrays[2]` only exists if `kstfit_linear_weighted` is being called, and must contain the array of weights to use for the fit.

The easiest way to ensure that inArrays is structured correctly is to specify the correct order of input vectors in the XML file for the plugin.

After `kstfit_linear_unweighted` or `kstfit_linear_weighted` is called, `outArrays` and `outScalars` will be set as follows:

- `outArrays[0]` will contain the array of fitted y values.

- `outArrays[1]` will contain the array of residuals.

- `outArrays[2]` will contain the array of best-fit parameters that were estimated.

- `outArrays[3]` will contain the covariance matrix, returned row after row in an array.

- `outScalars[0]` will contain chi^2/nu, where chi^2 is the weighted sum of squares of the residuals, and nu is the degrees of freedom.

`outArrayLens` will be correctly set to indicate the length of each output array.

Ensure that the specified outputs in the XML file match those that the exported C function returns (which in most cases will simply be the outputs returned by the fitting function).

### Example

The following is an example of a non-linear fit plugin that performs a fit to an exponential model.

    /*
     *  Exponential fit plugin for KST.
     *  Copyright 2004, The University of British Columbia
     *  Released under the terms of the GPL.
     */

    #define NUM_PARAMS 3
    #define MAX_NUM_ITERATIONS 500

    #include "../non_linear.h"

    void function_initial_estimate( const double* pdX, const double* pdY,
                                    int iLength, double* pdParameterEstimates ) {
      KST_UNUSED( pdX )
      KST_UNUSED( pdY )
      KST_UNUSED( iLength )

      pdParameterEstimates[0] =  1.0;
      pdParameterEstimates[1] =  0.0;
      pdParameterEstimates[2] =  0.0;
    }

    double function_calculate( double dX, double* pdParameters ) {
      double dScale	 = pdParameters[0];
      double dLambda = pdParameters[1];
      double dOffset = pdParameters[2];
      double dY;

      dY  = ( dScale * exp( -dLambda * dX ) ) + dOffset;

      return dY;
    }

    void function_derivative( double dX, double* pdParameters, double* pdDerivatives ) {
      double dScale	 = pdParameters[0];
      double dLambda = pdParameters[1];
      double dExp;
      double ddScale;
      double ddLambda;
      double ddOffset;

      dExp     = exp( -dLambda * dX );
      ddScale  = dExp;
      ddLambda = -dX * dScale * dExp;
      ddOffset = 1.0;

      pdDerivatives[0] = ddScale;
      pdDerivatives[1] = ddLambda;
      pdDerivatives[2] = ddOffset;
    }

    extern "C" int kstfit_exponential(const double *const inArrays[], const int inArrayLens[],
    		const double inScalars[],
    		double *outArrays[], int outArrayLens[],
    		double outScalars[]);

    int kstfit_exponential(const double *const inArrays[], const int inArrayLens[],
    		const double inScalars[],
    		double *outArrays[], int outArrayLens[],
    		double outScalars[])
    {
      return kstfit_nonlinear( inArrays, inArrayLens,
                               inScalars, outArrays,
    			   outArrayLens, outScalars );
    }

## Creating Pass Filter Plugins

&kst; provides header files to simplify the implementation of pass filter plugins. The use of these header files is described below.

### Header Files

The pass filter header file is located in `kst/plugins/pass_filters` of the &kst; source tarball. The file is named `filters.h` To use this file, include it in the source code for your plugin:

    #include <../filters.h>

(by convention, we will place the source code for the plugin one directory below where the header files are).

### Required Functions

The `filters.h` header file contains a single function that calculates the Fourier transform of a supplied function, applies the supplied filter to the Fourier transform, and then calculates the inverse Fourier transform of the filtered Fourier transform. To supply the filter, the following function needs to be implemented in the source code for your plugin:

`double filter_calculate( double `dFreqValue`, const double `inScalars[]` )`

This function should calculate the filtered amplitude for the frequency `dFreqValue`. `inScalars[]` will contain the unaltered input scalars for the plugin, specified in the XML file. Most likely `inScalars[]` will contain cutoff frequencies or other properties of the filter. For example, to implement a Butterworth high-pass filter, `filter_calculate` could be implemented as follows:

    double filter_calculate( double dFreqValue, const double inScalars[] ) {
      double dValue;
      if( dFreqValue > 0.0 ) {
        dValue = 1.0 / ( 1.0 +
        		pow( inScalars[1] / dFreqValue, 2.0 * (double)inScalars[0] ) );
      } else {
        dValue = 0.0;
      }
      return dValue;
    }

### Calling the Filter Function

Once the required `filter_calculate` has been implemented, the filter function from the header file can be called:

    kst_pass_filter( inArrays,
                     inArrayLens,
                     inScalars,
                     outArrays,
                     outArrayLens,
                     outScalars );

The arguments supplied to the exported C function can usually be passed to `kst_pass_filter` without modification. However, there are a few restrictions on the arguments:

- `inArrays[0]` must contain the array of data to filter.

- `inScalars` should contain the filter-specific parameters to be used by the `filter_calculate` function.

After the function call, `outArrays[0]` will contain the filtered array of data, and `outArrayLens` will be set appropriately. The `kst_pass_filter` function does not use `outScalars`.

### Example

The following is an example of a pass filter plugin that implements the Butterworth high-pass filter.

    /*
     *  Butterworth low pass filter plugin for KST.
     *  Copyright 2004, The University of British Columbia
     *  Released under the terms of the GPL.
     */

    #include <stdlib.h>
    #include <math.h>
    #include "../filters.h"

    extern "C" int butterworth_highpass(const double *const inArrays[], const int inArrayLens[],
    		const double inScalars[],
    		double *outArrays[], int outArrayLens[],
    		double outScalars[]);

    int butterworth_highpass(const double *const inArrays[], const int inArrayLens[],
    		const double inScalars[],
    		double *outArrays[], int outArrayLens[],
    		double outScalars[])
    {
      int iReturn;

      iReturn = kst_pass_filter( inArrays,
                                 inArrayLens,
                                 inScalars,
                                 outArrays,
                                 outArrayLens,
                                 outScalars );

      return iReturn;
    }

    double filter_calculate( double dFreqValue, const double inScalars[] ) {
      double dValue;

      if( dFreqValue > 0.0 ) {
        dValue = 1.0 / ( 1.0 + pow( inScalars[1] / dFreqValue, 2.0 * (double)inScalars[0] ) );
      } else {
        dValue = 0.0;
      }

      return dValue;
    }