Solved

Allow end users to make custom formulas

  • 8 July 2024
  • 1 reply
  • 8 views

Userlevel 5
Badge +6

I got a question from @José Fernando Bosch Moreno and I thought it would be interesting to share here.

I would like to develop an application for financial projections in which the user can add new parameters that contain formulas based in existing parameters. This is important because generally new calculations have to be developed by information specialist and the time to develop and include new formulations is long. I have developed information systems in which the user can add new calculations and I think AIMMS would be a good alternative to develop financial systems in which the user can add new formulation from the user interface without the need of an information specialist.

icon

Best answer by luispinto 8 July 2024, 18:27

View original

1 reply

Userlevel 5
Badge +6

This was my reply
 

I see this in two ways:

 

  1. Simple way

 

If the level of customization you require is not very deep, that is, if you don’t need to provide your end-user with all the freedom, I would think of an interface that allows them to add as many input parameters as they like (importing from somewhere) and selecting “pre-defined” coefficients and interactions between identifiers.

In other words, let’s say you have a set of input parameters

Set s_inputParameters {

     Index: i_inputParameter;

     Definition: data { 'Historical Demand of Client', 'Reported Growth by Client', 'Region GDP Growth' };

}

 

And you create a set and a parameter for keeping the coefficients

Set s_demandEquation {

     Index: i_demandEquation;

     Definition: {

          { 1..2}

     }

}





Parameter p_demandCoeficient {

     IndexDomain: (i_demandEquation,i_inputParameter);

     Definition: {

          data

          { ( 1, 'Historical Demand of Client' ) : 1.000,  ( 1, 'Reported Growth by Client'   ) : 1.000,

            ( 2, 'Historical Demand of Client' ) : 1.000,  ( 2, 'Region GDP Growth'           ) : 0.010 }

     }

}

 

you could generate a resulting output parameter (demand) based on these coefficients:

Parameter p_demand {

     IndexDomain: i_client;

     Definition: {

     sum((i_demandEquation,i_inputParameter),

              p_demandCoeficient(i_demandEquation, i_inputParameter)

              * p_inputParameter(i_inputParameter, i_client))

     }

}

 

 

Of course, my example is super simple and is only dealing with multiplications between input parameters, but you could add more coefficients types that change the input parameters in different ways, such as division and other mathematical operations.

Having the input parameters as a set, allows people to create and add new parameters on go, so that is also helpful.

 

  1. Full customization

 

The second way I can imagine this is really allowing your end user to input AIMMS code.

Having a string parameter that end users and type in what they would like to do, creating the run time identifiers and then compiling it and running it.

This presents challenges in itself, given that end users will now need to know AIMMS and you need to provide good feedback on compilation and/or runtime errors.

Here is an example of this:

StringParameter sp_calculateDemand {

     Definition: {
          "p_demand(i_client)
          :=
          p_inputParameter('Historical Demand of Client',i_client)
          * p_inputParameter('Reported Growth by Client',i_client)
          + p_inputParameter('Historical Demand of Client',i_client)
          * 0.01
          * p_inputParameter('Region GDP Growth',i_client);"
     }
}


Procedure pr_nothing;


Procedure pr_runTimeCalculate {

     Body: {

          ! If the library already exists, delete it.
          if 'calc_demand' in AllIdentifiers then
              _ep_library := 'calc_demand';
              me::Delete(_ep_library );
          endif;

          ! Create the library
          _ep_library := me::CreateLibrary(
               libraryName :  "calc_demand",
               prefixName  :  "cd");
        
          ! Create the procedure
          _ep_pr_calcDemand := me::Create(
               name     :  "pr_calcDemand",
               newType  :  'procedure',
               parentId :  _ep_library);
         
          ! Set the body of the procedure to the string parameter
          _p_ret := me::SetAttribute(
               runtimeId :  _ep_pr_calcDemand,
               attr      :  'body',
               txt       :  sp_calculateDemand);

          ! Compile the library
          _p_ret := me::Compile(_ep_library);
         
          ! run the procedure
          apply(_ep_pr_calcDemand);

     }

     ElementParameter _ep_library {

          Range: AllIdentifiers;

     }

     ElementParameter _ep_pr_calcDemand {

          Range: AllIdentifiers;

          Default: 'pr_nothing';

     }

     Parameter _p_ret;

}

 

If I run ‘pr_runTimeCalculate’ it will generate the runtime library ‘calc demand [runtime]’ and will execute the code in the string and fill out the demand.

This is what the runtime library looks like:

LibraryModule calc_demand {

     Prefix: cd;

     Procedure pr_calcDemand {

          Body: {

               p_demand(i_client)

               :=

          p_inputParameter('Historical Demand of Client',i_client)

               * p_inputParameter('Reported Growth by Client',i_client)

               +

          p_inputParameter('Historical Demand of Client',i_client)

               * 0.01

               * p_inputParameter('Region GDP Growth',i_client);

          }

     }

}

 

Of course, if you change the String again and run again, it will do something different.

 

Let me know your thoughts.

Reply


Didn't find what you were looking for? Try searching on our documentation pages:

AIMMS Developer & PRO | AIMMS How-To | AIMMS SC Navigator