Hi,
On 28.07.04, Joerg Lehmann wrote:
> > "=="
> IMHO it's too implicit. In particular, not having to explicitly specify
> the solver is probably not a good idea.
I think, having only one solver (as in MetaPost) is not that
problematic. OTOH, yes, you may make use of several solvers. But then
I think we should do the bookkeeping, whether a variable is already
defined (currently by the is_set() method), local within each solver.
Could be usefull ... I'm not sure.
AFAIRC in MetaPost there is a possibility to "redefine" the value of a
variable and "resolve" the equations. Might be a task for several
solvers. This would be much more elegant IMHO (compared to the
MetaPost way; MetaPost doesn't have (several) solver instances.)
André
--
by _ _ _ Dr. André Wobst
/ \ \ / ) wobsta@..., http://www.wobsta.de/
/ _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX
(_/ \_)_/\_/ visit http://pyx.sourceforge.net/

Hi,
On 28.07.04, Magnus Lie Hetland wrote:
> Joerg Lehmann <joergl@...>:
> >
> [snip]
> > IMHO it's too implicit. In particular, not having to explicitly specify
> > the solver is probably not a good idea.
>
> I think using == for anything with side-effects (or even anything that
> isn't interpretable as a boolean equality check) is quite unfortunate.
> It might *look* good, but still... (if x == y: alwaysexecuted()...)
Thanks, Jörg, Magnus. I was not satisfied myself. The comparison makes
me troubles as well ... see the id comparision and the XXX comment,
where I check for zero using "is", which works, since all integer
constants below 100 are predefined and used by reference. In the
beginning I liked the idea, but its probably a bad idea.
> If one was feeling adventurous, one could even do stuff like
>
> solver(x = y)
>
> but only for single-variable left-hand-sides, of course.
I do not quite understand. What's the result of an assignment? Anyway,
once we do not use x == y, we should allow for x = y as well. Its
becoming even less symmetric: Currently you need to set x and y to be
scalar instances (or variable instances). In your case this becomes
assymetric. You would need to initialize just x.
Beside that, I think where might be usecases, where you store a term.
Say:
x = scalar()
y = scalar()
z = scalar()
A = x + 5*y
B = A + y
C = A + z
solver.eq(B, C)
I don't know whether this will make sense, but you can do so.
> It would be possible to do some property magic too... I'm pretty sure
> I could write code such that
>
> a.x = b.y
> a.y = b.x
>
> could be turned into a set of equations (by using wrapper classes
> around the objects returned by the properties, somewhat like bound
> methods).
>
> Just musing.
You can currently do so with the "==" logic. That's because the
components of a vector ar scalars and you can formulate scalar
eqations and vector equations. So you can currently write:
a = vector(2)
b = vector(2)
a[0] == b[1]
a[1] == b[0]
Note I have not (yet) a getattr on the vectors for "x", "y", "z". But
right, thats trivial to be added.
Now you can write:
a == point(0, 1)
Guess, what b becomes? ... ;-)
André
--
by _ _ _ Dr. André Wobst
/ \ \ / ) wobsta@..., http://www.wobsta.de/
/ _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX
(_/ \_)_/\_/ visit http://pyx.sourceforge.net/

Andre Wobst <wobsta@...>:
>
[snip]
> Well, I have to look at MetaPost at some point.
If you mean a the language/user level -- indeed. Very relevant to PyX.
But I guess, perhaps, you mean at the implementation/code level?
Well... I guess it's still relevant (although I'm not sure I'd dare
venture into that without a good lantern and a trusty sword by my
side... Or something :])
> Don't we really want to create a transformation by setting 3 2d
> input and output points?
That's the canonical problem, I guess.
> This is a linear problem. How is it in MetaPost?
There's an example on page 32 of the User Manual; the transform T
(called T' in the manual) is not yet defined:
(0, 1) transformed T =3D (3, 4)
(1, 1) transformed T =3D (7, 1)
(1, 0) transformed T =3D (4, -3)
The solugion to this gives the transform factors
txx =3D 4
txy =3D -3 [called tyx in the manual]
tyx =3D 3
tyy =3D 4
tx =3D 0
ty =3D 0
But the problem needn't be this straightforward; these equations must
be fully integrated with the general equation stuff, of course. For
example, on the same page in the manual, the following equations are
used to specify that a given (as yet unknown) transform is
shape-preserving:
txx =3D tyy; tyx =3D -txy
This automatically only allows rotation, shifting and uniform scaling.
This is used in a very neat example on page 33 of the manual (figure
28).
> Is it allowed to define write P =3D t*p where the points P and p are
> not defined as well as t is not defined? What does MetaPost with
> such an equation? Does it store it for later evaluation
I don't really know; for each non-linear term you have to know (*or*
be able to figure out through some other linear equation system) one
of the two factors before you can start solving things, of course. But
how you deal with this in general (i.e. how you know when to solve a
certain subset of the equations but not the rest of the equations) I
don't know.
> (for example until p and P becomes known)? And what about having
> several transformations: P =3D t2*t1*p?
I would think that would be allowed (as long as you supplied more
equations in addition) but I'm not sure.
> And what about restricting a transformation to a rotation, for
> example?
That can certainly be done (see the example above).
> I should take a look at MetaPost/MetaFont at some point.
Sounds like a good move, indeed :)
I guess one of the few reasons I haven't dropped MetaPost is that PyX
doesn't quite implement all of its functionality (such as this) yet.
:]
(Also, it's nice to be able to use the output from MetaPost directly
in pdflatex without a hickup; if PyX could restrict itself to the same
subset of EPS, possibly with a switch, such as mps=3DTrue or something,
that would be very nice :})
> Andr=E9
--=20
Magnus Lie Hetland "Canned Bread: The greatest thing since sliced
http://hetland.org bread!" [from a can in Spongebob Squarepants]

