4
$\begingroup$

I have a list of functions

func[x_]={1,Cos[x],Sin[x]}

(for example) and I want to apply each function to a list of values

xval={a,b,c,d}

obtaining a list of tuples. The problem is that the constant function will not evaluate to a tuple (it will remain as a scalar)

func[xval] = {1, {0, -1, 0, 1}, {1, 0, -1, 0}}

Is there a way to force the creation of a tuple (with the function value) of the same size as xval, if I don't know what are the functions in func (if there is or not a constant function in it)?

$\endgroup$
  • 2
    $\begingroup$ You can use pure functions: funcs = {# , Cos[#] , Sin[#] }& $\endgroup$ – Marius Ladegård Meyer Oct 27 '16 at 10:14
  • $\begingroup$ @MariusLadegårdMeyer Won't that give x in the first component, not a list of copies of the constant? $\endgroup$ – Michael E2 Oct 27 '16 at 12:03
  • 1
    $\begingroup$ @MichaelE2, sorry, I changed it and forgot to change it back again :p The correct thing is funcs = {1 + 0 # , Cos[#] , Sin[#] }&. $\endgroup$ – Marius Ladegård Meyer Oct 27 '16 at 14:14
  • $\begingroup$ @MariusLadegårdMeyer Or just 1& ({1, Cos@#, Sin@#}&). $\endgroup$ – The Vee Oct 27 '16 at 19:53
  • $\begingroup$ you could also add one more definition to your function: func[t:{__}]:=func/@t $\endgroup$ – Algohi Oct 30 '16 at 0:01
5
$\begingroup$

The reason why it works with Cos and Sin is that those functions are Listable. So just make a constant function that is Listable, too:

func[x_]:={Function[y, 1, Listable][x],Cos[x],Sin[x]}
xval={a, b, c, d}
func[xval]
(*
==> {{1, 1, 1, 1}, {Cos[a], Cos[b], Cos[c], Cos[d]},
     {Sin[a], Sin[b], Sin[c], Sin[d]}}
*)

Note that here it is important that you use := to define func, otherwise it gets evaluated already at definition time, resulting in a single 1 again.

If you need it often, you can define a function to generate a listable constant function:

const[x_] := Function[y, x, Listable]

Then you can write:

func2[x_] := { const[1][x], Cos[x], Sin[x] }
func2[xval]
(*
==> {{1, 1, 1, 1}, {Cos[a], Cos[b], Cos[c], Cos[d]}, 
     {Sin[a], Sin[b], Sin[c], Sin[d]}}
*)
$\endgroup$
  • $\begingroup$ I really liked the simplicity of your answer, and the function definition const[x_] you showed. It works very well (and solves @Mr.Wizard's problem with Lists inside the argument list). Thank you! $\endgroup$ – malu Nov 6 '16 at 20:08
10
$\begingroup$

First: you should probably be defining func using SetDelayed rather than Set.

The behavior you want is closely related to the concept of Listable functions in Mathematica. It is this attribute that makes Sin and Cos behave as you observe. If you add the Listable attribute to func either directly (using SetAttribute, i.e. SetAttribute[func, Listable]) or by way of a container Function you get this:

f2 = Function[xx, func[xx], Listable];

f2[{a, b, c, d}]
{{1, Cos[a], Sin[a]},
 {1, Cos[b], Sin[b]},
 {1, Cos[c], Sin[c]},
 {1, Cos[d], Sin[d]}}

You may Transpose this output to achieve the form you desire:

f2[{a, b, c, d}]\[Transpose]
{{1, 1, 1, 1},
 {Cos[a], Cos[b], Cos[c], Cos[d]},
 {Sin[a], Sin[b], Sin[c], Sin[d]}}

Another approach may be to Map the function over the list manually, then transpose:

Map[func, {a, b, c, d}]\[Transpose]    (* same output as above *)

A more targeted approach

Reconsidering this problem perhaps what you want can be attained with post-processing the existing output of func. I tried to keep the method above as general as possible, however since you did specify "a vector of values" rather than an arbitrary List-based structure there is a simpler method using Thread. Consider the behavior:

Thread[{1, {a, b, c, d}}]
{{1, a}, {1, b}, {1, c}, {1, d}}

This allows us:

func[{a, b, c, d}] // Thread // Transpose
{{1, 1, 1, 1},
 {Cos[a], Cos[b], Cos[c], Cos[d]},
 {Sin[a], Sin[b], Sin[c], Sin[d]}}

Caveat: if the constant term in the original function is itself a List this fails:

func2[x_] := {{1}, Cos[x], Sin[x]};

func2[{a, b, c, d}] // Thread // Transpose

Thread::tdlen: Objects of unequal length in {{1},{Cos[a],Cos[b],Cos[c],Cos[d]},{Sin[a],Sin[b],Sin[c],Sin[d]}} cannot be combined. >>

$\endgroup$
3
$\begingroup$

I've been interested in a vectorized constant function that, like built-in mathematical functions, evaluates packed arrays to packed arrays. Here's a way, but you have to manually implement listability for non-packed arrays and other lists.

constFN[k_][x_?ArrayQ] := ConstantArray[k, Dimensions[x]];
constFN[k_][x_List] := constFN[k] /@ x;
constFN[k_][x_?NumericQ] := k;

Examples:

constFN[2][{2, 3, {4}}]                (* listability *)
(*  {2, 2, {2}}  *)

constFN[2.][E]
(*  2.  *)

constFN[2.][x]                         (* produces a packed array *)
% /. x -> RandomReal[1, {2, 3}]
% // Developer`PackedArrayQ
(*
  constFN[2.][x]
  {{2., 2., 2.}, {2., 2., 2.}}
  True
*)

ifn = Interpolation@ Table[{{t}, {Cos[t], Sin[t]}}, {t, 0., 2., 1./128}];
constFN[Tan[x]][ifn[x]] /. x -> Pi/6   (* constant symbolic expression *)
(* {1/Sqrt[3], 1/Sqrt[3]}  *)

constFN[Range[4]][x]                   (* multiples of a vector constant *)
% /. x -> RandomReal[1, 3]
% // Developer`PackedArrayQ
(*
  constFN[{1, 2, 3, 4}][x]
  {{1, 2, 3, 4}, {1, 2, 3, 4}, {1, 2, 3, 4}}
  True
*)

Notes: Using Listable unpacks packed arrays, which is why I did it manually.

ClearAll[fUP];
SetAttributes[fUP, Listable];
fUP[k_, x_] := k;

On["Packing"];
fUP[2., RandomReal[1, 3]] // Developer`PackedArrayQ
Off["Packing"]

Developer`FromPackedArray::unpack: Unpacking array in call to HoldForm.

Developer`FromPackedArray::punpack: Unpacking array with dimensions {3} in call to fUP.

(*  False  *)

Note also that constFN[] replaces the packed arrays in any sort of (nested) list of packed arrays with constant packed arrays. One could also use SparseArray instead of packed arrays. Normal converts these to packed arrays when k is a machine Integer or Real. Just replace the ConstantArray definition with the following:

constFN[k_][x_?ArrayQ] := SparseArray[{}, Dimensions[x], k];
$\endgroup$
3
$\begingroup$

You can Map the function over the list and Transpose the result:

func[x_] := {1,Cos[x],Sin[x]};
xval = {a, b, c, d};
Transpose[func /@ xval]

Result:

{
  {1, 1, 1, 1}, {Cos[a], Cos[b], Cos[c], Cos[d]},
  {Sin[a], Sin[b], Sin[c], Sin[d]}
}

You can even overload the function if you want it to do this on any list automatically:

func[x : Except[_List]] := {1,Cos[x],Sin[x]};
func[list_List] := Transpose[func /@ list];
$\endgroup$
2
$\begingroup$

You can use ConstantArray, but you have to specify your variable.

Clear[func2]

func2[x_List] := {ConstantArray[1, Length[x]], Cos[x], Sin[x]}
func2[x_] := {1, Cos[x], Sin[x]}

func2[xval]
(* {{1, 1, 1, 1}, {Cos[a], Cos[b], Cos[c], Cos[d]}, {Sin[a], Sin[b], Sin[c], Sin[d]}} *)

func2[a]
(* {1, Cos[a], Sin[a]} *)

There is also a way to do it with pure functions:

funcP := {1 + #*0, Cos[#], Sin[#]} &

You have to add 0 to your constant. I dont know if this is the best way to do this, but maybe these two approaches will help you.

$\endgroup$
1
$\begingroup$

Also

Through[{1 & /@ # &, Cos, Sin}@xval]

{{1, 1, 1, 1}, {Cos[a], Cos[b], Cos[c], Cos[d]}, {Sin[a], Sin[b], Sin[c], Sin[d]}}

Or, if func[x_]:={1, Cos[x],Sin[x]} is already defined,

Through[(Function[y, #, Listable] & /@ func[y])@xval]

{{1, 1, 1, 1}, {Cos[a], Cos[b], Cos[c], Cos[d]}, {Sin[a], Sin[b], Sin[c], Sin[d]}}

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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