|
| 1 | +""" |
| 2 | +Miscellaneous Structural Operations on Expressions |
| 3 | +""" |
| 4 | + |
| 5 | +from mathics.core.attributes import A_PROTECTED |
| 6 | +from mathics.core.builtin import Builtin |
| 7 | +from mathics.core.evaluation import Evaluation |
| 8 | +from mathics.core.expression import Expression |
| 9 | +from mathics.core.systemsymbols import SymbolIdentity |
| 10 | +from mathics.eval.exp_structure import eval_Distribute |
| 11 | +from mathics.eval.tensors import eval_Outer |
| 12 | + |
| 13 | + |
| 14 | +class Distribute(Builtin): |
| 15 | + """ |
| 16 | + <url>:WMA link:https://reference.wolfram.com/language/ref/Distribute.html</url> |
| 17 | +
|
| 18 | + <dl> |
| 19 | + <dt>'Distribute'[$expr$] |
| 20 | + <dd>distributes $expr$ over 'Plus' (addition). |
| 21 | + <dt>'Distribute'[$expr$, $operator$] |
| 22 | + <dd>distributes $expr$ over the specified $operator$. |
| 23 | + <dt>'Distribute'[$expr$, $operator$, $f$] |
| 24 | + <dd>applies $f$ to each component of the result. |
| 25 | +
|
| 26 | + ## <dt>'Distribute'[$expr$, $operator$, $f$, $gp$, $fp$] |
| 27 | + ## <dd>distributes $expr$ over $operator$, replacing outer function with $gp$ and inner function with $fp$. |
| 28 | + </dl> |
| 29 | +
|
| 30 | + Distribute multiplication over addition: |
| 31 | + >> Distribute[a(b + c)] |
| 32 | + = a b + a c |
| 33 | +
|
| 34 | + >> Distribute[(a + b)(c + d)] |
| 35 | + = a c + a d + b c + b d |
| 36 | +
|
| 37 | + Using a custom target head: |
| 38 | + >> Distribute[f[a + b, c], Plus] |
| 39 | + = f[a, c] + f[b, c] |
| 40 | +
|
| 41 | + Distribute can also work with lists: |
| 42 | + >> Distribute[{a(b + c), d(e + f)}] |
| 43 | + = {a b + a c, d e + d f} |
| 44 | +
|
| 45 | + ## Applying a function to results: |
| 46 | + ## >> Distribute[a(b + c), Plus, Square] |
| 47 | + ## = Square[a b] + Square[a c] |
| 48 | +
|
| 49 | + Special forms: |
| 50 | + >> Distribute[f[g[a + b]]] |
| 51 | + = f[g[a]] + f[g[b]] |
| 52 | +
|
| 53 | + Distribute $f$ over $g$: |
| 54 | + >> Distribute[f[g[a, b], g[c, d, e]], g] |
| 55 | + = g[f[a, c], f[a, d], f[a, e], f[b, c], f[b, d], f[b, e]] |
| 56 | +
|
| 57 | + ## Using a custom operator and functions: |
| 58 | + ## >> Distribute[f[g[a, b], g[c, d, e]], g, f, gp, fp] |
| 59 | + ## = gp[fp[a, c], fp[a, d], fp[a, e], fp[b, c], fp[b, d], fp[b, e]] |
| 60 | + """ |
| 61 | + |
| 62 | + attributes = A_PROTECTED |
| 63 | + |
| 64 | + eval_error = Builtin.generic_argument_error |
| 65 | + expected_args = range(1, 6) |
| 66 | + |
| 67 | + rules = { |
| 68 | + "Distribute[expr_]": "Distribute[expr, Plus]", |
| 69 | + "Distribute[expr_, operator_]": "Distribute[expr, operator, Identity]", |
| 70 | + } |
| 71 | + |
| 72 | + summary_text = "distribute functions over a head" |
| 73 | + |
| 74 | + def eval(self, expr, operator, filt, evaluation: Evaluation): |
| 75 | + "Distribute[expr_, operator_, filt_]" |
| 76 | + |
| 77 | + # Handle Identity filter |
| 78 | + if filt is SymbolIdentity: |
| 79 | + filt = None |
| 80 | + |
| 81 | + result = eval_Distribute(expr, operator, evaluation) |
| 82 | + |
| 83 | + if result is None: |
| 84 | + return expr |
| 85 | + |
| 86 | + if filt: |
| 87 | + return Expression(filt, result) |
| 88 | + |
| 89 | + return result |
| 90 | + |
| 91 | + # def eval_with_function_replacement( |
| 92 | + # self, expr, operator, f, g, gp, fp, evaluation: Evaluation |
| 93 | + # ): |
| 94 | + # "Distribute[expr_, f_, g_, gp_, fp_]" |
| 95 | + |
| 96 | + # result = eval_Distribute_with_replacement(expr, f, g, gp, fp, evaluation) |
| 97 | + |
| 98 | + # if result is None: |
| 99 | + # return expr |
| 100 | + |
| 101 | + # return result |
| 102 | + |
| 103 | + |
| 104 | +class Outer(Builtin): |
| 105 | + """ |
| 106 | + <url>:Outer product:https://en.wikipedia.org/wiki/Outer_product</url> \ |
| 107 | + (<url>:WMA link: https://reference.wolfram.com/language/ref/Outer.html</url>) |
| 108 | +
|
| 109 | + <dl> |
| 110 | + <dt>'Outer'[$f$, $x$, $y$] |
| 111 | + <dd>computes a generalised outer product of $x$ and $y$, using the function $f$ in place of multiplication. |
| 112 | + </dl> |
| 113 | +
|
| 114 | + >> Outer[f, {a, b}, {1, 2, 3}] |
| 115 | + = {{f[a, 1], f[a, 2], f[a, 3]}, {f[b, 1], f[b, 2], f[b, 3]}} |
| 116 | +
|
| 117 | + Outer product of two matrices: |
| 118 | + >> Outer[Times, {{a, b}, {c, d}}, {{1, 2}, {3, 4}}] |
| 119 | + = {{{{a, 2 a}, {3 a, 4 a}}, {{b, 2 b}, {3 b, 4 b}}}, {{{c, 2 c}, {3 c, 4 c}}, {{d, 2 d}, {3 d, 4 d}}}} |
| 120 | +
|
| 121 | + Outer product of two sparse arrays: |
| 122 | + >> Outer[Times, SparseArray[{{1, 2} -> a, {2, 1} -> b}], SparseArray[{{1, 2} -> c, {2, 1} -> d}]] |
| 123 | + = SparseArray[Automatic, {2, 2, 2, 2}, 0, {{1, 2, 1, 2} ⇾ a c, {1, 2, 2, 1} ⇾ a d, {2, 1, 1, 2} ⇾ b c, {2, 1, 2, 1} ⇾ b d}] |
| 124 | +
|
| 125 | + 'Outer' of multiple lists: |
| 126 | + >> Outer[f, {a, b}, {x, y, z}, {1, 2}] |
| 127 | + = {{{f[a, x, 1], f[a, x, 2]}, {f[a, y, 1], f[a, y, 2]}, {f[a, z, 1], f[a, z, 2]}}, {{f[b, x, 1], f[b, x, 2]}, {f[b, y, 1], f[b, y, 2]}, {f[b, z, 1], f[b, z, 2]}}} |
| 128 | +
|
| 129 | + 'Outer' converts input sparse arrays to lists if f=!=Times, or if the input is a mixture of sparse arrays and lists: |
| 130 | + >> Outer[f, SparseArray[{{1, 2} -> a, {2, 1} -> b}], SparseArray[{{1, 2} -> c, {2, 1} -> d}]] |
| 131 | + = {{{{f[0, 0], f[0, c]}, {f[0, d], f[0, 0]}}, {{f[a, 0], f[a, c]}, {f[a, d], f[a, 0]}}}, {{{f[b, 0], f[b, c]}, {f[b, d], f[b, 0]}}, {{f[0, 0], f[0, c]}, {f[0, d], f[0, 0]}}}} |
| 132 | +
|
| 133 | + >> Outer[Times, SparseArray[{{1, 2} -> a, {2, 1} -> b}], {c, d}] |
| 134 | + = {{{0, 0}, {a c, a d}}, {{b c, b d}, {0, 0}}} |
| 135 | +
|
| 136 | + Arrays can be ragged: |
| 137 | + >> Outer[Times, {{1, 2}}, {{a, b}, {c, d, e}}] |
| 138 | + = {{{{a, b}, {c, d, e}}, {{2 a, 2 b}, {2 c, 2 d, 2 e}}}} |
| 139 | +
|
| 140 | + Word combinations: |
| 141 | + >> Outer[StringJoin, {"", "re", "un"}, {"cover", "draw", "wind"}, {"", "ing", "s"}] // InputForm |
| 142 | + = {{{"cover", "covering", "covers"}, {"draw", "drawing", "draws"}, {"wind", "winding", "winds"}}, {{"recover", "recovering", "recovers"}, {"redraw", "redrawing", "redraws"}, {"rewind", "rewinding", "rewinds"}}, {{"uncover", "uncovering", "uncovers"}, {"undraw", "undrawing", "undraws"}, {"unwind", "unwinding", "unwinds"}}} |
| 143 | +
|
| 144 | + Compositions of trigonometric functions: |
| 145 | + >> trigs = Outer[Composition, {Sin, Cos, Tan}, {ArcSin, ArcCos, ArcTan}] |
| 146 | + = {{Composition[Sin, ArcSin], Composition[Sin, ArcCos], Composition[Sin, ArcTan]}, {Composition[Cos, ArcSin], Composition[Cos, ArcCos], Composition[Cos, ArcTan]}, {Composition[Tan, ArcSin], Composition[Tan, ArcCos], Composition[Tan, ArcTan]}} |
| 147 | + Evaluate at 0: |
| 148 | + >> Map[#[0] &, trigs, {2}] |
| 149 | + = {{0, 1, 0}, {1, 0, 1}, {0, ComplexInfinity, 0}} |
| 150 | + """ |
| 151 | + |
| 152 | + summary_text = "generalized outer product" |
| 153 | + |
| 154 | + def eval(self, f, lists, evaluation: Evaluation): |
| 155 | + "Outer[f_, lists__]" |
| 156 | + |
| 157 | + return eval_Outer(f, lists, evaluation) |
0 commit comments