Andre Wobst <wobsta@...>:
>
> (BTW: it is neither very complicated to change this at a later
> point, nor would it be visible to the outside world. So I can live
> this this issue quite well as it currently is -- independing from
> what you're telling me ;-).)
That's fine -- as long as you know what you're doing (and you do, of
course, know your code much better than me). :)
I've just had a deep-rooted fear and loathing for type checking
instilled in me by Alex Martelli ;)
[snip]
> Right. Don't take the isinstance code too serios right now. I added
> quite some isinstance checks and assert checks to help me getting it
> running properly.
Quite all right.
> Andr=E9
--=20
Magnus Lie Hetland "Canned Bread: The greatest thing since sliced
http://hetland.org bread!" [from a can in Spongebob Squarepants]

Andre Wobst <wobsta@...>:
>
> Hi,
>=20
[snip]
> Ok, lets do some ASCII art (is it art?):
>=20
> /x'\ /a b\/x\ /e\
> | | =3D | || | + | |
> \y'/ \c d/\y/ \f/
>=20
> If you do not define the transformation matrix (i.e. the variables a,
> b, c, and d) and you also keep the point p, i.e. x and y, variable,
> the left hand side contains terms like a*x, which is non-linear.
>=20
> You may, at a later point, find out, that you already know the value
> of x, for example. Than the term becomes linear ...
Ah, right. Indeed. Intuitively it just seems underdetermined if you're
trying to find out the transform *and* the points, even if you know
enough to make it, say, a quadratic problem (or something).
In practical applications I guess you'd always(?) know enough about
the points to get rid of such non-linear terms.
> And I think, that's what needs to be coded.
Right. I have no idea (off the top of my head, at least) how one would
deal with that; just a bit of book-keeping, perhaps? ;)
[snip]
> Or just think what we can code right now after thinking about the
> problem. I think its not that complex.
Sounds good.
> Those non-linear terms might be hidden in some additional
> "non-linear variable" in a code along the lines I've started. Should
> be possible. Just replace a*x by AX and keep on going.
Sounds like a clever move.
> Once a or x becomes available by other means, the "non-linear
> variable" can be resolved and a linear equation is restored.
Yeah -- a bit of book-keeping, still...
> What's also needed are equations for 2d points etc., but those might
> be build on top of the term class as well.
I'm dealing with these email out of order, so I guess this part is
already solved (by your vector equation code).
> So I think, yes, we can really get this working ...
Wohoo! :)
[snip]
> Andr=E9
>=20
> PS: I've also seen your comment about isinstance. Right. Currently I'm
> using it in this solver code to differenciate in __add__ etc.
> between the different types. Not yet perfect, right ... ;-)
> (I've the Python Cookbook and read about isstring, yes.)
Heh. It works, for now; just thought I'd mention it. Perhaps the code
can be made more generic at some later stage (so that I can, for
example, multiply terms by a number-like class without having to
subclass int or the like).
BTW: Unless you're targeting old Pythons (maybe you are?) you don't
have to use types.IntType and the like; just use int (and float and
long). e.g. isinstance(foo, (int, float, long)). (No big point, as
types.IntType is simply int and so forth.)
--=20
Magnus Lie Hetland "Canned Bread: The greatest thing since sliced
http://hetland.org bread!" [from a can in Spongebob Squarepants]

Andre Wobst <wobsta@...>:
>
> Hi,
>=20
> On 28.07.04, Andr=E9 Wobst wrote:
> > Update of /cvsroot/pyx/pyx/test/experimental
> > In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13287
> >=20
> > Modified Files:
> > solve.py=20
> > Log Message:
> > vector equations (can be mixed with scalar equations)
> [snip]
>=20
> I've just checked in another update to the experimental linear
> equation solver. You can now mix all kind of vector and scalar
> equations.
Sounds very cool. This basically means that most of the needed (basic)
geometry functionality is in place, then... (It still would be nice to
have access to the first three dimensions of vectors as v.x, v.y and
v.z but that's just a minor convenience.)
It would be interesting to see what could be done with the transform
equations... But even without them, stuff like "the difference between
a and b is equal to the difference between c and d" and the like is
very useful.
I couldn't get the vector stuff from CVS it seems -- maybe it's just
the anonymous CVS caching that's a bit slow again.
> But I'm not quite sure whether the "=3D=3D" notation is a good
> idea, i.e. will work well in non-trivial code. My biggest concerns are
> in the area of usuability, i.e. is it too implicit? Comments?
(I replied to the "=3D=3D" part elsewhere.)
> Andr=E9
--=20
Magnus Lie Hetland "Canned Bread: The greatest thing since sliced
http://hetland.org bread!" [from a can in Spongebob Squarepants]

Joerg Lehmann <joergl@...>:
>
[snip]
> IMHO it's too implicit. In particular, not having to explicitly specify
> the solver is probably not a good idea.=20
I think using =3D=3D for anything with side-effects (or even anything tha=
t
isn't interpretable as a boolean equality check) is quite unfortunate.
It might *look* good, but still... (if x =3D=3D y: alwaysexecuted()...)
Using a method or function may be a bit more verbose but still much
better, IMO. We don't really have much to complain about; the Java
people have to use foo.equals(bar) where we can use foo =3D=3D bar :)
I think
solver.equate(x, y)
or something similar (e.g. "equals", "equal", "eq") would be better.
If one was feeling adventurous, one could even do stuff like
solver(x =3D y)
but only for single-variable left-hand-sides, of course.
It would be possible to do some property magic too... I'm pretty sure
I could write code such that
a.x =3D b.y
a.y =3D b.x
could be turned into a set of equations (by using wrapper classes
around the objects returned by the properties, somewhat like bound
methods).
Just musing.
> J=F6rg
--=20
Magnus Lie Hetland "Canned Bread: The greatest thing since sliced
http://hetland.org bread!" [from a can in Spongebob Squarepants]

Hi André,
On 28.07.04, Andre Wobst wrote:
[snip]
> I've just checked in another update to the experimental linear
> equation solver. You can now mix all kind of vector and scalar
> equations.
Nice!
> But I'm not quite sure whether the "==" notation is a good
> idea, i.e. will work well in non-trivial code. My biggest concerns are
> in the area of usuability, i.e. is it too implicit? Comments?
IMHO it's too implicit. In particular, not having to explicitly specify
the solver is probably not a good idea.
Jörg

Hi,
On 27.07.04, Magnus Lie Hetland wrote:
> So, here is a version which uses André's linear equation stuff
> (although I've switched to numarray -- it's what I use; and numarray
> 1.0 is out now, so... Hooray for that :).
Hey, isn't that something for try-import-except? No isinstance
available ... ;-)
> So, presto, we've got a bidirectional thingy. Note that the angle in
> the transform is still a constant, though. As André pointed out, if
> the angle is to be a variable, we'd end up getting equations with
> sin() and cos(), and that's not exactly pleasant.
Well, I have to look at MetaPost at some point. Don't we really want
to create a transformation by setting 3 2d input and output points?
This is a linear problem.
How is it in MetaPost? Is it allowed to define write P = t*p where the
points P and p are not defined as well as t is not defined? What does
MetaPost with such an equation? Does it store it for later evaluation
(for example until p and P becomes known)? And what about having
several transformations: P = t2*t1*p? And what about restricting a
transformation to a rotation, for example? I should take a look at
MetaPost/MetaFont at some point.
André
--
by _ _ _ Dr. André Wobst
/ \ \ / ) wobsta@..., http://www.wobsta.de/
/ _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX
(_/ \_)_/\_/ visit http://pyx.sourceforge.net/

