robpike / ivy Goto Github PK
View Code? Open in Web Editor NEWivy, an APL-like calculator
License: Other
ivy, an APL-like calculator
License: Other
I'm trying to simply run, go get github.com/robpike/ivy
, but get the following errors:
# robpike.io/ivy/value
value/asin.go:22: undefined: big.Float
value/asin.go:44: undefined: big.Float
value/asin.go:53: undefined: big.Float
value/asin.go:136: undefined: big.Float
value/bigfloat.go:14: undefined: big.Float
When checking https://golang.org/pkg/math/big/ I don't see anything documented for big.Float
Been staring at this for a while and I think it must be a bug.
A surprising one, but either that or I've missed something quite fundamental.
(2 2 rho 2 3 5 7) / (11 13)
11/2 13/3
11/5 13/7
a=4 4 rho iota 16
b=4 2 rho iota 8
a+.*b
shape mismatch for inner product (4 4) times (4 2)
should be:
50 60
114 140
178 220
242 300
The demo.ivy
is a good starting point. But everyone has to know where it is. A new interactive command like ) demo
bundle it with the binary like the mobile version.
Consider:
% ivy
x = 3 4 rho iota 9
x[2][2 3]
6 7
This works fine today. But now consider
x[2 3]
5 6 7 8
9 1 2 3
x[2 3][1 4]
index 4 out of range (shape (2 4))
y=x[2 3]; y[1 4]
index 4 out of range (shape (2 4))
Here, x[2 3][1 4] is being treated the same as the y= version,
just without the y.
There is no way in Ivy today to extract a sub-shape of a matrix,
what in APL would be x[2 3; 1 4].
I started working on making vector-indexed assignment work,
so that things like APL's (from tryapl.org):
x ← 3 4 ⍴⍳9
x[2 3; 1 4] ← 0
x
1 2 3 4
0 6 7 0
0 1 2 0
or
x ← 3 4 ⍴⍳12
x[2 3; 1 4] ← -2 2 ⍴⍳4
x
1 2 3 4
¯1 6 7 ¯2
¯3 10 11 ¯4
would work. Those both work (in Ivy syntax) in my copy now,
but then I noticed that x[2 3][1 4] = x[2 3][1 4] does not,
because x[2 3][1 4] didn't mean what I expected it to.
I see three possible options:
Do nothing, just don't provide that functionality.
Switch to ; for separating indexes, like APL.
Define that x[i][j][k] is processing 3 dimensions of x, not ((x[i])[j])[k]. Perhaps code that wants the latter interpretation can add parens.
Perhaps there are more.
Reproducer:
) demo
# This is a demo of ivy. Type a newline to advance to each new step. Type one now.
? 2 ** 32
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x49ace4]
goroutine 6 [running]:
robpike.io/ivy/parse.(*Parser).runFromReader.func1()
/home/aurelien/godev/pkg/mod/robpike.io/[email protected]/parse/special.go:358 +0x1ea
panic({0x4fa4e0, 0x5fbc80})
/home/aurelien/sdk/go1.17.4/src/runtime/panic.go:1038 +0x215
robpike.io/ivy/parse.(*Parser).runUntilError.func1()
/home/aurelien/godev/pkg/mod/robpike.io/[email protected]/parse/special.go:384 +0x1ea
panic({0x4fa4e0, 0x5fbc80})
/home/aurelien/sdk/go1.17.4/src/runtime/panic.go:1038 +0x215
math/big.(*Int).Add(0xc0000a8080, 0x49a93f, 0x0)
/home/aurelien/sdk/go1.17.4/src/math/big/int.go:118 +0x24
robpike.io/ivy/value.bigIntRand({0x544d70, 0xc0000753e0}, 0xffffffffffffffff, 0x4108b7)
/home/aurelien/godev/pkg/mod/robpike.io/[email protected]/value/unary.go:56 +0x77
robpike.io/ivy/value.unaryBigIntOp({0x544d70, 0xc0000753e0}, 0x5201e0, {0x544eb0, 0xc0000a8060})
/home/aurelien/godev/pkg/mod/robpike.io/[email protected]/value/unary.go:21 +0x8d
robpike.io/ivy/value.init.1.func2({0x544d70, 0xc0000753e0}, {0x544eb0, 0xc0000a8060})
/home/aurelien/godev/pkg/mod/robpike.io/[email protected]/value/unary.go:137 +0x71
robpike.io/ivy/value.(*unaryOp).EvalUnary(0xc00007e050, {0x544d70, 0xc0000753e0}, {0x544eb0, 0xc0000a8060})
/home/aurelien/godev/pkg/mod/robpike.io/[email protected]/value/eval.go:52 +0x119
robpike.io/ivy/exec.(*Context).EvalUnary(0xc0000753e0, {0xc0000a2020, 0x1}, {0x544eb0, 0xc0000a8060})
/home/aurelien/godev/pkg/mod/robpike.io/[email protected]/exec/context.go:138 +0x128
robpike.io/ivy/parse.(*unary).Eval(0xc0000a8000, {0x544d70, 0xc0000753e0})
/home/aurelien/godev/pkg/mod/robpike.io/[email protected]/parse/parse.go:193 +0x62
robpike.io/ivy/parse.(*Parser).runUntilError(0xc00009c000, {0x50f24a, 0x0})
/home/aurelien/godev/pkg/mod/robpike.io/[email protected]/parse/special.go:389 +0x113
robpike.io/ivy/parse.(*Parser).runFromReader(0xc000154d80, {0x544d70, 0xc0000753e0}, {0x50f24a, 0x0}, {0x541ca0, 0xc000176d80}, 0x0)
/home/aurelien/godev/pkg/mod/robpike.io/[email protected]/parse/special.go:362 +0x3ca
created by robpike.io/ivy/parse.DemoRunner
/home/aurelien/godev/pkg/mod/robpike.io/[email protected]/parse/special.go:434 +0x1ef
The fix is easy but since this is relatively minor (only happens during demo) and since there are different ways to fix it I'm not opening a PR, preferring to check if you're interested to fix this, and how.
When the demo starts, DemoRunner
is provided a zero-valued default config.Config
. Config.bigOrigin
is a nil big.Int
. Also, Config.BigOrigin()
doesn't call Config.init()
. Finally, Config.init
doesn't initialize Config.bigOrigin
.
One possible fix:
diff --git a/config/config.go b/config/config.go
index 74b2a8e..4c210d3 100644
--- a/config/config.go
+++ b/config/config.go
@@ -57,6 +57,7 @@ func (c *Config) init() {
c.output = os.Stdout
c.errOutput = os.Stderr
c.origin = 1
+ c.bigOrigin = big.NewInt(1)
c.seed = time.Now().UnixNano()
c.source = rand.NewSource(c.seed)
c.random = rand.New(c.source)
@@ -171,6 +172,7 @@ func (c *Config) Origin() int {
// BigOrigin returns the index origin as a *big.Int.
func (c *Config) BigOrigin() *big.Int {
+ c.init()
return c.bigOrigin
}
Here: https://github.com/robpike/ivy/blob/master/demo/demo.ivy#L158
The input base is 64, so 2**63
is actually 99(base 10) bit, and 2**64
is 100(base 10) bit.
Just play around with number base, but found the following is irreversible:
)base 36
)base 10
expected [Identifier Op], got Number: "base"
which it treats the base as a number 527198 (base 10), instead of the command "base".
2*2+2 =8 , why?
Given that "binary operators apply to the operand immediately to the left and to everything to the right", I was expecting:
(2/3**2) == (2/(3**2)) , and not (2/3**2) == ((2/3)**2)) as it seems to be.
And fwiw, if one replaces division with multiplication, one gets as expected:
(2*3**2) == (2*(3**2)) ( and not (2*3**2) == ((2*3)**2) )
I did not find any explanation for this behavior either on https://godoc.org/robpike.io/ivy or on https://en.wikipedia.org/wiki/APL_syntax_and_symbols.
What am I missing please?
Note that this is on the dev branch. Example:
rho 4
nothing returned.
rho 4 5
2
Shouldn't rho 4
to return 1 ?
Go 1.11 has added an experimental port to WebAssembly. ivy already supports iOS and Android via x/mobile, and if I'm not mistaken, the code for that lives in this repository.
@robpike, are you interested in a WebAssembly port being added to this repository?
I've ported ivy to run in a browser via GopherJS in the past, see demo at https://dmitri.shuralyov.com/projects/ivy/. It was very easy to port it to use syscall/js
package and compile with GOOS=js GOARCH=wasm
. See shurcooL/ivybrowser@44e3bb3.
If you choose to accept it, I'm happy to help maintain it. If you'd rather not, that's fine, I will just keep it in my fork then.
op pal x = t = '%d' text x; (rot t) *.== t
Is an attempt at palindrome, and it almost works:
pal 1001
1
pal 98
0
I expected:
pal 1001 1002
1 0
But I get
pal 1001 1002
0
The text operator seems to include spaces, so maybe that's part of the problem?
rho "%d" text 1 2 3
5
Since Ivy is a calculator, I would expect it honours conventional precedence of arithmetic operators. However, 2-1-1
gives 2
(instead of 0), and 2-1+1
gives 0
(instead of 2).
OS: iOS 9.1
If a file named abc.ivy
contains a = 0xff
(and nothing else), then the output of )get "abc.ivy"
is "error: bad number syntax: 0x"
.
Given
scalar binop vector
the scalar will be promoted to vector; that's how ivy works. But it's not how APL works. It usually doesn't matter, but for binary iota (index) it does. 3 iota 3 should yield 3, not 1. It yields 1 because the LHS becomes 3 3 3.
This is on the android app.
> ) base 16
> abc + def
error: unexpected Def: "def"
> ) base 30
> abc + log
error: unexpecred Newline: "\n"
> abc + lpg
126s
> abc + defd
doqp
@robpike we're using this function in our code now and I was wondering if it was any reason for not having it in the math/big package. Cheers :)
For interactive use, it would be nice if the last result could be accessed somehow (e.g. via a variable "ans" or similar that is set in the repl).
Unless I am missing something, the termination condition for the loop seems incorrect:
if l.delta.Cmp(l.prevDelta) == 0
https://github.com/robpike/ivy/blob/master/value/loop.go#L53
delta
is the delta between the current estimate and the previous estimate
prevDelta
is the delta between the previous estimate and the one before that
Why should we stop if the deltas are the same? If my iterative method happens to generate estimates 0, 10, 20, 30, 33, 35, 34
, why should we stop at 30?
Seems like the correct thing to check is if delta
is 0? I am guessing that this is how this condition ends up working in practice (it detects a string of 0s), as it's highly unlikely to actually see a string of equal but non-zero deltas.
Perhaps I'm reading the docs wrong, but ceil
is supposed to result in an integer; should this work?
n = 1 drop iota 10
(ceil (rho n)/2)
5
5 take n
2 3 4 5 6
(ceil (rho n)/2) take n
bad count for take
After updating my iPhone 5s to the latest iOS release (10.0.1, 14A403), Ivy briefly comes up but then disappears (crashes?) and I'm back at the home screen.
Double-clicking the Home button to look at all running apps shows Ivy running, with its last result showing, but clicking on it results in the same crash again.
Probably not Ivy- but x/mobile specific.
Here is a way to test order of evaluation.
# Order of evaluation
tags = 0 rho 0
op f tag = tags; tags = tags, tag; 1
op order x = x = tags; tags = 0 rho 0; x
m = 1 1 rho 0
order (f 1) + (f 2)
order m[f 1; f 2]
order +/ (f 1) (f 2)
2 1
2 1
1 2
It shows that in (f 1) + (f 2)
, (f 2)
runs first.
And in m[f 1; f 2]
, f 2
runs first, which I preserved from the binary case.
But it also shows that in +/ (f 1) (f 2)
and g (f 1) (f 2)
, (f 1)
runs first.
This confused me because to use a complex
expression multiple times,
the idiom from APL is something like
m+m=complex thing
but it turns out to use it multiple times in a vector its the other way around:
+/ (m=complex thing) m
Is this correct?
Is it documented somewhere?
What does the variable name x mean in a function (user-defined operator) body?
Right now the rule in function f is:
(1) if x is the name of one of f's argument, then x refers to that local variable.
(2) if x is the name of a global, then x refers to that global.
(3) otherwise, x refers to a local variable (possibly not yet created).
I sent PR #81 to fix a bug where Ivy wasn't respecting these rules (a definite bug fix),
but I wonder whether the rules should be changed anyway.
Consider some function that uses (intended as local) variables in its implementation,
like this not-very-good Fibonacci function:
% ivy
op fib n =
n <= 1: n
a = fib n-1
b = fib n-2
a + b
fib 10
55
Now suppose that is loaded from a library and we don't know how its implemented.
And we are playing around:
% ivy -f fib.ivy
fib 10
55
a
undefined variable "a"
a = 1
fib 10
5
Creating the global named 'a' changes the meaning of the fib function:
what used to be a local variable becomes a global variable.
This means that functions using local variables aren't robust
against the creation of globals that happen to use the same names.
I think probably something should be done about that.
Python has this problem too, and solves it by saying that the first
use of a variable in the function determines what it is.
If the first use is a write, then the name refers to a new local.
If the first use is a read, then the name can refer to a global.
If you want to override this, the special statement 'global x'
forces the name to refer to a global.
Use is not strictly in writing-code order: x = x+1 reads x before writing it,
so it refers to a global if one exists.
Ivy could adopt this rule too, but in Python, I have a hard time remembering it,
and while it usually does the right thing, it's mysterious when it doesn't.
Definitely one option though.
Another option would be to require saying 'global x' any time you
want to refer to a global. That would be easier to remember at least.
but maybe a bit too annoying.
A third option would be, in a nod to Go's export rule, to say that
inside functions, capitalized variables are always globals,
while lowercase variables are always locals.
This would make it very clear at all mentions of a variable
whether it was private to the function or not.
You could still manipulate lowercase globals in the interactive loop,
but those would not be available to functions.
Lowercase globals would essentially be the local variables for the interactive loop.
Not sure what the right answer is here.
In #97 there's a way to return infinity but there may be other ways. Is it expected to have an infinity float within Ivy? Many operations work correctly, with some exceptions:
1 log 2
+Inf
# Exits with stack trace
(1 log 2) - 1 log 2
panic: subtraction of infinities with equal signs
# Hangs, same for 'cos' and 'tan'
sin 1 log 2
There are similar issues with +
, /
, unary **
, cos
, and tan
. Some others such as log
and binary **
don't converge.
Hi,
The monadic 'flip' and 'rot' operators destructrively modify their input. That doesn't look right to me. The matrix product +.* only works for square matrices.
See attached session transcript:
term% ivy
a=(2,3) rho iota 6
b=(2,2) rho 4 -1 -1 4
c=(2,2) rho iota 4
a
1 2 3
4 5 6
b
4 -1
-1 4
c
1 2
3 4
flip a
4 5 6
1 2 3
a
4 5 6
1 2 3
flip a
1 2 3
4 5 6
a
1 2 3
4 5 6
a +.* b
shape mismatch for inner product (2 3) times (2 2)
b +.* c
1 4
11 14
b
4 -1
-1 4
c
1 2
3 4
go get github.com/robpike/ivy
generates the error:
package github.com/robpike/ivy: code in directory /home/gcloud/work/src/github.com /robpike/ivy expects import "robpike.io/ivy"
I'm wondering if this could be fixed by replacing all robpike.io/ivy/
with github.com/robpike/ivy/
.
The weird thing is that go get robpike.io/ivy
works, but somehow the import renders github.com/robpike/ivy
un-go-get-able.
when i get the source code by the command: go get github.com/robpike/ivy
it print: can't load package: package github.com/robpike/ivy: code in directory D:\gocode\src\github.com\robpike\ivy expects import "robpike.io/ivy"
could you get me a hand!
Base 1 logs give results that I didn't expect. I compared with a couple of other sources:
Ivy tryapl.org WolframAlpha
----------- ------------- ----------------
1 log .5 | -Inf DOMAIN ERROR complex infinity
|
1 log 1 | 0 1 (undefined)
|
1 log 2 | +Inf DOMAIN ERROR complex infinity
Perhaps all three should report a base error?
In contrast to all other commands, residue argument order is inconsistent with APL syntax.
I would like to propose to swap them, as it would then simplify porting of APL algorithms to Ivy.
Documentation excerpt:
Residue A∣B B modulo A
mod A modulo B (Euclidean)
imod A modulo B (Go)
Should read:
Residue A∣B B modulo A
mod B modulo A (Euclidean)
imod B modulo A (Go)
"%x" text 1
does not produce what you'd expect. The problem is that formatOne assumes that the argument should be converted to float first. Instead, it should find the verb in the format and convert the argument according to that verb. For instance "%d" should be print 1/2 as 1/2, not %!d(*big.Float=0.5).
The special command ) help print the special command list, but the mobile version prints the hole godoc content. I find it very useful to see the supported APL operators inside ivy.
Although there is an explanation of the indexing operation in demo.ivy, the doc only hints at it in the 'largest' op example.
It might be that I'm missing something, but mapping an operator over a vector is super useful and I had to jump through some hoops to do it.
Given a vector, compute the triangle number for each element (but any function would apply). I ended up defining the function as an operator that ignored one of its args. then reducing over a vector of pairs...
op a triangle b = (b * (b + 1)) / 2
op trianglevs vs = triangle/ (rho vs) 2 rho ((rho vs) rho 2) sel vs
Is a map / each syntactically difficult to support though, feels like it should be similar to reduce?
There is no import comment in the generated code.
Also the DO NOT EDIT should be brought up to current standard.
In binaryMatrixOp (eval.go):
Line 394 in 485a172
I believe this comment is backwards and this is actually the Matrix op Vector
case. (Both this case and the previous one are labeled Vector op Matrix
)
Just thought you might want to see this:
op recursive x =
ivy (((x==0) sel '__=0'),((x==1) sel '__=1'),((x>=2) sel ('__=(recursive ',(text x-2),')+(recursive ',(text x-1),')')))
__
It uses the 'ivy' operator to run a command that sets '__' to a different type of value based on one of three conditions:
__=0
__=1
__=(recursive x-2)+(recursive x-1)
(x-2
and x-1
are calculated first, then converted to text format and added to the string)If you haven't noticed yet, this is a recursive implementation of the Fibonacci Sequence.
The command selector utilizes two properties I noticed in the 'sel' operator:
The only other thing you need to do is concatenate the strings and execute the result. The output value gets stored in __, which can then easily be returned by the function.
Values close to -1 are correct, but **-1
returns zero:
**-.9999999999999
0.367879441171
**-1
0
**-1.000000000001
0.367879441171
The 'ivy' operation returns the result of a given expression. This is usually perfectly fine, but becomes an issue when given an empty vector:
ivy ''
The above code returns nil
. Not a vector, or a number. Just nil
. This isn't enough to crash ivy on its own. However, when attempting to use it in an operation... things break. Attempting to run ivy ivy ''
will cause Go to fully crash, returning the following error:
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x503e46]
goroutine 1 [running]:
robpike.io/ivy/run.Run.func1(0x623ba0, 0xc4200ee000, 0xc42004fd01, 0x57a340, 0xc42009a008, 0xc42004fe48)
/home/joey/go/src/robpike.io/ivy/run/run.go:58 +0x270
panic(0x532aa0, 0x61b3c0)
/usr/lib/go-1.10/src/runtime/panic.go:502 +0x229
robpike.io/ivy/parse.(*unary).Eval(0xc42008c2a0, 0x57ae20, 0xc42009f0e0, 0x499607, 0xc4200f4000)
/home/joey/go/src/robpike.io/ivy/parse/parse.go:186 +0x56
robpike.io/ivy/exec.(*Context).Eval(0xc42009f0e0, 0xc420082370, 0x1, 0x1, 0x1, 0x0, 0x0)
/home/joey/go/src/robpike.io/ivy/exec/context.go:116 +0xa1
robpike.io/ivy/run.Run(0xc4200ee000, 0x57ae20, 0xc42009f0e0, 0xc4200ea001, 0x1000)
/home/joey/go/src/robpike.io/ivy/run/run.go:69 +0x1a1
main.main()
/home/joey/go/src/robpike.io/ivy/ivy.go:101 +0x584
As far as I can tell, the same problem occurs when ivy ''
is passed as an argument to any operation.
Possible fixes:
ivy x
return the text result of an expression
val x
(or value x
) operation that returns the actual value of an expressionivy x
nil
values to empty vectorsOS: Android.
Input: +\1,2,3
.
Result: the output is correct (1 3 6
), but the prompt looks like
> +,2,3
1 3 6
instead of
> +\1,2,3
1 3 6
It looks like something somewhere escapes the backslash and the character after it.
EDIT: Now that I think of it, shouldn't this issue really be an issue on the github.com/golang/go tracker?
I want to use Sourcegraph for ivy code search, browsing, and usage examples. Can an admin enable Sourcegraph for this repository? Just go to https://sourcegraph.com/github.com/robpike/ivy. (It should only take 30 seconds.)
Thank you!
Not sure whats going on, but when I try this on Android:
((sqrt(5)+1.0)/2.0)==((1.0+sqrt(5))/2.0)
0(1.0+sqrt(5))/2.0
1.61803398875((sqrt(5)+1.0)/2.0)
1.22474487139
It seems that if the left side is a constant, then it works, but if the left side is a function then it doesn't. Am I using it wrong?
% ivy
e**pi
23.1406926328
e
0.367879441171
pi
0.14159265359
It says "Prototype apps for iPhone, iPad, and Android"
Okay but I am working with the types of numbers that will fill up a 19 inch screen multiplied by hundreds of screens....
Are there any demos for: Mac laptops, Windows.
The big numbers will have trouble fitting on a cell phone screen. iPad indeed comes close since it is a 10 inch screen or so.
If there are no Windows/macOS compiled demos, I might just be the one to create one.
We have found a couple of places that assume a vector has at least one element. It's conceivable that is an unwise assumption.
x=iota 10
x
1 2 3 4 5 6 7 8 9 10
rev x
10 9 8 7 6 5 4 3 2 1
x
10 9 8 7 6 5 4 3 2 1
I think x should not be modified after "rev x".
For a compound expression to the left of indexing operator, when it is printed, its parentheses get dropped. e.g.
op f x = (iota x)[1]
)op f
op f x = iota x[1]
also when saving to file (f defined as above)
f 3
1
)save "/tmp/1.ivy"
)get "/tmp/1.ivy"
f 3
binary [] not implemented on type int
A possible fix might be
--- a/parse/parse.go
+++ b/parse/parse.go
@@ -204,7 +204,11 @@ type binary struct {
func (b *binary) ProgString() string {
// Special case for indexing.
if b.op == "[]" {
- return fmt.Sprintf("%s[%s]", b.left.ProgString(), b.right.ProgString())
+ if isCompound(b.left) {
+ return fmt.Sprintf("(%s)[%s]", b.left.ProgString(), b.right.ProgString())
+ } else {
+ return fmt.Sprintf("%s[%s]", b.left.ProgString(), b.right.ProgString())
+ }
}
if isCompound(b.left) {
return fmt.Sprintf("(%s) %s %s", b.left.ProgString(), b.op, b.right.ProgString())
Thanks!
Hi Rob, sorry to bother you here, but I don't see how in Ivy you can use the APL α and ω commands, e.g. if I wanted to create a gcd operator like so:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.