0
$\begingroup$

Here is my new attempt to ask this question, I'm sorry I didn't meat the criteria before, where I thought it more as a general question, whereas everybody trying to answer it needed completely working code:

I defined a complicated function, that maps a set of points to a real value. Uppon inserting values that are not numerical values (like variables or not defined symbols), the function will return 0, probably because I use the Method "ConvexHullMesh" inside.

Minimizing the function yields a set of points, whose minimum value doesn't differ from the minimum value of the intial set of points, that's why I believe that NMinimize doesn't insert numerical values inside the function, when it's optimizing it. So my general question is:

How do I force mathematica to only feed numerical parameters to the function that it tries so minimize via NMinimize?

Edit: Defining the function f which is to minimize with "_?NumericQ" in the arguments did not solve the issue: I got an error message:

"The function value f[{-7.09209,12.9089,25.8178,50.1866,73.1876,94.8978,115.389,134.731,152.987,170.218,186.482,201.834,216.323,230.,242.909,255.093,266.594,277.449,287.695,297.365,306.493,326.5},{5,12.2987,16.3304,<<16>>,15.2537,12.3177,5},1.7181] is not a number at {d,xh,xv,y1,y10,y11,y12,y13,y14,y15,y16,y17,y18,y19,y2,y20,y3,y4,y5,y6,y7,y8,y9} = {1.7180975605699877,326.4995102566004,-7.09209,12.298651633222217,35.48926119707203,34.389624468052816,32.836788750076444,30.901471797482397,28.747854174834647,26.342071573727743,23.668130249100834,20.990466045340952,18.133746548827396,15.25370458807958,16.3304441781095,12.317745897770104,23.037848717759495,28.06257263862169,31.775870376906525,34.085061867367024,35.58074103909772,36.28827757370625,36.26569294949447"

My explanation is that adding ?NumericQ prevents the code from being evaluated, before xvalues and yvalues become a list of numerical values, but it isn't evaluated afterwards, leaving NMinimize trying to minimize f[.....], which (if not evaluated) is indeed not a real number.

Next I will give my complete code, with comments that additionally tell about the purpose of the code bits: The code contains the problem and an explanation of the problem in itself, and tries to demonstrate my suspection that NMinimize tries to evaluate the function it is supposed to minimize, without inserting numerical values before.