Hi,
On 27.07.04, Magnus Lie Hetland wrote:
> I guess in these cases a simple isinstance might be easier. After all,
> we *do* have multiple inheritance, so one could always just subclass
> the needed class as if it were an interface.
First of all, I agree to you, that we should use try-except clauses
instead of isinstance almost everywhere. But we do (maybe missuse) the
class structure and isinstance in one place, where I think its worth
to be discussed in order to make clear, whether its a design mistake
from the very beginning. I'm not so sure. Let me explain it a little.
It's related to the attribute system. Suppose you call the fill method
or the draw method of a canvas and provide some additional attributes
like colors, linewidths, linestyles, patterns, etc.
(Additionally, the fill and draw methods can take some decorators,
which might add other stuff (like arrows) and shorten the path (for
the head of the arrow and the like). BTW Jörg, Michael: we wanted to
restrict ourselfs to not really modify the original path anymore by
decorators, which are now called deformers. Did anybody the coding for
that? The decorated path should be simplified in that sense. We need
to come back to that issue at some point. Maybe in Basel, Jörg? But
back to the draw and fill attributes.)
So what we did is, we introduced a strokestyle and fillstyle class,
which is used as a mix-in to certain attributes. The idea is, that a
color can be used as a stroke as well as a fill style, while a line
width is usefull when stroking only. Once we mix-in the strokestyle
and/or fillstyle class, we can use isinstance to check, whether the
style is appropriate to be used at a given point. (Isn't that's what
Jim Fulton always says, that __implements__ is something else that a
position in a class hierarchy.) I know, that we created some
restriction here: you can't derive a class from, say, the line width,
which will not be a stroke attribute anymore. But I don't care (we
really don't need to take care). I think, at this very point, the
isinstance is the most simple check possible. We could define some
"ask-what-it-is" functionality on attributes as a whole, but I'm not
sure we really need this. (BTW: it is neither very complicated to
change this at a later point, nor would it be visible to the outside
world. So I can live this this issue quite well as it currently is --
independing from what you're telling me ;-).)
(As a side remark: The merging an clearing-functionality of the
attributes is based on isinstance as well ...)
> The reason I noticed this was that I saw plenty of isinstance() calls
> in the linear equation code -- might be inconvenient if one wanted to
> mix in multidimensional points between the variables and numbers it
> already knows about, because, basically, the "aren't allowed" in
> there.
Right. Don't take the isinstance code too serios right now. I added
quite some isinstance checks and assert checks to help me getting it
running properly.
André
--
by _ _ _ Dr. André Wobst
/ \ \ / ) wobsta@..., http://www.wobsta.de/
/ _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX
(_/ \_)_/\_/ visit http://pyx.sourceforge.net/

