Subject: [UFL-dev] [HG UFL] Implemented __iter__ in Expr such that

From: [email-masked] (UFL)
Date: Mon, 6 Apr 2009 13:29:08 +0200 (CEST)

One or more new changesets pushed to the primary ufl repository.
A short summary of the last three changesets is included below.

changeset:   803:3fb5d12db6129110f3f434fee27c5a642db698b4
tag:         tip
user:        "Martin Sandve Alnæs <[email-masked]>"
date:        Mon Apr 06 13:30:50 2009 +0200
files:       doc/manual/chapters/formlanguage.tex ufl/expr.py ufl/terminal.py
description:
Implemented __iter__ in Expr such that
a, b = v
a, b, c = w
etc
is allowed for any vector valued expression,
e.g. with len(v.shape()) == 1.

changeset:   802:b224b4fcfd7a9c3cc8e42de5aaf0437eef840785
user:        "Martin Sandve Alnæs <[email-masked]>"
date:        Mon Apr 06 12:36:07 2009 +0200
files:       doc/manual/chapters/formlanguage.tex doc/manual/ufl-user-manual.tex
description:
Work on forms, integrals and measures part of form language in manual.

changeset:   801:72641a62dcd0bae0aa7556dd38999866252028c6
user:        "Martin Sandve Alnæs <[email-masked]>"
date:        Mon Apr 06 12:08:25 2009 +0200
files:       doc/manual/chapters/formlanguage.tex doc/manual/chapters/introduction.tex
description:
Rework Form Language introduction.

----------------------------------------------------------------------
For more details, visit http://www.fenics.org/hg/ufl

---

From: Anders Logg <[email-masked]>
Date: Mon, 6 Apr 2009 13:30:54 +0200

> e.g. with len(v.shape()) == 1.

Should this be done for Function.split() in (Py)DOLFIN as well?

-- 
Anders

> _______________________________________________
> UFL-dev mailing list
> [email-masked]
> http://fenics.org/mailman/listinfo/ufl-dev

---

From: Martin Sandve Alnæs <[email-masked]>
Date: Mon, 6 Apr 2009 13:44:14 +0200

I'm not sure, I was just thinking about this...

Say we have:

  V = VectorElement("CG", triangle, 1)
  P = FiniteElement("CG", triangle, 1)
  TH = V+P
  up = Function(TH)

then up.shape() == (3,) and thus len(up) == 3 and you can do

  ux, uy, p = up # only considers the value shape
  u = as_vector((ux, uy))

while ufl.split works like this:

  u, p = split(up) # deals with the mixed element structure
  len(u) == 2

We can only implement __iter__ to do one thing!
It might be for ambiguities similar to this that I didn't
implement this earlier, but I don't remember any
more compelling reasons...

In PyDOLFIN, Function.split() is something else?
It creates SubFunctions, which can't be used in forms.
Right? I think we should keep that explicit.

Martin

On Mon, Apr 6, 2009 at 1:30 PM, Anders Logg <[email-masked]> wrote:
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.9 (GNU/Linux)
>
> iEYEARECAAYFAknZ5+4ACgkQTuwUCDsYZdGaOQCcCnCBHs5Ge5f5SAUlx30z9sYl
> nCcAn1uIoaMH8Ch0uKZVmWOLkE7N/SN/
> =O9Nh
> -----END PGP SIGNATURE-----
>

---

From: Johan Hake <[email-masked]>
Date: Mon, 6 Apr 2009 14:17:01 +0200

> We can only implement __iter__ to do one thing!

A naive solution could be to overload the __len__ and __iter__ functions in 
ufl.Functions, so they return the subfunctions as they are used in split()?

I have __no__ clue what so ever to what other things this potentially could 
break ;)

> Right? 

As it is now you are actually able to use the returned subfunction to define 
forms. It's a bug which I will fix :P. 

It turns out that the subfunctions has two version of the FunctionSpace. One 
in python, which is just the original FunctionSpace that was used to define 
the MixedFunctionSpace and one which is the correct FunctionSpace, including 
the correct offset, which is stored by the c++ version. Nice! Not...

