Message Boards Message Boards

1
|
2986 Views
|
57 Replies
|
3 Total Likes
View groups...
Share
Share this post:

Referring to arguments of a function by order in which they appear

Posted 6 months ago

I have an application in which I need to pass arguments to a function in non-list form (I know functions can take arguments in list form, but there is a bug that makes this not work in my case, we don't have to get into it), but then I need to recreate the lists from the arguments. So for example, if

f[a1_,a2_]:=a1+a2

I would like to define, inside f a list={a1,a2] and then redefine

f[a1_,a2_]:=list[[1]]+list[[2]]

I would like to refer to a1 as first argument of f, a2 as second argument instead of by their names, because I have a much more complicated function in my real case and I want to automate list by using such Mathematica constructs as Table and Array. So how to do this?

POSTED BY: Iuval Clejan
57 Replies
Posted 5 months ago

Okay, maybe this is what you're after. I changed variable names to help myself keep track of what's going on.

avInit = {1, 1};
avFns = Array[av, 2];
avFnsApplied = Through[avFns[t]]; (* {av[1][t], av[2][t]} *)

avProtoList[a_List] := 0.5 (1 - a[[2]]) a[[1]]^2 + (1 - a[[1]]) a[[2]]^2 + a[[1]]  a[[2]];
avProto[args___] := avProtoList[{args}];

g1 = Derivative[1, 0][avProto];
g2 = Derivative[0, 1][avProto];

eqnlist = {
  Derivative[1][av[1]][t] == avProtoList[avFnsApplied], 
  Derivative[1][av[2]][t] == avProtoList[avFnsApplied], 
  av[1][0] == avInit[[1]], 
  av[2][0] == avInit[[2]]};
solDEsuper = NDSolve[eqnlist, avFnsApplied, {t, 0, 1.4}]

I didn't try to check that the result is correct. I just tried to do a formal transformation into what I think you're asking for.

POSTED BY: Eric Rimbey
Posted 5 months ago

Oh, well then I've completely misunderstood where the problem is.

Look you posted this:

f1[a_] := a[[1]] (1 - a[[2]]) + a[[2]] - a[[2]]^2;
f2[a_] := a[[1]] - 0.5 a[[1]]^2 + 2 (1 - a[[1]]) a[[2]];
av0 = {1., 1.};
varex = Table[av[i][t], {i, 2}];
eqnlist = {Derivative[1][av[1]][t] == f1[varex], 
   Derivative[1][av[2]][t] == f2[varex], av[1][0] == av0[[1]], 
   av[2][0] == av0[[2]]};
solDEsuper = NDSolve[eqnlist, varex, {t, 0, 1.4}]

and said that it worked.

I posted this:

avInit = {1, 1};
avFns = Array[av, 2];
avFnsApplied = Through[avFns[t]]; 

avProtoList[a_List] := 
  0.5  (1 - a[[2]])  a[[1]]^2 + (1 - a[[1]])  a[[2]]^2 + 
   a[[1]]   a[[2]];
avProto[args___] := avProtoList[{args}];

g1 = Derivative[1, 0][avProto];
g2 = Derivative[0, 1][avProto];

eqnlist = {Derivative[1][av[1]][t] == g1 @@ avFnsApplied, 
   Derivative[1][av[2]][t] == g2 @@ avFnsApplied, 
   av[1][0] == avInit[[1]], av[2][0] == avInit[[2]]};
solDEsuper = NDSolve[eqnlist, avFnsApplied, {t, 0, 1.4}]

and you said that it didn't work.

When I look at eqnlist for each of them, I can't tell the difference. I thought if we used a simpler function, I could see the difference by inspection. But you don't want to provide a simpler function. Fine, just tell me what's different about these two. I'm sorry that I'm just not picking up on it, but that's where we are.

But then later, I forced you to tell me whether that expression was correct or not, and you said that it wasn't. So, I thought the problem was in generating eqnlist, and that maybe you could indicate the difference by giving me a reference. But now you seem to be saying that you don't know what eqnlist should even look like, you just know it's wrong. Okay, that's fair, but now we're talking about a mathematics problem, not a programming problem. So, you'll have to lead me through the mathematics before I can provide the code.

Now, you probably think I'm being stupid, and you'll say "no, no, that's not what I said/meant", and that's totally fine. Communication is hard. But I, as of right now, have no idea what/where the problem is. I know that for you, the problem is something about whether arguments are passed as a sequence or as a list. To me, that's a trivial problem, and I'm extremely confident that I can solve it. But I've been failing to produce a good result even though it seems to match the good results you've previously given me. It sounds like you're getting "good answers" with sequences, so I'm just trying to pin down what a good answer is.

So, at the end of the day, it sounds to me like we'd like to feed some inputs into NDSolve. We've got to generate those inputs somehow. You seem to be able to recognize when those inputs are good or not. I don't seem to be able to figure that part out. Also, I'm not really sure where we're starting here. It sounds like there are things "before" we start defining f, f1, etc. I know in your real world problem, those things are big and complicated, but in our toy problem, I would think you could give them to me explicitly. So, my brain is saying, "if he gives me the uber inputs, A, and then the inputs to NDSolve, B, then I can figure out how to transform A into B".

If you can tell me where I'm going wrong in any of the above thinking, that would help greatly. If you can give me any concrete examples that I can compare to at any point in the computation, that would help greatly. If you can try different words to explain your intent, that might help.

POSTED BY: Eric Rimbey
Posted 6 months ago

I'm not at all sure what you're trying to do, but here are some thoughts to get you started:

f1[args__] := {args}[[1]] + {args}[[2]];
f1[1, 2]
(* 3 *)

f2 = #1 + #2 &;
f2[1, 2]
(* 3 *)

Or, you can just overload your function:

f3[list : {_, _}] := list[[1]] + list[[2]];
f3[a1_, a2_] := f3[{a1, a2}];
f3[1, 2]
(* 3 *)

Or you can change the way you apply your function:

f4[a1_, a2_] := a1 + a2;
f4 @@ {1, 2}
(* 3 *)

But I don't understand why you think using Table and Array is creating some sort of impedance, so I don't have confidence in any of these approaches.

POSTED BY: Eric Rimbey
Posted 5 months ago

Thanks Eric for your help. I don't think I expressed what I need well, so your examples probably won't work. Here is a better explanation referring to the included notebook: The first evaluation cell has no problem because f1 and f2 are symbolically defined.

The second evaluation cell also has no problem because f1 and f2 are able to be symbolically evaluated in eqnlist, before being passed to NDSolve, by taking a symbolic Derivative of f.

The 3rd cell has a problem because I insist that the Derivative not be evaluated unless one of the arguments to f is a real number (instead of a symbol), which is the case once NDSolve gets going, but not initially. Though in this case f1 and f2 can be symbolically evaluated, in my real case they can't. If they are numerically evaluated before being passed to NDSOlve, then NDSolve will not get a list of symbolic equations. So they need to delay evaluation till NDSolve gets going (The real f is a conditional eigenvalue of a complicated large rank matrix that also depends on solving another ODE).

In the fourth cell, I show that as long as there is no list in the arguments of f, there is no problem with the same insistence on not evaluating eqnlist and keeping it symbolic so NDSolve is happy.

In the fifth cell, I try to convert the list to a sequence before passing to f, and then convert back to a list inside of f (It looks silly in this example, but in the real application keeping list structures is essential, there are many lists, they get passed on to other functions, and it becomes an unwieldy mess without them), but it doesn't work (I need to do the Extract while stripping the "Real" qualifier, I don't know how to do that yet)

