2
$\begingroup$

I have a problem I am attempting to solve with NMinimize.

I have a scoring function, which will give me a final score. The problem has many variables, so instead of having them as parameters, they are assembled by variable name. Here is a simple example:

scoreResults = Function[
Module[{temp, i},
 temp = 0;
 Print["Checkpoint 0: " <> ToString[temp]];
 For[i = 0, i < 2, i++,
  temp = temp + ToExpression["aaa" <> ToString[i]];
  If[ToExpression["aaa" <> ToString[i]] > 300,
   temp = temp + 100000;
   ];
  Print["Checkpoint 1: " <> ToString[Evaluate[temp]]];
  ];
 Return[temp, Module];
 ]
];

Notice the If condition. Now, when I evaluate with a ReplaceAll, I get the following:

scoreResults[] /. {aaa0 -> 123, aaa1 -> 456}
Checkpoint 0: 0
Checkpoint 1: aaa0
Checkpoint 1: aaa0 + aaa1
579

That is not correct! I believe what's happening is the function is being evaluated before the ReplaceAll. Is it possible to get around this without having input parameters? My problem has many variables, and the scoring function will have many nested for loops, so assembling variable names on the fly inside the scoring function is key. Even if I do use input parameters for the function, I'm finding that my ToExpression["aaa"<>ToString[i]] is not effective.

Ultimately, I would like to use this scoring function with NMinimize like such:

NMinimize[{scoreResults[], {aaa0 > 222, aaa1 > 333}}, {aaa0, aaa1}]

Currently the answer it gives me is wrong:

{555., {aaa0 -> 222., aaa1 -> 333.}}

Thank you in advance for your help, I have been trying to figure this out for a while now. I understand that when I get to the NMinimize part, I'll probably have to use NumericQ, but for now, I just need to get past the initial problem I'm having just getting the function to produce the correct value, including the If statement.

$\endgroup$
1
  • $\begingroup$ Yes, ReplaceAll does not have any Hold attributes so it will evaluate the first argument before doing any replacements. If it didn't, something like scoreResults[] /. {aaa0 -> 123, aaa1 -> 456} could never work because the first argument would just be scoreResults[] and there would be nothing to replace in that because it's just a Symbol and nothing else. What you can try, is (scoreResults /. {...})[], but this won't work in your code because the thing you're trying to replace doesn't exist in your function yet. $\endgroup$ Feb 13, 2020 at 14:31

1 Answer 1

1
$\begingroup$

Here is some code which I think should do what you are after:

ClearAll@scoreResults;
scoreResults[a_ /; NumericQ[a[0]]] := Module[{temp, i},
  temp = 0;
  For[i = 0, i < 2, i++,
    temp = temp + a[i];
    If[a[i] > 300,
      temp = temp + 100000
    ]
  ];
  temp
]

NMinimize[{scoreResults[a], {a[0] > 222, a[1] > 333}}, {a[0], a[1]}]

note that it generally is not a good idea to generate variable from strings. Using other approaches is usually more efficient and results in clearer code. In your case you can take advantage of the fact that NMinimize as many similar functions accepts indexed variables. This is not really documented but has worked since all versions I know of. The second trick to make things work as intended is the use of NumericQ which will make scoreResults only evaluate when numeric values are given for the indexed variables. If you find this does what you want, here is another version which is how you typically would formulate the problem in Mathematica:

ClearAll@scoreResults;
scoreResults[a_ /; NumericQ[a[1]]] := Sum[
  a[i] + If[a[i] > 300, 100000, 0],
  {i, 2}
];
NMinimize[{scoreResults[a], {a[1] > 222, a[2] > 333}}, {a[1], a[2]}]
$\endgroup$
1
  • $\begingroup$ Thank you very much, that is perfect! For the longest time, I just couldn't figure it out, this helps a lot. $\endgroup$
    – Helix888
    Sep 17, 2019 at 19:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.