Hi,
On 27.07.04, Magnus Lie Hetland wrote:
> I know the curves are
> non-linear... I just (naïvely ;) thought that, perhaps, through some
> transformational magic the problem could be solved within the same
> framework of linear equations.
I don't think so. The intersection of two bezier curves is solved
numerically. (Or may be by solving a polynom of grad three.) In case
you want to take a look into MetaPost (I would like to learn more
about that). Is it really possible to say: transform a path by a not
yet completely defined transformation, so that an intersection point
with another path is located at a y coordinate with a certain value?
I just think you can't transform a path with a not yet completely
defined transformation. It becomes too complicated. But, of course,
what you can do for sure is to transform a point by a not yet
completely defined transformation. This is how you can define a
transformation by saying this point goes into this and a second one
into another one etc.
> [snip]
> > Say p and p' are (2d) points, and t is a transformation (i.e. a 2x2
> > matrix + a 2d vector). Then you can write
> >
> > p' = t(p)
> >
> > Once you say p' and p are lazy vectors (i.e. they contain 2 variables)
> > as well as t is a lazy transformation (i.e. it contain 6 variables),
> > than the equation above is non-linear. There are non-linear terms in
> > matrix times vector when they both contain variables.
>
> Right -- you're basically saying that this isn't linear in the
> elements of the transform? But if we freeze the transform it is linear
> in p' and p...?
Ok, lets do some ASCII art (is it art?):
/x'\ /a b\/x\ /e\
| | = | || | + | |
\y'/ \c d/\y/ \f/
If you do not define the transformation matrix (i.e. the variables a,
b, c, and d) and you also keep the point p, i.e. x and y, variable,
the left hand side contains terms like a*x, which is non-linear.
You may, at a later point, find out, that you already know the value
of x, for example. Than the term becomes linear ...
And I think, that's what needs to be coded.
> This seems quite muddled. I'm not very tempted to read the MetaPost
> source code to find out how they do it either; I guess maybe I can
> snoop around and see if I find some other pointers on how to do this
> sort of thing.
Or just think what we can code right now after thinking about the
problem. I think its not that complex. Those non-linear terms might be
hidden in some additional "non-linear variable" in a code along the
lines I've started. Should be possible. Just replace a*x by AX and
keep on going. Once a or x becomes available by other means, the
"non-linear variable" can be resolved and a linear equation is
restored. What's also needed are equations for 2d points etc., but
those might be build on top of the term class as well. So I think,
yes, we can really get this working ...
I've worked on my solver code a bit further. You can find an updated
version enclosed. It solves equations "on the fly" and it also tries
to solve equations as soon as possible (it searches for decoupled
systems of equations). I've just checked it into the CVS under
test/experimental/solve.py as well ...
André
PS: I've also seen your comment about isinstance. Right. Currently I'm
using it in this solver code to differenciate in __add__ etc.
between the different types. Not yet perfect, right ... ;-)
(I've the Python Cookbook and read about isstring, yes.)
--
by _ _ _ Dr. André Wobst
/ \ \ / ) wobsta@..., http://www.wobsta.de/
/ _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX
(_/ \_)_/\_/ visit http://pyx.sourceforge.net/