POSTED BY: Iuval Clejan
Posted 5 months ago

In your third cell, why don't you just do the following?

f[a : {_Real, _}] := 0.5 (1 - a[[2]]) a[[1]]^2 + (1 - a[[1]]) a[[2]]^2 + a[[1]] a[[2]]
POSTED BY: Eric Rimbey
Posted 5 months ago

Oh, nevermind. It seems you're saying that you want to be able to assert that a symbol should be assumed to be Real. Is that the root of the problem?

POSTED BY: Eric Rimbey
Posted 5 months ago

The only reason I want at least one of the elements of the list of arguments passed to f to be real, is a hack. I want to pass NDSolve a list of symbolic equations. Since f in my actual problem can't be symbolically evaluated, the Real qualifier prevents evaluation when the derivatives are taken (the ones with respect to the arguments of f, not the ones with respect to t), to produce acceptable (to NDSolve) symbolic equations. Once NDSolve gets going, it replaces the arguments by real numbers, and then f can be evaluated. This can be seen to work in the fourth cell, which does not have a list of arguments passed to f. But I need to have a bunch of lists passed to f in my real (meaning actual, not as in real vs complex vs symbolic) example.

Perhaps this could be done with Hold and Release as well (instead of the Real hack) but I have been so far unsuccessful in achieving that.

POSTED BY: Iuval Clejan
Posted 5 months ago

So, which of these 5 examples is closest to what you actually want? You're demonstrating things that work, but apparently they aren't good enough. So what is your actual situation and what is it that you're actually trying to get to work?

POSTED BY: Eric Rimbey
Posted 5 months ago

