Difference between revisions of "Repeated Parameter Forwarding"

m (added missing end tags)
 
(10 intermediate revisions by 4 users not shown)
Line 1: Line 1:
[[Category:Concepts]]
+
[[Category:Functions]]
''new to [[Analytica 4.5]]''
 
  
Repeated parameter forwarding is a language extension that enables you to pass a list of computed results to a repeated function parameter. To understand repeated parameter forwarding, you must first understand [[Repeated parameters|repeated parameters]], hence the [[Repeated parameters]] section of [[Function Parameter Qualifiers]] is a prerequisite to the content on this page.
+
__TOC__
  
= Basic idea =
+
Repeated parameter forwarding lets you pass a list of items to a repeated function parameter. A repeated function parameter, with qualifier "..." accepts one or more parameters of the specified type. If you don't know about [[Repeated parameters|repeated parameters]], you may want to first read [[Function_Parameter_Qualifiers#Repeated_parameters_.28....29|Repeated parameters]] section of [[Function Parameter Qualifiers]].
  
Consider the following [[User-Defined Function]]:
+
== Basic idea ==
 +
Consider this [[User-Defined Function]]:
  
:<code>Function TreeNode( X ; Children : ... )</code>
+
:<code>Function TreeNode(x; children:...)</code>
:<code>'''Definition:''' [[Array]](TreeNodeIdx,[X,\Children] )</code>
+
:<code>Definition: Array(TreeNodeIdx, [x, \children])</code>
 +
where
 +
:<code>Index TreeNodeIdx := ['value', 'children']</code>
  
where <code>TreeNodeIdx</code> is an index having two elements, e.g., <code>['value','children']</code>.
+
Then <code>TreeNode(1, 2, 3, 4)</code> creates this data structure:
  
The call <code>TreeNode( 1, 2, 3, 4 )</code> is used to create this data structure:
+
:[[image:SmallTree.png]]
  
[[image:SmallTree.png]]
+
The repeated parameter, «children», allows you to list as many children as you'd like, without having to make use of square brackets in the syntax.  Hence, we could built a multi-level tree using:
  
The repeated parameter, «children», allows you to list as many children as you'd like, without having to make use of square brackets in the syntax.  Hence, we could built a multi-level tree using:
+
:<code>TreeNode(1, TreeNode(2, 5, 6), TreeNode(3, 7, 8, 9, TreeNode(10, 11, 12, 13)), 4)</code>
  
:<code>TreeNode( 1, TreeNode(2,5,6), TreeNode( 3, 7,8,9, TreeNode(10,11,12,13) ), 4 )</code>
+
:[[image:MultiLayerTree.png]]
  
[[image:MultiLayerTree.png]]
+
Now suppose you have some expression -- say, <code>F()</code> -- that computes and returns a list of "nodes", and you want to create a tree node having this set of nodes as the children, with 7 as the root. Your expression doesn't know what <code>F()</code> will return, or even how many children it will return, but for illustration let's pretend it returns <code>[1, 2, 3]</code>.  Your first attempt might be
 +
:<code>TreeNode(a, F())</code>
  
Now suppose you have some expression -- I'll just write <code>F( )</code> -- that computes and returns a list of "nodes", and you want to create a tree node having this set of nodes as the children, with 7 as the root. Your expression doesn't know what <code>F( )</code> will return, or even how many children it will return, but for illustration let's pretend it returns <code>[1,2,3]</code>.  Your first attempt might be
 
:<code>TreeNode(a,F())</code>
 
 
This produces an error, because you cannot pass a list value to a repeated parameter; however, logically what you are actually doing here is producing the wrong tree, which would actually be
 
This produces an error, because you cannot pass a list value to a repeated parameter; however, logically what you are actually doing here is producing the wrong tree, which would actually be
[[image:OneChildTree.png]]
+
 
 +
:[[image:OneChildTree.png]]
 +
 
 
The call is providing one instance of the repeated parameter, hence one child node.  The value of that child node is the list of values. What we really want is to be able to pass three instances of the «children» parameter to the function, basically
 