Luckily, I couldn't quite stop playing around with this... My first
thought was to rewrite everything, writing custom stuff to integrate
the linear equation solving stuff and my lazy points. I guess that is
still an option, but I decided to do a very simple version instead.
So, here is a version which uses Andr=E9's linear equation stuff
(although I've switched to numarray -- it's what I use; and numarray
1.0 is out now, so... Hooray for that :).
A point is simply a list of variables. By using ordinary list
notation, such as p[0] one gets at these variables (their values are
available through p[0].value and so forth) but by using the syntactic
sugar p.x (or p.y or p.z) one gets (or sets) their values. For general
dimensions beyond the third, use p.get(dim) or p.set(dim, val) (or,
equivalently, p[dim].value and p[dim].value =3D val).
The lsys class is a simple wrapper that handles left and right hand
sides with more than one element (for multidimensional equations).
There is precious little one can do directly with points at the moment
(such as addition/multiplication) but adding that should be easy. I've
added a single transformation as an example:
"""
def rotated(point, a):
assert len(point) =3D=3D 2 # To keep it simple :)
return (point[0]*cos(a)-point[1]*sin(a),
point[1]*cos(a)+point[0]*sin(a))
"""
Note that this works with the *variables*, not their *values*. Thus,
one can use this transform either way in an equation:
"""
from geom import *
from math import pi
a =3D pt()
b =3D pt()
c =3D pt()
eqs =3D lsys()
a.x =3D 0
a.y =3D 10
eqs.eq(a, rotated(b, pi/2))
eqs.eq(c, rotated(a, pi/2))
eqs.solve()
print b.x, b.y
# Prints out 10.0 0.0
print c.x, c.y
# Prints out -10.0 0.0
"""
So, presto, we've got a bidirectional thingy. Note that the angle in
the transform is still a constant, though. As Andr=E9 pointed out, if
the angle is to be a variable, we'd end up getting equations with
sin() and cos(), and that's not exactly pleasant.
So, this code is still sort of hackish, but it's a starting point that
actually works... And that's always good, isn't it? :)
--=20
Magnus Lie Hetland "Canned Bread: The greatest thing since sliced
http://hetland.org bread!" [from a can in Spongebob Squarepants]

Joerg Lehmann <joergl@...>:
>
[snip]
> You're right, this should not be the case. Sometimes, however, we
> introduced some checks such that the user doesn't get hit by some
> strange error when he calls writeEPSfile.
Checks are good. I guess I'm just saying that more flexible (i.e.
signature-based) checks could be used instead.
> For instance, we check whether the arguments passed to some canvas
> methods are of a certain type. I don't think this is necessarily
> bad. See for instance the insert and set method of the
> canvas._canvas class for two examples.
The best solution here, IMO, (in an ideal world ;) would be to use
either (1) interfaces or (2) protocol adaptation. There are PEPs for
both, but I'm not sure whether either will ever become standard
Python. (I guess interfaces have the best chance, even though
adaptation is much cooler ;)
If the API is simple one could check manually (as with the helper
functions for strings etc.; you could write similar ones to check
whether something seems to be a good canvas).
I guess in these cases a simple isinstance might be easier. After all,
we *do* have multiple inheritance, so one could always just subclass
the needed class as if it were an interface.
> > Such type-based discrimination <wink> is in most cases necessary; if
> > an object can do the job, just let it.
> >
> > (Often, using polymorphism directly, i.e. simply calling a method on
> > the given object, would be better than using an if statement checking
> > properties on the object. That's not feasible in special cases such a=
s
> > this, where you have to deal with numbers and strings and the like, o=
f
> > course.)
>=20
> Again, you're right - which unfortunately does not mean that the code i=
s
> perfect in this sense - it developped over quite some time... So if
> there are some points where you think the present code can be improved
> in this regard, feel free to point them out (or just send a patch).
The reason I noticed this was that I saw plenty of isinstance() calls
in the linear equation code -- might be inconvenient if one wanted to
mix in multidimensional points between the variables and numbers it
already knows about, because, basically, the "aren't allowed" in
there.
No big deal, though. If it turns out to be problematic, I can always
fiddle a bit with the code, I guess :)
> J=F6rg
--=20
Magnus Lie Hetland "Canned Bread: The greatest thing since sliced
http://hetland.org bread!" [from a can in Spongebob Squarepants]