The third cell would be ideal since it keeps the list structure. The fifth cell tries to get rid of the list structure (in reality I would also Flatten some Joined lists, some of which are also 2 dimentional, nested lists, to create args, I didn't bother showing that in this example), but it's far from ideal and also a hack, to compensate for the bug that Derivative can't handle qualifiers in lists. The other examples that actually work, either do not have lists (in arguments to f), or have functions that can be evaluated symbolically (and their derivatives too), which is not my case in the actual application. And I haven't gotten the fifth cell to work yet, though maybe it is close?

POSTED BY: Iuval Clejan
Posted 5 months ago

Not quite. Maybe somehow include g1 and g2 in the eqnlist. As it is now, the equation does not have the Derivatives (with respect to the list arguments, not time), and also eqnlist has done the evaluation symbolically of f, instead of keeping it unevaluated.

POSTED BY: Iuval Clejan
Posted 5 months ago

Ugh. Got wrapped around the axle in my translation. What about this?

eqnlist = {
  Derivative[1][av[1]][t] == g1 @@ avFnsApplied,
  Derivative[1][av[2]][t] == g2 @@ avFnsApplied,
  av[1][0] == avInit[[1]],
  av[2][0] == avInit[[2]]}
POSTED BY: Eric Rimbey
Posted 5 months ago

But you get the idea, right? One function to deal with lists and another to deal with sequences.

POSTED BY: Eric Rimbey
Posted 5 months ago

So close! It works, but unfortunately eqnlist has evaluated the derivative of f symbolically. Recall that in the actual case this cannot be done, since f can only be evaluated numerically. If I recall, what happens if I don't use the "Real" hack (which gives me a "non-numerical derivative at t=0 when I have lists, but works without lists), which is also what will happen if I use your solution in the actual case, is that it keeps trying to evaluate Eqnlist symbolically, never even getting to evaluate NDSolve and just doesn't give me output.

POSTED BY: Iuval Clejan
Posted 5 months ago

Okay, then I need more information. I can't tell the difference between this and what you had previously that I thought you said was good.

Is there anyway you can simplify the problem? Like just give a simple function whose derivative is just dead simple to look at and immediately recognize and then provide an actual correct solution that I can use as a test case?

I feel like we're getting lost in implementation details, and I haven't actually figured out what you really want.

Also, some commentary with your examples would be helpful. I'm not sure what's working as desired and what's not. You seem to set up an example just to indicate that it's not what you want, but it seems to work, so I don't know what the problem is.

Or maybe work backward. Hand-roll the entire NDSolve expression. Just provide the literal inputs that you want that work for NDSolve. Then show me what you have as inputs to this whole process. From there, maybe I can figure out how to get from your inputs to the final NDSolve expression.

POSTED BY: Eric Rimbey
Posted 5 months ago

I am willing to pay you $200 to solve this problem. OK, so we need two things: 1. Arguments to f have to be a bunch of lists (and some non-lists too) 2. eqnlist that is given to NDSolve should not attempt to evaluate f (or its non-time derivatives) but should be in symbolic, unevaluated Derivatives form. The reason is that f (and hence its derivatives) is not possible to evaluate symbolically, only numerically.

I don't know how to come up with a simple f that can't be evaluated symbolically (like it is in all the examples we have produced so far). I can send you two files that have the real f (called ffoutsuper in there, you will have to scroll down to see its definition which involves eigenvalues of a high rank matrix and calls to another ODE solver, and then scroll down even further to see where NDSolve is called): The first shows that as long as there are no lists in the arguments to f, NDSolve is happy and so am I.

The second doesn't work because there are lists in the args to f. You can see from these 2 files that f has no hope of being evaluated symbolically (maybe there is a clever way to do it using certain symmetries, but I will have cases in the future where these symmetries are not present that might make f be evaluatable symbolically)

POSTED BY: Iuval Clejan
Posted 5 months ago

I'm sorry, this isn't helping me. Please, please just give me literal concrete expressions that I can work with. I know it's obvious to you, but I'm not grokking. So, like in your original example, is the following even correct?

NDSolve[
  {Derivative[1][av[1]][t] == 1.*av[1][t]*(1 - av[2][t]) + av[2][t] - av[2][t]^2, 
   Derivative[1][av[2]][t] == av[1][t] - 0.5*av[1][t]^2 + 2*(1 - av[1][t])*av[2][t], 
   av[1][0] == 1., av[2][0] == 1.}, 
  {av[1][t], av[2][t]}, 
  {t, 0, 1.4}]

Does that produce the result you want? If not, show me an NDSolve expression that does produce the result you want. Then we can work backward from there. I can't figure out if this is the right "end game" and you're struggling to figure out how to get there, or if this is the wrong "end game" and you don't know where things went wrong.

POSTED BY: Eric Rimbey
Posted 5 months ago

You said

The first shows that as long as there are no lists in the arguments to f, NDSolve is happy and so am I.

and

You can see from these 2 files that f has no hope of being evaluated symbolically (maybe there is a clever way to do it using certain symmetries, but I will have cases in the future where these symmetries are not present that might make f be evaluatable symbolically)

I know you think you're being helpful, but this is just a distraction. I need to know the actual things you want to pass in to NDSolve. Just forget about the list versus non-list arguments for now. I just need to have a target to shoot for. These informal descriptions are just not helping me.

I'm like 95% sure this is doable in a very easy way, but so far I've failed to hit your target, and I don't know what my errors are. Don't tell me in words like "evaluate f symbolically". Just give me a concrete target that I can shoot for.

POSTED BY: Eric Rimbey
Posted 5 months ago

Hmm... I don't know how to make it simpler. If you look at the two big files I included, the first has no lists and it works, the second has lists and it doesn't work. The same is true for the simpler file I included originally, but in that one there is an f that can have its derivative be taken symbolically (but can be prevented from doing so by the "Real" hack). In the more complex files, in both cases there is an f that is not able to have its derivatives be taken symbolically. The code snippet you posted:

NDSolve[
  {Derivative[1][av[1]][t] == 1.*av[1][t]*(1 - av[2][t]) + av[2][t] - av[2][t]^2, 
   Derivative[1][av[2]][t] == av[1][t] - 0.5*av[1][t]^2 + 2*(1 - av[1][t])*av[2][t], 
   av[1][0] == 1., av[2][0] == 1.}, 
  {av[1][t], av[2][t]}, 
  {t, 0, 1.4}]

has explicit expressions for the equations in eqnlist (effectively evaluating derivatives of f), so it it NOT what is needed.

POSTED BY: Iuval Clejan
Posted 5 months ago

so it it NOT what is needed.

Excellent! That's progress. So, what is needed?

Clearly we're having a communication issue. You think you've provided me with enough info, but I don't understand. Why can't you literally just manually change the thing that doesn't work to the thing that does work and show me that? If you do that, I can almost guarantee that I can show you how to get there.

POSTED BY: Eric Rimbey
Posted 5 months ago

Now you're just being funny, or sarcastic? I guess I need to think of a simple example of a function that can't be evaluated symbolically for you to "grok" what is going on. The example in those two files (nestedlevels4.3 and nestedlevels4.5 was too complicated apparently). This is probably just as frustrating for you as it is for me.

POSTED BY: Iuval Clejan
Posted 5 months ago

I'm very serious. I opened the notebooks, saw how large they were, and closed them. I'm not spending my day reverse-engineering all of that code.

You don't need to find a simpler function (although, I don't know why that's such a big deal), you just need to tell me what the "answer" is. Something I can use as a reference to check against. I'm not going to try to figure out the mathematics and I'd rather not reverse engineer a ton of code. I'm pretty sure there is a simple code issue here, but I'm tired of guessing. So, just show me exactly what a correct result would look like so that we don't just keep wasting time.

Look, you're the one with a problem to solve. I'm just trying to help out. My only investment here is just being curious about solving the programming problem. If it's not worth it to you to give me something explicit I can use as a test case, then it's not worth it. I can just move on to the rest of my life.

POSTED BY: Eric Rimbey
Posted 5 months ago

You might find a larger audience over at https://mathematica.stackexchange.com/ . Someone over there might just understand what you're asking for. Sorry I couldn't be of more help.

POSTED BY: Eric Rimbey
Posted 5 months ago

Eric, I'm thinking you might have missed one of my posts, please go back through them all (I should too, to make sure they all posted successfully). I've done my utmost best to simplify the problem and tell you why your solution doesn't work, and tell you exactly what is needed. Your comment sounded sarcastic because if I knew how to "manually change the thing that doesn't work to the thing that does work and show me that" then the problem would be solved.

POSTED BY: Iuval Clejan
Posted 5 months ago

Dear Eric, I do appreciate your help very much and your persistence on insisting for clarity. Please see below:

"Oh, well then I've completely misunderstood where the problem is.

Look you posted this:

f1[a_] := a[[1]] (1 - a[[2]]) + a[[2]] - a[[2]]^2;
f2[a_] := a[[1]] - 0.5 a[[1]]^2 + 2 (1 - a[[1]]) a[[2]];
av0 = {1., 1.};
varex = Table[av[i][t], {i, 2}];
eqnlist = {Derivative[1][av[1]][t] == f1[varex], 
   Derivative[1][av[2]][t] == f2[varex], av[1][0] == av0[[1]], 
   av[2][0] == av0[[2]]};
solDEsuper = NDSolve[eqnlist, varex, {t, 0, 1.4}]

and said that it worked."

My bad, that IS confusing. I meant it worked in this example and didn't give an error. It will not work in my actual problem, because in my actual problem I can't have the RHS of eqnlist be given as an analytic function of the arguments, as in what f1 and f2 are doing.

"I posted this:

avInit = {1, 1};
avFns = Array[av, 2];
avFnsApplied = Through[avFns[t]]; 

avProtoList[a_List] := 
  0.5  (1 - a[[2]])  a[[1]]^2 + (1 - a[[1]])  a[[2]]^2 + 
   a[[1]]   a[[2]];
avProto[args___] := avProtoList[{args}];

g1 = Derivative[1, 0][avProto];
g2 = Derivative[0, 1][avProto];

eqnlist = {Derivative[1][av[1]][t] == g1 @@ avFnsApplied, 
   Derivative[1][av[2]][t] == g2 @@ avFnsApplied, 
   av[1][0] == avInit[[1]], av[2][0] == avInit[[2]]};
solDEsuper = NDSolve[eqnlist, avFnsApplied, {t, 0, 1.4}]

and you said that it didn't work."

It didn't work for my actual problem for the same reason the previous didn't work. I tried the Real hack for this code and it produced an error (as it did for my examples with lists), but maybe I didn't do it right.

"When I look at eqnlist for each of them, I can't tell the difference. I thought if we used a simpler function, I could see the difference by inspection. But you don't want to provide a simpler function. Fine, just tell me what's different about these two. I'm sorry that I'm just not picking up on it, but that's where we are."

There is no difference for any function. Coming up with a simple yet non-analytic in terms of its variables function has eluded me, though I haven't tried very hard. Perhaps there are other ways to deal with this besides what has worked for me without lists (the Real hack--also other conditionals on the variables that leave the function unevaluated until its arguments are non-symbolic, like NumberQ)

"But then later, I forced you to tell me whether that expression was correct or not, and you said that it wasn't. So, I thought the problem was in generating eqnlist, and that maybe you could indicate the difference by giving me a reference. But now you seem to be saying that you don't know what eqnlist should even look like, you just know it's wrong. Okay, that's fair, but now we're talking about a mathematics problem, not a programming problem. So, you'll have to lead me through the mathematics before I can provide the code."

You can see what a good eqnlist should look like for either the simple file I initially posted or the more complicated ones that came later. Just type eqnlist in a separate cell after evaluating one of the cells that produced errors (because they had lists and the Real hack) or the ones that had the real hack, no lists and did not produce errors. Or take the semicolon out after the definition of eqnlist. It is something that has derivatives in it on the RHS of the equations, as opposed to explicit expressions in terms of the NDSolve variables.

"Now, you probably think I'm being stupid, and you'll say "no, no, that's not what I said/meant", and that's totally fine. Communication is hard. But I, as of right now, have no idea what/where the problem is. I know that for you, the problem is something about whether arguments are passed as a sequence or as a list. To me, that's a trivial problem, and I'm extremely confident that I can solve it. But I've been failing to produce a good result even though it seems to match the good results you've previously given me. It sounds like you're getting "good answers" with sequences, so I'm just trying to pin down what a good answer is."

It's a combination of TWO things. One is lists vs Sequence. The other is a function that can be algebraically written in terms of its arguments vs one that cannot. I want to deal with both simultaneously. I have a solution to either one by itself, but not when both are combined.

"So, at the end of the day, it sounds to me like we'd like to feed some inputs into NDSolve. We've got to generate those inputs somehow. You seem to be able to recognize when those inputs are good or not. I don't seem to be able to figure that part out. Also, I'm not really sure where we're starting here. It sounds like there are things "before" we start defining f, f1, etc. I know in your real world problem, those things are big and complicated, but in our toy problem, I would think you could give them to me explicitly. So, my brain is saying, "if he gives me the uber inputs, A, and then the inputs to NDSolve, B, then I can figure out how to transform A into B"."

The uber inputs would be a function that is not able to be written down explicitly in terms of its arguments (which also happen to be lists). The eigenvalue of a rank 5 matrix would be the simplest one I can think of. Or maybe a function defined implicitly as a solution to an equation that can only be solved numerically. But you can also look at the bigger files without having to reverse engineer them, just look at ffoutsuper as a black box function that is not possible to evaluate symbolically in terms of its arguments. Barring that, just make something work that uses the Real, or NumberQ hack as a conditional for one of the arguments to f. Or find some way to keep eqnlist without evaluating f or its derivatives. Or find a way to do what I was proposing at the beginning of this thread: take a list, turn it to a sequence before passing to f, then reconstruct it inside f, so that I can pass lists to other functions that I call from within f.

"If you can tell me where I'm going wrong in any of the above thinking, that would help greatly. If you can give me any concrete examples that I can compare to at any point in the computation, that would help greatly. If you can try different words to explain your intent, that might help."

I tried. I appreciate you trying to understand what I wrote and asking more questions if need be.

POSTED BY: Iuval Clejan
Posted 5 months ago

Okay, I think I see what you're saying now. I'll try to take a stab at it later. But in the meantime, here's the simplest thing I can think of to satisfy your suggestion to

take a list, turn it to a sequence before passing to f, then reconstruct it inside f, so that I can pass lists to other functions that I call from within f.

In fact, I'll show both "directions":

functionList[list_List] := dependencySeq @@ list;
functionList[{1, 2, 3}]
(* dependencySeq[1, 2, 3] *)

functionSeq[args___] := dependencyList[{args}];
functionSeq[1, 2, 3]
(* dependencyList[{1, 2, 3}] *)

inputList = {a, b, c};
functionSeq @@ inputList
(* dependencyList[{a, b, c}] *)

My intuition is that this is too simplistic to work, but without going back to look at eqnlist or ffoutsuper right now, that's the simplest thing that satisfies my best attempt to interpret your words here.

POSTED BY: Eric Rimbey
Posted 5 months ago

I'm not confident that this is the final answer, but I just want to use this as a point of discussion to try to nail down where the disconnect is. What if we just used a purely formal symbol for the "non-analytical" function and then provided whatever implementation you want as a second function?

av0 = {1., 1.};
varex = Table[av[i][t], {i, 2}];
eqnlist =
  {Derivative[1][av[1]][t] == Derivative[1, 0][f] @@ varex,
   Derivative[1][av[2]][t] == Derivative[0, 1][f] @@ varex,
   av[1][0] == av0[[1]],
   av[2][0] == av0[[2]]}

(* 
  {Derivative[1][av[1]][t] == Derivative[1, 0][f][av[1][t], av[2][t]],
   Derivative[1][av[2]][t] == Derivative[0, 1][f][av[1][t], av[2][t]], 
   av[1][0] == 1., 
   av[2][0] == 1.}
 *)

And then,

fImplementation[x_, y_] := (1/2) (1 - y) x^2 + (1 - x) y^2 + x  y;
eqnlist /. f -> fImplementation
(*
  {Derivative[1][av[1]][t] == av[1][t]*(1 - av[2][t]) + av[2][t] - av[2][t]^2, 
   Derivative[1][av[2]][t] == av[1][t] - av[1][t]^2/2 + 2*(1 - av[1][t])*av[2][t], 
   av[1][0] == 1., 
   av[2][0] == 1.}
 *)

Or even this:

f[x_, y_] := (1/2) (1 - y) x^2 + (1 - x) y^2 + x  y;
av0 = {1., 1.};
varex = Table[av[i][t], {i, 2}];
eqnlist =
 {Derivative[1][av[1]][t] == Derivative[1, 0][Inactive[f]] @@ varex,
  Derivative[1][av[2]][t] == Derivative[0, 1][Inactive[f]] @@ varex,
  av[1][0] == av0[[1]],
  av[2][0] == av0[[2]]}

And then when you want your implementation to be applied:

eqnlist // Activate

Does that get us anywhere?

POSTED BY: Eric Rimbey
Posted 5 months ago

Maybe! Just to make sure I understand your suggestion, here is the test file I implemented it in. Do I need to combine this with your previous suggestions on the 2-way conversions of lists to sequences, so I can pass varex (there will be several of these) to other functions from f? I fear there will be scoping problems, as varex is global and not scoped within f (I could be wrong about this). All the lists need to be updated as NDSolve chugs along.

Also, I need to figure out how to automate taking derivatives before forming eqnlist. I was using such constructs as this, when I was hoping I can pass f lists directly, to differentiate f for forming eqnlist:

nderaR[i1_, i2_, m_, 
   f_] := (Derivative @@ {0, 0, ConstantArray[0, m], 
      ConstantArray[0, m], ConstantArray[0, {m, m}], 
      Normal[SparseArray[{{i1, i2} -> 1}, {m, m}]], 0, 
      ConstantArray[0, m], ConstantArray[0, m], 
      ConstantArray[0, {m, m}], ConstantArray[0, {m, m}]})[f];

one for each list

POSTED BY: Iuval Clejan
Posted 5 months ago

Do I need to combine this with your previous suggestions on the 2-way conversions of lists to sequences, so I can pass varex (there will be several of these) to other functions from f?

I don't think so. Just my opinion here, and I obviously am not an expert in the domain you're working with, but it seems to me to think of f and whatever other functions you want to take partial derivatives of as being a function of multiple variables (arguments in sequence) rather than a single list argument. So, I would write these functions that way (e.g. f[x_,y_,z_] := ...) and deal with the List <-> Sequence stuff somewhere else. All I was really showing you before is that Apply (shortcut @@) is how you replace the List head with your function f. So, the Derivative[0, 1][f] @@ varex bit is the key to that.

I fear there will be scoping problems, as varex is global and not scoped within f

I don't think that's a problem based on what I've seen so far. If you think there's a risk of that somewhere, point it out to me and I'll analyze it. Scoping in Mathematica isn't really true scoping, it's just playing games with names, so what we'd really be concerned about is actually name collisions.

All the lists need to be updated as NDSolve chugs along.

Not sure what you mean by this. We can't really control what's going on inside NDSolve, so I'm not sure what you're getting at.

Also, I need to figure out how to automate taking derivatives before forming eqnlist.

Yeah, I figured at some point we'd need a way to automate this. That should be doable, but honestly I'm confused by your derivatives. For example, if I evaluate this

nderaR[2, 1, 2, g]
(* Derivative[0, 0, {0, 0}, {0, 0}, {{0, 0}, {0, 0}}, {{0, 0}, {1, 0}}, 0, {0, 0}, {0, 0}, {{0, 0}, {0, 0}}, {{0, 0}, {0, 0}}][g] *)

I don't know how to interpret that result. I think I just don't have the mathematical knowledge, but I don't know what having all those sublists in Derivative means. But setting that aside, if you can articulate the rules for how to create that expression, we can figure out how to generalize that.

POSTED BY: Eric Rimbey
Posted 5 months ago

OK, should be able to work on it tonight and tomorrow. I will let you know if it works, and if you're amenable I still want to give you that financial reward. Thanks!

POSTED BY: Iuval Clejan
Posted 5 months ago

As far as the derivatives, they have to have the same number of arguments as the function f. But I want to be able to have them applied in a systematic way to the list elements. So I will need something to convert the lists to sequences that I can pass to Derivative, while manipulating the lists.

POSTED BY: Iuval Clejan
Posted 5 months ago

Yep, pretty sure we can take lists and transform them to sequences for Derivative.

POSTED BY: Eric Rimbey
Posted 5 months ago

Eric, the only thing I still need help with is to index correctly into the lists

ffoutsuper[args__] := fList[{args}];
fList[{flag_, LRc_, cL_, cR_, alphaL_, alphaR_, LRC_, CL_, CR_, 
AlphaL_, AlphaR_}] :=???

flag, LRc and LRC are scalars, cL and cR are 4-vectors, alphaL, alphaR, AlphaL, and AlphaR are 4x4 matrices (for now, the dimensions of lists will change in the future). Is there a good way to do this? The eariler simple example had only one 1-dimensional list, so it was trivial. I need to recreate the original lists from the flattened, joined lists that are served as a sequence to ffoutsuper.

POSTED BY: Iuval Clejan
Posted 5 months ago

If the argument list always has this same structure, you can just refer to the pattern names you've already used:

fList[{flag_, LRc_, cL_, cR_, alphaL_, alphaR_, LRC_, CL_, CR_, AlphaL_, AlphaR_}] := 
 StringForm["``-``-``-``-``-``-``-``-``-``-``", LRc, CR, AlphaL, flag, cL, alphaL, cR, alphaR, CL, AlphaR, LRC];

fList[Range[11]] // ToString
(* "2-9-10-1-3-5-4-6-8-11-7" *)

But if you really want to access by index, then you'll need a pattern name for the whole list:

fList[args : {flag_, LRc_, cL_, cR_, alphaL_, alphaR_, LRC_, CL_, CR_, AlphaL_, AlphaR_}] := 
 StringForm["``-``-``-``-``-``-``-``-``-``-``:::``:::(``)", LRc, CR, AlphaL, flag, cL, alphaL, cR, alphaR, CL, AlphaR, LRC, args, args[[5]]];

fList[Range[11]] // ToString
(* "2-9-10-1-3-5-4-6-8-11-7:::{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}:::(5)" *)

And if you only want to access by index, you don't need all those other pattern names:

fList[args : {_, _, _, _, _, _, _, _, _, _, _}] := args[[RandomSample[Range[11]]]];    

fList[Range[11]]
(* {3, 1, 4, 6, 8, 11, 2, 7, 10, 5, 9} *)

And if you don't actually want to restrict it to 11 arguments, but any arbitrary number:

fList[args : {___}] := RandomSample[args];

fList[Range[5]]
(* {1, 5, 4, 2, 3} *)

And also, you can index into any one particular argument:

fList[{flag_, LRc_, cL_, cR_, alphaL_, alphaR_, LRC_, CL_, CR_, 
   AlphaL_, AlphaR_}] := cL[[2]];

fList[{1, 2, {100, 101, 102, 103}, 4, 5, 6, 7, 8, 9, 10, 11}]
(* 101 *)
POSTED BY: Eric Rimbey
Posted 5 months ago

At least one of us is not understanding the other here. I need to access all these args that come from a flattened list of joined lists for computation. So flag is just flag (a scalar), RLc is just RLc (a scalar), I need to access different element of cL and cR (which are one dimensional lists of length 4), I need to access different elements of aL and aR which are 4x4 2 dimensional lists, etc. I want fList to be the function that has those original lists in it, somehow reconstructed from the flattened sequence that ffoutsuper gets.

How does mixing up the order of the arguments (some of which are lists) and then finding their original order help me?

POSTED BY: Iuval Clejan
Posted 5 months ago

I need to tell flist that the first argument is a scalar called flag, the second also a scalar called RLc, the third through 6th correspond to a 1D list of dimension 4 called cL, etc.

POSTED BY: Iuval Clejan
Posted 5 months ago

How does mixing up the order of the arguments (some of which are lists) and then finding their original order help me?

I was just demonstrating various ways of accessing the arguments and parts of argument in the RHS of the function definition. I don't know how you actually need to use the indexes, so I made something up.

Also the title your post here is

Referring to arguments of a function by order in which they appear

so, I dunno, sorry to not be able to read your mind, but it sounds like you're struggling with, I dunno, making the RHS of your function definition refer to the arguments in some way. I'd ask for you to give me a concrete output that you're trying to achieve so that I can then show you how to write your function to produce that output, but I'm afraid you'd insult my intelligence again. Dude, just ask a straightforward question using a minimal example. Provide inputs and expected outputs. If you can't do that, then you'll just have to endure the frustration of watching me trying to figure out what you're asking for.

POSTED BY: Eric Rimbey
Posted 5 months ago

I need to tell flist that the first argument is a scalar called flag, the second also a scalar called RLc, the third through 6th correspond to a 1D list of dimension 4 called cL, etc.

If you mean you need to set these conditions as constraints on the arguments, then you could try any of these (I'm going to simplify your example, we don't need eleven arguments to demonstrate this):

myFuncTakesScalars[arg1_Real, arg2_Integer, arg3_?NumericQ, arg4_?AtomQ] := "constraints satisfied";
myFuncTakesScalars[1., 2, Pi, "a"]
(* "constraints satisfied" *)

myFuncTakesLists[arg1 : {_, _, _, _}, arg2_?ArrayQ, arg3_?MatrixQ] := "constraints satisfied";
myFuncTakesLists[{1, 2, 3, 4}, RandomReal[{0, 1}, 100], RandomInteger[{1, 10}, {20, 30}]]
(* "constraints satisfied" *)

myFuncTakesListsWithFurtherConstraints[arg1 : {_, _, _, _}, arg2_?ArrayQ, arg3_?MatrixQ] := "constraints satisfied" /; 4 == Length[arg1] && 2 == ArrayDepth[arg3];
myFuncTakesListsWithFurtherConstraints[{1, 2, 3, 4}, RandomReal[{0, 1}, 100], RandomInteger[{1, 10}, {20, 30}]]
(* "constraints satisfied" *)
POSTED BY: Eric Rimbey
Posted 5 months ago

OK, we're almost there. I don't want constraints. I want to recover the original lists and pass them on to other functions that use them. So I think this does it:

av0 = {1., 1.};
a = Table[av[i][t], {i, 2}]; b = {{0.01, 0.02}};
apat = {x_, y_}; bpat = {{z_, w_}};
vars = a;
varex = Flatten[{a, b}];
varpat = Flatten[{apat, bpat}];
fImplementation[(*x_,y_,z_,w_*)Sequence @@ varpat] := 
  (a = {x, y}; 
   b = {{z, w}}; (1/2) (1 - a[[2]]) a[[1]]^2 + (1 - a[[1]]) a[[2]]^2 +
     a[[1]] a[[2]] - b[[1, 1]] b[[1, 2]]);
eqnlist = {Derivative[1][av[1]][t] == 
   Derivative[1, 0, 0, 0][f] @@ varex, 
  Derivative[1][av[2]][t] == Derivative[0, 1, 0, 0][f] @@ varex, 
  av[1][0] == av0[[1]], av[2][0] == av0[[2]]}
solDEsuper = 
 NDSolve[eqnlist /. f -> fImplementation, vars, {t, 0, 1.4}]

It's a bit unwieldy, but probably good enough. How can I pay you?

POSTED BY: Iuval Clejan
Posted 5 months ago

Ooooooooohhhhhhh. Good grief. I finally understand. It's just so much easier to look at something concrete. Not that it matters now, but maybe you can appreciate my confusion if I explain what I thought you meant. You said

I need to tell flist that the first argument is a scalar called flag, the second also a scalar called RLc,

Now, since you already had arguments named flag and RLc, I thought your problem was telling flist that they were scalars. And so I gave you several ways to apply the constraint that an argument is a scalar. Then you said

the third through 6th correspond to a 1D list of dimension 4 called cL, etc.

which sounded to me like each of these arguments was a list of length 4, not that you wanted to re-interpret the 3rd through 6th arguments as members of a new list. Given your language of "tell flist that..." I wasn't understanding that you were talking about the implementation details instead of the interface. Telling a function about its arguments is what it means to give a function a signature. I don't think I ever, ever would have understood this to mean "flist's implementation should construct a list from arguments 3-6".

Okay, with that out of the way, I'll send some suggestions.

POSTED BY: Eric Rimbey
Posted 5 months ago

The basic problem is that we have two equivalent data structures that have different "shapes". One of the shapes is a flat list and the other has some extra levels of structure. What I don't know is if you have a bunch of different shapes rather than just two. Or maybe you have a bunch of scenarios that all have two shapes, but the two shapes are different across the pairs. So, situation #1 would be having, say 16 arguments that can show up in three different shapes: a flat list, a 4x4 matrix, and a 2x8 matrix. Situation #2 would be that for some particular function, we need to transform between a flat list and a 4x4 matrix, but for some other function we need to transform between a flat list and a 2x8 matrix. Anyway, what I would suggest is that you build helper functions to do the transformations and have your main functions leverage these helpers.

We know we can go from the more structured shape to a flat list with Flatten, so we only need to implement an Unflatten (or maybe several different Unflatten functions, in which case you should name them distinctly). It might also just help readability to be even more explicit about naming the argument patterns.

StructuredPattern = {{_, _}, {{_, _}}};
ListPattern = Flatten[StructuredPattern];

UnflattenSequence[a_, b_, c_, d_] := {{a, b}, {{c, d}}};
UnflattenList[list : ListPattern] := UnflattenSequence @@ list;

FunctionSequence[args___] :=
  <|"what I pass to the list processing function" -> List[args],
   "what I pass to the structure processing function" -> UnflattenSequence[args]|>;
FunctionList[args : ListPattern] :=
  <|"what I pass to the sequence processing function" -> Sequence @@ args,
   "what I pass to the structure processing function" -> UnflattenList[args]|>;
FunctionStructure[args : StructuredPattern] :=
  <|"what I pass to the sequence processing function" -> Sequence @@ Flatten[args],
   "what I pass to the list processing function" -> Flatten[args]|>;

FunctionSequence[w, x, y, z]
(*
  <|"what I pass to the list processing function" -> {w, x, y, z}, 
   "what I pass to the structure processing function" -> {{w, x}, {{y, z}}}|>
 *)

FunctionList[{w, x, y, z}]
(*
  <|"what I pass to the sequence processing function" -> Sequence[w, x, y, z], 
   "what I pass to the structure processing function" -> {{w, x}, {{y, z}}}|>     
 *)

FunctionStructure[{{w, x}, {{y, z}}}]
(*
  <|"what I pass to the sequence processing function" -> Sequence[w, x, y, z], 
   "what I pass to the list processing function" -> {w, x, y, z}|>
 *)
POSTED BY: Eric Rimbey
Posted 5 months ago

No need to pay.

POSTED BY: Eric Rimbey
Posted 5 months ago

Sorry, I am not familiar with associations, but I will learn if you can you try to implement your more general solution to this specific example, so that I can make fflat1 work, instead of having to type in all the dummy elements of all the lists as in fflat2 and then (double tedium) reconstructing them (I commented out all but the first list, because it is too tedious to type in all the dummy arguments, and will only get more tedious as I will have higher dimensional lists in the future)? Somehow, my attempts to make fflat1 receive a sequence with no structure did not work, and it still keeps the structure, as we can see with the failed first attempt to call fflat1 with a structureless sequence, and successful second attempt with a stuctured sequence which contains a list.

POSTED BY: Iuval Clejan
Posted 5 months ago

Sorry, I am not familiar with associations

Sorry for the distraction then. Associations aren't inherent to the problem you're trying to solve. I was just trying to demonstrate how to transform between representations.

I'll try to refactor your code later. I only have a couple of minutes right now. So for now, I'll just try to explain why it failed.

Here's your definition for fflat1 (I'm removing the comments for clarity)