I will fix this so the versions correponds.

> I think we should keep that explicit. 

While I see the ambiguity I implemented this in PyDOLFIN.

Now in the stokes demo we can do:

  u, p = problem.solve()

This is what you thought of Anders? You desides what get pushed :)

Johan

---

From: Martin Sandve Alnæs <[email-masked]>
Date: Mon, 6 Apr 2009 14:42:33 +0200

> ufl.Functions, so they return the subfunctions as they are used in split()?

Yes, that's the general idea, but then iteration over a Function and
another ufl expression won't work the same way. If that's what we
choose to do I will probably remove iteration over general expressions,
because this will be too confusing.

> I have __no__ clue what so ever to what other things this potentially could

For some reason I had explicitly implemented __iter__ using
NotImplemented to signal it shouldn't be used, and as I said

> I will fix this so the versions correponds.

I won't even try to wrap my head around that paragraph,
this is why I'm continuing to use C++ while getting UFL done :-/
(Don't waste your time trying to explain it better,
I just don't want to know at this point)

> This is what you thought of Anders? You desides what get pushed :)

But what does this mean?
  u, p = ufl.split(up)
or
  u, p = up.split()
?

Unless there is a solution to make these the same (which seems
complicated), I still think this should be done explicitly.

Also, I've said before: PyDOLFIN.Function should _not_ implement
any special functions (__foo__) since they may interfere with UFL.
The "u, p = up" feature breaks with this.

Martin

---

From: Anders Logg <[email-masked]>
Date: Mon, 6 Apr 2009 15:07:51 +0200

On Mon, Apr 06, 2009 at 02:42:33PM +0200, Martin Sandve Aln=E6s wrote:
> > On Monday 06 April 2009 13:44:14 Martin Sandve Aln=E6s wrote:
> >> =A0 V =3D VectorElement("CG", triangle, 1)
> >> =A0 P =3D FiniteElement("CG", triangle, 1)
> >> =A0 TH =3D V+P
> >> =A0 up =3D Function(TH)
> >>
> >> then up.shape() =3D=3D (3,) and thus len(up) =3D=3D 3 and you can do
> >>
> >> =A0 ux, uy, p =3D up # only considers the value shape
> >> =A0 u =3D as_vector((ux, uy))
> >>
> >> =A0 u, p =3D split(up) # deals with the mixed element structure
> >> =A0 len(u) =3D=3D 2
> >>
> > A naive solution could be to overload the __len__ and __iter__ function=
s in
> > ufl.Functions, so they return the subfunctions as they are used in spli=
t()?
>=20

If I do

  TH = V + P
  w = Function(TH)

then I expect w to be vector-valued function of size d + 1.

So I suggest the split functionality to be a special call:

  ux, uy, p = w
  (ux, uy), p = w.split()

> > I have __no__ clue what so ever to what other things this potentially c=
ould
>=20
>=20
>=20
> > As it is now you are actually able to use the returned subfunction to d=
efine
> > It turns out that the subfunctions has two version of the FunctionSpace=
=2E One
> > in python, which is just the original FunctionSpace that was used to de=
fine
> > the MixedFunctionSpace and one which is the correct FunctionSpace, incl=
uding
>=20
>=20
>=20
> > =A0u, p =3D problem.solve()
> >

It looks good, but perhaps it should not be implemented using __iter__
but instead the solve() function checks if there it is solving a mixed
system and in that case calls split() before returning. So that it
sometimes returns a single solution and sometimes a tuple. Or might
that also be confusing?

>   u, p =3D ufl.split(up)
>   u, p =3D up.split()
>=20
>=20
> The "u, p =3D up" feature breaks with this.

I'm not sure that's an absolute rule. There may be exceptions.
One such is __repr__.

--
Anders

---

From: Martin Sandve Alnæs <[email-masked]>
Date: Mon, 6 Apr 2009 15:40:03 +0200

> then I expect w to be vector-valued function of size d + 1.

