From: Bob Hanlon on

G[a, b] := f[a, b];
G[a, c] := g[a, c];
G[b, c] := h[b, c];

G[x_, y_] := -G[y, x] /;

MemberQ[{a, b, c}, x] &&
MemberQ[{a, b, c}, y] && (x =!= y)

G @@@ {{a, b}, {b, a}, {a, a}, {a, x}, {a, f[b]}}

{f[a, b], -f[a, b], G[a, a], G[a, x], G[a, f[b]]}


Bob Hanlon

---- Torsten Schoenfeld <kaffeetisch(a)gmx.de> wrote:

=============
I'd like to define an antisymmetric function by giving its value on a
set of known objects. I'm having trouble enforcing antisymmetry. Say I
want to define G[_, _] on the objects {a, b, c}:

G[a, b] := f[a, b]
G[a, c] := g[a, c]
G[b, c] := h[b, c]

If I now enforce antisymmetry simply by

G[x_, y_] := -G[y, x]

then it mostly works (e.g., G[b, a] evaluates to -f[a, b]). But if I
apply G to something that is not in {a, b, c}, then I run into an
infinite loop: G[a, f[b]] yields "$RecursionLimit::reclim: Recursion
depth of 256 exceeded."

Ideally, I would like applications to unknown input to stay unevaluated
(e.g., G[a, f[b]] just yields G[a, f[b]]). How can I achieve that while
also enforcing antisymmetry?



From: David Park on
G[a, b] := f[a, b]
G[a, c] := g[a, c]
G[b, c] := h[b, c]
G[x_, y_] /; \[Not] OrderedQ[{x, y}] := -G[y, x]

{G[a, b], G[b, a], G[c, b], G[a, f[b]], G[f[b], a]}
{f[a, b], -f[a, b], -h[b, c], G[a, f[b]], -G[a, f[b]]}


David Park
djmpark(a)comcast.net
http://home.comcast.net/~djmpark/


From: Torsten Schoenfeld [mailto:kaffeetisch(a)gmx.de]

I'd like to define an antisymmetric function by giving its value on a
set of known objects. I'm having trouble enforcing antisymmetry. Say I
want to define G[_, _] on the objects {a, b, c}:

G[a, b] := f[a, b]
G[a, c] := g[a, c]
G[b, c] := h[b, c]

If I now enforce antisymmetry simply by

G[x_, y_] := -G[y, x]

then it mostly works (e.g., G[b, a] evaluates to -f[a, b]). But if I
apply G to something that is not in {a, b, c}, then I run into an
infinite loop: G[a, f[b]] yields "$RecursionLimit::reclim: Recursion
depth of 256 exceeded."

Ideally, I would like applications to unknown input to stay unevaluated
(e.g., G[a, f[b]] just yields G[a, f[b]]). How can I achieve that while
also enforcing antisymmetry?



From: Torsten Schoenfeld on
On 12.02.10 10:45, Leonid Shifrin wrote:
> ClearAll[G];
> G[a, b] := f[a, b]
> G[a, c] := g[a, c]
> G[b, c] := h[b, c]
> G[x_, y_] := -G[y, x] /;
> Hold[G[y, x]] =!= (Hold[G[y, x]] /. Most(a)DownValues[G])

This actually works perfectly fine even if pattern-based definitions are
involved! (Pattern-based definitions appear normally in DownValues.) I
neglected to mention it, but G[_, _] is also supposed to act as a
derivative in both slots, e.g. G[x_, y_^2] := 2 G[x, y] y. Your
approach handles this gracefully: G[x^2, y] yields -2 x G[y, x].

So, thanks a lot! Thanks also for all the other suggestions in this
thread; I learned many nice tricks.

From: Szabolcs on
On Feb 12, 11:44 am, Torsten Schoenfeld <kaffeetisch(a)gmx.de> wrote:
> On 12.02.10 10:45, Leonid Shifrin wrote:
>
> > ClearAll[G];
> > G[a, b] := f[a, b]
> > G[a, c] := g[a, c]
> > G[b, c] := h[b, c]
> > G[x_, y_] := -G[y, x] /;
> > Hold[G[y, x]] =!= (Hold[G[y, x]] /. Most(a)DownValues[G])
>
> This actually works perfectly fine even if pattern-based definitions are
> involved! (Pattern-based definitions appear normally in DownValues.) I
> neglected to mention it, but G[_, _] is also supposed to act as a
> derivative in both slots, e.g. G[x_, y_^2] := 2 G[x, y] y. Your
> approach handles this gracefully: G[x^2, y] yields -2 x G[y, x].
>
> So, thanks a lot! Thanks also for all the other suggestions in this
> thread; I learned many nice tricks.

I believe that what Leonid referred to when he said that it might not
work if you add pattern based definitions later on was that this
approach assumed that the "antisymmetry definition" is the very last
one in the DownValue list. Mathematica tries to automatically order
definitions from most specific to most general, but this is not always
possible. When it can't do this, it just adds rules to the DownValue
list in the order they were defined. So a general pattern based
definition *may* displace the antisymmetry rule from the very last
position, especially if it is added later. Just be careful with the
order in which definitions are made.

From: Carl K. Woll on
Another alternative is:

G[a, b] := f[a, b]
G[a, c] := g[a, c]
G[b, c] := h[b, c]
G[a_, b_] /; Signature[{a, b}] == -1 := -G[b, a]

It requires that you give literal definitions for the signature 1
ordering of arguments.

Carl Woll
Wolfram Research

On 2/12/2010 4:46 AM, Leonid Shifrin wrote:
> Hi Torsten,
>
> here is a little more high-level variation of Szabolcs's solution, which
> isn't robust either, since it assumes only Downvalue - based definitions and
> that you will not add more general (pattern-based) definitions to G later
> on.
>
> ClearAll[G];
> G[a, b] := f[a, b]
> G[a, c] := g[a, c]
> G[b, c] := h[b, c]
> G[x_, y_] := -G[y, x] /;
> Hold[G[y, x]] =!= (Hold[G[y, x]] /. Most(a)DownValues[G])
>
> It may however be a little faster since the specific rules (without
> patterns) in DownValues are
> hash-table based and therefore the rule look-up should be constant time in
> the size of the definition list.
>
> Out of curiosity, I also tried to address a different fomulation of your
> problem, where for unknown
> arguments the function must use antisymmetry, but just once:
> G[x,y]->-G[y,x]. The following
> hack seems to do it:
>
> In[318]:= Clear[GG];
>
> GG[a,b]:=f[a,b]
> GG[a,c]:=g[a,c]
> GG[b,c]:=h[b,c]
>
> Module[{tried, reset},
> reset[] := (Clear[tried]; tried[_, _] = False);
> reset[];
> GG[x_, y_] /; ! tried[x, y] := (tried[y, x] = True; -GG[y, x]);
> GG[x_, y_] := "" /; reset[];
> ]
>
> In[323]:= GG[a,b]
>
> Out[323]= f[a,b]
>
> In[324]:= GG[b,a]
>
> Out[324]= -f[a,b]
>
> In[325]:= GG[d,e]
>
> Out[325]= -GG[e,d]
>
> In[326]:= GG[e,d]
>
> Out[326]= -GG[d,e]
>
> One problem with it is that it may keep some garbage in<tried> for
> arguments on which
> GG has been defined (a,b,c here) - it will still work but consume a little
> extra memory.
>
> Regards,
> Leonid
>
>
> 2010/2/11 Szabolcs Horv=E1t<szhorvat(a)gmail.com>
>
>
>> On 2010.02.11. 12:53, Torsten Schoenfeld wrote:
>>
>>> I'd like to define an antisymmetric function by giving its value on a
>>> set of known objects. I'm having trouble enforcing antisymmetry. Say I
>>> want to define G[_, _] on the objects {a, b, c}:
>>>
>>> G[a, b] := f[a, b]
>>> G[a, c] := g[a, c]
>>> G[b, c] := h[b, c]
>>>
>>> If I now enforce antisymmetry simply by
>>>
>>> G[x_, y_] := -G[y, x]
>>>
>>> then it mostly works (e.g., G[b, a] evaluates to -f[a, b]). But if I
>>> apply G to something that is not in {a, b, c}, then I run into an
>>> infinite loop: G[a, f[b]] yields "$RecursionLimit::reclim: Recursion
>>> depth of 256 exceeded."
>>>
>>> Ideally, I would like applications to unknown input to stay unevaluated
>>> (e.g., G[a, f[b]] just yields G[a, f[b]]). How can I achieve that while
>>> also enforcing antisymmetry?
>>>
>>>
>> Hello Torsten,
>>
>> I do not think that it is possible to do this in a general way. It
>> might, however, be possible to make it work for the special cases that
>> you need.
>>
>> The reason why it is not possible to implement it in a completely
>> general way is this:
>>
>> Suppose we input G[a,b], and suppose that there is no definition
>> associated with G that would allow computing the value of G[a,b]. Now
>> we need to check if G[b,a] can be computed, and if so, then use the
>> value -G[b,a] for G[a,b]. But how can we check if G[b,a] "can be
>> computed", that is, if it evaluates to something different than itself?
>> If we aim for complete generality, this is only possible by trying to
>> evaluate G[b,a], which will then trigger the antisymmetry definition
>> again, and lead to infinite recursion...
>>
>> So, let's not aim for completely generality. Instead, let's just check
>> if an *explicit* definition exists for G[b,a] (i.e. for the explicit
>> values b and a):
>>
>> G[x_, y_] := -G[y, x] /; hasValue[G[y,x]]
>>
>> hasValue[f_[args___]] :=
>> MemberQ[First /@ DownValues[f], Verbatim(a)HoldPattern[f[args]]]
>>
>> This will work for simple cases, but it is neither pretty, nor robust.
>> I hope someone will post a better suggestion.
>>
>> One more thing that needs to be mentioned is that there is already a
>> function similar to hasValue[] built into Mathematica: ValueQ[].
>> However, it cannot be used here because for non-atomic arguments
>> (anything more complicated than a symbol) it determines if it has a
>> value by evaluating it and checking whether it has changed. So the
>> infinite recursion still wouldn't be avoided.
>>
>> I hope this helps,
>> Szabolcs
>>
>>
>>
>