(*This file contains a programm which is supposed to do the following: 
Imagine you have a Set of points, that are positioned in a way that \
every point lies at the envelope of all the points:
If you change the y value of one of the points (+ a distance s), then \
the envelope of all the points will become larger.
The difference between the long and the old values is called d.  
The goal of the programm is to find a configuration for the points, \
for which the above action (displacing a certain point p) will yield \
the same value of d for each of the points, no matter which of the \
points is p (I excluded the outer points from that, they are not \
supposed to be displaced.
To Accomplish this, I wrote a function f, which is given to the whole \
set of points, calculates the difference for each of the points, \
subtracts a value , squares them, and adds them all up. This way, \
minimizing f will result in a configuration of the points where the \
difference in the envelopes for every point will be the same value
Since I want the programm to be applicable to different situations (x \
positions of the points, number of points, spacing in between them, I \
generate the symbols I use within the programm, instead of \
hard-typing them*)

(*Distanz is supposed to get a set of point, and calculate the length \
of the shortest closed path arround them that includes all points as a 
part of the 2D surface that is the inner region of the path *)

Distanz[xvalues_, yvalues_] := 
  ArcLength[
   RegionBoundary[ConvexHullMesh[{xvalues, yvalues} // Transpose]]];

(*Setting up all the constants needed for the program. *)
l = 460;
hi = 1;
hf = 20;
s = 5;
y = 5;
xabstand = 20;
(*Next: Creation of symbolic expressions for the x- and y- components \
of the points, which I want to optimize *)
(*I want to vary most of \
the points with respect to their y value, except the first and the \
last x value, where the y value
is fixed to the value of "y", and the xvalues xv and xh are varried *)

xvalues0 = 
  Piecewise[{{Table[(l - l*2^(-(i - 1)/12)) // N, {i, hi, hf}], 
     hi != 1}, {Prepend[
      Table[(l - l*2^(-(i - 1)/12)) // N, {i, hi + 1, 
        hf}], (l - l*2^(-1/12))/2 // N], hi == 1}}];
xvalues = Append[Prepend[xvalues0, xv], xh]
(*Gleichungen erstellen*)

yvalues0 = Table[Symbol[StringJoin["y", ToString[i]]], {i, hi, hf}];
yvalues = Append[Prepend[yvalues0, y], y]


(*Define a function that, for a given set of points created before, \
will yield the sum of squared differences between the arclength \
arround them, and the arclength of the same set of points, with one \
of the y values being displaced by the value s which was set before. \
The sum is carried out over all inner points. 
Minimizing f for by finding the best points for the minimum points \
will (supposedly) yield a configuration of points, where every \
displacement in y direction of an inner point will result in the same \
enlargement of the length of a shortest path going arround all the \
points   *)

f[xarray_, yarray_, d_] :=  
 Sum[( Distanz[xarray, ReplacePart[yarray, i -> yarray[[i]] + s]] - 
     Distanz[xarray, yarray] - d)^2, {i, 2, hf - hi + 1}]

(*Generate a set of test points, which are supposed to work as \
initial values*)

xvaluestest = 
  xvalues /. {xv -> xvalues0[[1]] - xabstand, 
    xh -> xvalues0[[hf - hi + 1]] + xabstand};
yvaluestest = 
  Table[Sqrt[
     460^2 - (xvaluestest[[
          i]] - (xvaluestest[[1]] + xvaluestest[[hf - hi + 3]])/
          2)^2] - Sqrt[
     460^2 - (xvaluestest[[
          1]] - (xvaluestest[[1]] + xvaluestest[[hf - hi + 3]])/
          2)^2] + y, {i, 1, Length[xvaluestest]}];
Initialvalues = 
 Join[Table[{Symbol[StringJoin["y", ToString[i]]], 
    yvaluestest[[i + 1]] - 0.1, yvaluestest[[i + 1]] + 0.1}, {i, 1, 
    hf - hi + 1}],  {{xv, xvaluestest[[1]] - 0.01, 
    xvaluestest[[1]] + 0.01}, {xh, xvaluestest[[hf - hi + 3]] - 0.01, 
    xvaluestest[[hf - hi + 3]] + 0.01}, {d, 1, 2}}]             

(*Generate additional equations, that serve as constraints*)

eqs1 = Table[
   yvalues0[[i]] >= y, {i, 1, 
    hf - hi + 
     1}]; (*all the y values of the points, where the y values are to \
be varried, should be bigger than y (which was set in the beginning*)

eqs2 = Table[
   yvalues[[i]] >= 
    yvalues[[
      i - 1]] + (yvalues[[i + 1]] - 
         yvalues[[i - 1]])/(xvalues[[i + 1]] - 
         xvalues[[i - 1]])*(xvalues[[i]] - xvalues[[i - 1]]), {i, 2, 
    hf - hi + 
     2 }];   (*I want the final set of points to be a set where every \
point is a point that lies at the envelope of all the points, this \
condition guarantees this*)

eqs3 = {xv  <= xvalues0[[1]] - xabstand, 
   xh >= xvalues0[[hf - hi + 1]] + xabstand, 
   d >= 1} ;(*The Xvalues of the outer points are varried as well, I \
don't want their values to be too close to the inner points*)
\
(*Finally: The NMinimize call, where everything comes together *)
vec \
= NMinimize[Join[{f[xvalues, yvalues, d]}, eqs1, eqs2, eqs3], 
  Initialvalues, MaxIterations -> 500]


(*Now here just come some tools I wrote to see what the effect was. \
ShowEffect will yield all the desired differences, hopefully they are \
all the same, and of the same amount as "d" *)

ShowPoints[v_] := ListPlot[{xvalues, yvalues} /. v[[2]] // Transpose]
Showeffect[v_] := 
 Table[(ReleaseHold[
     Distanz[xvalues /. 
       v[[2]], (ReplacePart[yvalues, i -> yvalues[[i]] + s]) /. 
       v[[2]]]] - 
    ReleaseHold[Distanz[xvalues /. v[[2]], yvalues /. v[[2]]]]), {i, 
   2, hf - hi + 1}]
ShowPoints[vec]
Showeffect[vec]

(*Trying the same thing with initial data will yield: *)
\
ListPlot[{xvaluestest, yvaluestest} // Transpose]
Table[ Distanz[xvaluestest, 
   ReplacePart[yvaluestest, i -> yvaluestest[[i]] + s]] - 
  Distanz[xvaluestest, yvaluestest], {i, 2, hf - hi + 1}]

(*Those points do look roughly the same as the result of the \
optimization. Furthermore, the desired differences in the length of \
the envelope does look equally good, which is a sign for the fact \
that the optimization didn't improve the result *)
(*Finally, the \
minimum value that is the result of the optimization process is given \
as "19."*)
f[xvaluestest, yvaluestest, 1]  
f[xvalues, yvalues, 1]
(*This leads to the suspection that NMinimize tries to evaluate f \
without giving numerical data as input, hence the outcome of f will \
always be 42, and the the optimization process will halt after 100 \
iterations without changing it *)
$\endgroup$
  • 3
    $\begingroup$ Without reading all your code, the answer to your bolded question is to "protect" the inputs with NumericQ, i.e. functionToBeMinimized[x_?NumericQ] := .... See mathematica.stackexchange.com/a/26037/27951 $\endgroup$ – MarcoB May 21 '18 at 15:51
  • $\begingroup$ I did this. The outcome was that I get the error message (kind of): f[{-7.0, 12 ... ........}, {... more numerical values ...}, 1.7] is not a real number. Which makes sense: NMinimize firstly evaluates f, then replaces the variables by numeric values. The actions you suggest will supress this first evaluation, but doesn't generate a second generation after the variables are exchanged for numerical values. $\endgroup$ – Quantumwhisp May 21 '18 at 23:26
  • 1
    $\begingroup$ Since your function is expectting a list of numbers and not just a single number, try x:{__?NumericQ} instead of x_?NumericQ. $\endgroup$ – Carl Woll May 24 '18 at 20:11
  • $\begingroup$ This helped! It is at least computing now (but without halting .... but this probably is an other issue. I don't get any error messages) $\endgroup$ – Quantumwhisp May 24 '18 at 22:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.