Skip to content

Commit

Permalink
ivy: support non-scalar elements of vectors and matrices
Browse files Browse the repository at this point in the history
In #117, @smasher164 observed that
	,\1 2 3
generates
	1 (1 2) (1 2 3)
despite the claim that ivy does not support non-scalar elements.
I looked into preventing them from ever appearing, but that was
messy and slow as it requires a lot of post-construction checking
of things.

So I tried the other direction, and made them legal. That turned
out to be much more straightforward than I thought, although there
were a number of associated changes to make it work. Whenever the
existing barriers were removed, the code must actually work.
Significant changes and bug fixes:

- Fixed a trivial bug in parse that did not allow indexed expressions
in vectors, for example
  	1 2 x[3] 4
- Can now input compound elements of vectors: 1 (1 2) 3
- They must also be printable clearly, and for simplicity I made
  the output model the input, at least for these simple vector
  cases.
- Indexed assignment needed some tweaks, including fixing an
  existing bug: The rhs was not copied, so aliasing could happen
  and it was easy to demostrate bad behavior. Added a Copy method
  to Value to clean this up.
- Support matrices and vectors in OrderedCompare.
- Update and extend the tests. Some failure tests now worked!

I didn't touch the docs. Not clear a change is needed, although
this might be worth a phrase somewhere, perhaps in the demo.

Also simplify the method for printing matrices, which will make it
easier to fix a layout issue: A multiline matrix as an element of
an array or matrix results in ugly output. Relatively easy to fix,
but to be a separate change.

Update #117
  • Loading branch information
robpike committed Jul 2, 2023
1 parent e48b913 commit a0c08b0
Show file tree
Hide file tree
Showing 23 changed files with 352 additions and 137 deletions.
28 changes: 14 additions & 14 deletions demo/demo.out
Original file line number Diff line number Diff line change
Expand Up @@ -305,18 +305,18 @@ wroo
105
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
6 4 7 3 1
A♠ A♡ A♣ A♢
2♠ 2♡ 2♣ 2♢
3♠ 3♡ 3♣ 3♢
4♠ 4♡ 4♣ 4♢
5♠ 5♡ 5♣ 5♢
6♠ 6♡ 6♣ 6♢
7♠ 7♡ 7♣ 7♢
8♠ 8♡ 8♣ 8♢
9♠ 9♡ 9♣ 9♢
0♠ 0♡ 0♣ 0♢
J♠ J♡ J♣ J♢
Q♠ Q♡ Q♣ Q♢
K♠ K♡ K♣ K♢
J♠ 6♡ 4♢ 5♢ 4♡ 7♢ 8♡ 2♢ 4♣ Q♣ 9♡ 3♣ Q♠ 2♣ J♡ J♣ A♠ K♡ 3♢ 0♣ 6♢ 8♠ A♢ A♣ 0♡ 9♢ 8♣ K♠ 2♠ 3♡ Q♡ J♢ 9♠ 3♠ K♢ 9♣ Q♢ 7♡ 0♢ 5♠ 4♠ 0♠ 7♠ 7♣ 5♡ 6♣ 6♠ 5♣ 8♢ A♡ K♣ 2♡
(A♠) (A♡) (A♣) (A♢)
(2♠) (2♡) (2♣) (2♢)
(3♠) (3♡) (3♣) (3♢)
(4♠) (4♡) (4♣) (4♢)
(5♠) (5♡) (5♣) (5♢)
(6♠) (6♡) (6♣) (6♢)
(7♠) (7♡) (7♣) (7♢)
(8♠) (8♡) (8♣) (8♢)
(9♠) (9♡) (9♣) (9♢)
(0♠) (0♡) (0♣) (0♢)
(J♠) (J♡) (J♣) (J♢)
(Q♠) (Q♡) (Q♣) (Q♢)
(K♠) (K♡) (K♣) (K♢)
(J♠) (6♡) (4♢) (5♢) (4♡) (7♢) (8♡) (2♢) (4♣) (Q♣) (9♡) (3♣) (Q♠) (2♣) (J♡) (J♣) (A♠) (K♡) (3♢) (0♣) (6♢) (8♠) (A♢) (A♣) (0♡) (9♢) (8♣) (K♠) (2♠) (3♡) (Q♡) (J♢) (9♠) (3♠) (K♢) (9♣) (Q♢) (7♡) (0♢) (5♠) (4♠) (0♠) (7♠) (7♣) (5♡) (6♣) (6♠) (5♣) (8♢) (A♡) (K♣) (2♡)
22
73 changes: 60 additions & 13 deletions order_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,24 @@ var (
complex1j2 = value.NewComplex(int1, int2) // Same real, bigger imaginary.
complex2j1 = value.NewComplex(int2, int1) // Bigger real, lesser imaginary
complex2j2 = value.NewComplex(int2, int2) // Same real, bigger imaginary

vector0000 = value.NewIntVector([]int{0, 0, 0, 0})
vector012 = value.NewIntVector([]int{0, 1, 2})
vector022 = value.NewIntVector([]int{0, 2, 2})

matrix000_000 = value.NewMatrix([]int{2, 3}, newMatrixData(0, 0, 0, 0, 0, 0))
matrix12_34 = value.NewMatrix([]int{2, 2}, newMatrixData(1, 2, 3, 4))
matrix12_44 = value.NewMatrix([]int{2, 2}, newMatrixData(1, 2, 4, 4))
)

