Thread: [q-lang-users] Complex numbers
Brought to you by:
agraef
From: John C. <co...@cc...> - 2006-06-12 16:24:26
|
Is there some particular reason why complex numbers (in complex.q) are not a type Complex with a constructor comp_rect? This would be helpful for clean conversion from and to Scheme, and would allow the addition of polar complex numbers if someone needs them. The conversion of complex.q should be extremely straightforward. (Of course, it would be even cooler if 2i, which is currently the useless application of 2 to i, were syntax sugar for "comp_rect 0 2".) -- John Cowan co...@cc... "Not to know The Smiths is not to know K.X.U." --K.X.U. |
From: Albert G. <Dr....@t-...> - 2006-06-12 21:11:45
|
John Cowan wrote: > Is there some particular reason why complex numbers (in complex.q) > are not a type Complex with a constructor comp_rect? This > would be helpful for clean conversion from and to Scheme, and would > allow the addition of polar complex numbers if someone needs them. No other than that mathematicians since Euler himself actually think of them as points in the complex plane. So pairs of numbers seemed to be the most obvious representation to me, at the time (several years ago) I wrote this module. Incidentally, I've discussed this with Rob in private mail a few days ago, who reminded me of my own proposal on the ML to change this. I didn't get much feedback then, almost one year ago, so I eventually didn't bother to make the change. Maybe we can discuss this now and settle it once and for all. I agree that the algebraic type representation has its advantages. It would make complex numbers a real type which could be used as a guard and which could even become a subtype of Num as it should be. And it would also be consistent with Rob's rational number representation, if his "Q+Q" module becomes part of the standard library in the future. The only real problem I see is readability, compare (7,5) to something like "comp_rect 7 5". Rob's module has a similar issue (not that there's currently much that he can do about it), the rational number X/Y is represented as "rat X Y". Therefore I'm currently considering the idea to provide a hook into the expression pretty-printer which would make it possible to define a custom unparsing for certain types of objects. (Of course the defined unparsing would have to be reparseable so that it can reconstruct the original object. Like compiled lambdas unparse into a lambda expression in Q 7.1.) Then you could define, say, unparse (comp_rect X Y) = '(X+Y*i); and have 'i' defined as complex 0 1 in complex.q, so that X+Y*i would actually reconstruct comp_rect X Y. Note that the unparse hook, or whatever we'd call this function, would in fact return a quoted term and not a string to the pretty-printer. That makes sure that the unparsing is at least something that is reparseable, and gives the pretty-printer a chance to figure out the proper precedence level for the expression. Of course this goes a long way to handle such simple things like complex or rational numbers. But this feature could be useful for other data types, too, in particular external types. What do you all think about it? Is it "the right thing"? > (Of course, it would be even cooler if 2i, which is currently > the useless application of 2 to i, were syntax sugar for > "comp_rect 0 2".) Hmm, didn't we have enough of that syntax candy already with the last release? ;-) I'd prefer the X+Y*i notation I suggested above, that's almost as tidy and you don't need any special syntax to support that. Opinions? Other ideas? Cheers, Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: John C. <co...@cc...> - 2006-06-12 22:03:27
|
Albert Graef scripsit: > unparse (comp_rect X Y) = '(X+Y*i); And likewise "unparse (rat X Y) = '(X/Y)"? > Of course this goes a long way to handle such simple things like complex > or rational numbers. But this feature could be useful for other data > types, too, in particular external types. What do you all think about > it? Is it "the right thing"? Sounds excellent to me. Of course, there would be the risk that whatever the pretty-printer produced would not in fact parse and evaluate to the same thing, but that's the price you pay for sharp tools. -- A few times, I did some exuberant stomping about, John Cowan like a hippo auditioning for Riverdance, though co...@cc... I stopped when I thought I heard something at http://ccil.org/~cowan the far side of the room falling over in rhythm with my feet. -- Joseph Zitt |
From: Albert G. <Dr....@t-...> - 2006-06-12 22:22:45
|
John Cowan wrote: > And likewise "unparse (rat X Y) = '(X/Y)"? Nope that won't work, because X:Int/Y:Int is a builtin which returns a floating point value. (No way to change this any more, this would just break too much existing code.) But Rob has a rational division operator 'over' in his module, so unparsing to '(X over Y) would work in this case. > [unparsing stuff...] > Sounds excellent to me. Of course, there would be the risk that > whatever the pretty-printer produced would not in fact parse > and evaluate to the same thing, but that's the price you pay > for sharp tools. O.k., I think I'll give it a go. (Actually I wanted to start writing a Q yacc in Q this week, but that can wait some more...) -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Albert G. <Dr....@t-...> - 2006-06-13 09:54:12
|
John Cowan wrote: > Albert Graef scripsit: >>unparse (comp_rect X Y) = '(X+Y*i); > > Sounds excellent to me. Of course, there would be the risk that > whatever the pretty-printer produced would not in fact parse > and evaluate to the same thing, but that's the price you pay > for sharp tools. I've implemented this (in cvs now) and it works very nicely indeed. Here's a little example (Rob, you should find that interesting, too): type Rat = private const rat P Q; public (over) P Q @ (/); P:Int over Q:Int = rat (P div D) (Q div D) where D = gcd P Q; unparse (rat P Q) = '(P over Q); ==> 2 over 6 // the result is actually rat 1 3, which is unparsed as: 1 over 3 Note how the `over' operator behaves like a constructor (with equations) for Rat objects. The real constructor `rat' is never seen outside the module where the type is defined and can thus be hidden by making it private. Neat, isn't it? :) The application to complex.q and rational.q is obvious. But I'm also considering equipping the standard container types with unparsing rules, e.g., the result of dict [(1,2),(3,4)] would then actually be printed as dict [(1,2),(3,4)]. Any objections? Also, I'm about to turn complex numbers into an algebraic type now. If anyone wants to complain, now is your last opportunity. ;-) Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: John C. <co...@cc...> - 2006-06-13 15:08:47
|
Albert Graef scripsit: > Also, I'm about to turn complex numbers into an algebraic type now. If > anyone wants to complain, now is your last opportunity. ;-) Excellent. Just let me know when you've settled on the constructors for rationals and rectangular complex numbers, and I'll make sure they get into my Chicken egg appropriately. I've asked Felix for help on the bignum issue. -- Kill Gorgun! Kill orc-folk! John Cowan No other words please Wild Men. co...@cc... Drive away bad air and darkness http://www.ccil.org/~cowan with bright iron! --Ghan-buri-Ghan http://www.ccil.org/~cowan |
From: Albert G. <Dr....@t-...> - 2006-06-16 09:58:15
|
John Cowan wrote: > I suppose we could bit-by-bit replace the entire Q-in-C system with > a Q-in-Scheme system, compiler, interpreter, user program and all. Yes, that should be possible, but I'm probably not the one who is going to do it. ;-) I'd rather like to bootstrap Q in Q once we have native compilation. > It wouldn't need its own garbage collector any more I'm not sure about that. At least it would still need to do reference counting, since I want objects encapsulating state to be cleaned up (closing files, etc.) as soon as possible. This makes handling temporary stateful objects much more convenient. > Chicken doesn't do native threads, though, only call/cc based ones. I guess that this would be a showstopper for me, with all the realtime multimedia applications I'm doing. -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Albert G. <Dr....@t-...> - 2006-06-14 05:13:59
|
John Cowan wrote: > Excellent. Just let me know when you've settled on the constructors > for rationals and rectangular complex numbers, and I'll make sure they > get into my Chicken egg appropriately. I've asked Felix for help on > the bignum issue. Rationals aren't in the stdlib yet, but as I said elsewhere in this thread I might soon add a stripped-down version of Rob's module to fix that. The Complex constructor will probably be named just 'complex', but I'll let you know when I know for sure. Cheers, Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Albert G. <Dr....@t-...> - 2006-06-15 00:34:22
|
Hi John, > The Complex constructor will probably be named just 'complex', but I'll > let you know when I know for sure. Yes, it's called complex, and it's in cvs now. I still have to do a little massage to Q's system of number types to make it easier to integrate stuff like rational.q, but you should already be able to test your Chicken egg with the version that is in cvs now. Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Albert G. <Dr....@t-...> - 2006-06-16 18:10:08
|
John Cowan wrote: > Well, there's a few approaches to that. I presume a classical compiler > to assembly language chain is out. Then there's compilation to C, > or compilation to something else (Chicken, e.g.) that compiles to C. > Then there is compilation to some standard byte code set with JIT > compilation in place, like the JVM or the CLR. Any or all of these > would probably make q-in-Q feasible. Yes, we already discussed this here a while ago... Translating Q's bytecode would be the most straightforward approach. JIT compilation looks nice, but I'm also considering compilation to C or maybe Ksi (the latter approach would be tied to gcc, however). This is still a while away, though (I first want to resolve all the remaining portability issues and finish the Q companion book). > Reference counting has its own costs, though, specifically the cost of > recursive freeing, which is unpredictable in scope. Yes, that's true. But in my experience it seems to work well enough for soft-realtime apps even on slower PCs (especially the computer music applications wouldn't work at all if this wasn't the case; there you can hear timing glitches quite easily). Doing low-latency audio stuff is more demanding, of course, but there are other, more specialized audio engines like SuperCollider for that, which can be controlled from Q via OSC. > Q has a global interpreter lock, right? So all threads except one have > to be running C code, but which one it is can change. Yes, that's right. That global mutex will probably have to go before we all run computers with 1024 cpus. ;-) But right now this implementation seems to work pretty well and also makes the job for the external module writer much simpler. > The whole idea probably isn't practical. :( -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: John C. <co...@cc...> - 2006-06-16 20:23:29
|
Albert Graef scripsit: > Yes, we already discussed this here a while ago... Translating Q's > bytecode would be the most straightforward approach. JIT compilation > looks nice, but I'm also considering compilation to C or maybe Ksi (the > latter approach would be tied to gcc, however). This is still a while > away, though (I first want to resolve all the remaining portability > issues and finish the Q companion book). Sure. There's a neat trick for fairly portable JIT compilation: you wrap a block of code using labels, and then use memcpy to copy everything from one label to the next into a buffer area. Do that for each bytecode and you've built up your compiled code in the buffer. That way you have no dependency on assembly language. See http://lemick.sourceforge.net/papers/JIT_design.pdf for a brief paper. -- Possession is said to be nine points of the law, John Cowan but that's not saying how many points the law might have. co...@cc... --Thomas A. Cowan (law professor and my father) |
From: Albert G. <Dr....@t-...> - 2006-06-16 21:18:50
|
John Cowan wrote: > See http://lemick.sourceforge.net/papers/JIT_design.pdf for a brief paper. Looks interesting, thanks for the link! -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: John C. <co...@cc...> - 2006-06-16 13:50:28
|
Albert Graef scripsit: > Yes, that should be possible, but I'm probably not the one who is going > to do it. ;-) I'd rather like to bootstrap Q in Q once we have native > compilation. Well, there's a few approaches to that. I presume a classical compiler to assembly language chain is out. Then there's compilation to C, or compilation to something else (Chicken, e.g.) that compiles to C. Then there is compilation to some standard byte code set with JIT compilation in place, like the JVM or the CLR. Any or all of these would probably make q-in-Q feasible. > I'm not sure about that. At least it would still need to do reference > counting, since I want objects encapsulating state to be cleaned up > (closing files, etc.) as soon as possible. This makes handling temporary > stateful objects much more convenient. Reference counting has its own costs, though, specifically the cost of recursive freeing, which is unpredictable in scope. Chicken has two garbage collectors, both stop-and-copy. The first one is intertwined with how Chicken uses the stack. All Scheme objects are initially allocated on the C stack, and each Scheme procedure is compiled into a C procedure which explicitly calls its continuation. However, the continuation never returns. Instead, when the stack gets full, all live Scheme objects are copied to the heap and a longjmp() is done to reset the stack. A regular Baker two-space collector is periodically run on the two halves of the heap. Any object can have a finalizer, which is run when the object becomes garbage. > > Chicken doesn't do native threads, though, only call/cc based ones. > > I guess that this would be a showstopper for me, with all the realtime > multimedia applications I'm doing. Q has a global interpreter lock, right? So all threads except one have to be running C code, but which one it is can change. I'm not sure whether Chicken can safely spawn OS threads, but if so, they can't be running Chicken code, at least not easily, given the mechanism explained above. The whole idea probably isn't practical. -- John Cowan <co...@cc...> Yakka foob mog. Grug pubbawup zink wattoom gazork. Chumble spuzz. -- Calvin, giving Newton's First Law "in his own words" |
From: Albert G. <Dr....@t-...> - 2006-06-15 21:40:52
|
John Cowan wrote: > So we could, although I think the current C-based version does well > enough. Yes it does, but it's a PITA having to maintain all that C code. ;-) Because of this, I consider turning the Q machine (i.e., the interpreter minus the user interface) into a Q module and then reimplement the user interface in Q itself. > My main ambition is not to compete with the Q interpreter, > but to make the power of generalized term rewriting readily available > to Scheme programmers. For that they need an appropriately Schemish > and S-expression-based interface. Yes, I understand. That's a good idea, it might even lure some Schemers into trying Q itself. ;-) > Thanks, fixed. On most systems you should only need to say -lqint anyhow. Right. > I spotted another typo in the Q manual in at least two places: "quadrupel" > should be "quadruple". Another one of those annoying "German English" things. ;-) Thanks, it's fixed now. -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Albert G. <Dr....@t-...> - 2006-06-13 10:07:06
|
Errata: > type Rat = private const rat P Q; Of course that should read: public type Rat = private const rat P Q; > The application to complex.q and rational.q is obvious. But I'm also > considering equipping the standard container types with unparsing rules, > e.g., the result of dict [(1,2),(3,4)] would then actually be printed as > dict [(1,2),(3,4)]. Any objections? That is, "dict [(1,2),(3,4)]" would be printed instead of "dict::bin 2 (1,2) dict::nil (dict::bin 1 (3,4) dict::nil dict::nil)". Besides being prettier this also serves the purpose of information hiding. Of course this kind of custom pretty-printing must be disabled in the debugger. -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Albert G. <Dr....@t-...> - 2006-06-15 11:12:54
|
John Cowan wrote: > But hey, it does work! Any Chicken user who wants to play around with > this version can grab it (with a temporary README and a few examples) > from http://www.ccil.org/~cowan/q-lolevel.zip . License is GPL, bug > reports invited, guarantees not provided. Cool. :) So all that we need now is an additional API to change the internal state of the Q interpreter. Then we could rewrite the toplevel loop of the Q interpreter in Scheme. BTW, there's a typo in the compile line in your README: s/-lgpm/-lgmp/. (At least I think so, I haven't actually tried it yet, need to install Chicken first.) Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: John C. <co...@cc...> - 2006-06-16 05:02:09
|
Albert Graef scripsit: > Yes, I understand. That's a good idea, it might even lure some Schemers > into trying Q itself. ;-) Well, they'll have to if they want to write rules, since there is no S-expression version of rules. I suppose we could bit-by-bit replace the entire Q-in-C system with a Q-in-Scheme system, compiler, interpreter, user program and all. It wouldn't need its own garbage collector any more, and would probably be much easier to maintain -- provided the maintainer knows Scheme. I'd think the performance would be about as good. Chicken doesn't do native threads, though, only call/cc based ones. -- Time alone is real John Cowan <co...@cc...> the rest imaginary like a quaternion --phma http://www.ccil.org/~cowan |
From: Rob H. <hub...@gm...> - 2006-06-13 10:53:57
|
On 13/06/06, Albert Graef <Dr....@t-...> wrote: > type Rat = private const rat P Q; > public (over) P Q @ (/); > P:Int over Q:Int = rat (P div D) (Q div D) where D = gcd P Q; > unparse (rat P Q) = '(P over Q); Should that go into rational.q, or is there a separate location for the 'unparse' stuff? > Also, I'm about to turn complex numbers into an algebraic type now. If > anyone wants to complain, now is your last opportunity. ;-) In the Rational code, the construction function rational takes a pair. For 'deconstruction', I provide numerator denominator and in the update I'm preparing, there's now a num_den function returning the numerator and denominator in a pair. It would be good if the Complex type could follow similarly, and for both rectangular and polar coordinates as has been suggested. So: public type Complex = private const cplx_rect X Y; //rectangular complex_rect (X, Y) = cplx_rect X Y; //takes a pair //polar complex_pol (R, Theta) = cplx_rect (R * cos Theta) (R * sin Theta); re (cplx_rect X Y) = X; im (cplx_rect X Y) = Y; rect (cplx_rect X Y) = (X, Y); //returns a pair abs (cplx_rect X Y) = sqrt (X^2 + Y^2); arg (cplx_rect X Y) = atan2 Y X; pol (cplx_rect X Y) = (abs (cplx_rect X Y), arg (cplx_rect X Y)); This means that, as rational and num_den are inverses; so are complex_rect and rect, and complex_pol and pol. I'm not sure about the naming, but I'd like to keep Rational as consistent as possible with Complex. I'm happy to consider renaming my symbols if desired (e.g. 'Rat' rather than 'Rational', 'num' rather than 'numerator', swapping 'rational' with 'rat' to have the shorter symbol public, num_den as something else,...). Rob. |
From: Albert G. <Dr....@t-...> - 2006-06-13 22:39:32
|
Rob Hubbard wrote: >>unparse (rat P Q) = '(P over Q); > > Should that go into rational.q, or is there a separate location for > the 'unparse' stuff? Yes, into rational.q. (In fact it doesn't really matter where that equation is, but keeping it with the data type is the most obvious choice.) But note that the little example I gave was just to illustrate the new custom unparsing scheme. You might consider to do it similarly in rational.q when Q 7.2 comes out, though. > It would be good if the Complex type could follow similarly, and for > both rectangular and polar coordinates as has been suggested. Well, my immediate goal is to just change the concrete representation to an algebraic type and keep the rest of the interface as it is now, to provide as much backward compatibility as possible. Then I'll probably do a release candidate so that we can discuss what else needs to be added. > 'Rat' rather than 'Rational' No no, Rational is fine. :) Cheers, Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: John C. <co...@cc...> - 2006-06-15 03:36:56
|
Albert Graef scripsit: > Yes, it's called complex, and it's in cvs now. I still have to do a > little massage to Q's system of number types to make it easier to > integrate stuff like rational.q, but you should already be able to test > your Chicken egg with the version that is in cvs now. No hurry. I'm not going to even start with the support for the full numeric tower until the basic S-expression support (which thinks all numbers outside the 2^30 Chicken fixnum range are flonums) is written. And I haven't even started on that yet -- I just finished the Chicken-to-C glue code. So as of now you can do the same things with Q from Scheme, using mostly the same routines, that a C programmer can. That is, you can load a script, define global variables, and evaluate expressions using either Scheme strings or c-pointer objects. You have to play by the same rules as the C programmer as far as allocating and freeing qexprs, too. But hey, it does work! Any Chicken user who wants to play around with this version can grab it (with a temporary README and a few examples) from http://www.ccil.org/~cowan/q-lolevel.zip . License is GPL, bug reports invited, guarantees not provided. -- You know, you haven't stopped talking John Cowan since I came here. You must have been http://www.ccil.org/~cowan vaccinated with a phonograph needle. co...@cc... --Rufus T. Firefly |
From: John C. <co...@cc...> - 2006-06-15 14:41:31
|
Albert Graef scripsit: > Cool. :) So all that we need now is an additional API to change the > internal state of the Q interpreter. Then we could rewrite the toplevel > loop of the Q interpreter in Scheme. So we could, although I think the current C-based version does well enough. My main ambition is not to compete with the Q interpreter, but to make the power of generalized term rewriting readily available to Scheme programmers. For that they need an appropriately Schemish and S-expression-based interface. The next release will provide those things, but will not have numeric tower support or list memoization; when you pass a list to Q and Q returns a syntactically equal list, it will be only semantically equal (equal and not eq) on the Scheme side. > BTW, there's a typo in the compile line in your README: s/-lgpm/-lgmp/. > (At least I think so, I haven't actually tried it yet, need to install > Chicken first.) Thanks, fixed. On most systems you should only need to say -lqint anyhow. I spotted another typo in the Q manual in at least two places: "quadrupel" should be "quadruple". -- Mark Twain on Cecil Rhodes: John Cowan I admire him, I freely admit it, http://www.ccil.org/~cowan and when his time comes I shall co...@cc... buy a piece of the rope for a keepsake. |