(sampler_stochastic_method_tconstrlhs)= # TConstrLHS example This section will discuss the way to produce a constrained LHS {{doe}} from scratch, focusing on the main problematic part: defining one or more constraints and on which variable to apply them. The logic behind the heuristic is supposed to be known, so if it's not the case, please have a look at the dedicated section in {{metho}}. This section will mostly rely on the way to define the constraints and the variables on which these should be applied on which are specified by the method `addConstraint`. This methods takes 4 arguments: 1. a pointer to the function that will compute the constraints values; 2. the number of constraint defined in the function discussed above; 3. the number of parameters that are provided to the function discussed above; 4. the values of the parameters that are provided to the function discussed above; The main object is indeed the constraint function and the way it is defined is discussed here. It is a C++ function (for convenience as the platform is C++-coded) but this should not be an issue even for python users. The followings lines are showing the example of the constraint function used to produce the plot in [](#use_cases_macro_sampler_lhs_linear). ```cpp void Linear(double *p, double *y) { double p1=p[0], p2=p[1]; double p3=p[2], p4=p[3]; // Linear constaint y[0] = (( (p1 + p2>=2.5) || (p1-p2<=0) ) ? 0 : 1); y[1] = ((p3 - p4<0) ? 0 : 1); } ``` Here are few elements to discuss and explain this function: - its prototype is the usual C++-{{root}} one with a pointer to the input parameter `p` and a pointer to the output (here the constraint results) `y`; - the first lines are defining the parameters, meaning the couples $(x_{row}, x_{col})$ for all the constraints. By convention, the first element of every line ($p1$ and $p3$) are of the row type (they will not change in the {{doe}} through this constraint, see {{metho}} for clarification) while the second parameters ($p2$ and $p4$) are of the column type, meaning their order in the {{doe}} will change through permutations through this constraint. - the rest of the lines are showing the way to compute the constraints and to interpret them thanks to the trilinear operator (even though the classical `if`, `else` would perfectly do the trick as well. Let's focus first on the second constraint: ```cpp y[1] = ((p3 - p4<0) ? 0 : 1); ``` if `p3` is lower than `p4` (`p3-p4<0`) then the function will put 0 in `y[1]` (stating that this configuration is not fulfilling the constraint), and it will put 1 otherwise (stating that the constraint is fulfilled). The other line is defining another constraint which is composed of two tests on the same couple of variables: ```cpp y[0] = (( (p1 + p2>=2.5) || (p1-p2<=0) ) ? 0 : 1); ``` Here, two constraints are combined in once, as they affect the same couple of variables, the configuration will be rejected either if `p1+p2` is greater than 2.5 or if `p1` is lower than `p2`. Once done, this function needs to be plugged into our code in order to state what variables are `p1`, `p2`, `p3` and `p4` so that the rest of the procedure discussed in {{metho}} can be run. This is done in the `addConstraint` method, thanks to the third and fourth paramaters which are taken from a single object: a `vector` in C++ and a `numpy.array` in python. It defines a list of indices (integers) that corresponds to the number of the input attributes as it is has been added into the `TDataServer` object. For instance in our case, the list of input attributes is `"x0:x1:x2"` while the constraints are coupling $(x_1,x_0)$ and $(x_2,x_1)$ respectively. Once translated in term of indices, the constraints are coupling respectively $(1,0)$ and $(2,1)$, so the list of parameter should reflect this which is shown below: ````{only} cpp ```cpp vector inputs = {1,0,2,1}; constrlhs->addConstraint(Linear, 2, inputs.size(), &inputs[0]); ``` ```` ````{only} py ```python inputs = numpy.array([1, 0, 2, 1], dtype='i') constrlhs.addConstraint(ROOT.Linear, 2, len(inputs), inputs) ``` ```` ````{warning} The consistency between the function and the list of parameters is up to you and you should keep a carefull watch over it. It is true that an inequality can be written in two ways, as ```{math} x_1 - x_0 < 0 \;\; \Leftrightarrow \;\; x_0 - x_1 > 0 ``` but when the constraint is defined as ```cpp y[0] = ((p[0] - p[1]<0) ? 0 : 1); ``` the results will be drastically different if the list of parameters is $(1,0)$ (which should fulfill the constraint shown above) or $(0,1)$ which would results in the exact opposite behaviour (and might make the heuristic crash if the constraint cannot be fulfilled). ````