On 27.07.04, Magnus Lie Hetland wrote:
[snip]
> OK, let's take a look at that as an example...
Ok, but as I said, the code in CVS is completely different. Maybe
I should have picked another example...
> First of all, it uses helper.isstring and helper.isnumber, both of
> which use the Leap Before You Look idiom -- I have no quarrels with
> them. That is not type checking/type based polymorphism. This is how
> it *should* be done <wink>
>
> The only use of isintance here is:
>
> if isinstance(l, length):
> self.length = l.length
> elif helper.isstring(l):
> # ...
>
> Why is this check necessary? Why not use something like this (possibly
> wrapped in helper.islength or something, if desired):
>
> try:
> self.length = l.length
> except AttributeError:
> if helper.isstring(l):
> # ...
>
> Then, if I decided to implement my own length-like class which
> supplied the required length attribute (which is, after all, all
> that's required) it would work nicely.
>
> The only thing you achieve by using isintance instead is to close the
> door in my face -- "no wannabe lengths permitted".
You're right, this should not be the case. Sometimes, however, we
introduced some checks such that the user doesn't get hit by some
strange error when he calls writeEPSfile. For instance, we check whether
the arguments passed to some canvas methods are of a certain type. I
don't think this is necessarily bad. See for instance the insert and set
method of the canvas._canvas class for two examples.
> Such type-based discrimination <wink> is in most cases necessary; if
> an object can do the job, just let it.
>
> (Often, using polymorphism directly, i.e. simply calling a method on
> the given object, would be better than using an if statement checking
> properties on the object. That's not feasible in special cases such as
> this, where you have to deal with numbers and strings and the like, of
> course.)
Again, you're right - which unfortunately does not mean that the code is
perfect in this sense - it developped over quite some time... So if
there are some points where you think the present code can be improved
in this regard, feel free to point them out (or just send a patch).
Jörg

Joerg Lehmann <joergl@...>:
>
[snip]
> Another typical use of isinstance is to implement some type based
> polymorphism. For instance, if you pass some object to a function and
> this function checks in which way he can deal with this object.
Sure -- but in Python the Right Thing(tm) in most cases isn't
type-base polymorphism, but signature-based polymorphism. If something
quacks like a duck, you treat it like a duck. It doesn't really have
to *be* a duck. (Someone might have implemented a duck-robot, for
example, and it would be impolite not to let them use that instead :)
> Here, I have to admit, I'm not always sure about the Python way of
> doing things like that... A typical example was the constructor of
> the unit.length class (only in the released PyX versions), which was
> able to convert nearly anything to a length. In CVS we already got
> rid of that...
OK, let's take a look at that as an example...
First of all, it uses helper.isstring and helper.isnumber, both of
which use the Leap Before You Look idiom -- I have no quarrels with
them. That is not type checking/type based polymorphism. This is how
it *should* be done <wink>
The only use of isintance here is:
if isinstance(l, length):
self.length =3D l.length
elif helper.isstring(l):
# ...
Why is this check necessary? Why not use something like this (possibly
wrapped in helper.islength or something, if desired):
try:
self.length =3D l.length
except AttributeError:
if helper.isstring(l):
# ...
Then, if I decided to implement my own length-like class which
supplied the required length attribute (which is, after all, all
that's required) it would work nicely.
The only thing you achieve by using isintance instead is to close the
door in my face -- "no wannabe lengths permitted".
Such type-based discrimination <wink> is in most cases necessary; if
an object can do the job, just let it.
(Often, using polymorphism directly, i.e. simply calling a method on
the given object, would be better than using an if statement checking
properties on the object. That's not feasible in special cases such as
this, where you have to deal with numbers and strings and the like, of
course.)
> J=F6rg
--=20
Magnus Lie Hetland "Canned Bread: The greatest thing since sliced
http://hetland.org bread!" [from a can in Spongebob Squarepants]

Hi Magnus,
On 27.07.04, Magnus Lie Hetland wrote:
> Just a general comment on the use of isinstance -- I think it's best
> avoided when possible. Type checking breaks polymorphism... Signature
> checking or the "leap before you look" paradigm are, IMO, more
> Pythonic. Just noticed a few isintance calls in the code here and
> there -- just thought I'd pipe in. Feel free to ignore :)
In general, you're right - and there are probably some places where
avoiding isinstance would be better. However, there are some
places where this is not so easy, and a simple isinstance is clearer
than code which tries to generalize even the special case.
Sometimes we also check for instances of a specific class to throw
an exception already quite early. In principle this is not necessary,
but believe me, it helps when you now early on that you did something
wrong...
Another typical use of isinstance is to implement some type based
polymorphism. For instance, if you pass some object to a function and
this function checks in which way he can deal with this object. Here, I
have to admit, I'm not always sure about the Python way of doing things
like that... A typical example was the constructor of the unit.length
class (only in the released PyX versions), which was able to convert
nearly anything to a length. In CVS we already got rid of that...
Jörg

Just a general comment on the use of isinstance -- I think it's best
avoided when possible. Type checking breaks polymorphism... Signature
checking or the "leap before you look" paradigm are, IMO, more
Pythonic. Just noticed a few isintance calls in the code here and
there -- just thought I'd pipe in. Feel free to ignore :)
--
Magnus Lie Hetland "Canned Bread: The greatest thing since sliced
http://hetland.org bread!" [from a can in Spongebob Squarepants]

Just somewhat related -- the constraint package of logilab:
http://www.logilab.org/projects/constraint/documentation
It only works with finite domains and isn't very fast, I believe.
Might still be a source of inspiration; I don't know :}
--
Magnus Lie Hetland "Canned Bread: The greatest thing since sliced
http://hetland.org bread!" [from a can in Spongebob Squarepants]

Andre Wobst <wobsta@...>:
>
[snip]
> BTW: I didn't wrote a linear equation solver, but what I did was
> exactly a "variable class" and the "linear equations behind the
> scenes", i.e. building up linear terms. To actually solve the linear
> equations I used Numeric in my sample code already.
I noticed that when I looked at the code :)
I've actually seens some similar code somewhere else before (the code
I mentioned earlier) -- and I've written similar things too (also
related to logic programming and the like). Seems like (possibly) a
reasonable approach.
> > Certainly. It would also be neat to be able to use parametrized curve=
s
> > in the same system, as in MetaPost; but I guess it's not quite that
> > critical, as you already have support for finding intersections
> > in PyX.
>=20
> Finding intersections etc. is usually a nonlinear operation. But you
> can define points by that ... sure. I guess it will be similar to
> MetaPost in that respect (though I'm not quite sure right now).
I don't know how MetaPost does it either. I know the curves are
non-linear... I just (na=EFvely ;) thought that, perhaps, through some
transformational magic the problem could be solved within the same
framework of linear equations. I guess finding out how MetaPost
actually does this might be quite useful.
[snip]
> Say p and p' are (2d) points, and t is a transformation (i.e. a 2x2
> matrix + a 2d vector). Then you can write
>=20
> p' =3D t(p)
>=20
> Once you say p' and p are lazy vectors (i.e. they contain 2 variables)
> as well as t is a lazy transformation (i.e. it contain 6 variables),
> than the equation above is non-linear. There are non-linear terms in
> matrix times vector when they both contain variables.
Right -- you're basically saying that this isn't linear in the
elements of the transform? But if we freeze the transform it is linear
in p' and p...?
> So I think what one has to do is to postpone this non-linear equation
> until the non-linear terms are solved by other constraints. I guess,
> thats what MetaPost does as well ... as soon as p or t gets defined
> somewhere else, the equation above becomes linear.
Well, you certainly can't solve an underdetermined set of equations
anyway, whether they're linear or not :)
I guess the problem will be writing it up as a set of linear equations
(i.e. in matrix form). What you're saying is that we can't really do
this before either p, p' of t has been determined by other means?
This seems quite muddled. I'm not very tempted to read the MetaPost
source code to find out how they do it either; I guess maybe I can
snoop around and see if I find some other pointers on how to do this
sort of thing.
Does anyone know of any other systems/sources of information than
MetaPost?
> > Anyway, writing a system like this based on linear equations is a
> > bit more work than a one-way function-based one. I'll have a look
> > but I can't promise anything.
>=20
> Sure, nobody does. But we might have a starting point now ... and
> maybe somebody is really interested in this.
Well, I'd be very interested in having it available :]
> Its not top priority for me, but well, on the other hand, I already
> like it ... we can get something really funny quite quickly I guess.
I guess. The problem is laying the foundation in such a way as not to
proclude the kind of future development we might want (e.g. full
linear equation solver linked to parametric curves and transforms)
without falling into the paralyzing trap of hypergeneralization (which
I know all to well -- and I've got the scars to prove it ;)
I'll think a bit about whether it's possible to a very simple thing
here (a "Smallest Thing That Could Possibly Work" kind of thing) --
beyond what we already have, of course.
> Andr=E9
--=20
Magnus Lie Hetland "Canned Bread: The greatest thing since sliced
http://hetland.org bread!" [from a can in Spongebob Squarepants]

Hi,
On 27.07.04, Magnus Lie Hetland wrote:
> > > Equal(b, a + Point(10, 0))
> >
> > I just couldn't resist: A linear equation solver can be easily build
> > like this.
>
> I know -- I've thought about this myself in the past, and I've even
> found code that does this sort of thing. I don't remember exactly
> where, but it had a variable class and did all the linear equations
> behind the scenes.
>
> Also, numarray (and Numeric) has support for the equation solving
> part; I guess they could be optional mechanisms for speed, if this is
> indeed the way to go.
BTW: I didn't wrote a linear equation solver, but what I did was
exactly a "variable class" and the "linear equations behind the
scenes", i.e. building up linear terms. To actually solve the linear
equations I used Numeric in my sample code already.
> Certainly. It would also be neat to be able to use parametrized curves
> in the same system, as in MetaPost; but I guess it's not quite that
> critical, as you already have support for finding intersections
> in PyX.
Finding intersections etc. is usually a nonlinear operation. But you
can define points by that ... sure. I guess it will be similar to
MetaPost in that respect (though I'm not quite sure right now).
> > My grasp tells me, that a transformation contains another set of
> > variables (6 for a 2d->2d affine transformation). When multiplying
> > with a (lazy) point (two variables) the system becomes non-linear.
> > I'm kind of confused at the moment ... ;-)
>
> Hm. No, I don't think so... If you say that one point, transformed by
> an affine transform, equals another point, all you have is a linear
> set of two equations in two variables -- no?
Say p and p' are (2d) points, and t is a transformation (i.e. a 2x2
matrix + a 2d vector). Then you can write
p' = t(p)
Once you say p' and p are lazy vectors (i.e. they contain 2 variables)
as well as t is a lazy transformation (i.e. it contain 6 variables),
than the equation above is non-linear. There are non-linear terms in
matrix times vector when they both contain variables.
So I think what one has to do is to postpone this non-linear equation
until the non-linear terms are solved by other constraints. I guess,
thats what MetaPost does as well ... as soon as p or t gets defined
somewhere else, the equation above becomes linear.
> Anyway, writing a system like this based on linear equations is a bit
> more work than a one-way function-based one. I'll have a look but I
> can't promise anything.
Sure, nobody does. But we might have a starting point now ... and
maybe somebody is really interested in this. Its not top priority for
me, but well, on the other hand, I already like it ... we can get
something really funny quite quickly I guess.
André
--
by _ _ _ Dr. André Wobst
/ \ \ / ) wobsta@..., http://www.wobsta.de/
/ _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX
(_/ \_)_/\_/ visit http://pyx.sourceforge.net/

Andre Wobst <wobsta@...>:
>
> Hi,
>=20
> I'm moving my posting to pyx-devel ... I think its a much better place
> to post the following code. (Magnus, you're listing here, don't you? I
> think I remember your name from when I looked at the subscriber list
> quite some time ago.)
Sure. I didn't quite remember myself, which is why I used pyx-user...
But, yes, I'm on pyx-devel as well.
> On 27.07.04, Andre Wobst wrote:
> > Beside that my main concern is, whether its enough to stick on this
> > strict assignments. This is quite a limitation. We can't compete to
> > MetaPost by that (but we want, don't we?). I'm not sure whether its
> > possible to cook it all down to an Equality-function:
> >=20
> > Equal(b, a + Point(10, 0))
>=20
> I just couldn't resist: A linear equation solver can be easily build
> like this.
I know -- I've thought about this myself in the past, and I've even
found code that does this sort of thing. I don't remember exactly
where, but it had a variable class and did all the linear equations
behind the scenes.
Also, numarray (and Numeric) has support for the equation solving
part; I guess they could be optional mechanisms for speed, if this is
indeed the way to go.
> Find some code enclosed. You may consider to go along that line
> (just in case you want to spend some time on this issue).
I might. This is sort of an issue that crops up every now and then for
me (i.e. every time I want to create a figure, basically ;)
> I think, a 2d-point can be build on top of this already by combining
> two variables.
Certainly. It would also be neat to be able to use parametrized curves
in the same system, as in MetaPost; but I guess it's not quite that
critical, as you already have support for finding intersections
in PyX.
> The same for higher dimension points. I'm not totally sure whether a
> transformation can be easily integrated in that concept.
Well, if we use a general matrix-based version, it's all good.
(Haven't looked at your code -- it might work just as well.) That is,
an affine transform are basically just added terms to the linear
equations. And it would be *very* useful to allow them... E.g. "this
point rotated around that point by 60 degrees equals this point
rotated around that point by 30 degrees" and the like.
> My grasp tells me, that a transformation contains another set of
> variables (6 for a 2d->2d affine transformation). When multiplying
> with a (lazy) point (two variables) the system becomes non-linear.
> I'm kind of confused at the moment ... ;-)
Hm. No, I don't think so... If you say that one point, transformed by
an affine transform, equals another point, all you have is a linear
set of two equations in two variables -- no?
Not sure what you mean by "multiplying with a [...] point" here,
though. I may be missing something :]
Anyway, writing a system like this based on linear equations is a bit
more work than a one-way function-based one. I'll have a look but I
can't promise anything.
> Andr=E9
--=20
Magnus Lie Hetland "Canned Bread: The greatest thing since sliced
http://hetland.org bread!" [from a can in Spongebob Squarepants]

Hi,
I'm moving my posting to pyx-devel ... I think its a much better place
to post the following code. (Magnus, you're listing here, don't you? I
think I remember your name from when I looked at the subscriber list
quite some time ago.)
On 27.07.04, Andre Wobst wrote:
> Beside that my main concern is, whether its enough to stick on this
> strict assignments. This is quite a limitation. We can't compete to
> MetaPost by that (but we want, don't we?). I'm not sure whether its
> possible to cook it all down to an Equality-function:
>
> Equal(b, a + Point(10, 0))
I just couldn't resist: A linear equation solver can be easily build
like this. Find some code enclosed. You may consider to go along that
line (just in case you want to spend some time on this issue). I
think, a 2d-point can be build on top of this already by combining two
variables. The same for higher dimension points. I'm not totally sure
whether a transformation can be easily integrated in that concept. My
grasp tells me, that a transformation contains another set of
variables (6 for a 2d->2d affine transformation). When multiplying
with a (lazy) point (two variables) the system becomes non-linear. I'm
kind of confused at the moment ... ;-)
André
--
by _ _ _ Dr. André Wobst
/ \ \ / ) wobsta@..., http://www.wobsta.de/
/ _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX
(_/ \_)_/\_/ visit http://pyx.sourceforge.net/