Yes, nobody disagrees with that.

> So I suggest the split functionality to be a special call:

Ok, just like I just implemented __iter__ in UFL.
Requires that __iter__ is _not_ overloaded for Function in dolfin.

>  (ux, uy), p = w.split()

This line is not as innocent as it looks...

Consider these two lines:
A)  u, p = w.split()
B)  (ux, uy), p = w.split()

We already have (A), and it works fine.
B is like A plus this line:
C)  ux, uy = u

so B will _either_ need to use __iter__ on u for splitting u -> (ux,uy),
_or_ w.split() must already have returned a tuple(tuple(ux,uy), p),
in which case A will result in u being a tuple (which nobody wants).

Note that you didn't differentiate between ufl.split(w) and w.split(),
they are still not the same.

> sometimes returns a single solution and sometimes a tuple. Or might

We can't do that, because the user might want the solution function
in the mixed space for further use. There's no "un-split".

> I'm not sure that's an absolute rule. There may be exceptions.

Repr has a very specific meaning and should not be
implemented for dolfin.Function, because a proper
implementation of it would require a full string representation
of the mesh.

Repr is used for comparing objects many places in
UFL and as such is particularly important to _not_
overload. I said this several times before, and nobody
argued then. Feel free to overload __str__.

If you disagree with UFL design, please say so at ufl-dev.
Nobody has done so, and I can't read minds.

Martin

---

From: Anders Logg <[email-masked]>
Date: Mon, 6 Apr 2009 16:24:48 +0200

> in which case A will result in u being a tuple (which nobody wants).

Yes, B will need to use __iter__ for splitting u into ux an px.

> they are still not the same.

They need to be merged somehow so that w.split() in Python does
something sensible, that is, returns Functions which can be used as any
other Functions (both in forms and for plotting).

> in the mixed space for further use. There's no "un-split".

Yes we can. We can do as in the assembler, that the assemble()
function returns an object (which is a matrix, vector or number) but
also have the possibility of providing an extra argument in which to
put the solution:

  problem.solve(solution=w)

It's also the case that someone wanting more control and doing
something "advanced" will assemble and solve manually anyway and have
full control over when to split.

> argued then. Feel free to overload __str__.

ok, good, __str__ then. So there are exceptions. There may be others.

> Nobody has done so, and I can't read minds.

This is about DOLFIN design: which functions to put in the DOLFIN
Function class and of course we need to be careful when overloading
UFL special functions.

-- 
Anders

---

From: Martin Sandve Alnæs <[email-masked]>
Date: Mon, 6 Apr 2009 16:36:18 +0200

> Yes, B will need to use __iter__ for splitting u into ux an px.

Then (ux, uy) won't have the same type as p, unless you solve the

> other Functions (both in forms and for plotting).

Good luck :)

> full control over when to split.

Ok.

> ok, good, __str__ then. So there are exceptions. There may be others.

Indeed there may be, but implementing any such should only
be done after carefully considering the implications in UFL.

> Function class and of course we need to be careful when overloading

That's just the problem: you're treating this as just dolfin design
but it's not just that when it puts restrictions on the design of ufl.
That's the result of the multiple inheritance pattern used for Function.

Martin

---

From: Anders Logg <[email-masked]>
Date: Mon, 6 Apr 2009 16:47:27 +0200

> > other Functions (both in forms and for plotting).

Thanks.

> That's the result of the multiple inheritance pattern used for Function.

It's natural that the requirements in DOLFIN may influence some
decision in UFL. But so far it's only __repr__ (which should be
renamed __str__) that's overloaded and we'll be careful in choosing
what to overload next.

-- 
Anders

---

From: Johan Hake <[email-masked]>
Date: Mon, 6 Apr 2009 21:37:35 +0200

On Monday 06 April 2009 16:47:27 Anders Logg wrote:
> > >> >> Yes, that's the general idea, but then iteration over a Function
> > >> >> and another ufl expression won't work the same way. If that's what
> > >> >> we choose to do I will probably remove iteration over general
> > >> >> expressions, because this will be too confusing.
> > >> >
> > >> > If I do
> > >> >
> > >> >  TH = V + P
> > >> >  w = Function(TH)
> > >> >

