From: Torsten Schoenfeld on
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: Szabolcs Horvát on
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

From: Daniel Lichtblau on
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?
>

One way to do this is to only assign to the ordered set of arguments,
then impose down values to handle out-of-order argument lists. For the
case of two arguments this can be done as below.

assignOrdered[G_,a_,b_,f_] :=
If[a===b, G[a,b] := 0,
If [OrderedQ[{a,b}], G[a,b] := f[a,b], G[b,a] := -f[a,b]]]

Here I replicate your assignments, plus another which gives the
arguments in non-OrderedQ form.

assignOrdered[G,a,b,f]
assignOrdered[G,a,c,g]
assignOrdered[G,b,c,h]
assignOrdered[G,d,c,k]

Now we impose antisymmetry.

G[a_,a_] := 0
G[a_,b_] /; !OrderedQ[{a,b}] := -G[b,a]

let's see what we have.

In[61]:= DownValues[G]
Out[61]= {HoldPattern[G[a, b]] :> f[a, b],
HoldPattern[G[a, c]] :> g[a, c],
HoldPattern[G[b, c]] :> h[b, c], HoldPattern[G[c, d]] :> -k[d, c],
HoldPattern[G[a_, a_]] :> 0,
HoldPattern[G[a_, b_] /; !OrderedQ[{a, b}]] :> -G[b, a]}

Quick test:

In[62]:= {G[x,y], G[y,x], G[c,d],G[d,c],G[x,a]}
Out[62]= {G[x, y], -G[x, y], -k[d, c], k[d, c], -G[a, x]}

Daniel Lichtblau
Wolfram Research

From: Albert Retey on
Hi,

> 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?

I think your problem is that G[x_, y_] := -G[y, x] will result in a
infinit recursion, since part of the right hand side will match the left
hand side no matter what arguments you give.

To enforce antisymmetry you could do this, so the pattern will match
only when the arguments are not in canonicial order:

g[b_, a_] /; Not[OrderedQ[{b, a}]] := -g[a, b]


hth,

albert


From: Leonid Shifrin on
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
>
>