func newMatrixData(data ...int) []value.Value {
v := make([]value.Value, len(data))
for i := range data {
v[i] = value.Int(data[i])
}
return v
}

func TestOrderedCompare(t *testing.T) {
var tests = []orderTest{
// Same types.
Expand Down Expand Up @@ -130,73 +146,73 @@ func TestOrderedCompare(t *testing.T) {
{complex2j2, complex2j1, 1},
{complex2j2, complex2j2, 0},

// Int less than every possible type.
// Int less than every possible scalar type.
{int0, bigInt1, -1},
{int0, bigRat1o1, -1},
{int0, bigFloat1p0, -1},
{int0, complex1j0, -1},

// Int equal to every possible type.
// Int equal to every possible scalar type.
{int1, bigInt1, 0},
{int1, bigRat1o1, 0},
{int1, bigFloat1p0, 0},
{int1, complex1j0, 0},

// Int greater than every possible type.
// Int greater than every possible scalar type.
{int2, bigInt1, 1},
{int2, bigRat1o1, 1},
{int2, bigFloat1p0, 1},
{int2, complex1j0, 1},

// BigInt less than every possible type.
// BigInt less than every possible scalar type.
{bigInt0, int1, -1},
{bigInt0, bigRat1o1, -1},
{bigInt0, bigFloat1p0, -1},
{bigInt0, complex1j0, -1},

// BigInt equal to every possible type.
// BigInt equal to every possible scalar type.
{bigInt1, int1, 0},
{bigInt1, bigRat1o1, 0},
{bigInt1, bigFloat1p0, 0},
{bigInt1, complex1j0, 0},

// BigInt greater than every possible type.
// BigInt greater than every possible scalar type.
{bigInt2, int1, 1},
{bigInt2, bigRat1o1, 1},
{bigInt2, bigFloat1p0, 1},
{bigInt2, complex1j0, 1},

// BigRat less than every possible type.
// BigRat less than every possible scalar type.
{bigRat0o1, int1, -1},
{bigRat0o1, bigInt1, -1},
{bigRat0o1, bigFloat1p0, -1},
{bigRat0o1, complex1j0, -1},

// BigRat equal to every possible type.
// BigRat equal to every possible scalar type.
{bigRat1o1, int1, 0},
{bigRat1o1, bigInt1, 0},
{bigRat1o1, bigFloat1p0, 0},
{bigRat1o1, complex1j0, 0},

// BigRat greater than every possible type.
// BigRat greater than every possible scalar type.
{bigRat2o1, int1, 1},
{bigRat2o1, bigInt1, 1},
{bigRat2o1, bigFloat1p0, 1},
{bigRat2o1, complex1j0, 1},

// BigFloat less than every possible type.
// BigFloat less than every possible scalar type.
{bigFloat0p0, int1, -1},
{bigFloat0p0, bigInt1, -1},
{bigFloat0p0, bigFloat1p0, -1},
{bigFloat0p0, complex1j0, -1},

// BigFloat equal to every possible type.
// BigFloat equal to every possible scalar type.
{bigFloat1p0, int1, 0},
{bigFloat1p0, bigInt1, 0},
{bigFloat1p0, bigFloat1p0, 0},
{bigFloat1p0, complex1j0, 0},

// BigFloat greater than every possible type.
// BigFloat greater than every possible scalar type.
{bigFloat2p0, int1, 1},
{bigFloat2p0, bigInt1, 1},
{bigFloat2p0, bigFloat1p0, 1},
Expand All @@ -218,12 +234,43 @@ func TestOrderedCompare(t *testing.T) {
{complex1j0, bigRat1o1, 0},
{complex1j0, bigFloat1p0, 0},

// Complex with imaginary part is always greater than every other type.
// Complex with imaginary part is always greater than every other scalar type.
{complex1j1, int1, 1},
{complex1j1, char1, 1},
{complex1j1, bigInt1, 1},
{complex1j1, bigRat1o1, 1},
{complex1j1, bigFloat1p0, 1},

// Vector bigger than every type.
{vector012, int3, 1},
{vector012, char3, 1},
{vector012, char3, 1},
{vector012, bigInt3, 1},
{vector012, bigRat3o7, 1},
{vector012, bigFloat3p5, 1},
{vector012, complex2j2, 1},

// Vector comparisons.
{vector0000, vector012, 1}, // Length dominates.
{vector012, vector022, -1},
{vector012, vector012, 0},
{vector022, vector012, 1},

// Matrix bigger than every type.
{matrix12_34, int3, 1},
{matrix12_34, char3, 1},
{matrix12_34, char3, 1},
{matrix12_34, bigInt3, 1},
{matrix12_34, bigRat3o7, 1},
{matrix12_34, bigFloat3p5, 1},
{matrix12_34, complex2j2, 1},
{matrix12_34, vector012, 1},

// Matrix comparisons.
{matrix000_000, matrix12_34, 1}, // Length dominates.
{matrix12_34, matrix12_44, -1},
{matrix12_34, matrix12_34, 0},
{matrix12_44, matrix12_34, 1},
}
var testConf config.Config
c := exec.NewContext(&testConf)
Expand Down
22 changes: 10 additions & 12 deletions parse/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,7 @@ func (s sliceExpr) Eval(context value.Context) value.Value {
// x=1000; x + x=2
// (yielding 4) work.
for i := len(s) - 1; i >= 0; i-- {
elem := s[i].Eval(context)
// Each element must be a singleton.
if !isScalar(elem) {
value.Errorf("vector element must be scalar; have %s", elem)
}
v[i] = elem
v[i] = s[i].Eval(context)
}
return value.NewVector(v)
}
Expand Down Expand Up @@ -627,7 +622,7 @@ func (p *Parser) numberOrVector(tok scan.Token) value.Expr {
var slice sliceExpr
if expr == nil {
// Must be a string.
slice = append(slice, evalString(str)...)
slice = sliceExpr{evalString(str)}
} else {
slice = sliceExpr{expr}
}
Expand All @@ -647,7 +642,7 @@ func (p *Parser) numberOrVector(tok scan.Token) value.Expr {
expr, str = p.number(p.next())
if expr == nil {
// Must be a string.
slice = append(slice, evalString(str)...)
slice = append(slice, evalString(str))
continue
}
default:
Expand All @@ -672,13 +667,16 @@ func (p *Parser) variable(name string) *variableExpr {
}
}

// evalString turns a parsed string constant into a slice of
// value.Exprs each of which is a value.Char.
func evalString(str string) []value.Expr {
// evalString turns a string constant into an Expr
// that is either a single Char or a slice of Chars.
func evalString(str string) value.Expr {
r := ([]rune)(str)
if len(r) == 1 {
return value.Char(r[0])
}
v := make([]value.Expr, len(r))
for i, c := range r {
v[i] = value.Char(c)
}
return v
return sliceExpr(v)
}
25 changes: 23 additions & 2 deletions testdata/binary_vector.ivy
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,18 @@
23 45 5 == 5
0 0 1

x=(1 2) 3 4 5; x==x
(1 1) 1 1 1

x=(1 2) 3 4 5; y=1 (2 3) 4 5; x==y
(1 0) (0 1) 1 1

x=(1 2) 3 4 5; y=1 (2 3) 4 5; y==x
(1 0) (0 1) 1 1

x=(1 0) 3 4 5; y=1 (2 4) 5 4; x<y
(0 1) (0 1) 1 0

23 1e10 67 == 1e10
0 1 0

Expand Down Expand Up @@ -686,9 +698,12 @@ circle 10
1 2

# Chars are extra tricky
1 2 'abc' 3 4 union 2 'cba' 5 1 'x'
1 2 'a' 'b' 'c' 3 4 union 2 'c' 'b' 'a' 5 1 'x'
1 2 a b c 3 4 5 x

1 2 'abc' 3 4 union 2 'cba' 5 1 'x'
1 2 (abc) 3 4 (cba) 5 x

1j0 2j0 3j1 3j2 union 2 3j1 4 3j3
1j0 2j0 3j1 3j2 4 3j3

Expand Down Expand Up @@ -727,9 +742,15 @@ circle 10
empty

# Chars are extra tricky
1 2 'abc' 3 4 intersect 2 'ca' 5 1 'x'
1 2 'a' 'b' 'c' 3 4 intersect 2 'c' 'a' 5 1 'x'
1 2 a c

1 2 'abc' 'd' 3 4 intersect 2 'ca' 'd' 5 1 'x'
1 2 d

1 2 'abc' 'd' 3 4 'xyz' intersect 2 'abc' 'd' 5 1 'x'
1 2 (abc) d

1j0 2j0 3j1 3j2 intersect 2 3j1 4 3j3
2j0 3j1

Expand Down
19 changes: 19 additions & 0 deletions testdata/char.ivy
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ c`
10 rho 'a'
aaaaaaaaaa

10 rho 'a' 'cd'
a (cd) a (cd) a (cd) a (cd) a (cd)

10 rho 'ab' 'cd'
(ab) (cd) (ab) (cd) (ab) (cd) (ab) (cd) (ab) (cd)

10 rho 'a' 'b'
ababababab

Expand Down Expand Up @@ -137,6 +143,19 @@ c`
'abc' , 'def'
abcdef

'abc' 'def'
(abc) (def)

3 4 rho 'a' 'bc'
a (bc) a (bc)
a (bc) a (bc)
a (bc) a (bc)

3 4 rho 'ab' 'cd'
(ab) (cd) (ab) (cd)
(ab) (cd) (ab) (cd)
(ab) (cd) (ab) (cd)

rho ''
0

Expand Down
25 changes: 10 additions & 15 deletions testdata/exec_fail.ivy
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,6 @@
1/0
X

# vector element must be scalar
x = 1 2 3
x 1
X

# vector element must be scalar
x = 1 2 3
1 x
X

# vector element must be scalar
x = 1 2 3
1 x 1
X

# undefined variable "x"
x
X
Expand All @@ -48,8 +33,18 @@ x
1 2[1 2 log 2]
X

# zero denominator in rational
1/0
X

# division by zero
1 / 0
X

# division by zero
1 / 2 2 rho 0
X

# shape mismatch (4) != (2) in assignment x[2] = 3 4 5
x = 3 4 rho iota 12; x[1]=1 2
X
7 changes: 7 additions & 0 deletions testdata/statement.ivy
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ x = 5 5 rho iota 25; x[6 - iota 5; 6 - iota 5] = x; x
10 9 8 7 6
5 4 3 2 1

x = 1 2 3; x[2] = 3 4 5; x
1 (3 4 5) 3

# Test that assignment copies the data.
x = 1 2 3; y = 4 5 6; x[2] = y; y[1] = 8; x
1 (4 5 6) 3

# Assignment is an expression.
1 + y = 100
101
Expand Down
4 changes: 4 additions & 0 deletions testdata/unary_matrix.ivy
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
1 5/2 1
5/2 1 5/2

2 3 rho 1 (2 3)
1 (2 3) 1
(2 3) 1 (2 3)

# Higher dimensions

2 3 4 rho iota 24
Expand Down
Loading

0 comments on commit a0c08b0

Please sign in to comment.