The call is providing one instance of the repeated parameter, hence one child node.  The value of that child node is the list of values. What we really want is to be able to pass three instances of the «children» parameter to the function, basically
:<code>TreeNode(7, [[Slice]](F(),1), [[Slice]](F(),2), [[Slice]](F(),3) )</code>
+
:<code>TreeNode(7, Slice(F(),1), Slice(F(),2), Slice(F(),3))</code>
This would be one way of doing it if we actually knew that <code>F()</code> would return 3 values, but part of the point here is that we can't assume we know how many items <code>F()</code> is going to return.
 
  
''Repeated parameter forwarding'' solves the problem. To ''forward'' the result of <code>F()</code> to the repeated parameter, we preface it with three dots
+
This would work if we knew that <code>F()</code> would return 3 values -- but [[Repeated_Parameter_Forwarding|Repeated parameter forwarding]] lets us handle this when we don't know how many items <code>F()</code> will to return. To ''forward'' the result of <code>F()</code> to the repeated parameter, we preface it with three dots
:<code>TreeNode( 7, ...F( ) )</code>
+
:<code>TreeNode(7, ...F())</code>
  
The three dots (which matches the three dots used when declaring a repeated parameter) indicates that we want each item in the computed result to be passed as one instance of the repeated parameter.  When you use this syntax, the expression following the three dots must produce a list or a 1-D array.
+
The three dots (or ellipsis, which matches the three dots used when declaring a repeated parameter) indicate that we want each item in the computed result to be passed as one instance of the repeated parameter.  When you use this syntax, the expression following the three dots must produce a list or a 1-D array.
  
When you are looking at the examples on this page, be aware that the three dots are part of the expression -- they aren't being used to indicate that we've omitted something.
+
Be aware that <code>...</code> is part of the expression -- not that we've omitted something!
  
= Uses =
+
== Uses ==
 +
=== Operating over all indexes of an array ===
 +
{{Assista|key|MaxOverAll, MinOverAll, SumOverAll, AverageOverAll, Max over all indexes, average over all indexes, Min over all indexes, Sum over all indexes, Product over all indexes}}
  
== Operating over all indexes of an array ==
+
Several built-in functions accept a repeated index parameter, so that you can apply them over multiple dimensions, such as:
 +
:<code>Sum(X, I, j, K)</code>
 +
Other such functions, besides [[Sum]], include [[Max]], [[Min]], [[ArgMax]], [[ArgMin]], [[CondMax]], [[CondMin]], [[Average]], [[Product]], and [[Array]].
  
Several built-in functions accept a repeated index parameter, so that more than one index can be passed when you are applying the operation to multiple dimensions at the same time.  The functions include [[Sum]], [[Max]], [[Min]], [[ArgMax]], [[ArgMin]], [[CondMax]], [[CondMin]], [[Average]], [[Product]], and [[Array]]When operating over multiple indexes, you list the indexes explicitly, e.g.
+
Sometimes you'd like to apply these functions over every index of an arrayParameter forwarding makes it easy, e.g.
:<code>[[Sum]](A,I,J,K)</code>
+
:<code>Sum(A,...IndexesOf(A))</code>
  