fflat1[Sequence @@ Flatten[{flag_, LRc_, cL_}]] := cL[[1]]

If you look at just the argument list you created, it looks like this.

Sequence @@ Flatten[{flag_, LRc_, cL_}]
(* Sequence[flag_, LRc_, cL_] *)

So, your function expects exactly three arguments. But when you tried to apply it:

fflat1[Sequence @@ Flatten[{1, 0.1, cout0L}]]

you actually provided 6 arguments:

Sequence @@ Flatten[{1, 0.1, cout0L}]
(* Sequence[1, 0.1, 4., 4., 4., 4.] *)

When you applied it the other way:

fflat1[1, 0.1, cout0L]

you did provide exactly three arguments:

Sequence[1, 0.1, cout0L]
(* Sequence[1, 0.1, {4., 4., 4., 4.}] *)

but the last argument was itself a list. So, with the argument constraint satisfied, the function was evaluated to be the first item in the third argument, which is 4..

POSTED BY: Eric Rimbey
Posted 5 months ago

Yes, I understand why it won't evaluate that one line, but I was hoping you could figure out how to make it work in an elegant (non-tedious way).

Also, the whole reason for getting away from lists and giving ffoutsuper list-free arguments was initially that Derivative was not able to handle conditionals in lists, and those were only necessary in order to keep eqnlist unevaluated. But you figured out how to not evaluate it by having a dummy function in the definition of eqnlist, that only later gets substituted with the function I want (ffoutsuper).

