Omnimaga

Calculator Community => Other Calc-Related Projects and Ideas => TI-Nspire => Topic started by: 3rik on June 22, 2012, 06:28:21 pm

Title: Matrix Library
Post by: 3rik on June 22, 2012, 06:28:21 pm
As the title implies, I'm planning on making a matrix library for Lua. I want it to be usable in computer Lua, too, so I don't want to use math.eval or other calculator dependent functions in this library. The math related aspect of this project can be found here (http://ourl.ca/13816).

Spoiler For fixed (I think):
My first roadblock is with being able to make the matrix itself.

I want the matrix to be unable to be edited without using the matrix functions. This can be accomplished using up-values, __index, and __newindex but I cant seem to figure out a good way to do this. I also want all the matrix functions to be available from the matrix.

Basically, I want it to be tamper-proof. I imagine it functioning like like this: (don't worry about the functions them selves)

Code: [Select]
matrix1 = matrix.new({{2, 3, 4}, {3, 4, 5}})
matrix2 = matrix.new({{1, 0, 5}, {2, 5, 9}})

matrix1:add(matrix2) == matrix.add(matrix1, matrix2) --true

matrix1[1] = {2} --error

matrix1[1][1] = 2 --no error

matrix1[1][1] = "2" --converts to number

matrix1[1][1] = true --error

table.maxn(matrix1) == 0 --true matrix1 doesn't actually contain any values, it forwards them to a local table in the matrix.new function

matrix1[1][300] = 2 --error wrong dimensions

If anyone has any suggestions, it would be very helpful.

Edit: I've attached a copy to the first post so people don't have to search through the topic for a copy.



Lua Matrix Library (http://www.omnimaga.org/index.php?action=dlattach;topic=13853.0;attach=13245)
Last Update: July 16, 2012, 03:03:56 pm
Version: 0.9.2
New Features: added lu, trace, det, ref, rref, and rank

Title: Re: Matrix Library
Post by: Levak on June 22, 2012, 08:43:16 pm
In Nspire Lua, it is exactly the goal of the class() function. But as you want it to be also usuable in standard Lua, I guess you should look at this (http://lua-users.org/wiki/SimpleLuaClasses)

But ... there is another problem.

In Lua, everything is tables .. and most likely I'm interested in the way someone will handle call errors on elements likes the one you describe. IMHO it is impossible the way you want the errors to be handled.

Also, Lua is multitype, so this :
Code: [Select]
matrix1[1][1] = "2" --converts to number

matrix1[1][1] = true --error
will be possible only with direct calls to functions. The way you want it to be is not possible in Lua, same in many languages.... but I can be wrong.

matrix1[1][1] will be possible with something like this :

matrix1 = {{1, 2, 3}, {4, 5, 6}, ["add"]=function(self, m2) end}
Title: Re: Matrix Library
Post by: 3rik on June 22, 2012, 08:46:26 pm
matrix1 = {{1, 2, 3}, {4, 5, 6}, ["add"]=function(self, m2) end}
what about
Code: [Select]
setmetatable(matrix1, {__index=matrix})?
I think it can be done but I'm just not sure how to organize it.
Title: Re: Matrix Library
Post by: Levak on June 22, 2012, 09:10:44 pm
getmetatable(matrix), but yes, it was just to show the content of matrix1.
matrix.new is the function described in the link I gave you.
Title: Re: Matrix Library
Post by: 3rik on June 22, 2012, 09:30:40 pm
here is an example of what I was trying to say:
Code: [Select]
matrix = {}
matrix.new = function(data)
local mat={}
setmetatable(mat, {__index = function(tbl, nobbc)
return data[nobbc]
end
})
return mat
end
matrix1 = matrix.new({{4}})
matrix2 = matrix.new({{7}})
print(matrix1[1][1], matrix2[1][1], mat, data) --prints 4 7 nil nil
nobbc was supposed to be key but I was having issues

This is an insecure example, but it works
Title: Re: Matrix Library
Post by: Jim Bauwens on June 23, 2012, 12:17:42 pm
Here is the class function (although slightly modified):
Code: [Select]
class = function(prototype)
local derived={}

  if prototype then
derived.__proto = prototype
  function derived.__index(t,key)
  return rawget(derived,key) or prototype[nobbc]
  end
  else
  function derived.__index(t,key)
  return rawget(derived,key)
  end
  end
 
  function derived.__call(proto,...)
  local instance={}
  setmetatable(instance,proto)
  instance.__obj = true
  local init=instance.init
  if init then
  init(instance,...)
  end
  return instance
  end
 
  setmetatable(derived,derived)
  return derived
end

I think you want something like this:

Code: [Select]
Matrix = class()

function Matrix:init(mat)
    self.matrix = mat
end

function Matrix:set(x, y, n)
    self.matrix[y][x] = n
end


matrix1 = Matrix{{1,2,3},{1,2,3},{1,2,3}}
matrix2 = Matrix{{1,2,3},{1,2,3}}

matrix1:set(1,1,3)

matrix1 and matrix2 are not connected in anyway. You can expand the matrix class to your wishes.
Also, with metatables/-methods you could even make that matrix3=matrix1+matrix2 is possible :)
Title: Re: Matrix Library
Post by: 3rik on June 23, 2012, 12:35:00 pm
It's just that I don't want the data to be directly accessible to the programmer. I also want zeros to be stored as nils but when they're indexed, they return a 0. This is my version. It's still a work in progress.

I'm having issues with [ key ] turning into [nobbc] so I attached the file instead.
Title: Re: Matrix Library
Post by: Jim Bauwens on June 23, 2012, 12:48:21 pm
If you don't want users to have direct write access to the table, you can use something like this:

Code: [Select]

function createProxy(tbl)
local mt = {}
local proxy = {}
mt.__index = function (_, key)
return tbl[ key]
end

mt.__newindex = function ()
print("You are not allowed to write to this table!")
end
setmetatable(proxy, mt)
return proxy
end


a = createProxy{1,2,3,4,5}

print(a[2])
a[2] = 3

I'm taking a look at your code.

Edit: ouch, the forum is changing table keys to nobbc
Edit2: Okay, I think class you be something for you. You should be able to protect your variables enough.
Title: Re: Matrix Library
Post by: 3rik on June 23, 2012, 01:15:00 pm
It's just that the matrix is a bunch of tables inside a table. Since the tables are passed by reference they could be altered using class(). Another tricky part is that when assigning to a spot in the matrix like
Code: [Select]
A[1][2] = 6
the A[1] is found by __index not __newindex so it is like
Code: [Select]
B = A[1]
B[2] = 6
--or
(A[1])[2] = 6
the reference to A[1] is assigned to B and then B[2] is assigned 6. This means __index and __newindex have to work together to allow assignment but control what can be assigned where while protecting the reference to the actual data.
Title: Re: Matrix Library
Post by: Jim Bauwens on June 23, 2012, 04:06:12 pm
Well, here is almost the same for a matrix:

Code: [Select]
function createProxy(tbl)


local mt = {}
local proxy = {}
mt.__index = function (t, key)
local rownumber = rawget(t, "rownumber")
if rownumber then
return tbl[ rownumber][ key]
else
return tbl[ key]
end
end

mt.__newindex = function ()
print("You are not allowed to write to this table!")
end

for key, row in ipairs(tbl) do
proxy[ key] = {}
proxy[ key].rownumber = key
setmetatable(proxy[ key], mt)
end

setmetatable(proxy, mt)

return proxy
end


a = createProxy{{5,4,3,2,1},{3,6,9,1}}

print(a[ 2][ 3])
a[ 2][ 3] = 3
print(a[ 2][ 3])
Title: Re: Matrix Library
Post by: 3rik on June 23, 2012, 04:20:29 pm
One issue with this kind of matrix is that it allows a whole row to be assigned at one time without being checked if all the values are numbers or nils. I also want to still be able to assign to the matrix, just not directly. Also I would like to make all zeros be nils in the matrix. It allows large spaece matrices to be stored without taking up a massive amount of space.
Code: [Select]
a = matrix.new({[10000] = { [100000] = 8}})
a[2][3] = 6
print(a[2][3]+a[250][44]+a[10000][100000]) --prints 14
Title: Re: Matrix Library
Post by: Jim Bauwens on June 23, 2012, 04:24:08 pm
It should not allow it. You can modify __newindex so to parse the input and possible modify it.
The above code is just the concept, you can build on it and make it do what you want.

Edit: wait, it does. Let me fix it
Title: Re: Matrix Library
Post by: 3rik on June 23, 2012, 04:30:20 pm
I think this is getting closer to what I came up with. It's just that my code is so inflated.
btw thanks for all the help you're offering.  :)
Title: Re: Matrix Library
Post by: Jim Bauwens on June 23, 2012, 04:35:56 pm
Okay, I guess you just need to modify some things to get it working.
However, why don't you just make the matrix local to your functions, so only your functions can access it ?
That way you could have matrix.getValue and matrix.setValue. Matrix[ y][ x] would not work. That way you don't need to deal with lot's of stuff.
Title: Re: Matrix Library
Post by: 3rik on June 23, 2012, 04:39:01 pm
I was considering that. If this is too much to manage, I'll probably just switch to that plan.
Title: Re: Matrix Library
Post by: Jim Bauwens on June 23, 2012, 04:44:58 pm
Take a look at this:

Code: [Select]

class = function(prototype)
local derived={}

  if prototype then
derived.__proto = prototype
  function derived.__index(t,key)
  return rawget(derived,key) or prototype[nobbc]
  end
  else
  function derived.__index(t,key)
  return rawget(derived,key)
  end
  end
 
  function derived.__call(proto,...)
  local instance={}
  setmetatable(instance,proto)
  instance.__obj = true
  local init=instance.init
  if init then
  init(instance,...)
  end
  return instance
  end
 
  setmetatable(derived,derived)
  return derived
end



Matrix = class()

function Matrix:init(mat)
self.get = function(self, y, x)
assert(type(y) == "number" and type(x) == "number", "Invalid input to Matrix:get!")
return mat[y][x]
end

self.set = function(self, y, x, n)
local nn = tonumber(n)
assert(type(y) == "number" and type(x) == "number" and nn, "Invalid input to Matrix:set!")
mat[y][x] = nn
end
end

function Matrix:someFunction()
self:set(whateveryouwant)
end

matrix1 = Matrix{{1,2,3},{4,5,6}}

matrix1:set(2,1, "9")
print(matrix1:get(2,1))

What do you think ?
Title: Re: Matrix Library
Post by: 3rik on June 23, 2012, 04:50:53 pm
Yeah this type of thing would work and it would be far easier to manage. I think in the long run it will probably be shorter and faster to do some thing like this, rather than basically making a new type out of an old one.
Thanks.
Title: Re: Matrix Library
Post by: DJ Omnimaga on June 24, 2012, 12:58:20 am
Nspire Lua didn't have native matrix support? O.O
Title: Re: Matrix Library
Post by: Levak on June 24, 2012, 01:11:01 am
Nspire Lua didn't have native matrix support? O.O
Nope.
Everything is table in Lua. So it has to be made either in a library or by the programmer with some offset tricks.
Title: Re: Matrix Library
Post by: Jim Bauwens on June 24, 2012, 06:23:26 am
Depends what you mean with a Matrix.

If you just want a matrix for a map, it's as simple as:
Code: [Select]
map = {
 {1,1,1,1,1,1,1,1},
 {1,1,0,0,0,0,0,1}
 {1,0,0,1,1,1,1,1}
 {1,0,1,1,1,1,1,1}
 {1,0,0,0,0,0,0,1}
 {1,1,0,0,0,1,0,1}
 {1,0,0,1,0,0,0,1}
 {1,1,1,1,1,1,1,1}
}
or however you want to store the data. It's just a combination of tables, and you can access it using map[ x][ y].

If you are talking about mathematical matrices, no Lua does not provide support for the math of it. However, Nspire-Lua does. You can access Matrices in the document using var.recall, var.recallAt and math.eval. You can write to the matrix using math.eval, var.store or var.storeAt. Performing (mathematical) operations is done using math.eval.

So: You can easily have normal data and mathematical matrices in Nspire-Lua. But mathematical matrixes in normal (computer) lua is not possible, hence why 3rik is writting this lib.
Title: Re: Matrix Library
Post by: 3rik on July 05, 2012, 08:33:41 pm
I've finished all the basic stuff.

I'll attach it here and update the first post.

Edit:

The documentation is included in the source.

Here's some code to test the library out.
Code: [Select]
m = matrix.new({{2, 5, 7}, {5, 7}, [4] = {5, 6, 7, 3}})
print(m)
n = matrix.new({{2, false, "7"}, {true, 7}, {5, 6, 0, 3}, {}})
print(n)
o = m[1]
print(o[1], o[2], o[3], o[4], "\n")
print(o[2], m[1][2])
o[2] = 4
print(o[2], m[1][2])
m[1][2] = 5
print(o[2], m[1][2])
m[3] = {4, [3] = 5}
print(m)
print(tostring(-m))
p = m:copy()
print("\n", "\n", m, "\n", p, "\n", "\n")
print(m:isequal(p))
print(m + p == p:ebemult(3) - m)
print(p:ebemult(m:ebediv(5)))
print(m:totable())
print(unpack(m:totable()[1]))
print()
for row, col, val in p:mpairs() do
    print(row, col, val)
end
print()
m = m + 1
print(p*m)
print(m*p)
print(m:trans()*p)
print(m.dim[1], m.dim[2], m.dim.rows, m.dim.cols)
print(matrix.gen.zero(4)+m == m)
print(matrix.gen.random(5))
print(matrix.gen.random(5, 2))
print(matrix.gen.random(5, 2, 3))
print(matrix.gen.random(5, 2, 3, 10))
m = m:submatrix(2, 3, 2, 3) --now a 2 x 2
print(m)
m = matrix.insert.row(m, 2, {44, 6, 7}) --truncated
print(m)
m = matrix.insert.col(m, 2, {44, 6, 7, 8}) --truncated
print(m)
print(matrix.remove.row(m, 2))
print(m)
m = matrix.remove.row(select(1, matrix.remove.col(m, 1)))
print(m)
Title: Re: Matrix Library
Post by: 3rik on July 09, 2012, 01:44:08 am
I fixed a few bugs, and changed all the asserts to errors so it's easier to find mistakes.
Title: Re: Matrix Library
Post by: Adriweb on July 16, 2012, 06:57:22 am
I just found something, maybe it can help if ever needed ?
https://github.com/smeschia/gsl-lua/blob/master/src/gsl.lua

(edit : eh, maybe not... looks like just a binding :P)
Title: Re: Matrix Library
Post by: 3rik on July 16, 2012, 03:03:53 pm
I think I'm okay for now. I'm sort of taking a crash course in linear algebra before I finish up the next version. I do have a lot of new things added, so I'll post what I have so far and update the first post.