There are times when you'd like to apply these operators to every index of an array.  You could be careful because doing so may violate the principle of array abstraction, which could limit the flexibility of your model.  But, there certainly are legitimate times to do this. Parameter forwarding makes it easy, e.g.
+
Note that this sometimes violates the [[Expressions_that_don%27t_array-abstract#Principle_of_Array_Abstraction|principle of array abstraction]], which could limit the flexibility of your model.  So you should be careful with this.
:<code>[[Sum]](A,...[[IndexesOf]](A))<code>
 
  
== Creating a constant array over computed dimensions ==
+
=== Creating a constant array over computed dimensions ===
  
Sometimes it is useful to create a constant array over a set of indexes. You can do this using, e.g, <code>[[Array]](I,J,0)</code>, to create an array on indexes I and J having the value 0 everywhere.  But what if the set of indexes is computed?  Here is an example
+
Sometimes it is useful to create a constant array over a set of indexes. You can do this using, e.g, <code>Array(I, J, 0)</code>, to create an array on indexes I and J having the value 0 everywhere.  But what if the set of indexes is computed?  Here is an example
:<code>[[Array]]( ...#[[SetDifference]]( \[[IndexesOf]](A), \[[IndexesOf]](B)), 0 )</code>
+
:<code>Array( ...#SetDifference(\IndexesOf(A), \IndexesOf(B)), 0)</code>
So here we are going to create an array containing all the indexes of A that are not also indexes of B, such that all elements of the array start at 0.
 
  
== Forwarding a repeated parameter in a [[User-Defined Function|UDF]] ==
+
This creates an array containing all the indexes of <code>A</code> that are not also indexes of <code>B</code>, with all values 0.
  
There are times when it is useful to wrap an existing function with a [[User-Defined Function]] that you create.  For example, if an existing function does 99% of what you want, but doesn't treat [[Null]] values the way you like, has a bug you need to work around, or for some other reason you might create your own variation.  The original function might have a bunch of parameters that you just simply want to forward to the original function. When the parameter being forwarded is a repeated parameter, you need to use the repeated parameter forwarding syntax.
+
=== Forwarding a repeated parameter in a user-defined function===
  
The [[User-Defined Function]] ''AbsMax'' here accepts the same parameters as [[Max]], but returns the maximum of the absolute value. It is essentially a wrapper of the [[Max]] function -- we copy the parameter declaration for Max:
+
Sometimes it's useful to wrap an existing function with a [[User-Defined Function]] that you create.  For example, if an existing function does most of what you want, but doesn't treat [[Null]] values the way you like, has a bug you need to work around, or for some other reason you might create your own variation.  The original function might have a bunch of parameters that you just simply want to forward to the original function. When the parameter being forwarded is a repeated parameter, you need to use the repeated parameter forwarding syntax.
  
<pre>Function AbsMax(X ; I : ... recommended Index WarnDynamic ; IgnoreNonNumbers, IgnoreNaN : optional named atomic boolean ; caseInsensitive : optional atomic boolean)
+
The [[User-Defined Function]] <code>AbsMax</code> here accepts the same parameters as [[Max]], but returns the maximum of the absolute value. It is essentially a wrapper of the [[Max]] function -- we copy the parameter declaration for Max:
'''Definition:''' [[Max]]([[Abs]](X),...I,IgnoreNonNumbers,IgnoreNaN,caseInsensitive)
+
 
 +
<pre style="background:white; border:white; margin-left: 1em;">
 +
Function AbsMax(X; I: ... recommended Index WarnDynamic; IgnoreNonNumbers, IgnoreNaN: optional named atomic boolean; caseInsensitive: optional atomic boolean)
 +
Definition: Max(Abs(X),...I, IgnoreNonNumbers, IgnoreNaN, caseInsensitive)
 
</pre>
 
</pre>
  
 
To forward the repeated parameter, we need the ... in front of I, otherwise it would look like one parameter instance.
 
To forward the repeated parameter, we need the ... in front of I, otherwise it would look like one parameter instance.
  
== Computing a set of decisions or constraints for [[DefineOptimization]] ==
+
=== Computing a set of decisions or constraints for DefineOptimization ===
  
The [[DefineOptimization]] function accepts a list of decision and constraint nodes as repeated parameters. If you decide to get fancy, you might decide to create an interface, or some logic, that computes which constraints or decisions should be active.  Repeated Parameter forwarding allows you to provide a computed list of decisions or constraints to the optimization declaration, e.g.
+
The [[DefineOptimization]] function accepts a list of decision and constraint nodes as repeated parameters. If you decide to get fancy, you might decide to create an interface, or some logic, that computes which constraints or decisions should be active.  [[Repeated Parameter Forwarding|Repeated parameter forwarding]] allows you to provide a computed list of decisions or [[DefineOptimization#Constraints|constraints]] to the optimization declaration, e.g.
 +
<code>[[DefineOptimization]](decisions: ...ActiveDecisions, constraints: ...ActiveConstraints, maximize: myObjective )</code>
  
<code>[[DefineOptimization]](decisions: ...ActiveDecisions, constraints: ...ActiveConstraints, maximize: myObjective )</code>
+
Note that although the two parameters normally look for object identifiers, here the identifier <code>ActiveDecisions</code> is actually the variable that computes which decisions to use (as a 1-D array or list), and not one of the decision nodes.  Similarly for <code>ActiveConstraints</code>.
Note that although the two parameters normally look for object identifiers, here the identifier ActiveDecisions is actually the variable that computes which decisions to use (as a 1-D array or list), and not one of the decision nodes.  Similarly for ActiveConstraints.
 
  
= See Also =
+
==History==
 +
Introduced in [[Analytica 4.5]].
  
 +
== See Also ==
 +
* [[Function calls and parameters]]
 +
* [[Function_Parameter_Qualifiers#Repeated_parameters_.28....29|Repeated parameters]]
 
* [[Function Parameter Qualifiers]]
 
* [[Function Parameter Qualifiers]]
 +
* [[Parametric Analysis]]
 
* [[IndexesOf]]
 
* [[IndexesOf]]
 +
* [[DefineOptimization#Constraints|Constraints]]
 +
* [[DefineOptimization]]

Latest revision as of 20:27, 6 February 2025


Repeated parameter forwarding lets you pass a list of items to a repeated function parameter. A repeated function parameter, with qualifier "..." accepts one or more parameters of the specified type. If you don't know about repeated parameters, you may want to first read Repeated parameters section of Function Parameter Qualifiers.

Basic idea

Consider this User-Defined Function:

Function TreeNode(x; children:...)
Definition: Array(TreeNodeIdx, [x, \children])

where

Index TreeNodeIdx := ['value', 'children']

Then TreeNode(1, 2, 3, 4) creates this data structure:

SmallTree.png

The repeated parameter, «children», allows you to list as many children as you'd like, without having to make use of square brackets in the syntax. Hence, we could built a multi-level tree using:

TreeNode(1, TreeNode(2, 5, 6), TreeNode(3, 7, 8, 9, TreeNode(10, 11, 12, 13)), 4)
MultiLayerTree.png

Now suppose you have some expression -- say, F() -- that computes and returns a list of "nodes", and you want to create a tree node having this set of nodes as the children, with 7 as the root. Your expression doesn't know what F() will return, or even how many children it will return, but for illustration let's pretend it returns [1, 2, 3]. Your first attempt might be

TreeNode(a, F())

This produces an error, because you cannot pass a list value to a repeated parameter; however, logically what you are actually doing here is producing the wrong tree, which would actually be

OneChildTree.png

The call is providing one instance of the repeated parameter, hence one child node. The value of that child node is the list of values. What we really want is to be able to pass three instances of the «children» parameter to the function, basically

TreeNode(7, Slice(F(),1), Slice(F(),2), Slice(F(),3))

This would work if we knew that F() would return 3 values -- but Repeated parameter forwarding lets us handle this when we don't know how many items F() will to return. To forward the result of F() to the repeated parameter, we preface it with three dots

TreeNode(7, ...F())

The three dots (or ellipsis, which matches the three dots used when declaring a repeated parameter) indicate that we want each item in the computed result to be passed as one instance of the repeated parameter. When you use this syntax, the expression following the three dots must produce a list or a 1-D array.

Be aware that ... is part of the expression -- not that we've omitted something!

Uses

Operating over all indexes of an array

Several built-in functions accept a repeated index parameter, so that you can apply them over multiple dimensions, such as:

Sum(X, I, j, K)

Other such functions, besides Sum, include Max, Min, ArgMax, ArgMin, CondMax, CondMin, Average, Product, and Array.

Sometimes you'd like to apply these functions over every index of an array. Parameter forwarding makes it easy, e.g.

Sum(A,...IndexesOf(A))

Note that this sometimes violates the principle of array abstraction, which could limit the flexibility of your model. So you should be careful with this.

Creating a constant array over computed dimensions

Sometimes it is useful to create a constant array over a set of indexes. You can do this using, e.g, Array(I, J, 0), to create an array on indexes I and J having the value 0 everywhere. But what if the set of indexes is computed? Here is an example

Array( ...#SetDifference(\IndexesOf(A), \IndexesOf(B)), 0)

This creates an array containing all the indexes of A that are not also indexes of B, with all values 0.

Forwarding a repeated parameter in a user-defined function

Sometimes it's useful to wrap an existing function with a User-Defined Function that you create. For example, if an existing function does most of what you want, but doesn't treat Null values the way you like, has a bug you need to work around, or for some other reason you might create your own variation. The original function might have a bunch of parameters that you just simply want to forward to the original function. When the parameter being forwarded is a repeated parameter, you need to use the repeated parameter forwarding syntax.

The User-Defined Function AbsMax here accepts the same parameters as Max, but returns the maximum of the absolute value. It is essentially a wrapper of the Max function -- we copy the parameter declaration for Max:

Function AbsMax(X; I: ... recommended Index WarnDynamic; IgnoreNonNumbers, IgnoreNaN: optional named atomic boolean; caseInsensitive: optional atomic boolean)
Definition: Max(Abs(X),...I, IgnoreNonNumbers, IgnoreNaN, caseInsensitive)

To forward the repeated parameter, we need the ... in front of I, otherwise it would look like one parameter instance.

Computing a set of decisions or constraints for DefineOptimization

The DefineOptimization function accepts a list of decision and constraint nodes as repeated parameters. If you decide to get fancy, you might decide to create an interface, or some logic, that computes which constraints or decisions should be active. Repeated parameter forwarding allows you to provide a computed list of decisions or constraints to the optimization declaration, e.g. DefineOptimization(decisions: ...ActiveDecisions, constraints: ...ActiveConstraints, maximize: myObjective )

Note that although the two parameters normally look for object identifiers, here the identifier ActiveDecisions is actually the variable that computes which decisions to use (as a 1-D array or list), and not one of the decision nodes. Similarly for ActiveConstraints.

History

Introduced in Analytica 4.5.

See Also

Comments


You are not allowed to post comments.