However, though Derivative can handle lists (now without the hack conditionals), it takes a really long time to evaluate my actual ffoutsuper when we keep the lists. And moreover, there is another bug, which is that since my ffoutsuper needs to take the real part of certain eigenvalues of certain matrices, the derivative outputs such nonsense as Re'[number]. I have included a file that demonstrates both of these problems, for a simpler ffoutsuper than my actual one. If we could fix this, we probably wouldn't need to do the whole list-> sequence rigamarole.

POSTED BY: Iuval Clejan
Posted 5 months ago

So, back to trying without lists in the args to ffoutsuper, since we Derivative has problems with lists in my real case, either being too slow, or trying to do nonsense like differentiaing the real part of a variable. This should not be complicated, but I don't know how to do it. I want to reconstruct the lists inside ffoutsuper. ffoutsuper gets a flat sequence, so the information of the list stuctures is lost to it, but not in my head. For each simulation I will have a constant list structure, though these will vary from simulation to simulation, and for now I don't mind rewriting code for different simulations.

So I know where in the flat structure to start and where to end, for reconstructing all the lists, no matter what shape they have. I just need a way to refer to the nth argument of the flat sequence in the args of ffoutsuper. Can you show me how to do that? Do I need to define a pure function and use Slot? Here is a failed attempt to reconstruct one of the lists for a simple example Wolfram Notebook

