This was my reply
I see this in two ways:
- 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.
- 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.