-
Notifications
You must be signed in to change notification settings - Fork 25
Structured Random Design
After getting to know the concept of structural parameters and code mappings, we will in this tutorial give some examples of combining structural and numeric parameters in one design, especially with respect to parameter hierarchies.
Using InPUT makes most sense when having multiple parameters and when they are a combination of structural and numeric ones. The design space for the JUnit tests contains all kinds of parameter definitions. A valid exported design is here. Here is the respective code mapping and in here you find the model classes. InPUT is non-invasive, none of the model classes require any InPUT specific interface or the like. By solely changing the code mapping, they could be exchanged by other means of implementation.
Given the first structural example
<SParam id="Decision">
<SChoice id="FirstChoice"/>
<SChoice id="SecondChoice"/>
<SChoice id="ThirdChoice"/>
</SParam>
there is nothing said about how Decision is implemented, nor it's choices. A valid alternative to the presented mapping in the former tutorial is to create an enum:
public enum Decision {
FIRST,SECOND,THIRD;
}
and define the mapping as in
<Mapping id="Decision" type="model.Decision" />
<Mapping id="Decision.FirstChoice" type="FIRST" />
<Mapping id="Decision.SecondChoice" type="SECOND" />
<Mapping id="Decision.ThirdChoice" type="THIRD" />
As a consequence, the parameter is now treated as an enumeration type.
Also structural parameters can be specified as array types. The only difference to numeric array definitions is that the type String does not contain a primitive datatype. Examples:
type="[10][2]" // two dimensional array with ten rows and two columns
type="[2][]" // two dimensional array with two rows and user defined amount of columns
type="[]" // one dimensional array with user defined amount of columns
Each unspecified dimension will in a random design lead to a the default number of entries, 1.
At times, we deal with parameters that carry along sub-parameters. For evolutionary algorithms, a typical example might be the tournament size for the tournament selection method. InPUT allows for structural parameters to contain sub-parameters of both, numeric, and structural nature. As a consequence, parameter descriptors can become arbitrary tree structures (see example on start page).
The parameters have to fulfill a contract in order to allow calls for arbitrary Java objects like this
MyAlgorithm a = design.getValue("MyAlgorithm");
This, because sub-parameters have to be injected into the objects using reflection mechanisms, and those mechanisms require handles for constructors or setter methods, given by a user. However, the contract does not require any changes in your code, such as the extension of an interface or abstract class. Thus, an injector has to be specified for each sub-parameter. Further, in order for InPUT to gain access to the whole parameter tree given a variable as this
design.setValue("MyAlgorithm", a2);
user defined getters have to be specified that extracts the parameters recursively from the tree.
By default, InPUT assumes for each parameter "Parent" with a sub-parameter "Sub" to contain a setter method of name "setSub" and a getter "getSub" with the appropriate types according to the mapping type definition in "Sub" in the "Parent" class. However, the name can be customized in the mapping as in
<Mapping id="Parent.Sub" type="my.Subclass" get="theRealGetterName" set="theReadSetterName">
For primitive types and String no type attribute shall exist. In cases where the value is a calculated one or derived and set from within the class, the set attribute can be set to false using set="false". Still, setter injection can be accompanied or substituted by constructor injection.
Often we wish to set the field members of an object at the time of creation. Parameters are passed via the constructor, making the constructor an alternative way of injecting sub-parameter values. Where that is the case, the constructor attribute can be used in the mapping to point the constructor to those sub-parameters in the given order, in which they are to be expected in the constructor call, e.g.:
<Mapping id="Parent" type="my.ParentClass" constructor="Sub">
tells InPUT that the sub-parameter "Sub" is the first and only argument of the constructor, and that injection neglects the setter attribute of "Sub". Further parameters can be added, using a space in between them:
<Mapping id="Parent" type="my.ParentClass" constructor="Sub Sub2 Sub3">
One correct Constructor order has to be held. A parameter that contains another parameter within a design space as a formal parameter for a constructor can also be referred to in the constructor definition with the fully qualified parameter ids in combination with sub parameters:
<Mapping id="Parent" type="my.ParentClass" constructor="Some.Other.ParamId Sub1">
In this tutorial we learned how to implement structural parameters. we learned how to define hierarchies and what the code mapping descriptor requires in order to give InPUT full access to all parameters without user intervention or code changes in the user code.