POSTED BY: Iuval Clejan
Posted 5 months ago
fflat2[args__] := (cL = args[[3 ;; 6]]; cL[[1]]^2 ); (*how do I say cL=Table[third through sixth argument of fflat2?*)

Since args is a Sequence, it doesn't do what you expect when used as an argument to Part. The easiest way to fix this is to coerce it to a List:

fflat2[args__] := (cL = {args}[[3 ;; 6]]; cL[[1]]^2 )
POSTED BY: Eric Rimbey
Posted 5 months ago

Getting close, but still no cigar. I'm now able to give ffoutsuper a sequence, and resonstruct lists inside it (I only did it for 2 of the 2dimensional lists to keep it simple). But now I'm still getting superslow evaluation, and the Re'[number] bug. Any ideas?

POSTED BY: Iuval Clejan
Posted 5 months ago

And other issues that are seen in the actual code. Is there a way to tell Derivative to not even try to evaluate something symbolically?

POSTED BY: Iuval Clejan
Posted 5 months ago

If I understood what you're trying to achieve mathematically, maybe I could give you suggestions, but I don't, so I can't. But as a Mathematica programming problem, you might be beating your head against a wall. I'm not sure that we can expect Mathematica to figure out the derivative that you want from the form that you're providing to it.

Not that it really matters to the underlying problem, but you can avoid using temporary variables:

ffoutsuper[args__] := Max[Re[Eigenvalues[Dot @@ ArrayReshape[Drop[{args}, 10], {2, 4, 4}]]]]

I really don't think I can help you if you can't provide simpler examples. I mean, just forget your actual domain completely, and just show an example of dynamically generating a deferred derivative (or whatever we're trying to do here). Show an example where you know the right answer and show me the right answer. The stuff you give me is too far along, and I don't know where to start looking, and so I end up spending a lot of time reverse engineering your code. I'm not willing to do that any longer.

Some general advice: start small and take baby steps. Don't get this far into your coding before you try to fix it. Start with the absolute simplest representation of your problem (or a minimal alternative to your problem) and just do one computation/transformation and make sure it works. Then build on that foundation with one more dead simple computation/transformation. You have so much crazy code here that I don't know if you've made a straight up semantic error or if you're just mis-using a Mathematica function or if we just need to change the order of evaluation. There may be a simple fix, or this whole thing might actually be impossible. I have no idea where we are on that spectrum.

POSTED BY: Eric Rimbey
Posted 5 months ago

I tried your ArrayReshape. It makes no difference either to the timing for Derivative (way too slow) or the bug with the Re'[1]. I am including the same file, but now instead of taking the highest eigenvalue of the product of two of the input matrices, it takes the highest eigenvalue of the product squared. Here it never stops the calculation, even after 20 minutes. There is something very wrong with Derivative, and I want you to admit it, instead of putting it on me to come up with something simpler (are you working for Wolfram?). It might be possible to simplify further the list of args, but why? It is much simpler than my actual application already. Derivative has a problem, maybe it can only take so many variables? Also, please take a look at the two comments in the file.

P.S. I haven't tried the square of the product of matrices with your ArrayReshape, because I don't have the latest version where I am right now, I only have version 8.4 here, which doesn't know about that function. I can try it later today when I get back home to my desktop, but I doubt it will be any different. Maybe Mathematica is not up to this task?

POSTED BY: Iuval Clejan
Posted 5 months ago

I tried your ArrayReshape. It makes no difference either to the timing for Derivative (way too slow) or the bug with the Re'[1].

I'm a bit surprised that it didn't make a difference on timing, but I didn't intend it to fix anything. It's just more wolfram-idiomatic style.

There is something very wrong with Derivative, and I want you to admit it, instead of putting it on me to come up with something simpler (are you working for Wolfram?).

I'm not a Wolfram employee. I don't have to admit anything. Finding the simplest demonstration of something is a tried-and-true debugging technique, but if you don't want to try it, that's fine by me. I'm not invested in any of this--it's your baby.

POSTED BY: Eric Rimbey
Posted 5 months ago

What do you find complicated about my example? Maybe it can't be simplified any further.

POSTED BY: Iuval Clejan

I need to recreate the lists from the arguments

Is this all you really meant? Inside fn, al is a list of the arguments.

fn[args___] := Module[{al=List[args]},
  ...
]

The arguments can also be given their own names:

On[Assert];
fn[args : PatternSequence[a1_, a2_]] := Module[{al = List[args]},
  Assert[a1 == al[[1]]];
  Assert[a2 == al[[2]]];
  Echo["OK"];
  ]
fn[1, 2]

Inside the 2nd function,

I would like to define, inside f, a list={a1,a2} and then redefine f[a1_,a2_]:=list[[1]]+list[[2]]

What do you mean by "redefine"? The best I can tell is that you're asking for a functor (a function-rewriting-function) that takes a function in one form, and returns a function that does the same job but in another form. Please provide concrete examples of a function before and after the modification.

If I were to guess, you want to write, literally, a function like: fn[a1_,a2_]:=a1+a2, pass it to a functor, and get back a function like fn[args:PatternSequence[a1_,a2_]]:=Module[{al=List[args]}, al[[1]]+al[[2]] ].

More concretely, you'd want a rewriter that can be used as follows: rewriter[fn[a1_,a2_]:=a1+a2] returns HoldForm[fn[args:PatternSequance[a1_,a2_]]:=Module[{al=List[args]}, al[[1]]+al[[2]] ] ].

The held form can be then released (using ReleaseHold), and the replacement function will be created. The original function can be Clear-ed in the meantime, i.e. after using the rewriter but before the held form is released.

Please confirm that that is indeed what you'd want. Otherwise, provide a concrete example of what you'd pass into the rewriter, and what result you expect back. Thank you.

there is a bug that makes this not work in my case, we don't have to get into it

That has a strong smell of an XY Problem. Perhaps fixing that "bug" would be desirable instead of devising clumsy workarounds? Could you please give concrete examples of the "bug" you allude to?

I have a much more complicated function in my real case and I want to automate list by using such Mathematica constructs as Table and Array.

There are no fundamental differences between code that generates Part[argList, index] vs code that generates Symbol["arg"<>ToString[index]].

For example, here's a function that creates a function definition for a function that sums a certain number of arguments. The function definition action (SetDelayed) is inactive, so the form of the function can be inspected before the function definition is performed.

makeSum[fn_Symbol, count_Integer] := 
 Module[{argSymbols, argPatterns},
  argSymbols = Table[Symbol["a" <> ToString[i]], {i, count}];
  argPatterns = 
   Activate[ Inactive[Pattern][#, Blank[]] & /@ argSymbols];
  Inactivate[
   fn[Sequence @@ argPatterns] := Total[argSymbols]
   , SetDelayed
   ]
  ]
Clear[f2]
f2[3, 4] (* evaluates to f2[3,4] since f2 was cleared *)
makeSum[f2, 2] (* returns an inactive function definition *)
Echo[% // Activate, "Activating"] (* now the function definition is performed *)
f2[3, 4] (* evaluates to 7, since f2 is now defined: f2[a1_,a2_]:=a1+a2 *)

Mathematica's approach to controlling evaluation in the core language is a bit more clumsy than what you'd get in, say LISP macros. There is macro functionality in Mathematica, but it's not documented, so I didn't use it, even though it would get rid of some of the wrangling with active and inactive heads.

The argument symbols could have been also shielded from evaluating to something else by temporarily making them have no values (using a Block), but I didn't bother with that. That's a TODO.

And of course, what Mathematica version do you use?


A bit of explanation of inactivations folllows. Recall that := is syntactic sugar for SetDelayed. The two lines below are equivalent:

fn[_] := foo
SetDelayed[fn[_], foo]

Thus, we can create an inert function definition that will define the function only once SetDelayed is activated:

Inactivate[ fn[_] := foo, SetDelayed ]
(* equivalent to *)
Inactivate[ SetDelayed[fn[_], foo], SetDelayed ]
(* evaluates to *)
Inactive[SetDelayed][fn[_], foo]

Thus, depending on preference and circumstances, we can either use Inactivate and tell it what heads to deactivate, or use heads wrapped in Inactive directly.

Inactivate[expr_] wraps certain heads inside the expression inside an Inactive head. In Mathematica, expression forms are fully recursive - the head of an expression is usually a symbol, e,g, SetDelayed, but can be an expression itself, e.g. Inactive[SetDelayed]. Example:

Inactivate[5 + 6, Plus]
(* evaluates to *)
Inactive[Plus][5,6]

Then, Activate[%] will reactivate all inactive heads, then the expression will be evaluated, and the addition will be performed, thus Activate[%] here will evaluate to 11.

Posted 5 months ago

Kuba, The problem of delaying the evaluation of ffoutsuper till after eqnlist has been generated has been already solved by Eric. The problem of giving ffoutsuper a flat list and reconstructing the actual lists inside of ffoutsuper (so they can be passed on to other functions) has also been already solved by Eric, so thanks but I don't need that anymore. However, I was wrong to think that this would solve my problem. I thought it would, because when I gave ffoutsuper a flat list and delayed the evaluation of eqnlist with the "Real" hack (imposing that its args be real numbers instead of symbolic or otherwise), everything worked. But now I am thinking that the "Real" hack must have had other effects that made things work (but it doesn't work with lists, at least as far as Derivative is concerned, which is a BUG. See this thread:https://community.wolfram.com/groups/-/m/t/3092236 and Ginalucca's response:" Very curious. It appears that you cannot mix numeric tests like xReal or x?NumberQ with list structures inside Derivative")

So the problem in its most general form is to have the same code that worked without lists, work with lists (of different shapes and sizes).

POSTED BY: Iuval Clejan
Posted 5 months ago

Kuba, Eric, I am including a simple (as simple as I could get it) example to illustrate the bug. It seems to be an inability of NDSolve to handle conditionals. The first cell has a problem (even at t=0) because it can't deal with Re[Eigenvalues]. The second cell has no problem because I remove the Re[]. This is a bug, but do you have any suggestions for a workaround? I can't remove the Re in my actual problem, because the matrices are not symmetric and the eigenvalues can become complex (they don't in this example). Also, in my actual problem I have other conditionals on the equivalent of ff, which might also be causing problems, I'll check that next. (Note: don't confuse Re[Eigenvalues] with the Real_ (arguments to ff) hack I was doing initially to delay evaluation of eqnlist. They seem to be unrelated). Not also the nonsense with Re'[4], which is a problem with just Derivative (not even NDSolve) that I showed previously.

POSTED BY: Iuval Clejan
Posted 5 months ago

*Edited. And here is another bug, which I don't really understand (I thought problem was Derivative, but I was wrong, forgot to refresh definitions). The first cell is without the conditional, gives good output. Second gives output, but it's wrong. It has a local variable that gets confused and forgets that it is 1. If you uncomment the commented area, and comment the If statement in ffoutsuper, you will see that it works fine, so the local variable is fine, except if it's called with the conditional.

POSTED BY: Iuval Clejan
Posted 5 months ago

I think I solved it! So flattening the lists before passing to ffoutsuper, and reconstructing them should have worked, because that is what I had before, no lists. But I also had the arguments passed to ffoutsuper be _Real. So once I did that, it works again. I don't really know why. Somehow the conditionals work now. It would have been nice not to have to flatten the lists and reconstruct them. Something for the developers to figure out.

POSTED BY: Iuval Clejan
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract