Group Abstract Group Abstract

Message Boards Message Boards

0
|
275 Views
|
29 Replies
|
6 Total Likes
View groups...
Share
Share this post:

Variable arguments to functions

Posted 20 days ago

I'd like for this to work in general, with several lists (adj, famadj, etc, which actually look like matrices) of varying sizes. I'd like to not have to define local variables inside the function totaladj, as shown in the code that is commented out. Right now it doesn't work (unless I use the commented out code). Any help would be greatly appreciated.

POSTED BY: Iuval Clejan
29 Replies
Posted 5 days ago

POSTED BY: Eric Rimbey
Posted 4 days ago

I appreciate you putting so much time in to this, Eric. I may not be able to get back to you on it for a bit. I have figured something that seems to work, but I'm not sure yet. If not, I will try your suggestions, if they make sense.

POSTED BY: Iuval Clejan
Posted 3 hours ago

Good news: it works, despite the editor showing a syntax error (my version of Mathematica may not like multiple squiggly brackets in a With statement. The interpreter runs it fine. Bad news: totally impractical for actual problems that involve much bigger matrices. I tried it with an 81x81 first matrix, a 27x27 second matrix, and a 3x3 third matrix. It was not done after 4 hours on my mac air laptop.

It's the same problem as another kind person (forgot his name, but in this thread) tried unsuccessfully to solve by making the arguments to the function passed to NMinimize be forced to be numeric. I've done some tests, and the root problem is that the symbolic functions can run much faster when passed to NMinimize (does it form an approximation to the function which it keeps testing against the function? Just curious), but the functions in conjunction with NMinimize can't handle internal conditionals (if statements and such) that test numeric values for the matrix elements. Other conditionals, that have to do with array dimensions for example, are fine. I would call it a bug. You will probably deny it ad infinitum, and I won't keep fighting you about it, if you won't admit it.

I include your suggestions in a file, with a much smaller problem than what I really need, with a Timing statement to show how impractical it is. Thanks for trying.

POSTED BY: Iuval Clejan
Posted 5 days ago

I emphasize that the error is mathematical. It is not a programming error, nor a question of numerical method. If the function should theoretically be bounded, then I suggest there's an error in its code somewhere. The code defines totaladj[args] to be a linear function, which clearly is unbounded.

OTOH, if you just want it to try see what happens when NMinimize[] runs an numerical algorithm on your function, clear totaladj and use NumericQ with MatrixQ in its definition:

totaladj[{ms___?(MatrixQ[#,NumericQ]&)}]:= …

You should get the following (mathematical!) error, which numerically confirms the error NMinimize detected when NMinimize[] evaluated totaladj[args] with symbolic arguments:

NMinimize::cvdiv: Failed to converge to a solution. The function may be unbounded.
POSTED BY: Iuval Clejan

I emphasize that the error is mathematical. It is not a programming error, nor a question of numerical method. If the function should theoretically be bounded, then I suggest there's an error in its code somewhere. The code defines totaladj[args] to be a linear function, which clearly is unbounded.

OTOH, if you just want it to try see what happens when NMinimize[] runs an numerical algorithm on your function, clear totaladj and use NumericQ with MatrixQ in its definition:

totaladj[{ms___?(MatrixQ[#,NumericQ]&)}]:= …

You should get the following (mathematical!) error, which numerically confirms the error NMinimize detected when NMinimize[] evaluated totaladj[args] with symbolic arguments:

NMinimize::cvdiv: Failed to converge to a solution. The function may be unbounded.
POSTED BY: Michael Rogers
Posted 5 days ago

I am not even seeing your latest reply here, so I will quote from the email:

Michael said: I emphasize that the error is mathematical. It is not a programming error, nor a question of numerical method. If the function should theoretically be bounded, then I suggest there's an error in its code somewhere. The code defines totaladj[args] to be a linear function, which clearly is unbounded.

Iuval repiies: NO! THe function is bounded by the constraint that all its args are between -1 and 1. This constraint is passed to NMinimize in the code.

Michael said: "OTOH, if you just want it to try see what happens when NMinimize[] runs an numerical algorithm on your function, clear totaladj and use NumericQ with MatrixQ in its definition:

totaladj[{ms___?(MatrixQ[#,NumericQ]&)}]:= … You should get the following (mathematical!) error, which numerically confirms the error NMinimize detected when NMinimize[] evaluated totaladj[args] with symbolic arguments:

NMinimize::cvdiv: Failed to converge to a solution. The function may be unbounded."

Iuval replies: I do NOT see that, because in my example I have constraints which bound the function. So it does the right thing with this extra NumericQ addition. However, for any sizable problem, the time it takes to run is way too slow, making it completely impractical. Maybe I should send this to the developers as a bug?

POSTED BY: Iuval Clejan
Posted 5 days ago

I'm going to try Method->"DifferentialEvolution" or "NelderMead", to see if it speeds things up. Gradient methods will not work here.

POSTED BY: Iuval Clejan
Posted 5 days ago

Yes, so how do I pass totaladj a list of numerical matrices instead of symbolic ones? I have to go through NMinimize, because that is what I am trying to do (I'll say it again: Minimize a function of many matrix elements, subject to some constraints, it's really not complicated, I don't know how to make it simpler for you). Why is NMinimize not passing totaladj numbers? I don't know! You would think the N in NMinimize means it would do a numerical minimization instead of an algebraic one.

So How can I tell NMinimize to pass numbers to totaladj?

POSTED BY: Iuval Clejan
Posted 5 days ago

Okay. Write out for me the single NMinimize expression that you want to generate. Just pretend that all of your code that preceded the NMinimize worked and show me what you're trying to minimize. Do it by hand. Just forget about totaladj. Just show me the goal. Feel free to explain the semantics of whatever you come up with, or even relate it back to your original code, but do all of that as exposition, not code. The only code I want to see is a complete and valid NMinimize expression that you want to eventually be able to generate from your data and definitions.

POSTED BY: Eric Rimbey
Posted 5 days ago

Thanks for your patience. I am simplifying what I really want, for the sake of exposition.

I want totaladj to take all the matrix elements in all the matrices passed to it (which can vary as to their number and size, though they are always square matrices), and add up the number of times a matrix element exceeds a threshold (itself determined by the size of the matrix it comes from, so for each matrix a different threshold, in the sample code it was 1/sizeofmatrix^2 or something proportional to that). I believe this part you had no trouble understanding. For a specific case of 1 2x2 matrix, what I want to minimize would be a function of 4 variables (m[1,1],m[1,2],m[2,1] and m[2,2] with appropriate double bracket syntax), outputting how many of them exceed 0.25.

I want NMinimum to numerically find the value at the minimum of this function under the simple constraints that each matrix element can only vary between -1 and 1, as well as the values of all the dependent variables (all the matrix elements) at the minimum, which NMinimum does as a matter of course. For the specific example above, the minimum would occur anywhere in the region where these variables are between -1 and 0.25, and the value at the minimum would be 0. But for some reason, NMinimum seems to pass totaladj symbolic variables instead of numerical ones. Maybe it only does it once before it switches to numerical? That would be OK, but tests I've done with the more complicated real problem suggest otherwise.

The real function to minimize involves other additional operations on these matrices, such as adding up all the elements (the MatrixBenefit part), and splitting them into block matrices along the diagonal that have different operations done inside them than outside, and much more complicated constraints. I have all the code for that written, and it works. The only part that doesn't work is NMinimum passing symbolic arguments to the utility function (totaladj) instead of numerical ones.

POSTED BY: Iuval Clejan
Posted 5 days ago

Okay, I think I finally understand. So, NMinimize expects an expression that represents a function to be minimized. So you need that first argument to look like a function. Using things like Count will just perform the function, but we need an expression for the function. I'm not sure the best way to do this, but one way would be with UnitStep. So, for a 2x2 matrix and using m as our formal symbol, we want something like this:

NMinimize[
  {UnitStep[-1/4 + m[1, 1]] + UnitStep[-1/4 + m[1, 2]] + UnitStep[-1/4 + m[2, 1]] + UnitStep[-1/4 + m[2, 2]], 
    {-1 <= m[1, 1] <= 1, -1 <= m[1, 2] <= 1, -1 <= m[2, 1] <= 1, -1 <= m[2, 2] <= 1}}, 
  {m[1, 1], m[1, 2], m[2, 1], m[2, 2]}]

which evaluates to

{0., {m[1, 1] -> -0.782122, m[1, 2] -> -0.00737865, m[2, 1] -> -0.0810299, m[2, 2] -> -0.624118}}

We need a way to create this data programmatically, so here's one way. I assume you still want to create each matrix separately.

countLargeFn[matrix_?MatrixQ, thresholdFn_] :=
 With[
  {elems = Flatten[matrix], threshold = thresholdFn[matrix]},
  {vars = Select[elems, Not@*NumericQ]},
  {Total[UnitStep[elems - threshold]], -1 <= # <= 1 & /@ vars, vars}]

Use it this way:

countLargeFn[Array[m, {2, 2}], 1/Times @@ Dimensions[#] &]

which produces:

{UnitStep[-1/4 + m[1, 1]] + UnitStep[-1/4 + m[1, 2]] + UnitStep[-1/4 + m[2, 1]] + UnitStep[-1/4 + m[2, 2]], 
  {-1 <= m[1, 1] <= 1, -1 <= m[1, 2] <= 1, -1 <= m[2, 1] <= 1, -1 <= m[2, 2] <= 1}, 
  {m[1, 1], m[1, 2], m[2, 1], m[2, 2]}}

You'd then need to apply this to several symbolic matrices and then aggregate the bits together. All of the unit step expressions and the constraints go in the first argument to NMinimize and then all the variables go into the second argument.

Unfortunately, I must leave the house now and won't look at this again until I get back. I threw that together quickly, and I know it doesn't finish the job for you in its current form. If you can't figure it out, just ask more questions and I'll be back on line in 8 hours or so.

POSTED BY: Eric Rimbey
Posted 5 days ago
  1. I can't get the syntax you used for countLargeFn definition to work. The editor highlights a portion in red due to bad syntax. I don't know why it works for you, I have Mathematica ver. 13.3 Home Edition.
  2. I don't understand why you want to include the constraints in the function definition rather than sending them to NMinimize. In any case, I can't do that for this function because the constraints in the real problem couple variables from ALL the matrices, not just one of them (this is a function for each matrix separately).
  3. If you include constraints in the function to be minimized, I think NMinimize will get confused. It doesn't expect that in its syntax. It expects functions defined without constraints, with the constraints supplied to NMinimize separately.
  4. Please take a look at the solution offered by Michael Rogers in this thread (can you see it?) He suggested defining the main function to be minimized thus:

    totaladj[{ms___?(MatrixQ[#, NumericQ] &)}] :=

This works, as far as the TotalCost function actually doing the right thing, but it makes the Niminimize call too slow and impractical for my real problem. I tried to make Method -> "DifferentialEvolution", and it's still too slow. Might try NelderMead or something like that.

POSTED BY: Iuval Clejan
Posted 5 days ago
  1. It would be helpful if you could specify what's red or what the error you're seeing is. But if you can't run the code, then that's a bummer.
  2. Yes, I understand. You're getting ahead of yourself. There are some things about how Mathematica works that you seem confused about. Trust me, we can figure out how to restructure things as needed. This is a huge strength of Mathematica. But we need to get something working first. I'm doing it this way for a reason, but I didn't have time to explain it all earlier.
  3. No, that's not correct. First NMinimize actually does expect the constraints to be included with the function in the first argument. But really, we don't need to worry about that now. Trust me, we can always structure our data as needed once we get to that point.
  4. I've only glanced at Michael's solution, and that's not enough for me to understand it, but that leads to some caveats...

I just may have no idea what you're trying to do. I thought I figured it out, but if adding a numeric constraint to totaladj works, then I somehow just missing the entire point of your question. If you pass a numeric matrix to totaladj, then what NMinimize gets won't be a functional expression. It'll be a number, and that just doesn't make sense for NMinimize.

Also, you've said repeatedly something along the lines of "NMinimum passes arguments to totaladj". That's not at all what's happening. I had been trying to nail down the other issues before bringing this up, but since you keep repeating it, and since I keep failing to solve your problem, I think maybe it's time to switch to this. NMinimize is not passing any arguments to totaladj. It just isn't. That's not how WL code works. NMinimize isn't aware of totaladj at all. I don't me the system function, because obviously a system function knows nothing about your custom function. I mean in the expression you are trying to evaluate, NMinimize[...], the stuff that gets actually passed to NMinimize has no knowledge of totaladj, no remnants of totaladj remain when we finally get to NMinimize doing its thing. So, maybe this here is the crux of the issue. Either you are wanting totaladj to do something completely different than what it looks like right now, or you are not understanding how the arguments to NMinimize should look, or I just have absolutely no idea what anything you've said even means.

So, anyway, I'm back online and will try one more time to look through this, but in the meantime, I'm begging you to show me a full NMinimize expression that works for you. Get rid of totaladj, get rid of all of your setup code, and just show me what the `NMinimize should look like if you just wrote it "by hand" without any attempt to write re-usable helper code. I'm begging you to just show me the form of the thing you actually want to minimize.

POSTED BY: Eric Rimbey
Posted 19 days ago

Hi Eric! Thanks for that. I modified things a bit to fit what is needed (like I want to give a variable list of matrices to the function to be optimized totaladj, so I added {} in its definition). I just want to make sure that NMinimize is doing the right thing, can you please check if I am giving it the right format? In the example file (vastly simplified) it doesn't have the constraints that make this problem bounded, so I can't check if it's the right format. Note that Nminimize has all the matrices flattened and the variables are the matrix elements, whereas totaladj expects matrices.

POSTED BY: Iuval Clejan
Posted 19 days ago

I think that given your definitions

NMinimize[totaladj[{Adj, Adjfam}], vars]

is a correct form for NMinimize. As you say, in the specific case you provide, the there is no bounded solution.

POSTED BY: Eric Rimbey
Posted 19 days ago

It seems to work, though I don't know for sure yet. Running the full file, so far no errors. I also tried to have the list input when calling the function, but without the args list in the function definition, by adding function[Sequence[args]], but it didn't work. So I had to add the list in the function definition.

POSTED BY: Iuval Clejan
Posted 6 days ago

Still not working. NMinimize doesn't send numeric variables to MatrixBenefit and MatrixCost. I added a Print[m[[1,1]]] line to demonstrate this.

Attachments:
POSTED BY: Iuval Clejan
Posted 6 days ago

You're going to need to step back and explain what you're trying to do. I have no idea what you're talking about, and I have no idea what you think is going wrong. I see print output when I run your code, so I don't know why you think arguments aren't being passed. I've already reverse engineered your code once, and I'm not going to do it again. Can't you just explain what you're trying to do and what your expected output should be? Like, give us a sample problem where you know the expected output and walk us through it.

POSTED BY: Eric Rimbey
Posted 6 days ago

I am trying to do a constrained minimization of a utility function where the variables are matrix elements for different matrices. The utility function I have is more complicated than in the example, but even in the simple example, the matrix elements passed to MatrixCost and MatrixBenefit need to be numerical for those functions to do the right thing (the greater than threshold condition is never satisfied, if the matrix elements are symbolic, for example). For some reason they aren't, as demonstrated by m[[1,1]] being symbolic when printed.

Do you want to know the. big picture of what this minimization is about? I can't see why that is relevant, but I can tell you if you're interested.

POSTED BY: Iuval Clejan
Posted 6 days ago

No, I don't want to see the big picture. I want you to explain your code. What do you think it's doing? What should it do? What output, exactly what output, do you expect to see in this example? I don't know what you think is wrong. What exactly is wrong? How is it different, exactly, from what you expect? you choose variable names that mean nothing to me, so I don't' know what I'm looking at. Can you reduce the clutter at all? Do I really need to see numnodes? numfamilies? nlevel? L[0] and other Ls? B[0] and other Bs? You keep saying you need something numeric, but then you pass everything around as symbols. So, what do I need to trace through? Again, tell me exactly what output you expect so I can see how your actual output is different.

If I had a function like totaladj[{ms___?MatrixQ}] := ..., I would write a test with an empty list. Then a test with exactly on matrix, probably a 2x2. I would only move on if those worked as expected. Just simplify everything before moving on. You're asking me to reverse engineer your code to figure out the problem rather than just tell me exactly, exactly, what the problem is.

POSTED BY: Eric Rimbey
Posted 6 days ago

See attached even simpler example, where I sent totaladj a list of just 1 2x2 matrix, instead of several matrices. The minimum would occur if all matrix elements were less than 1/4. They are instead set to one by the minimization, the value of totaladj should be 4 (not 0 for those values, as returned by NMinimize), and the print statement prints a symbol instead of a number. See it do the right thing for both the call to MtarixCost and totaladj.

I have to pass the NMimimize a symbolic list of variables, don't I? But it doesn't pass totaladj a numeric list, for some reason.

POSTED BY: Iuval Clejan
Posted 6 days ago

I still have zero idea what you're trying to do, but let's just look at one function:

MatrixCost[m_?MatrixQ] := 
 With[{threshold = 1/Times @@ Dimensions[m]}, Print[m[[1, 1]]]; 
  Count[Map[GreaterThan[threshold], m, {2}], True, {2}]]

In your scenario, the m will be

{{adj[1,1],adj[1,2]},{adj[2,1],adj[2,2]}}

So, for each of those entries we're trying to compute something like

adj[1,1]>1/4

That does not evaluate to True for any of the entries. So, your count will be 0. So, totaladj will be 0. So

NMinimize[totaladj[args], constraints, vars]

Will actually be

NMinimize[0, {-1 <= adj[1, 1] <= 1, -1 <= adj[1, 2] <= 1, -1 <= 
   adj[2, 1] <= 1, -1 <= adj[2, 2] <= 1}, {adj[1, 1], adj[1, 2], 
  adj[2, 1], adj[2, 2]}]

So, why are your passing 0 to NMinimize? I don't understand. I don't know what else to tell you, because I don't understand what you're trying to do.

POSTED BY: Eric Rimbey
Posted 5 days ago

this is a duplicate reply, because the first time it just quoted Eric's reply. Yes, so how do I pass totaladj a list of numerical matrices instead of symbolic ones? I have to go through NMinimize, because that is what I am trying to do (I'll say it again: Minimize a function of many matrix elements, subject to some constraints, it's really not complicated, I don't know how to make it simpler for you). Why is NMinimize not passing totaladj numbers? I don't know! You would think the N in NMinimize means it would do a numerical minimization instead of an algebraic one.

So How can I tell NMinimize to pass numbers to totaladj?

POSTED BY: Iuval Clejan
Posted 5 days ago

bug in reply software

POSTED BY: Iuval Clejan

I used one of the later notebooks you posted, and there are no bounds in it. I did not look at the others, because I thought it would be the representation of your problem that you are currently working on. Sometimes the site does not load all the answers in page (as you've noticed). Perhaps your latest was missing and I used an out-of-date notebook. But I used YOUR NOTEBOOK, and it has NO CONSTRAINTS. Further you can see on this site's page that the error is the following:

NMinimize::ubnd: The problem is unbounded.

Here's your notebook with a couple of things added by me:

POSTED BY: Michael Rogers
Posted 5 days ago

Yes, frustrating that you can't see the other notebooks I added after realizing that NMinimize is failing to pass numerical arguments to at least MatrixCost (it seems to pass them on to MatrixBenefit). You can see that this is happening even when you add constraints (I did the same thing in those other notebooks), because the output from totaladj is just based on MatrixBenefit, you get an erroneous output of 0 from MatrixCost (it should have been B[0]*16+B[1] 8, since all the matrix elements are above the threshold, except that they are past symbolically to MatrixCost, so they are not in actuality and you get 0 instead). I am going to see what Eric's latest attempt to figure this maddening situation out is, but I will be grateful if you also have other ideas.

POSTED BY: Iuval Clejan

I'm mainly focusing on the code. I don't have time to really get into the problem itself. One recurring issue I see is how to tell NMinimize[] the constraints. They should be part of argument 1. The argument should have the form of a list, {objectiveFunction, constraints}. The other principal issue in the one you asked about, which Eric has helped with, namely, how to structure the pattern for the ms argument of totalAdj[]. I showed one way in my previous notebook. Below is another. I chose another way because I thought, since you mentioned MatrixBenefit, I should use a notebook in which it is defined and used. In that notebook, totalAdj[] is called with two arguments instead of just one; consequently, I changed the pattern to match the call. Either way can be used and are similarly efficient. Pick whichever way expresses your ideas best.

POSTED BY: Michael Rogers
Posted 5 days ago

This does NOT work, although yes, I didn't quite have the right syntax, but somehow it was still apparently interpreting it correctly. You can see that it only computes MatrixBenefit, not MatrixCost when it calls totaladj. The output from totaladj should be -8.0, not -16.8. Your fix with the NumericQ specification works to at least pass MatrixCost numerical arguments so it can compute what it's supposed to, but for a real problem this makes the minimization impractically slow. I tried both NelderMead and DifferentialEvolution for Method-> options and it didn't help

POSTED BY: Iuval Clejan
Posted 20 days ago

I don't understand the NMinimize at the end, because you're not passing arguments of the right shape for that. But I've made my best guess at what totaladj is supposed to do, and I'd recommend something like the following.

First, break the problem into smaller pieces. You eventually want to work and a bunch of matrices at once, but solve the problem for a single matrix first.

MatrixCost[m_?MatrixQ] :=
 With[
  {threshold = 0.1*Times @@ Dimensions[m]},
  Count[Map[GreaterThan[threshold], m, {2}], True, {2}]]

I saw "cost" in your code, so I thought maybe that would be a good name. We can test this:

SeedRandom[25];
testA = RandomReal[{0, 4}, {3, 4}]
(* {{0.0395876, 0.93185, 0.802464, 0.737812}, 
    {1.11064, 0.253445, 2.87187, 3.85676}, 
    {0.764152, 2.42748, 0.915865, 3.21226}} *)

MatrixCost[testA]
(* 4 *)

testB = RandomReal[{0, 4}, {2, 2}]
(* {{0.0495114, 2.80439}, {3.05848, 1.00112}} *)

MatrixCost[testB]
(* 3 *)

Now let's use this for a function that works with several matrices.

MatricesCost[ms___?MatrixQ] := Total[MatrixCost /@ {ms}];

MatricesCost[]
(* 0 *)

MatricesCost[testA]
(* 4 *)

MatricesCost[testA, testB]
(* 7 *)

MatricesCost[testA, testB, testA]
(* 11 *)
POSTED BY: Eric Rimbey
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard