1
$\begingroup$

Imagine, as a simple toy example, that it's valid to replace some list with any one of its elements. How could I write this into Simplify TransformationFunctions?

EDIT: And that list is not the head of the expression being simplified. It can occur anywhere within the expression.

This can only try the first option:

f[list_List] := list[[1]]

This doesn't work because there's no n:

f[list_List] := list[[n]]

Is there a way to do something like this?

f[list_List] := TryAnyOfThese[list]

Can the list_List pattern be somehow replaced with something that allows it to match a specific item but replace the whole list?

More generally, how do you transform the whole of some outer expression when the number of available transformations depends on the contents of that outer expression?

For a more realistic example, imagine a transformation function that can try different U substitutions on an integral and suggest them all.

$\endgroup$

1 Answer 1

2
$\begingroup$

I believe that TransformationFunctions are supposed to be unambiguous, i.e. they are supposed to return one and the same result each time they are called with the same argument, not AnyOfThese.

Therefore, to ask Simplify TryAnyOfThese I suppose there is no other option but to introduce n, which specifies the branch somehow. Consider the following example:

ClearAll[branch];
branch[n_Integer][multi_l] /; Length[multi] >= n := multi[[n]];
branch[_Integer][multi_l] := multi;

Here I replaced the general List head with more specific l in order not to confuse accidentally our toy object with some "usual" List, like a vector. Now branch[n] is a function which extracts n-th branch if possible and returns a whole object untouched otherwise, so that we can ask Simplify try many branches even if they don't exist for some of the l objects in our expresson.

Simplify[
  l[f[g[x]], g[x]] + l[f[g[x]], g[x], x],
  TransformationFunctions -> Prepend[Array[branch, 10], Automatic]
]
(*Returns x + g[x]*)

EDIT

No, it doesn't work. I've chosen a good example, when the simplest expression comes last. This example

Simplify[
  l[x, g[x], f[g[x]]], 
  TransformationFunctions -> Prepend[Array[branch, 10], Automatic]]
]
(*Returns: f[g[x]]*)

shows that Simplify will just pickup the first "working" branch. Well, it means that one will have to track all branches manually. For this purpose:

  1. The branch object l will now have the form l[choices, var], where choices is a List of possible branches of the object and var is a unique variable. This unique variable will prevent expressions such as l[...]+l[...] form evaluating to 2 l[...].
  2. All possible combinations of branches are generated and the one with minimal LeafCount after Simplify is selected.

Here is the code:

ClearAll[branches];
branches[expr_] := First @ MinimalBy[
  Simplify /@ ReplaceAll[
    ReplaceAll[
      expr,
      l[_, var_] :> var
    ],
    Tuples[
      Cases[
        expr,
        l[choices_, var_] :> Thread[var -> choices],
        {0, Infinity}
      ]
    ]
  ],
  LeafCount
]

And here is how it works:

branches[
 l[{x, -x}, Unique[]] + l[{x, -x}, Unique[]]
]    
branches[
 l[{x, -x}, Unique[]] + l[{-x, x}, Unique[]]
]
(*Returns 0 and 0*)

Now the order of the choices doesn't matter. In order not to create unique variables each time manually (and not to leave them in memory), the following definition can be given:

l[choices_] := Module[{id}, l[choices, id]]
$\endgroup$
1
  • $\begingroup$ Your edit is an interesting approach to this, thanks. I'm gonna go away and mess with that on some practical examples and see how well it plays out. $\endgroup$ Commented Aug 2, 2018 at 16:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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