Group Abstract Group Abstract

Message Boards Message Boards

1
|
14.6K 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 2 years 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 2 years ago
POSTED BY: Iuval Clejan
Posted 2 years ago
POSTED BY: Iuval Clejan
Posted 2 years ago
POSTED BY: Iuval Clejan
Posted 2 years ago
POSTED BY: Iuval Clejan
Posted 2 years 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 2 years ago
POSTED BY: Eric Rimbey
Posted 2 years ago
POSTED BY: Iuval Clejan
Posted 2 years 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 2 years 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 2 years 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 2 years 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 2 years ago
POSTED BY: Eric Rimbey
Posted 2 years 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 2 years 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 2 years 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 2 years 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 2 years 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 2 years ago
POSTED BY: Iuval Clejan
Posted 2 years 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 2 years ago
POSTED BY: Eric Rimbey
Posted 2 years ago
POSTED BY: Iuval Clejan
Posted 2 years ago
POSTED BY: Eric Rimbey
Posted 2 years ago

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

POSTED BY: Iuval Clejan
Posted 2 years ago

No need to pay.

POSTED BY: Eric Rimbey
Posted 2 years ago
POSTED BY: Eric Rimbey
Posted 2 years 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 2 years ago
POSTED BY: Iuval Clejan
Posted 2 years ago
POSTED BY: Eric Rimbey
Posted 2 years ago
POSTED BY: Iuval Clejan
Posted 2 years 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 2 years 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 2 years 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 2 years 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 2 years ago

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

POSTED BY: Eric Rimbey
Posted 2 years ago
POSTED BY: Iuval Clejan
Posted 2 years 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 2 years 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 2 years ago
POSTED BY: Eric Rimbey
Posted 2 years ago
POSTED BY: Iuval Clejan
Posted 2 years 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 2 years 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 2 years ago
POSTED BY: Eric Rimbey
Posted 2 years 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 2 years ago
POSTED BY: Eric Rimbey
Posted 2 years ago
POSTED BY: Iuval Clejan
Posted 2 years ago
POSTED BY: Eric Rimbey
Posted 2 years 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 2 years 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 2 years ago

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

POSTED BY: Eric Rimbey
Posted 2 years 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 2 years 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 2 years 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 2 years 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 2 years 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 2 years 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 2 years ago
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