Yes, but you cannot use it to define mixed ufl.forms, as the mixing of 
ufl.split and dolfin.split is non-trivial. 

> > >> so B will _either_ need to use __iter__ on u for splitting u ->
> > >> (ux,uy), _or_ w.split() must already have returned a
> > >> tuple(tuple(ux,uy), p), in which case A will result in u being a tuple
> > >> (which nobody wants).
> > >

He, he who is going to implement this?

We have two discussions here. Should we use __iter__ for splitting Functions. 
We have not landed on this, but 

The cpp.Function is correctly splitted, and it gets instantiated with the 
correct FunctionSpace through the c++ interface. We can use this information 
to instantiate a _new_ ufl.Element and use this to instantiate the 
correspondning ufl.Function.

I imagine that this is not good enough, as the splitting is not done through 
the ufl.split, so it cannot be used to define mixed forms.

One could maybe modularise the split function so one can call it inside the 
constructors of the new Functions, maybe implement it as a generator 
expression? But my knowledge of how ufl treat this is zero. 

I see that if we just use the ufl.split on the result from stokes we get:

  type(u): <class 'ufl.tensors.ListTensor'>
  type(p): <class 'ufl.indexed.Indexed'>
  type(u).__bases__: (<class 'ufl.expr.WrapperType'>,)
  type(p).__bases__: (<class 'ufl.expr.WrapperType'>,)
  u: [[ w_4[0], w_4[1] ]]
  p: w_4[2]

The same result printed for u, v = w.split()

  type(u): <class 'dolfin.ufl.function.DiscreteSubFunction'>
  type(p): <class 'dolfin.ufl.function.DiscreteSubFunction'>
  type(u).__bases__: (<class 'ufl.function.Function'>,
                      <class 'dolfin.cpp.DiscreteFunction'>,
                      <class 'dolfin.ufl.function.Function'>)
  type(p).__bases__: (<class 'ufl.function.Function'>,
                      <class 'dolfin.cpp.DiscreteFunction'>,
                      <class 'dolfin.ufl.function.Function'>)
  u: <Function in <Function space of dimension 22272 (
        <Mixed element: (<CG2 on a <triangle of degree 1>>,
                         <CG2 on a <triangle of degree 1>>)>)>>
  p: <Function in <Function space of dimension 2868 (
        <CG1 on a <triangle of degree 1>>)>>

So ufl.split does not return two ufl.Functions, right? Only some sort of view? 
Is that the WrapperType? Are these classes inheritable and could they be 
instantiated inside an __init__ function? I also see (or not see!) that the 
information about the original function is hidden somewhere.

> > >> >> I just don't want to know at this point)

That's ok, but then I have to understand some ufl internals, see question 
about ufl.split above.

> > >> > It looks good, but perhaps it should not be implemented using
> > >> > __iter__ but instead the solve() function checks if there it is
> > >> > solving a mixed system and in that case calls split() before
> > >> > returning. So that it sometimes returns a single solution and
> > >> > sometimes a tuple. Or might that also be confusing?
> > >>

So the solve, function will return a the result of w.split(). If the user want 
to the whole solution it will use a kwarg?

> > > ok, good, __str__ then. So there are exceptions. There may be others.

Now we explicitly say that 

  dolfin.Function.__repr__ == ufl.Function.__repr__

and 
  
  dolfin.Function.__str__ returns "<Function in %s>" % str(self._V)

see output above.

> > be done after carefully considering the implications in UFL.

Agree.

> renamed __str__) that's overloaded 

Is this correct, refer to what I wrote above.

> and we'll be careful in choosing what to overload next.

Johan

---

From: Martin Sandve Alnæs <[email-masked]>
Date: Tue, 7 Apr 2009 00:38:54 +0200

Sorry, I don't have time for this. Sorry for giving the impression
that I could participate in a discussion like this.

Martin