From owner-matrix-sig Mon Aug 28 17:08:11 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id RAA32423 for matrix-sig-people; Mon, 28 Aug 1995 17:08:11 -0400
Received: from qvarsx.er.usgs.GOV (qvarsx.er.usgs.gov [130.11.51.82]) by python.org (8.6.12/8.6.12) with ESMTP id RAA32408; Mon, 28 Aug 1995 17:07:58 -0400
Received: from localhost (dsjfqvarsa [130.11.51.73]) by qvarsx.er.usgs.GOV (EMAIL 1.2.1) with ESMTP id VAA04470; Mon, 28 Aug 1995 21:04:06 GMT
Message-Id: <199508282104.VAA04470@qvarsx.er.usgs.GOV>
To: Guido van Rossum
cc: "Barry A. Warsaw"
cc: "James L Fulton, Hydrologist, Reston, VA ",
meta-sig@python.org
cc: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Re: [PYTHON META-SIG] Re: SIGs
In-reply-to: <199508281956.PAA06875@monty>
Date: Mon, 28 Aug 1995 17:08:45 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Mon, 28 Aug 1995 15:56:13 -0400
Guido van Rossum said:
> Now that Barry's created the matrix and gui sigs, I've added their
> email addresses to the sigs web page on python.org. I guess Jim
> should prepare a little introduction to be placed in the .info
> files and also post that to the net so people are aware of the new
> sigs. (I could then also change the description of the sigs in the
> sigs web page.)
How about:
matrix-sig:
Special Interest Group for Built-in Matrix Types
There is growing interest in using Python to interface with
mathematical and scientific computation libraries. A commonly
needed data structure is a matrix of homogenous low-level objects,
such as numbers.
The purpose of this special interest group is to develop a
proposal for and possibly coordinate the implementation of a new
Python built-in Matrix type.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Aug 30 20:36:59 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id UAA13287 for matrix-sig-people; Wed, 30 Aug 1995 20:36:59 -0400
Received: from qvarsx.er.usgs.GOV (qvarsx.er.usgs.gov [130.11.51.82]) by python.org (8.6.12/8.6.12) with ESMTP id UAA13283 for ; Wed, 30 Aug 1995 20:36:55 -0400
Received: from localhost (dsjfqvarsa [130.11.51.73]) by qvarsx.er.usgs.GOV (EMAIL 1.2.1) with ESMTP id AAA27988 for ; Thu, 31 Aug 1995 00:32:51 GMT
Message-Id: <199508310032.AAA27988@qvarsx.er.usgs.GOV>
From: "James L Fulton, Hydrologist, Reston, VA "
Subject: [PYTHON MATRIX-SIG] Matrix Special Interest Group Formed
to: matrix-sig@python.org
Date: Wed, 30 Aug 1995 20:37:35 -0400
Sender: owner-matrix-sig@python.org
Precedence: bulk
A Python Software Activity (PSA)
Matrix Special Interest Group (SIG)
has been formed:
There is growing interest in using Python to interface with
mathematical and scientific computation libraries. A commonly
needed data structure is a matrix of homogenous low-level objects,
such as numbers.
The purpose of this special interest group is to develop a
proposal for and possibly coordinate the implementation of a new
Python built-in Matrix type.
The SIG will conduct its discussions using the mailing list
matrix-sig@python.org. You can join the matrix sig (and the mailing
list) by sending a message to matrix-sig-request@python.org with a
message body containing the word "subscribe".
To obtain a copy of the matrix-sig archive, you can send a message to
matrix-sig-request@python.org with a message body containing:
get matrix-sig.archive
To find out more about the PSA, see: http://www.python.org/psa/.
To find out more about PSA SIGS, see: http://www.python.org/sigs/.
To find out about additional commands that you can send to
matrix-sig-request@python.org, see
http://www.python.org/sigs/md_cmds.html/.
--
-- Jim Fulton jfulton@usgs.gov (703) 648-5622
U.S. Geological Survey, Reston VA 22092
This message is being posted to obtain or provide technical information
relating to my duties at the U.S. Geological Survey.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Aug 30 20:39:14 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id UAA13304 for matrix-sig-people; Wed, 30 Aug 1995 20:39:14 -0400
Received: from qvarsx.er.usgs.GOV (qvarsx.er.usgs.gov [130.11.51.82]) by python.org (8.6.12/8.6.12) with ESMTP id UAA13300 for ; Wed, 30 Aug 1995 20:39:11 -0400
Received: from localhost (dsjfqvarsa [130.11.51.73]) by qvarsx.er.usgs.GOV (EMAIL 1.2.1) with ESMTP id AAA28101 for ; Thu, 31 Aug 1995 00:35:07 GMT
Resent-Message-Id: <199508310035.AAA28101@qvarsx.er.usgs.GOV>
Path: qvarsx.er.usgs.gov!rsg1.er.usgs.gov!stc06.ctd.ornl.gov!fnnews.fnal.gov!usenet.eel.ufl.edu!news.mathworks.com!uhog.mit.edu!grapevine.lcs.mit.edu!usenet@lcs.mit.edu
From: jjh@ling-ling.lcs.mit.edu (James Hugunin)
Newsgroups: comp.lang.python
Subject: [PYTHON MATRIX-SIG] The Matrix Object Proposal (very long)
Date: 18 Aug 1995 17:16:55 GMT
Organization: MIT Laboratory for Computer Science
Lines: 292
Message-ID: <412hu7$92c@GRAPEVINE.LCS.MIT.EDU>
Reply-To: jjh@ling-ling.lcs.mit.edu
NNTP-Posting-Host: ling-ling.lcs.mit.edu
Keywords: numeric, matrix
Resent-To: matrix-sig@python.org
Resent-Date: Wed, 30 Aug 1995 20:39:51 -0400
Resent-From: "Jim Fulton, U.S. Geological Survey"
Apparently-To:
Sender: owner-matrix-sig@python.org
Precedence: bulk
I apologize for the length of this posting, but you were warned in the
subject header.
There seems to be a fair amount of interest in the python community
concerning the addition of numeric operations to python. My own desire is
to have as large a library of matrix based functions available as possible
(linear algebra, eigenfunctions, signal processing, statistics, etc.). In
order to ensure that all of these libraries interoperate, there needs to
be agreement on a basic matrix object that can be used to represent arrays
of numbers. The following is a proposal for such an object. It can be
viewed as an extension of the current array object to higher dimensions
and numerical operations.
The wheels have already been set in motion to create a Matrix SIG
mailing-list for further discussion of this proposal, as well as other
python numerics related issues. This should come on-line sometime next
week and serious discussion of this proposal should probably be deferred
until it can be done on that list. Summaries of that discussion will be
posted to the main list as consensus develops.
Below I specify the behavior of a Matrix object within python. The C (or
FORTRAN) API to such an object is obviously of significant importance as
this object is primarily intended as an interface to existing numerical
libraries. I'm currently working on that proposal (at least the C part)
as well, but I think everyone will agree that this post is long enough as
it is.
Note: Parts of this proposal are stolen from the python documentation for
the array object, and from discussions on the net. I'd like to
particularly thank Jim Fulton for letting me review (and steal ideas from)
his current Matrix object and for providing feedback on the first draft of
this proposal (though he certainly doesn't agree with everything in it,
even now).
Matrix Object
This defines a new object type which can efficiently represent a
multidimensional array of basic values (char, unsigned byte, signed byte,
int, short, long, float, double, complex float and complex double).
Matrices are defined as both a sequence type and a mapping type, and
usually (unless it's a matrix of char's) as a number type. It should be
noted that allowing a type to be both a sequence and a number and to
behave properly for addition and multiplication, will require some very
small patches to the ceval.c code.
All of the examples use the following matrices:
Assume A = [1,2,3], B = [11,12,13], C = [[1,4,9],[16,25,36]],
M=[[ 1, 2, 3, 4, 5],[11,12,13,14,15],[21,22,23,24,25],[31,32,33,34,35]]
**Numeric operations**
"+", "-", "*", "/", "^"(as power), abs, unary"-", and unary"+" are defined
as the corresponding element-wise arithmetic operations. Because these
assume element-wise correspondence between the two matrices, the operandi
must be of the same dimensions (however, note the dimensions-coercion
section below).
A+B = [12,14,16]
A*B = [11,24,39]
C*C = [[2,8,18],[32,50,72]]
-A = [-1,-2,-3]
Possible suggestions for the other numeric operations:
"%" represents matrix multiplication (or vector dot product).
"~" represents transposition.
Other suggestions?
**Sequence operations**
Concatenation and multiplication sequence operations are not defined, but
instead the arithmetic operations are invoked for the "+" and "*"
operators. Indexing by both single indices and by slices is supported.
All returned matrices are returned by reference rather than by value.
len(M) is defined to return the size of the first dimension in in matrix.
examples:
len(M) -> 4
D = M[0]
D[0] = 66
print M[0]
[66,2,3,4,5]
The semantics of returning a slice by reference is consistent with the
mapping operations given below, however, this is inconsistent with the
list objects semantics. This is open to debate.
D = M[0:2]
D[0][0] = 66
print M[0]
[66,2,3,4,5]
**Mapping operations**
Recent newsgroup discussions have offered some convincing arguments for
treating a matrix as a mapping type where sequences of ints are used as
the keys. The sequence must be of length less than or equal to the numer
of dimensions in the matrix. Each element of the sequence is either an
integer or a sequence. If it is an integer, it returns the corresponding
element for that dimension, if it is a sequence then it returns all of the
elements given in the sequence.
ie. A[0] -> 1, A[((1,2))] -> [2,3]
M[0] -> [1,2,3,4,5]
M[(0,3)] -> 4
M[((0,2),0)] -> [1,21]
M[(range(1,3),range(2,4))] -> [[13,14],[23,24]]
In order to simplify the syntax of these references, it would be valuable
to change the syntax of python so that M[0,3] is equivalent to M[(0,3)].
This is a small change to the current indexing semantics which will give a
reasonable meaning to a currently illegal syntactic item.
When used as a setvalue, the corresponding elements of the given array
will be set to the left hand side. All of these operations that return a
matrix will return it by-reference, meaning that the elements of the new
array will be references to the old one and changes to either array will
effect the other.
**Instantiation**
The following function will be used to instantiate a matrix:
Matrix(datasource(optional), d1, d2, ..., typecode='d')
The typecodes are as follows:
c - char (non-numeric)
1 - unsigned char
b - signed char
h - short
i - int
l - long
f - float
d - double
F - complex float
D - complex double
The optional datasource can be a file object or a string object and will
fill the matrix with the raw data contained therein. In this case the
dimensions are required. The first dimension can be input as -1, in which
case the first dimension will be set to be as large as necessary to hold
the entire contents of the file or string.
The optional datasource can also be a sequence, in which case the
dimensions of the array do not need to be passed in and are instead
determined by the hierarchical structure of the sequence.
examples:
Matrix([range(3),(4,5,6)], 'i') -> [[0,1,2],[4,5,6]]
Matrix(2,3) -> [[0,0,0],[0,0,0]]
Matrix('abcdef', 2,3, 'u') -> [[97,98,99],[100,101,102]]
Matrix('abcdef', -1,2, 'c') -> ['ab', 'cd', 'ef']
**Coercion**
Dimension coercion:The following is a proposal to make the matrix object
behave as closely as possible to matrices in matlab (to make it easy to
convert matlab code to python), and to generalize this behavior to
arbitrarily high dimensional objects.
If a matrix of fewer dimensions than that required is provided, then the
matrix can be converted to more dimensions by making copies of itself.
For purposes of dimension coercion, an int or a float is considered a 0-d
matrix.
ie. A+1 -> [2,3,4], A+C -> [[2,6,12],[17,27,39]]
B^2 -> [121,144,169] vs. B^A -> [11, 144, 2197]
Simliar coercion should be performed for most functions acting on
matrices. Helper functions will be provided to make this easy, however
there is no way to enforce this. The following is a proposed standard to
allow functions to be applied to higher-dimensional objects in a uniform
way.
If a function is defined to operate on scalars (say sqrt()), then when
applied to a matrix it should apply itself to every element in the matrix.
ie. sqrt(C) -> [[1,2,3],[4,5,6]]
This is generalized to higher dimensions so that if a function is provided
with a matrix of more dimensions that it requires, that function will
apply itself to each appropriately sized unit in the matrix.
ie. sum(A) -> 6 whereas sum(C) -> [14,77]
Type coercion: No type coercion will be performed. However, it is
possible to have a function defined to work on multiple types (operator
overloading). So sqrt might be defined on matrices of both floats and
doubles in which case it will work properly when given either matrix as
input, but will return a type error if applied to a matrix of ints.
**Basic Data Items and Methods (non-numeric)**
typecode - The typecode character used to create the matrix
itemsize - The length in bytes of one matrix item in the internal
representation
dimensions - A sequence containing the size of the matrix along each of
its dimensions
copy - Returns a copy of the matrix. This is a good way to take an array
returned by reference and turn it into one returned by value.
transpose - The transpose of the matrix (not needed if "~" is used for
transpose)
byteswap - Useful for reading data from a file written on a machine with a
different byte order
typecast(t) - Returns a new matrix of the same dimensions where each item
is cast (using C typecasting rules) from the matrix's type to the new type
t.
tofile(f) - Write all items (as machine values) to the file object f.
tolist() - Convert the matrix to an ordinary (possibly nested) list with
the same items.
tostring() - Convert the array to an array of machine values and return
the
string representation (the same sequence of bytes that would
be written to a file by the tofile() method.)
Note: the append, fromlist, etc. methods are not present because it is
assumed that a matrix object is of constant size (there are some strong
efficiency reasons for this assumption but this is likely to be another
source of arguments).
**Basic functions taking matrix arguments (non-numeric)**
Since it isn't possible to append to a matrix (or efficient if it's
decided it should be possible) I thought that some sort of function for
producing a new matrix from a list of matrices would be useful. So I'm
proposing the following function which is very similar in spirit to the
string.join operation.
concat(l) - l must be a list of matrices, and each matrix in l must have
the same number of dimensions, as well as the same size for every
dimension except for the first. This will return a new matrix M whose
first dimension is the sum of the first dimensions of all the matrices in
the list, and whose data is filled from the data in all of these matrices.
ie. concat([A,B]) -> [1,2,3,11,12,13]
This is different from Matrix([A,B]) -> [[1,2,3],[11,12,13]]
**Basic functions taking matrix arguments (numeric)**
It is hoped that there will be a large variety of special purpose modules
to do fancy numeric operations on matrices. There should also be a basic
set of operations provided in the Matrix module (the module with the
instantiation function). Because type coercion is not going to be
performed, it is important to decide both on the list of functions and on
the basic types they should support. The following is a very preliminary
list of the functions I consider most fundamental.
rng(start, stop, step(optional), typecode='d') where all points in the
range must be strictly less than the stop point, according to the
semantics of range(). This function should possibly be added to the
instantiation options.
ie. rng(0,0.4) = [0,0.1,0.2,0.3]; rng(0,0.1,0.41) = [0,0.1,0.2,0.3,0.4]
scalar operations (defined on floats, doubles, and all complex):
everything in mathmodule.c
round
scalar operations (defined on all numeric types):
sign
vector to scalar operations (defined on all numeric types):
max, min - Should these return the index of the max or min?
sum, prod
vector to vector operations (defined on all numeric types):
cumsum, cumprod
-----
Jim Hugunin - hugunin@mit.edu
Laboratory for Computer Science
Massachusetts Institute of Technology
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Mon Sep 11 19:03:26 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id TAA01503 for matrix-sig-people; Mon, 11 Sep 1995 19:03:26 -0400
Received: from servrcolkr.cr.usgs.gov (servrcolkr.cr.usgs.gov [136.177.112.5]) by python.org (8.6.12/8.6.12) with ESMTP id TAA01494 for ; Mon, 11 Sep 1995 19:03:20 -0400
Received: from localhost (dsjfqvarsa.er.usgs.gov [130.11.51.73]) by servrcolkr.cr.usgs.gov (EMAIL 1.2.1) with ESMTP id XAA13015 for ; Mon, 11 Sep 1995 23:04:40 GMT
Message-Id: <199509112304.XAA13015@servrcolkr.cr.usgs.gov>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Let's get going
In-reply-to: <412hu7$92c@GRAPEVINE.LCS.MIT.EDU>
Date: Mon, 11 Sep 1995 19:04:39 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
Well, now that we're all here, let's get started. :-)
As you know, the purpose of this SIG is to develop a proposal (and
possible an implementation) for a new Python matrix type. I'd like to
use Jim Hugunin's proposal as a starting point. I give my response to
his proposal below. Before beginning this, however, I thought I'd
summarize my involvement to date, so you know where I'm coming from.
At the python workshop, I presented some work in progress on a Fortran
(77) Interface Description Language (FIDL) for Python. This tool
allows one to very quickly create Python modules that call Fortran
libraries given brief high-level descriptions of the library routines
to be called. The primary data structure in Fortran 77 is the fortran
array, which is a multi-dimensional block of homogenous data elements.
Of course, many interesting C routines, especially in mathematic and
scientific domains, use this same data structure. To facilitate
interfacing to these sorts of routines, I wanted an an efficient
Python implementation of the same sort of data structure.
In particular:
o I wanted a python built-in data type containing a data structure
that I could pass directly to Fortran or C routines. I did not
want to have to do alot of data conversion or copying on each
call. For example, if a Fortran routine returns a matrix, then
it should be possible to pass this matrix to another Fortran
routine without reallocating a data structure or converting the
entire matrix.
o I wanted multi-dimensional access as would be familiar to a
python programmer. That is, if I have two-dimensional arrays, a
and b, then:
a[i][j] = b[k][l]
should work as expected and should work efficiently.
In general, given an n-dimensional matrix, m,
if n > 1, then m[i] should return an n-1 dimensional matrix,
and
if n = 1, then m[i] should return a scalar.
Note that for "m[i][j] = spam" to work correctly, assignment to
the jth element of m[i] must be reflected in m.
o I need to support *all* data types supported by Fortran 77,
including strings. Not only do I have to interface to
mathematical routines, but I need to interface to "legacy"
systems (Some of which are still being written :).
What I came up with was a fairly simple implementation of a matrix
type that was a pure container type. My matrix implementation
currently provides no numeric behavior, although a matrix type cries
out for numeric behavior.
My matrix implementation includes the following features:
o Matrix objects have an internal data pointer that points to a
homogenous block of memory. This pointer (or some offset from it,
of course) can be passed directly to C or Fortran.
o The internal data structure may be shared among multiple matrix
objects. In particular, a __getitem__ from a n-dimensional
matrix, where n>1, returns a matrix object that shares the same
data (at an appropriate offset) as the original matrix. This
shared data structure is reference counted, so if you have:
a=Matrix([[1,2,3],[4,5,6],[7,8,9]])
b=a[1] # b == Matrix([4,5,6])
del a
b is not invalidated by the third line.
Note that if you have:
a=Matrix([[1,2,3],[4,5,6],[7,8,9]])
b=Matrix([11,22,33])
a[1]=b
a[1][1]=99
You end up with:
a == Matrix([[1,2,3],[11,99,33],[7,8,9]])
b == Matrix([11,22,33])
That is, assigning to a matrix copies data. So assignment is by
copy, even though access is by reference.
(As you can see, matrices can be created from sequences of
sequences. Matrices can also be assigned from arbitrary
sequences of the right dimension or level of nesting.)
o I chose to preserve the copy semantics of slices from lists. So
in:
a=Matrix([[1,2,3],[4,5,6],[7,8,9]])
b=a[1][:] # b == Matrix([4,5,6])
b[1]=99 # does not affect a
The third line does not change a.
So that's where I'm coming from. I think this type is generally
useful, which is why I helped start this SIG. I'm pretty open on how
and if matrices should have numeric behavior. With that, here are my
comments on Jim's proposal.
On 18 Aug 1995 17:16:55 GMT
James Hugunin said:
> I apologize for the length of this posting, but you were warned in the
> subject header.
>
> There seems to be a fair amount of interest in the python community
> concerning the addition of numeric operations to python. My own desire is
> to have as large a library of matrix based functions available as possible
> (linear algebra, eigenfunctions, signal processing, statistics, etc.). In
> order to ensure that all of these libraries interoperate, there needs to
> be agreement on a basic matrix object that can be used to represent arrays
> of numbers. The following is a proposal for such an object. It can be
> viewed as an extension of the current array object to higher dimensions
> and numerical operations.
>
> The wheels have already been set in motion to create a Matrix SIG
> mailing-list for further discussion of this proposal, as well as other
> python numerics related issues. This should come on-line sometime next
> week and serious discussion of this proposal should probably be deferred
> until it can be done on that list. Summaries of that discussion will be
> posted to the main list as consensus develops.
>
> Below I specify the behavior of a Matrix object within python. The C (or
> FORTRAN) API to such an object is obviously of significant importance as
> this object is primarily intended as an interface to existing numerical
> libraries. I'm currently working on that proposal (at least the C part)
> as well, but I think everyone will agree that this post is long enough as
> it is.
>
> Note: Parts of this proposal are stolen from the python documentation for
> the array object, and from discussions on the net. I'd like to
> particularly thank Jim Fulton for letting me review (and steal ideas from)
> his current Matrix object and for providing feedback on the first draft of
> this proposal (though he certainly doesn't agree with everything in it,
> even now).
We aren't that far apart now, at least not on things that matter to
me. :-)
>
> Matrix Object
>
> This defines a new object type which can efficiently represent a
> multidimensional array of basic values (char, unsigned byte, signed byte,
> int, short, long, float, double, complex float and complex double).
> Matrices are defined as both a sequence type and a mapping type, and
> usually (unless it's a matrix of char's) as a number type. It should be
> noted that allowing a type to be both a sequence and a number and to
> behave properly for addition and multiplication, will require some very
> small patches to the ceval.c code.
While I think the patches are OK, I don't think they're necessary, so
if anyone objects to this proposal on the grounds that changes are
required to ceval.c, you should not be concerned.
> All of the examples use the following matrices:
> Assume A = [1,2,3], B = [11,12,13], C = [[1,4,9],[16,25,36]],
> M=[[ 1, 2, 3, 4, 5],[11,12,13,14,15],[21,22,23,24,25],[31,32,33,34,35]]
>
>
> **Numeric operations**
>
> "+", "-", "*", "/", "^"(as power), abs, unary"-", and unary"+" are defined
> as the corresponding element-wise arithmetic operations. Because these
I'd rather see multiplication and division not be elementwise. But I
don't feel strongly about this.
> assume element-wise correspondence between the two matrices, the operandi
> must be of the same dimensions (however, note the dimensions-coercion
> section below).
>
> A+B = [12,14,16]
> A*B = [11,24,39]
> C*C = [[2,8,18],[32,50,72]]
> -A = [-1,-2,-3]
>
> Possible suggestions for the other numeric operations:
> "%" represents matrix multiplication (or vector dot product).
If we go with elementwise interpretation for * and /, then I think
that all operators that make sense for floating point numbers should
have an elementwise interpretation, so m%spam should compute a matrix
of mods. Perhaps one would carry this same argument to bitwise
operators, since you could have matrices of integers.
If we go with elementwise interpretation of numeric operations, then
I'm inclined to use functional, rather than matrix notation for matrix
multiplication, dot products, and so on.
> "~" represents transposition.
> Other suggestions?
Perhaps, as a compromize, there could be a special method or operator
that creates a reference copied version of a matrix that provides a
different semantics. For example, perhaps the default matrix type
could provide standard matrix interpretation for * and /, but there
could be a method, elementwise that would allow:
m.elementwise() + a
to do elementwise arithmentic.
IMPORTANT NOTE: Mixed-mode arithmetic between matrices and scalars
should be defined.
>
> **Sequence operations**
>
> Concatenation and multiplication sequence operations are not defined, but
> instead the arithmetic operations are invoked for the "+" and "*"
> operators.
Right. Note if we don't need elementwise bitwise operators, then we
could concatinate with "|", which I find most natural in a matrix
context:
spam = foo | bar
I suppose one could even repeat with >> or <<, but this is not so natural.
> Indexing by both single indices and by slices is supported.
> All returned matrices are returned by reference rather than by value.
> len(M) is defined to return the size of the first dimension in in matrix.
>
>
> examples:
> len(M) -> 4
>
> D = M[0]
> D[0] = 66
> print M[0]
> [66,2,3,4,5]
Right, for my matrix type.
> The semantics of returning a slice by reference is consistent with the
> mapping operations given below, however, this is inconsistent with the
> list objects semantics. This is open to debate.
>
> D = M[0:2]
> D[0][0] = 66
> print M[0]
> [66,2,3,4,5]
I'm not sure what you were trying to say here, but this example holds
only if M is a list of lists. If M is one of my matrices, then M is
unchanged. Perhaps that is your point.
Note that I tried to maintain the spirit of list semantics, in which
the slice operator has copy semantics, but with a list, the things you
are copying are, themselves, references.
Anything is open to debate, but how else could this work, and satisfy
other requirements at the same time?
>
> **Mapping operations**
>
> Recent newsgroup discussions have offered some convincing arguments for
> treating a matrix as a mapping type where sequences of ints are used as
> the keys. The sequence must be of length less than or equal to the numer
> of dimensions in the matrix. Each element of the sequence is either an
> integer or a sequence. If it is an integer, it returns the corresponding
> element for that dimension, if it is a sequence then it returns all of the
> elements given in the sequence.
>
> ie. A[0] -> 1, A[((1,2))] -> [2,3]
> M[0] -> [1,2,3,4,5]
> M[(0,3)] -> 4
> M[((0,2),0)] -> [1,21]
> M[(range(1,3),range(2,4))] -> [[13,14],[23,24]]
A way of visualizing this is as multi-dimensional slices that let you,
for example, take a rectangular slice out of a 2-d matrix.
> In order to simplify the syntax of these references, it would be valuable
> to change the syntax of python so that M[0,3] is equivalent to M[(0,3)].
> This is a small change to the current indexing semantics which will give a
> reasonable meaning to a currently illegal syntactic item.
>
> When used as a setvalue, the corresponding elements of the given array
> will be set to the left hand side. All of these operations that return a
> matrix will return it by-reference, meaning that the elements of the new
> array will be references to the old one and changes to either array will
> effect the other.
Note that if one wants a reference, not a copy, one can always apply a
copy operator, [:] as one often does not with lists.
>
>
> **Instantiation**
>
> The following function will be used to instantiate a matrix:
> Matrix(datasource(optional), d1, d2, ..., typecode='d')
>
> The typecodes are as follows:
> c - char (non-numeric)
> 1 - unsigned char
> b - signed char
> h - short
> i - int
> l - long
> f - float
> d - double
> F - complex float
> D - complex double
>
> The optional datasource can be a file object or a string object and will
> fill the matrix with the raw data contained therein. In this case the
> dimensions are required. The first dimension can be input as -1, in which
> case the first dimension will be set to be as large as necessary to hold
> the entire contents of the file or string.
>
> The optional datasource can also be a sequence, in which case the
> dimensions of the array do not need to be passed in and are instead
> determined by the hierarchical structure of the sequence.
>
> examples:
> Matrix([range(3),(4,5,6)], 'i') -> [[0,1,2],[4,5,6]]
> Matrix(2,3) -> [[0,0,0],[0,0,0]]
> Matrix('abcdef', 2,3, 'u') -> [[97,98,99],[100,101,102]]
> Matrix('abcdef', -1,2, 'c') -> ['ab', 'cd', 'ef']
Note that I have found it convenient to implement matrices so that 1-d
matrices of chars behave alot like strings, as I often pass these to
or get these back from Fortran routines in which they are just that.
>
> **Coercion**
>
> Dimension coercion:The following is a proposal to make the matrix object
> behave as closely as possible to matrices in matlab (to make it easy to
> convert matlab code to python), and to generalize this behavior to
> arbitrarily high dimensional objects.
>
> If a matrix of fewer dimensions than that required is provided, then the
> matrix can be converted to more dimensions by making copies of itself.
> For purposes of dimension coercion, an int or a float is considered a 0-d
> matrix.
>
> ie. A+1 -> [2,3,4], A+C -> [[2,6,12],[17,27,39]]
> B^2 -> [121,144,169] vs. B^A -> [11, 144, 2197]
I agree wrt scalars.
> Simliar coercion should be performed for most functions acting on
> matrices. Helper functions will be provided to make this easy, however
> there is no way to enforce this. The following is a proposed standard to
> allow functions to be applied to higher-dimensional objects in a uniform
> way.
>
> If a function is defined to operate on scalars (say sqrt()), then when
> applied to a matrix it should apply itself to every element in the matrix.
>
> ie. sqrt(C) -> [[1,2,3],[4,5,6]]
>
> This is generalized to higher dimensions so that if a function is provided
> with a matrix of more dimensions that it requires, that function will
> apply itself to each appropriately sized unit in the matrix.
>
> ie. sum(A) -> 6 whereas sum(C) -> [14,77]
I am a bit concerned that this will lead to hard to find bugs. This
also put's extra burden on the function implementor. I realize (since
you explained it to me in a separate note :) your concern that this is
needed for efficiency, otherwise the map operator, or perhaps a
specialized matrix map operator could be used.
>
> Type coercion: No type coercion will be performed. However, it is
> possible to have a function defined to work on multiple types (operator
> overloading). So sqrt might be defined on matrices of both floats and
> doubles in which case it will work properly when given either matrix as
> input, but will return a type error if applied to a matrix of ints.
>
>
> **Basic Data Items and Methods (non-numeric)**
>
> typecode - The typecode character used to create the matrix
>
> itemsize - The length in bytes of one matrix item in the internal
> representation
>
> dimensions - A sequence containing the size of the matrix along each of
^^^^^^^^^^ tuple
> its dimensions
>
> copy - Returns a copy of the matrix. This is a good way to take an array
> returned by reference and turn it into one returned by value.
This is not needed. Use [:].
> transpose - The transpose of the matrix (not needed if "~" is used for
> transpose)
I'm thinking that this (or ~) should return by reference.
> byteswap - Useful for reading data from a file written on a machine with a
> different byte order
Hm. Does this operate in-place or return a value?
You know, there really *should* be a matrix map operator. This should
function like the standard map operator, but it will generate a matrix
and it will probably want to be told at what level/dimension to apply
the mapping function. In fact, this would give you arbitrary
elementwise operations given scalar functions.
>
> typecast(t) - Returns a new matrix of the same dimensions where each item
> is cast (using C typecasting rules) from the matrix's type to the new type
> t.
Our constructor already gives you this:
new_matrix=Matrix(old_matrix,new_type)
> tofile(f) - Write all items (as machine values) to the file object f.
>
> tolist() - Convert the matrix to an ordinary (possibly nested) list with
> the same items.
>
> tostring() - Convert the array to an array of machine values and return
> the
> string representation (the same sequence of bytes that would
> be written to a file by the tofile() method.)
>
> Note: the append, fromlist, etc. methods are not present because it is
> assumed that a matrix object is of constant size (there are some strong
> efficiency reasons for this assumption but this is likely to be another
> source of arguments).
>
> **Basic functions taking matrix arguments (non-numeric)**
>
> Since it isn't possible to append to a matrix (or efficient if it's
> decided it should be possible) I thought that some sort of function for
> producing a new matrix from a list of matrices would be useful. So I'm
> proposing the following function which is very similar in spirit to the
> string.join operation.
>
> concat(l) - l must be a list of matrices, and each matrix in l must have
> the same number of dimensions, as well as the same size for every
> dimension except for the first. This will return a new matrix M whose
> first dimension is the sum of the first dimensions of all the matrices in
> the list, and whose data is filled from the data in all of these matrices.
>
> ie. concat([A,B]) -> [1,2,3,11,12,13]
>
> This is different from Matrix([A,B]) -> [[1,2,3],[11,12,13]]
>
>
> **Basic functions taking matrix arguments (numeric)**
>
> It is hoped that there will be a large variety of special purpose modules
> to do fancy numeric operations on matrices. There should also be a basic
> set of operations provided in the Matrix module (the module with the
> instantiation function). Because type coercion is not going to be
> performed, it is important to decide both on the list of functions and on
> the basic types they should support. The following is a very preliminary
> list of the functions I consider most fundamental.
>
> rng(start, stop, step(optional), typecode='d') where all points in the
> range must be strictly less than the stop point, according to the
> semantics of range(). This function should possibly be added to the
> instantiation options.
>
> ie. rng(0,0.4) = [0,0.1,0.2,0.3]; rng(0,0.1,0.41) = [0,0.1,0.2,0.3,0.4]
>
Hm. Why not:
Matrix.range(start=1,stop,step=1, typecode='d')
and
Matrix.range([(start=1,stop,step=1), ...], typecode='d')
That is:
call it range, and give it same semantics, and
include a multi-dimensional range operator, mrange, that takes a
sequence of range specs.
>
> scalar operations (defined on floats, doubles, and all complex):
> everything in mathmodule.c
> round
>
> scalar operations (defined on all numeric types):
> sign
>
> vector to scalar operations (defined on all numeric types):
> max, min - Should these return the index of the max or min?
> sum, prod
>
> vector to vector operations (defined on all numeric types):
> cumsum, cumprod
Comments anyone?
-- Jim Fulton jfulton@usgs.gov (703) 648-5622
U.S. Geological Survey, Reston VA 22092
This message is being posted to obtain or provide technical information
relating to my duties at the U.S. Geological Survey.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 12 03:43:15 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id DAA03226 for matrix-sig-people; Tue, 12 Sep 1995 03:43:15 -0400
Received: from big.fishnet.net (root@big.fishnet.net [205.216.133.3]) by python.org (8.6.12/8.6.12) with ESMTP id DAA03222 for ; Tue, 12 Sep 1995 03:43:11 -0400
Received: from port32.fishnet.net (port32.fishnet.net [205.216.133.232]) by big.fishnet.net (8.6.12/8.6.9) with SMTP id AAA31805 for ; Tue, 12 Sep 1995 00:46:03 GMT
Message-Id: <199509120046.AAA31805@big.fishnet.net>
X-Sender: graham@fishnet.net
X-Mailer: Windows Eudora Pro Version 2.1.2
Mime-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Date: Tue, 12 Sep 1995 00:46:50 -0700
To: matrix-sig@python.org
From: Graham Hughes
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
Sender: owner-matrix-sig@python.org
Precedence: bulk
At 07:04 PM 9/11/95 -0400, you wrote:
> o I wanted a python built-in data type containing a data structure
> that I could pass directly to Fortran or C routines. I did not
> want to have to do alot of data conversion or copying on each
> call. For example, if a Fortran routine returns a matrix, then
> it should be possible to pass this matrix to another Fortran
> routine without reallocating a data structure or converting the
> entire matrix.
Instant problem right there. To the best of my knowledge (and I've read this
in several books), Fortran and C use completely different ways of storing
multidimensional arrays. To wit: given the array m, like this:
3 4 5 6
7 8 9 0
3 -2 4 1
There are two ways of storing this array in a continuous memory location; by
rows or by columns. By rows looks like this: '3 4 5 6 7 8 9 0 3 -2 4 1', and
is used by both C and Pascal. By columns looks like this: '3 7 3 4 8 -2 5 9
4 6 0 1', and is used by Fortran. In other words, to pass to either Fortran
or C functions you're going to need to know which type of function you have,
and then convert the internal representation.
This can be done, but it greatly complicates things.
>> "+", "-", "*", "/", "^"(as power), abs, unary"-", and unary"+" are defined
>> as the corresponding element-wise arithmetic operations. Because these
>
>I'd rather see multiplication and division not be elementwise. But I
>don't feel strongly about this.
Ah, but I'd prefer it be elementwise because it allows me to do wonderful
APLish things with it :)
A good reason for having multiplication/division (same thing on a
mathematical level, although much stickier anywhere else) elementwise is
that addition and subtraction already are, and it would be confusing to do
it otherwise. However, a better reason is the inherent complications with
matrix multiplications and divisions; specifically, multiplications require
that the matricies have their rows and columns match in specific ways that
are not immediately intuitive, and division is worse; not only does one need
to be square, but the square matrix must be invertible, a condition not
always fulfilled. Simple elementwise multiplication and division eliminates
these non-intutive requirements in exchange for the same requirements that
you have with addition and subtraction; both matricies must simply be of
equal dimension.
I'm not by any means suggesting that we not incorporate the matrixwise
multiplication and division, but perhaps it would be better to have these as
separate functions, both to avoid the above problems and to highlight their
non-intuitive natures (I mean, really, there *still* isn't an easy method to
invert a matrix, and the dimension requirements for matrix multiplication
probably took us all a while to get used to)...
>> Possible suggestions for the other numeric operations:
>> "%" represents matrix multiplication (or vector dot product).
>
>If we go with elementwise interpretation for * and /, then I think
>that all operators that make sense for floating point numbers should
>have an elementwise interpretation, so m%spam should compute a matrix
>of mods. Perhaps one would carry this same argument to bitwise
>operators, since you could have matrices of integers.
I agree. From experience with several kinds of matricies, I'm inclined to
think that something similar to APL would be a really good idea...
Given that, we *could* implement matrix multiplication, dot products, etc.
as something similar to APL's outer and inner product functions... This
would make it more general than simply matrix multiplication and therefore
more useful, and the matrix multiplication could simply be a function that
hardcodes a certain inner product operation...
For those that haven't been exposed to APL, the inner and outer product
functions are ways of implementing element-by-element functions to
generalized tensors (although they're usually used on vectors and matricies
only).
An outer product returns a table of results of an operation foo on all pairs
of one from matrix bar and one from matrix baz. That is, it does
foo(bar[i],baz[j])
for each 0 <= i < len(bar) and 0 <= j < len(baz). Naturally, it works best
in this form on vectors, but there is nothing preventing it being
implemented on matricies; mail me for additional information on how to do
this, as I need time to grovel over the manuals again so I can remember how
to do it :)
An inner product is similar; it takes two functions, foo and bar, and two
matricies, baz and quux. It performs
reduce(foo, (SUBSET1))
where SUBSET1 is
bar(baz[i],quux[i])
for each 0 <= i < len(baz). The entirety can be formulated more like
reduce(foo, map(bar, baz[0][0:], quux[0][0:]))
but it is a little less clear IMHO. Plus, I'm not really sure I got those
slices right; they're supposed to be the first row...
One intriguing possiblity with using the APL stuff is it allows mixing and
matching between matrix dimensions; there is nothing that says that you
cannot perform an outer product on a matrix and a vector. This is even
occasionally useful.
(Some may ask, why the APL stuff? This is because I am convinced that APL is
doing the Right Thing when it comes to matricies. J supports an even richer
set of 'adverbs', but I have no intention of infecting Python with that kind
of complexity ;)
>
>If we go with elementwise interpretation of numeric operations, then
>I'm inclined to use functional, rather than matrix notation for matrix
>multiplication, dot products, and so on.
This matches with what I said *waaay* back before the inner and outer
product stuff.
>Perhaps, as a compromize, there could be a special method or operator
>that creates a reference copied version of a matrix that provides a
>different semantics. For example, perhaps the default matrix type
>could provide standard matrix interpretation for * and /, but there
>could be a method, elementwise that would allow:
>
> m.elementwise() + a
>
>to do elementwise arithmentic.
I suppose if you use standard matrix interpretation for * and /, but if we
don't and use elementwise stuff instead for the reasons already mentioned,
than we can completely avoid this problem...
>IMPORTANT NOTE: Mixed-mode arithmetic between matrices and scalars
> should be defined.
Here, we can stand on math; it is similar to elementwise arithmetic, i.e. m
+ s where m is a matrix and s is a scalar adds s to every element of m. This
seems acceptable, and even intutive, to me.
>> Concatenation and multiplication sequence operations are not defined, but
>> instead the arithmetic operations are invoked for the "+" and "*"
>> operators.
>
>Right. Note if we don't need elementwise bitwise operators, then we
>could concatinate with "|", which I find most natural in a matrix
>context:
>
> spam = foo | bar
I can see your point... Dear me, looks like what we need are some APLlike
functions... (kicks self)
I happen to like having the "|" operator do bitwise stuff, simply because
somebody's going to ask "well, what does & do?". They complement each other
nicely.
>I suppose one could even repeat with >> or <<, but this is not so natural.
Yeah, plus << can give you a really *quick* multiplication by 2.
>> Recent newsgroup discussions have offered some convincing arguments for
>> treating a matrix as a mapping type where sequences of ints are used as
>> the keys. The sequence must be of length less than or equal to the numer
>> of dimensions in the matrix. Each element of the sequence is either an
>> integer or a sequence. If it is an integer, it returns the corresponding
>> element for that dimension, if it is a sequence then it returns all of the
>> elements given in the sequence.
>>
>> ie. A[0] -> 1, A[((1,2))] -> [2,3]
>> M[0] -> [1,2,3,4,5]
>> M[(0,3)] -> 4
>> M[((0,2),0)] -> [1,21]
>> M[(range(1,3),range(2,4))] -> [[13,14],[23,24]]
>
>A way of visualizing this is as multi-dimensional slices that let you,
>for example, take a rectangular slice out of a 2-d matrix.
Definitely useful; the most intutive (although not necessarily the fastest)
way of calculating a determinant is to use multi-dimensional slices; I was
stymied by the 'normal' Python list-of-lists implementation on this note
when trying to implement a matrix inverter.
>> **Instantiation**
>>
>> The following function will be used to instantiate a matrix:
>> Matrix(datasource(optional), d1, d2, ..., typecode='d')
>>
>> The typecodes are as follows:
>> c - char (non-numeric)
>> 1 - unsigned char
>> b - signed char
>> h - short
>> i - int
>> l - long
>> f - float
>> d - double
>> F - complex float
>> D - complex double
I'm not fond of this, as it breaks normal Python typing; let the matrix
figure out what's there. Matter of fact, might be a good idea to make the
matrix a general container like lists are now, so you can store, say, a
tuple representing a complex number in there? As it stands, with these
restrictions you can forget it. However, this makes it slightly more work
for those wonderful Fortran library routines.
>> **Coercion**
>>
>> Dimension coercion:The following is a proposal to make the matrix object
>> behave as closely as possible to matrices in matlab (to make it easy to
>> convert matlab code to python), and to generalize this behavior to
>> arbitrarily high dimensional objects.
>>
>> If a matrix of fewer dimensions than that required is provided, then the
>> matrix can be converted to more dimensions by making copies of itself.
>> For purposes of dimension coercion, an int or a float is considered a 0-d
>> matrix.
>>
>> ie. A+1 -> [2,3,4], A+C -> [[2,6,12],[17,27,39]]
>> B^2 -> [121,144,169] vs. B^A -> [11, 144, 2197]
>
>I agree wrt scalars.
>
Interesting... I like it. So what happens if I convert a matrix of [1,2,3]
into a 4x2 ([[x,x,x,x],[x,x,x,x]])? Do we get it wrapping around like
[[1,2,3,1],[2,3,1,2]]?
>> Simliar coercion should be performed for most functions acting on
>> matrices. Helper functions will be provided to make this easy, however
>> there is no way to enforce this. The following is a proposed standard to
>> allow functions to be applied to higher-dimensional objects in a uniform
>> way.
>>
>> If a function is defined to operate on scalars (say sqrt()), then when
>> applied to a matrix it should apply itself to every element in the matrix.
>>
>> ie. sqrt(C) -> [[1,2,3],[4,5,6]]
>>
>> This is generalized to higher dimensions so that if a function is provided
>> with a matrix of more dimensions that it requires, that function will
>> apply itself to each appropriately sized unit in the matrix.
>>
>> ie. sum(A) -> 6 whereas sum(C) -> [14,77]
Definitely. Back to the elementwise operations.
>I am a bit concerned that this will lead to hard to find bugs. This
>also put's extra burden on the function implementor. I realize (since
>you explained it to me in a separate note :) your concern that this is
>needed for efficiency, otherwise the map operator, or perhaps a
>specialized matrix map operator could be used.
I see your point. It might be better to use that matrix map operator... Best
of both worlds, I guess. Oh, for APL's parallelism ;) That might not be a
bad idea, but I think it would be a nasty bit of work for the interpreter
and underlying source code.
>>
>> Type coercion: No type coercion will be performed. However, it is
>> possible to have a function defined to work on multiple types (operator
>> overloading). So sqrt might be defined on matrices of both floats and
>> doubles in which case it will work properly when given either matrix as
>> input, but will return a type error if applied to a matrix of ints.
Well, if we simply let the caller figure out what it's got in it, like what
I suggested above, this would be unnecessary. Let sqrt() (or indirectly,
Python) figure out whether it's a double, int, or a complex.
>> byteswap - Useful for reading data from a file written on a machine with a
>> different byte order
>
>Hm. Does this operate in-place or return a value?
>
>You know, there really *should* be a matrix map operator. This should
>function like the standard map operator, but it will generate a matrix
>and it will probably want to be told at what level/dimension to apply
>the mapping function. In fact, this would give you arbitrary
>elementwise operations given scalar functions.
Or, we can always use inner/outer products... :)
>> typecast(t) - Returns a new matrix of the same dimensions where each item
>> is cast (using C typecasting rules) from the matrix's type to the new type
>> t.
>
>Our constructor already gives you this:
>
> new_matrix=Matrix(old_matrix,new_type)
>
Unnecessary if the matrix is a container.
>> concat(l) - l must be a list of matrices, and each matrix in l must have
>> the same number of dimensions, as well as the same size for every
>> dimension except for the first. This will return a new matrix M whose
>> first dimension is the sum of the first dimensions of all the matrices in
>> the list, and whose data is filled from the data in all of these matrices.
>>
>> ie. concat([A,B]) -> [1,2,3,11,12,13]
Hm. Seems to run into difficulty with >1D matricies. Ideally, concat should take
2 3 4 5 6 7
and
5 6 7 8 9 10
and return
2 3 4 5 6 7
5 6 7 8 9 10
But the given behaviour of concat depends greatly on whether [[x],[y]] is
stored by rows or by columns, ie. is it like this:
[ | , | ]
V V
[x] [y]
or like this:
[
-> [x]
,
-> [y]
]
This is a conceptual difficulty.
>>
>> This is different from Matrix([A,B]) -> [[1,2,3],[11,12,13]]
>>
>>
>> **Basic functions taking matrix arguments (numeric)**
>>
>> It is hoped that there will be a large variety of special purpose modules
>> to do fancy numeric operations on matrices. There should also be a basic
>> set of operations provided in the Matrix module (the module with the
>> instantiation function). Because type coercion is not going to be
>> performed, it is important to decide both on the list of functions and on
>> the basic types they should support. The following is a very preliminary
>> list of the functions I consider most fundamental.
>>
>> rng(start, stop, step(optional), typecode='d') where all points in the
>> range must be strictly less than the stop point, according to the
>> semantics of range(). This function should possibly be added to the
>> instantiation options.
>>
>> ie. rng(0,0.4) = [0,0.1,0.2,0.3]; rng(0,0.1,0.41) = [0,0.1,0.2,0.3,0.4]
>>
>
>Hm. Why not:
>
> Matrix.range(start=1,stop,step=1, typecode='d')
>
>and
>
> Matrix.range([(start=1,stop,step=1), ...], typecode='d')
>
>That is:
>
> call it range, and give it same semantics, and
>
> include a multi-dimensional range operator, mrange, that takes a
> sequence of range specs.
>
Sounds good to me.
>Comments anyone?
Ditto. Anyone see something they'd like to pull apart (besides APL...)?
Graham
Graham Hughes Home page http://www.fishnet.net/~graham/
``I think it would be a good idea.'' -- Mahatma Ghandi, when asked what he
thought of Western civilization
finger for PGP public key.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 12 05:16:32 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id FAA03398 for matrix-sig-people; Tue, 12 Sep 1995 05:16:32 -0400
Received: from tarzan.math.jyu.fi (ji@tarzan.math.jyu.fi [130.234.32.1]) by python.org (8.6.12/8.6.12) with ESMTP id FAA03394 for ; Tue, 12 Sep 1995 05:16:15 -0400
Received: by tarzan.math.jyu.fi
(1.37.109.16/16.2) id AA077177434; Tue, 12 Sep 1995 12:17:14 +0300
X-Charset: ISO_8859-1
Date: Tue, 12 Sep 1995 12:17:13 +0300 (EETDST)
From: Jonne Itkonen
To: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
In-Reply-To: <199509120046.AAA31805@big.fishnet.net>
Message-Id:
Mime-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Tue, 12 Sep 1995, Graham Hughes wrote:
> >I'd rather see multiplication and division not be elementwise. But I
> >don't feel strongly about this.
>
> Ah, but I'd prefer it be elementwise because it allows me to do wonderful
> APLish things with it :)
I prefer the mathematical, not elementwise multiplication. We are handling
matrixes here (not tables!). Besides, one doesn't want to define the *
operator for complex numbers to do 'elementwise' multiplication, does she?
It would be meaningless!
In my opinion, the overloading of operators is to make things more clear
than to mess them up. The elementwise multiplication by * operator would
be clear to anyone with no knowledge of matrices, but not to someone who
knows what matrices are.
Perhaps a table object should be developed for elementwise calculation?
> A good reason for having multiplication/division (same thing on a
> mathematical level, although much stickier anywhere else) elementwise is
> that addition and subtraction already are, and it would be confusing to do
That is not a good reason. It's more confusing to use mathematical
definition for some, and non-mathematical definitions for other operators,
as in your '+ vs. *' example.
-Jonne
URL: http://www.math.jyu.fi/~ji/
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 12 06:26:47 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id GAA03532 for matrix-sig-people; Tue, 12 Sep 1995 06:26:47 -0400
Received: from sunic.sunet.se (sunic.sunet.se [192.36.125.2]) by python.org (8.6.12/8.6.12) with ESMTP id GAA03528 for ; Tue, 12 Sep 1995 06:26:19 -0400
From: Michal.Spalinski@fuw.edu.pl
Received: from cocos.fuw.edu.pl by sunic.sunet.se (8.6.8/2.03)
id MAA28154; Tue, 12 Sep 1995 12:26:07 +0200
Received: from albert4.fuw.edu.pl by cocos.fuw.edu.pl (4.1/SMI-4.1)
id AA17210; Tue, 12 Sep 95 12:25:44 +0200
Received: by albert4.fuw.edu.pl (4.1/SMI-4.1)
id AA01195; Tue, 12 Sep 95 12:27:57 +0200
Date: Tue, 12 Sep 95 12:27:57 +0200
Message-Id: <9509121027.AA01195@albert4.fuw.edu.pl>
To: graham@fishnet.net
Cc: matrix-sig@python.org
In-Reply-To: <199509120046.AAA31805@big.fishnet.net> (message from Graham Hughes on Tue, 12 Sep 1995 00:46:50 -0700)
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
Sender: owner-matrix-sig@python.org
Precedence: bulk
If this is going to be useful to people like me who are very fond of Python
and have concrete numerical work to do in the field of Physics or whatever,
the `*`, `+` etc really should be reserved for matrix operations (not
elementwise). This is because one would like to be able to type a
complicated formula and have it parsed correctly, and at the same time one
should be able to look at it a week later and see immediately whether a
sign is wrong or a factor is missing. This is the problem with using lisp
for this kind of thing - if the notation is different from that which one
is used to (by writing on paper) it makes it laborious to check whether the
formulae are really correct.
Regarding the APL type of stuff, outer products and so on, I think it is
worth having and potentially useful, but it should use a different syntax.
Maybe new infix operators, or just function calls - but not `+`, `*` etc.
--
Michal S.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 12 09:33:32 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id JAA04037 for matrix-sig-people; Tue, 12 Sep 1995 09:33:32 -0400
Received: from rnet.rose.rsoc.rockwell.com (rose.rsoc.rockwell.com [161.40.39.100]) by python.org (8.6.12/8.6.12) with SMTP id JAA04033 for ; Tue, 12 Sep 1995 09:33:29 -0400
Received: by rnet.rose.rsoc.rockwell.com (4.1/SMI-4.1)
id AA18295; Tue, 12 Sep 95 08:34:43 CDT
Received: from sunrise(161.40.50.11) by rnet via smap (V1.3)
id sma018293; Tue Sep 12 08:34:41 1995
Received: from feynman.rsoc.rockwell.com by rose.rsoc.rockwell.com (5.0/SMI-SVR4)
id AA29273; Tue, 12 Sep 1995 08:34:40 -0500
Received: by feynman.rsoc.rockwell.com (5.0/SMI-SVR4)
id AA24449; Tue, 12 Sep 1995 08:34:38 -0500
Date: Tue, 12 Sep 1995 08:34:38 -0500
From: forrest@rose.rsoc.rockwell.com (Dave Forrest)
Message-Id: <9509121334.AA24449@feynman.rsoc.rockwell.com>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] * and / operators (was Re: Let's get going)
X-Sun-Charset: US-ASCII
Content-Length: 1422
Sender: owner-matrix-sig@python.org
Precedence: bulk
[Heartwarming personal intro - skip if you're not interested]
Hi, I'm Dave Forrest with Rockwell Space Ops. Co. A few of you have
met my colleagues Robin Friedrich, Greg Boes, and Charlie Fly. We are
working on ground-based simulation, monitoring, and analysis software
for the Space Shuttle. So, we have mostly an engineering/physics/math
bias in our use of Python - although we're watching the python GUI work
with keen interest. We love this language!
[Real comments - start reading here]
First off I'd like to add another vote for * being a matrix multiply,
rather than elementwise. As for /, however, that doesn't really have
meaning for matrices. When we built a C++ Matrix class, we only
defined / for element-wise division by a scalar. It works quite
nicely. We end up with something like this:
Matrix operator/(Matrix, scalar)
Matrix operator*(Matrix, Matrix)
Matrix operator*(Matrix, scalar) <- elementwise muliplication by scalar
Note that this avoids the sticky situation with inversion of matrices -
we found it better to leave that for derived classes so that people
could implement different inversion routines that capitalize on
any a_priori knowledge of what's in the Matrix.
e.g. class SquareMatrix( Matrix ):
def inv():
...
That being said, I would like to see elementwise multiplication, etc.
in there somewhere - but we use them far less frequently than real
matrix operations.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 12 14:08:12 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA05437 for matrix-sig-people; Tue, 12 Sep 1995 14:08:12 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id OAA05433 for ; Tue, 12 Sep 1995 14:08:06 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id OAA19276
(8.6.11/IDA-1.6 for ); Tue, 12 Sep 1995 14:08:19 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id OAA21978; Tue, 12 Sep 1995 14:08:16 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id OAA03216; Tue, 12 Sep 1995 14:08:18 -0400
Date: Tue, 12 Sep 1995 14:08:18 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509121808.OAA03216@cyclone.ERE.UMontreal.CA>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Python, matrices, and everything
Sender: owner-matrix-sig@python.org
Precedence: bulk
[Yet another introduction - skip the next paragraph if you want to
read something about matrices...]
My name is Konrad Hinsen (*not* Hinsen Konrad, as given in my
address), and I am a computational physicist, currently working
as a postdoc at the University of Montreal. My main field of
work is computer simulations of various kinds, and I use Python
for everything that it not time-critical. My opinion about
Python is a mixture of admiration for its elegant and efficient
structure and frustration about its lack of numerical features,
which is why I joined the Matrix-SIG.
[Here comes the real stuff.]
Before jumping into technical details, it might be a good idea to
define what we are aiming at. As it has already become clear from the
dicussion, matrices are used in two different (but not entirely
distinct) ways:
1) in linear algebra applications
2) as containers of values that are structured as tables
In linear algebra, one needs mainly one- and two-dimensional matrices
of real or complex numbers. Important operations are addition and
multiplication, factorization, inversion, eigenvalues etc. There are
efficient Fortran and C libraries for these operations, so what is
needed is a suitable representation in Python that allows interfacing
to these libraries and some convenient way of accessing them, i.e.
operators (+ - *, maybe / for scalar division) and tons of operations
that are best packaged into a matrix class.
The second application is just as important (in my view) and much more
complex. As APLers know, there are lots of useful operations on tables
of values, which can be of any dimension and contain any data type,
although the numerical are the most important ones (Python already
provides good string handling). It makes sense to have all
mathematical functions and operators acting by default elementwise,
including of course multiplication, but also user-defined functions.
Basically arrays would be extensions of numerical scalars, the
latter becoming arrays of rank zero. The by far most elegant and
powerful array concept I know of is the one implemented in J, a
relatively new language that can be regarded as an improved version
of APL. I certainly don't want to take over J's other features, but
its concept of array and operator ranks is extremely useful. I'll
give a short description below.
But first let's see how the two applications can be reconciled in
a single implementation. Many of the linear algebra operations
make sense only for two-dimensional matrices, but the J approach
of operator rank takes care of the nicely. And even without
that approach, this would only mean some more error checking for
such operations. The only point of conflict is the implementation
of multiplication (there is no difference for addition and
subtraction). Linear-algebra style matrix multiplication is
regarded as an inner product with addition and multiplication
operations from an APL/J point of view, so it is available,
but arguably not in the most convenient form. On the other hand,
once the * symbol means matrix multiplication, the total
general structure of elementwise operations is ruined.
I therefore propose to introduce some other symbol for matrix
multiplication. One could choose some combination like ".*" or "*.",
which looks similar enough to plain multiplication to make its
meaning evident.
And now I'll shortly describe J's rank system. Every object's rank is
identical to the number of its dimensions - a scalar has rank 0, a
vector rank 1, and so on. When used in operations however, a rank N
array is viewed as a rank A array of rank B cells, where A+B=N. A rank
3 array, for example, can be used as a three-dimensional array of
scalars, as a two-dimensional matrix of vectors, as a vector of
two-dimensional matrices, or as a "scalar" containing a
three-dimensional array. How it is used depends on the rank of the
operation acting on it. Most scalar operations have infinite rank,
which means that use their operators at the highest rank
possible. Effectively that means elementwise operation in the
classical sense, except that there are also rules that cover the
combination of object of different rank, such as multiplication of an
array by a scalar. But there are also other operations. Matrix
inversion, for example, by default operates on rank 2 cells.
So if you "invert" a three-dimensional array, this operation
will be interpreted as inversion of each rank-2 cell in the
array. The result is again a rank 3 array.
The beauty of the system is that the rank of each operation can be
changed at will, not only for the built-in operations, but also for
user-defined functions. If, for example, you want to add a rank 2
matrix to each rank 2 cell of a rank 3 array, you just specify rank 2
addition and get what you want. Nothing could be simpler or more
flexible. Unfortunately, I dislike many of J's other features as
a programming language, which is why I am still a Python user...
So that's my point of view in this discussion: let's concentrate
on APLish (or Jish) arrays and then add the operations that are
needed in linear algebra.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 12 15:33:33 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id PAA05906 for matrix-sig-people; Tue, 12 Sep 1995 15:33:33 -0400
Received: from servrcolkr.cr.usgs.gov (servrcolkr.cr.usgs.gov [136.177.112.5]) by python.org (8.6.12/8.6.12) with ESMTP id PAA05902 for ; Tue, 12 Sep 1995 15:33:28 -0400
Received: from localhost ([130.11.51.73]) by servrcolkr.cr.usgs.gov (EMAIL 1.2.1) with ESMTP id TAA12230; Tue, 12 Sep 1995 19:22:12 GMT
Message-Id: <199509121922.TAA12230@servrcolkr.cr.usgs.gov>
To: Graham Hughes
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
In-reply-to: <199509120046.AAA31805@big.fishnet.net>
Date: Tue, 12 Sep 1995 15:21:49 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Tue, 12 Sep 1995 00:46:50 -0700
Graham Hughes said:
> At 07:04 PM 9/11/95 -0400, you wrote:
> > o I wanted a python built-in data type containing a data structure
> > that I could pass directly to Fortran or C routines. I did not
> > want to have to do alot of data conversion or copying on each
> > call. For example, if a Fortran routine returns a matrix, then
> > it should be possible to pass this matrix to another Fortran
> > routine without reallocating a data structure or converting the
> > entire matrix.
>
> Instant problem right there. To the best of my knowledge (and I've read this
> in several books), Fortran and C use completely different ways of storing
> multidimensional arrays. To wit: given the array m, like this:
>
> 3 4 5 6
>
> 7 8 9 0
>
> 3 -2 4 1
>
> There are two ways of storing this array in a continuous memory location; by
> rows or by columns. By rows looks like this: '3 4 5 6 7 8 9 0 3 -2 4 1', and
> is used by both C and Pascal. By columns looks like this: '3 7 3 4 8 -2 5 9
> 4 6 0 1', and is used by Fortran. In other words, to pass to either Fortran
> or C functions you're going to need to know which type of function you have,
> and then convert the internal representation.
>
> This can be done, but it greatly complicates things.
Not really. Fortran stores data in multidimensional arrays with the
indexes changing most rapidly in the left. Some people give this the
interpretation that Fortran stores data "by column".
C multi-dimensional arrays are stored with the indexes changing most
rapidly on the right. Some people give this the interpretation that C
stores data "by row".
The difference is mainly one of interpretation. Both C and Fortran
store mult-dimensional data pretty much the same way, as a big
homogenous "rectangulish" glob of data. They differ only in the way
they order the indexes to this data, and this ordering is only
important for a certain set of interpretations.
I choose to interpret C and Python multidimensional indexes,
especially in the context of matrices, in a way in which this
difference in indexing does not present a problem. I think of
n-dimensional matrices as sequences of n-1-dimensional matrices. That
is, matrices are stored, "by sub-matrix". Now, in the case of 2-d
matrices, I was taught to think of a matrix as a collection of column
vectors. With this interpretation, C and python 2-d matrices are,
indeed, stored by column.
Consider m[i][j]. Since 2-d matrices are a collection of column
vectors, m[i] is the ith column. That is, we give the column index
*first*, and then the row index. With this interpretation, there is
no difference between C and Fortran storage formats.
(snip)
>
> >> **Instantiation**
> >>
> >> The following function will be used to instantiate a matrix:
> >> Matrix(datasource(optional), d1, d2, ..., typecode='d')
> >>
> >> The typecodes are as follows:
> >> c - char (non-numeric)
> >> 1 - unsigned char
> >> b - signed char
> >> h - short
> >> i - int
> >> l - long
> >> f - float
> >> d - double
> >> F - complex float
> >> D - complex double
>
> I'm not fond of this, as it breaks normal Python typing; let the matrix
> figure out what's there. Matter of fact, might be a good idea to make the
> matrix a general container like lists are now, so you can store, say, a
> tuple representing a complex number in there? As it stands, with these
> restrictions you can forget it. However, this makes it slightly more work
> for those wonderful Fortran library routines.
But the data must be stored in a homogenous format that can be passed
to C and Fotran libraries. Also, libraries often require specific
types, so you want control over the types used.
I suppose that the routines could be made smart enough to sniff at the
first element and check for numeric, complex (objects with re and im
attributes), or string data and pick the default type accordingly.
Note that the routines are already smart enough to convert arbitrary
compatible objects to the necessary format.
(snip)
> >> concat(l) - l must be a list of matrices, and each matrix in l must have
> >> the same number of dimensions, as well as the same size for every
> >> dimension except for the first. This will return a new matrix M whose
> >> first dimension is the sum of the first dimensions of all the matrices in
> >> the list, and whose data is filled from the data in all of these matrices.
> >>
> >> ie. concat([A,B]) -> [1,2,3,11,12,13]
>
> Hm. Seems to run into difficulty with >1D matricies. Ideally, concat should take
>
> 2 3 4 5 6 7
> and
> 5 6 7 8 9 10
>
> and return
>
> 2 3 4 5 6 7
>
> 5 6 7 8 9 10
>
> But the given behaviour of concat depends greatly on whether [[x],[y]] is
> stored by rows or by columns, ie. is it like this:
>
> [ | , | ]
> V V
> [x] [y]
>
> or like this:
>
> [
> -> [x]
> ,
> -> [y]
> ]
>
> This is a conceptual difficulty.
No, it is not a problem as long as you use the interpretation that
n-dimensional matrices are sequences of n-1-dimensional matrices.
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 12 15:56:29 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id PAA06051 for matrix-sig-people; Tue, 12 Sep 1995 15:56:29 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id PAA06047 for ; Tue, 12 Sep 1995 15:56:26 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id PAA22875
(8.6.11/IDA-1.6 for ); Tue, 12 Sep 1995 15:56:30 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id PAA00846; Tue, 12 Sep 1995 15:56:28 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id PAA08479; Tue, 12 Sep 1995 15:56:30 -0400
Date: Tue, 12 Sep 1995 15:56:30 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509121956.PAA08479@cyclone.ERE.UMontreal.CA>
To: matrix-sig@python.org
In-reply-to: <199509121922.TAA12230@servrcolkr.cr.usgs.gov> (jfulton@usgs.gov)
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
Sender: owner-matrix-sig@python.org
Precedence: bulk
Consider m[i][j]. Since 2-d matrices are a collection of column
vectors, m[i] is the ith column. That is, we give the column index
Of course you could also say that 2-d matrices are a collection
of row vectors, which then give you the other interpretation.
Anyway, I agree that this is just an interpretation problem and
not serious, although users will have to be aware of how Python
matrices correspond to Fortran/C matrices.
I suppose that the routines could be made smart enough to sniff at the
first element and check for numeric, complex (objects with re and im
attributes), or string data and pick the default type accordingly.
Speaking of complex numbers: has it ever been considered to make
them built-in objects in Python? This would simplify the matrix
implementation, and also make the math functions behave more
reasonably (i.e. sqrt(-1.) would return i instead of causing
an exception). It can't be much work and would be very useful
for numerical work.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 12 17:04:53 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id RAA06311 for matrix-sig-people; Tue, 12 Sep 1995 17:04:53 -0400
Received: from retro.jhuapl.edu (retro.jhuapl.edu [128.244.146.239]) by python.org (8.6.12/8.6.12) with ESMTP id RAA06307 for ; Tue, 12 Sep 1995 17:04:50 -0400
Message-Id: <199509122104.RAA06307@python.org>
Received: by retro.jhuapl.edu
(1.37.109.16/16.2) id AA140499955; Tue, 12 Sep 1995 17:05:55 -0400
Date: Tue, 12 Sep 1995 17:05:55 -0400
From: Chris Chase S1A
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Vector-style operations vs. matrix operations
In-Reply-To: <9509121027.AA01195@albert4.fuw.edu.pl>
References: <199509120046.AAA31805@big.fishnet.net>
<9509121027.AA01195@albert4.fuw.edu.pl>
Reply-To: chris.chase@jhuapl.edu
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Sender: owner-matrix-sig@python.org
Precedence: bulk
Hello,
This is a long post.
Self-introduction: I am an electrical engineer PhD working at the
Applied Physics Lab of Johns Hopkins University. I do a lot of remote
sensor data analysis requiring alot of rapidly developed numerical and
visualization software.
I would like to express some of my opinions regarding the elementwise
versus matrix operators discussion.
Elementwise operations for the proposed matrix objects fall under the
category of vector operations in many math libriaries. When the
matrix object is allocated in a single contiguous block of memory as
in C or Fortran (and has been proposed in this forum) vector
operations view the matrix as a one dimensional vector. These types
of libraries support as a minimum '+','-','*' (and maybe '/') in an
elementwise fashion. Each is invaluable in code that wishes to avoid
for loops.
Many popular interactive array oriented languages support vector
operations. Below I list how a few of these languages implement
vector and matrix operations.
In Matlab, vector operations (i.e. elementwise) are
called array operations and use the operators "+", "-", ".*" "./" and
".^". "*", "/", and "^" are used for matrix multiplication, right
inverse, and matrix powers, respectively.
In IDL (which I use more frequently than Matlab), "+", "-", "*", and
"/" are all vector operators. "#" is matrix multiplication (actually
"#" is used for Fortran-style arrays and "##" is used for C style
arrays which can be very confusing). There is no operator performing
matrix division.
In Mathematica, elementwise multiplication is a space or "*" and matrix
multiplication is given by the "." operator. There is not a matrix
division operator.
In APL (http://www.acm.org/sigapl/) "+", "-", "*", and "/" are
vector-style operations. They are even more general in that they can
be applied to specific ranks (as posted by Hinsen Konrad). Special
operators are used for matrix inversion. Matrix multiplication is
expressed using a combination of operators with the inner product
operator (specifically, "+.x"). As much as I like the generality and
completeness of the functional-style operators in APL and its
descendant J, I think that the operator explosion makes the code
extremely unreadable for the uninitiated.
The following are all the same in the use of vector and matrix
operations to Matlab and may be of interest for their other features
regarding arrays:
scilab http://www.inria.fr/Logiciels/SCILAB-eng.html
octave http://www.che.utexas.edu/octave.html (maintained by the FSF -
the GNU people that produce gcc, g++, emacs, etc.)
tela http://www.geo.fmi.fi/prog/tela.html
rlab ftp://evans.ee.adfa.oz.au/pub/RLaB/
or ftp://csi.jpl.nasa.gov/pub/matlab/RLaB/ (also GNU).
tela is tensor oriented (allowing >2 dimensional arrays), so it uses
generalizations of matrix multiplication and inner products and is a
nice improvenment on that point over Matlab.
-----
My preferences:
In short, I agree with the proposal by Hinsen Konrad
, i.e., "+", "-", "*", and "/" as vector
style operations but applied rank-wise.
I prefer to have arbitrary dimensioned arrays [i.e., tensors or tables
as hinsenk@ere.umontreal.ca (Hinsen Konrad) refered to them] rather
than only one dimensional vectors or two dimensional matrices.
I prefer that "+", "-", "*", "/" all perform vector style operations.
This makes their behavior consistent among each other. (The posted
comment about "*" not applying to complex valued arrays was
incorrect.)
In most of my scientific programming, I encounter these vector style
operators much more often than matrix multiplication and division.
They replace the many "for" loops that are so common in numerical
algorithms. They are displayed compactly and execute much faster than
the loops in an interpreted language.
When using higher dimensional arrays, matrix multiplication is rather
specialized even when generalized to tensors. Matrix multiplication
is actually a specialization of a general inner product (e.g., the
implementation in APL). As such, if matrix multiplication had to have
an operator I would choose a new one. Or, we would be better served
by a generalized inner product operator. But if we don't want to add
new operators I would let "*" be the more common vector (elementwise)
operator.
The Matlab approach is good, but it requires additional operators like
".*" and "./". Matlab also suffers from limitations to two dimensions
(this is the main motivation behind the development of Tela). "/"
does not generalize easily to the higher dimensions and the
generalization of "*" is useful but limited when applied (as in Tela)
only along the inner dimensions of two tensors. At the higher
dimensions the elementwise operators applied at specific ranks are
much more common and usefule then a generalized matrix multiplication
operator. If it is desirable to avoid the proliferation of operators
as in APL and J then matrix multiplication and division would be best
left as function calls.
There are additional reasons to advise against a "/" operator for
matrix division. First there is the issue of whether a left or right
inverse is desired (this is handled in Matlab by using two different
operators "/" and "\"). Second, many matrix inversion problems are
best solved using decomposition algorithms, such as LU decomposition
or SVD, that use multiple steps wherein the intermediate results of
the decomposition may saved for solving the same problem (e.g. with
many different parameter matrices.) Additionally, the user often
would like to pick which type of inversion algorithm to use and may
want to override default parameters for these algorithms. I would
prefer that matrix inversion be left to a group of explicit function
calls.
I suppose what is implemented for an array extension to Python depends
on the end goal:
1) a Matlab-like array extension for Python is oriented toward
2-dimensional matrix linear algebra. Although limited in scope,
this is useful and in particular allows very compact readable
notation for linear algebra equations. However, it also tends to
be stuck on a row/column view of arrays.
2) an array extension to handle higher dimension arrays (call them
tensors or tables) that can easily handle generalized rank
arithmetic (APL or J style as briefly set forth in Hinsen Konrad's
post) and with specialized functions for matrix multiplication and
division. Generalized inner and outer products (taking user
specified binary operators) would also be very helpful. This view
is more compatible with Jim Fulton's "n-dimensional matrices are
sequences of n-1-dimensional matrices."
In either case, I think it would be a mistake to add too many new
operators in the APL or J fashion. Such clutter would reduce the
readability of Python programs. One also runs the risk of trying to
make a subset of Python look like a functional programming language
which could further confuse users.
Sincerely,
Chris Chase
===============================
Bldg 24-E188
The Applied Physics Laboratory
The Johns Hopkins University
Laurel, MD 20723-6099
(301)953-6000 x8529
chris.chase@jhuapl.edu
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 12 17:28:06 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id RAA06443 for matrix-sig-people; Tue, 12 Sep 1995 17:28:06 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id RAA06439 for ; Tue, 12 Sep 1995 17:28:03 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id RAA25669
(8.6.11/IDA-1.6 for ); Tue, 12 Sep 1995 17:28:16 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id RAA23125; Tue, 12 Sep 1995 17:28:14 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id RAA12993; Tue, 12 Sep 1995 17:28:16 -0400
Date: Tue, 12 Sep 1995 17:28:16 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509122128.RAA12993@cyclone.ERE.UMontreal.CA>
To: matrix-sig@python.org
In-reply-to: <199509122104.RAA06307@python.org> (message from Chris Chase S1A on Tue, 12 Sep 1995 17:05:55 -0400)
Subject: Re: [PYTHON MATRIX-SIG] Vector-style operations vs. matrix operations
Sender: owner-matrix-sig@python.org
Precedence: bulk
In either case, I think it would be a mistake to add too many new
operators in the APL or J fashion. Such clutter would reduce the
readability of Python programs. One also runs the risk of trying to
make a subset of Python look like a functional programming language
which could further confuse users.
Let me add that I totally agree about that. There is no point
in re-creating APL or J, especially since Python is already
a better language in many respects (especially when it comes
to legibility).
All I would like to take over from J is its array and rank
concept. In terms of array-specific operations, I suggest
inner and outer product, transpose (in the general sense),
and reduction (I hope I haven't forgotten anything). These
would be implemented as methods, not as special operators as
in APL or J. Together with the elementwise operations
this would give an array system superior to most languages
(except for APL/J of course) and extremely useful both
for linear algebra and all kinds of data handling.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 12 21:23:03 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id VAA07211 for matrix-sig-people; Tue, 12 Sep 1995 21:23:03 -0400
Received: from CNRI.Reston.VA.US (cnri.reston.va.us [132.151.1.1]) by python.org (8.6.12/8.6.12) with SMTP id VAA07207 for ; Tue, 12 Sep 1995 21:23:01 -0400
Received: from monty.cnri.reston.va.us by CNRI.Reston.VA.US id aa21291;
12 Sep 95 21:22 EDT
Received: from localhost by monty via SMTP (940816.SGI.8.6.9/940406.SGI)
id VAA23857; Tue, 12 Sep 1995 21:22:17 -0400
Message-Id: <199509130122.VAA23857@monty>
To: Hinsen Konrad
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
In-reply-to: Your message of "Tue, 12 Sep 1995 15:56:30 EDT."
<199509121956.PAA08479@cyclone.ERE.UMontreal.CA>
References: <199509121956.PAA08479@cyclone.ERE.UMontreal.CA>
From: Guido van Rossum
X-Face: S#(=*/yxQ]{^7V>yr9ElReWJZvi^x!]tR6g^m5q?|f@/ChslBZm*tWH=*aicc{$>4c/M:$.
\@@o)psQitBQa!z()VjNsj+aqtDI/J0jX'a/=Qhn|gv6coKW}06%6gyuTn)oc6
Date: Tue, 12 Sep 1995 21:22:17 -0400
Sender: owner-matrix-sig@python.org
Precedence: bulk
> Speaking of complex numbers: has it ever been considered to make
> them built-in objects in Python? This would simplify the matrix
> implementation, and also make the math functions behave more
> reasonably (i.e. sqrt(-1.) would return i instead of causing
> an exception). It can't be much work and would be very useful
> for numerical work.
Hm. I think it *would* be much work, since the standard C math
library doesn't have any functions working on complex numbers.
I have a specific objection against making sqrt(-1) returning a
complex number. In Python, as a rule, the *type* of a function's
return value may depend on the *type* of its argument(s), but not in
their *value*. Even though there is nothing (or at least very little)
in the language's implementation that enforces this, it is a
consistent convention throughout the language. It even has one
important visible consequence: 1/3 is calculated as truncating, as in
C (though it truncates towards -infinity rather than towards 0),
rather than returning a floating point value if the result cannot be
represented as an integer.
I believe this is an important property -- it makes reasoning about
the types of variables/arguments/functions in a Python program easier
(and sooner or later, this reasoning is going to be done by programs
as well as by people).
--Guido van Rossum
URL:
(I have more to say about the matrix discussion but I need some more
time to digest everything that's been said.)
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 00:17:59 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id AAA07616 for matrix-sig-people; Wed, 13 Sep 1995 00:17:59 -0400
Received: from big.fishnet.net (root@big.fishnet.net [205.216.133.3]) by python.org (8.6.12/8.6.12) with ESMTP id AAA07612 for ; Wed, 13 Sep 1995 00:17:55 -0400
Received: from port26.fishnet.net (port26.fishnet.net [205.216.133.226]) by big.fishnet.net (8.6.12/8.6.9) with SMTP id VAA22737; Tue, 12 Sep 1995 21:20:12 GMT
Message-Id: <199509122120.VAA22737@big.fishnet.net>
X-Sender: graham@fishnet.net
X-Mailer: Windows Eudora Pro Version 2.1.2
Mime-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Date: Tue, 12 Sep 1995 21:21:25 -0700
To: Jonne Itkonen
From: Graham Hughes
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
Cc: matrix-sig@python.org
Sender: owner-matrix-sig@python.org
Precedence: bulk
At 12:17 PM 9/12/95 +0300, you wrote:
>I prefer the mathematical, not elementwise multiplication. We are handling
>matrixes here (not tables!). Besides, one doesn't want to define the *
>operator for complex numbers to do 'elementwise' multiplication, does she?
>It would be meaningless!
Ah, but would it be?
Part of the reason why I wanted to do elementwise multiplication and
division is that matrix multiplication is nearly trivial with inner and
outer products. E.g.; you want your `normal' complex multiplication? Try
outer_product(lambda x,y: x+y,lambda x,y: x*y, v1, v2)
which can easily be all a member function calls. Similarly, (BTW, this works
with APL's data parallelism; depending on how we implement outer_product it
may be a bit nastier), matrix multiplication is this:
outer_product(lambda x,y: x+y, lambda x,y: x*y, m1, m2.transpose())
(Note the transposition.)
Finally, we reach the question of efficiency. No matter how you implement
it, matrix multiplication will be slow(er). Divison, besides which it won't
always work, will take roughly forever; APL is quick on this note, but
that's because to do matrix multiplication you use a special purpose
function. This, and the odd dimension requirements for matrix
multiplication, are why I'm not fond of having the `mathematical' methods be
the default. Besides, if we do it that way, we can treat one-dimensional
vectors consisting of only one element (i.e. [3] or some such) essentially
as scalars, which is a parallelism I like; whereas matrix multiplication and
division will take 10 times as long to get the same answer.
>
>In my opinion, the overloading of operators is to make things more clear
>than to mess them up. The elementwise multiplication by * operator would
>be clear to anyone with no knowledge of matrices, but not to someone who
>knows what matrices are.
Correct me if I'm wrong, but doesn't matrix multiplication use a special
syntax all its own? All the matricies are either boxed or in boldface, some
times they even use 'X' instead of a dot, all to make absolutely sure that
the reader gets the point. Great efforts are taken to make sure that anyone
reading knows that the multiplication does weird stuff here (because weird
stuff happens; where else do you take two things of unequal size and come up
with something else with a possibly completely different size?)
I understand the engineering tendency to think in terms of matrix
multiplication, but there is a precedent for divorcing it from the matrix
addition; the two are radically different operations.
>
>Perhaps a table object should be developed for elementwise calculation?
Probably not; too much common code to make it worthwhile. Besides, since
Python is typeless, we *cannot* distinguish between multiplication by a
scalar or by a matrix. There is no typeof() function to distinguish them. To
avoid bizarrities concerning *scalar* multiplication, we must use
elementwise multiplication. If this were C++, we could use two seperate
operator *()s, but it's not. And we're pleased about that most of the time :)
>That is not a good reason. It's more confusing to use mathematical
>definition for some, and non-mathematical definitions for other operators,
>as in your '+ vs. *' example.
To you. To the person coming from APL, J or some other similar languages,
it's the other way around. Plus it raises problems dealing with scalar
multiplication.
Graham
Graham Hughes Home page http://www.fishnet.net/~graham/
``I think it would be a good idea.'' -- Mahatma Ghandi, when asked what he
thought of Western civilization
finger for PGP public key.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 09:20:16 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id JAA08901 for matrix-sig-people; Wed, 13 Sep 1995 09:20:16 -0400
Received: from servrcolkr.cr.usgs.gov (servrcolkr.cr.usgs.gov [136.177.112.5]) by python.org (8.6.12/8.6.12) with ESMTP id JAA08897 for ; Wed, 13 Sep 1995 09:20:11 -0400
Received: from localhost (dsjfqvarsa.er.usgs.gov [130.11.51.73]) by servrcolkr.cr.usgs.gov (EMAIL 1.2.1) with ESMTP id NAA07565; Wed, 13 Sep 1995 13:21:01 GMT
Message-Id: <199509131321.NAA07565@servrcolkr.cr.usgs.gov>
To: Graham Hughes
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
In-reply-to: <199509122120.VAA22737@big.fishnet.net>
Date: Wed, 13 Sep 1995 09:21:09 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
These are good discussions. I'm going to have to find some time to
digest what's been said. Here are a few quick comments that don't
require much thought. ;-)
On Tue, 12 Sep 1995 21:21:25 -0700
Graham Hughes said:
> At 12:17 PM 9/12/95 +0300, you wrote:
(snip)
> Finally, we reach the question of efficiency. No matter how you implement
> it, matrix multiplication will be slow(er). Divison, besides which it won't
> always work,
A number of people (not to pick on you) have made the agument that
"such and such shouldn't be done because it won't always work". I
really don't think this is a valid argument. Most, if not all, of the
operations and functions have some preconditions. You can't do
elementwise arithmetic unless both operands have the same dimensions.
You can't do elementwise division if *any* elements of the divisor are
zero. Heck, scalar division fails if a divisor is zero, but Guido was
still kind enough to include "/" in teh language. (Whew. :-)
> Probably not; too much common code to make it worthwhile. Besides, since
> Python is typeless,
Python is not typeless, only it's variables are.
> we *cannot* distinguish between multiplication by a
> scalar or by a matrix.
Not necessarily by reading the code.
> There is no typeof() function to distinguish them.
No, it's called type(). There's also hasattr, which can be used to
check basic object signatures.
> To
> avoid bizarrities concerning *scalar* multiplication, we must use
> elementwise multiplication. If this were C++, we could use two seperate
> operator *()s, but it's not.
>
It is easy enough to handle this with a check in the single *
operator.
In fact, I think that whathever we do, numeric operations involving
matrices should work with:
o Matrices and matrices,
o Matrices and scalars, and
o Matrices and sequences of sequences (non-Matrix objects that
support similar multi-dimensional access).
Supporting this does not really depend on whether a element-wise
interpretation is used for *.
Note that applying the mapping operator to take matrix
slices will *not* return matrix objects, since matrix objects are
supposed to contain contiguous globs of memory, so that their data can
be passed to C and Fortran libraries directly. These matrix slices
will reference matrix objects so that modification of slice elements
will be reflected in the matrices from which they are derived, but for
mathematical operations, they will probably use different code.
My suspicion is that there will be code that:
o Operates on two matrices (and perhaps a matrix and a scalar),
using knowledge of internal data structures to optimize
performance,
and separate code that
o Operates on sequences of sequences. These will be written in C,
for speed, but will use PySequence_GetItem calls to access
operand data.
>
> >That is not a good reason. It's more confusing to use mathematical
> >definition for some, and non-mathematical definitions for other operators,
> >as in your '+ vs. *' example.
Actually, the "mathematical" interpretation of + happens to be the
same as the elementwise interpretation.
Still, most of the people who call for a mathematical interpretation
of * seem only to want that operator to be non-elementwise. This
seems to me to stengthen the case of those who want an elementwise
interpretation for all operations.
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 09:45:11 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id JAA09047 for matrix-sig-people; Wed, 13 Sep 1995 09:45:11 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id JAA09043 for ; Wed, 13 Sep 1995 09:45:08 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id JAA08870
(8.6.11/IDA-1.6); Wed, 13 Sep 1995 09:44:59 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id JAA06209; Wed, 13 Sep 1995 09:44:57 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id JAA26202; Wed, 13 Sep 1995 09:45:03 -0400
Date: Wed, 13 Sep 1995 09:45:03 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509131345.JAA26202@cyclone.ERE.UMontreal.CA>
To: guido@CNRI.Reston.VA.US
CC: matrix-sig@python.org
In-reply-to: <199509130122.VAA23857@monty> (message from Guido van Rossum on Tue, 12 Sep 1995 21:22:17 -0400)
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
Sender: owner-matrix-sig@python.org
Precedence: bulk
Hm. I think it *would* be much work, since the standard C math
library doesn't have any functions working on complex numbers.
True, but complex libraries are easily available (e.g. GNU-C comes
with one).
I have a specific objection against making sqrt(-1) returning a
complex number. In Python, as a rule, the *type* of a function's
return value may depend on the *type* of its argument(s), but not in
their *value*. Even though there is nothing (or at least very little)
There is no need to make real and complex numbers different types. A
real number would just be a complex number with an imaginary part of
zero. Of course internally there should be a difference for efficiency
reasons, but the user need not see it. Contrary to integers, reals
numbers need not have a special type since any operation on complex
numbers with zero imaginary part is exactly equivalent to an operation
on real numbers (for integers there is a difference in division).
consistent convention throughout the language. It even has one
important visible consequence: 1/3 is calculated as truncating, as in
C (though it truncates towards -infinity rather than towards 0),
rather than returning a floating point value if the result cannot be
represented as an integer.
And I am sure this feature has already led to some surprises for
people who thought they could use Python like a pocket calculator.
For languages like Python, where numerical efficiency does not have
top priority, I'd consider it more useful to have a number model that
reflects mathematical categories instead of internal storage format
categories. There should be only one type "number" with
representations for integers, fractions, floating point numbers, and
complex numbers, the latter having real and imaginary parts that can
be of any of the basic representations. Then 1/3 would return a
fraction. This is what anyone without programming experience would
expect.
Of course I realize that it is much too late to introduce such a
change...
I believe this is an important property -- it makes reasoning about
the types of variables/arguments/functions in a Python program easier
(and sooner or later, this reasoning is going to be done by programs
as well as by people).
I certainly agree on that.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 11:57:55 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id LAA09738 for matrix-sig-people; Wed, 13 Sep 1995 11:57:55 -0400
Received: from mama-bear.LCS.MIT.EDU (mama-bear.lcs.mit.edu [18.27.0.167]) by python.org (8.6.12/8.6.12) with SMTP id LAA09734 for ; Wed, 13 Sep 1995 11:57:52 -0400
Received: from nineveh.LCS.MIT.EDU by mama-bear.LCS.MIT.EDU (4.1/SLS-4.1.1)
id AA00677; Wed, 13 Sep 95 11:58:13 EDT
Received: by nineveh.LCS.MIT.EDU (NX5.67e/SMI-4.1)
id AA04152; Wed, 13 Sep 95 12:04:44 -0400
Message-Id: <9509131604.AA04152@nineveh.LCS.MIT.EDU>
Content-Type: text/plain
Mime-Version: 1.0 (NeXT Mail 3.3 v118.2)
Received: by NeXT.Mailer (1.118.2)
From: James Hugunin
Date: Wed, 13 Sep 95 12:04:44 -0400
To: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Vector-style operations vs. matrix operations
References: <199509120046.AAA31805@big.fishnet.net>
<9509121027.AA01195@albert4.fuw.edu.pl>
<199509122104.RAA06307@python.org>
Sender: owner-matrix-sig@python.org
Precedence: bulk
Short personal intro, feel free to skip:
Hi, my name's Jim Hugunin and I'm a PhD student working in MIT's Laboratory
for Computer Science. My current work involves designing speech
recognition systems. My Master's thesis involved the fabrication and
computer modeling of superconducting transistors (using the Bogoliubov
deGennes equations if anyone cares) so I'm reasonably familiar with
numerical modelling in both the EE and the physics communities.
I'm the one who wrote the initial proposal for the matrix object that Jim
Fulton posted to start this discussion, so you already know my opinions on
most of these issues. I've also implemented a matrix object (on top of Jim
Fulton's) which provides most of the capabilities described in the proposal.
Real comments begin here:
First, some posts seem to be placing too much stock in the name "matrix"
object. I propose that the name should be changed to either table or tensor
(as mentioned by Chris Chase) if this is necessary to quell the arguments
that a "matrix" should behave like a mathematical "matrix". (Note: I have
no objections to arguments in favor of matrix-multiplication for "*", I just
don't think they should be based on something as arbitrary as the object's
name).
Next, I'd like to add my vote to those in favor of element-wise operations.
This just seems to me to be the most general purpose sort of functionality
that could be provided by an object that stores contiguous blocks of
homogeneous data. I think that all of the same element-wise operations
should be provided as are provided for the int and float objects (note this
is a small change to my proposal). This makes a table object simply a way
of storing a multi-dimensional block of homogeneous data-elements, that can
be operated on in exactly the same way as the equivalent scalars in python
with greatly improved performance.
I also completely agree with Konrad Hinsen's suggestion that J's rank
system be used for table objects. This is in fact a very minor change to my
original proposal. J's rank system is simply a clearer way of expressing
my dimensionality coercion ideas.
Chric Chase suggests:
Generalized inner and outer products (taking user specified binary
operators) would also be very helpful.
I'm curious to better understand this proposal. If it means defining an
outer product that can take python binary functions as it's arguments, I
would argue that it would be incredibly inefficient (compared to native C),
and therefore might as well be implemented in python as a set of "helper"
functions.
If it means specifying only primitive binary arithmetic operations (like
"+" and "*") then I think that it could be implemented efficiently, but I
would really like to see some examples of where this would be useful (other
than matrix multiplication which will probably be done with special purpose
code for efficiency reasons anyway).
All of the above comments also relate to proposals for specialized "map"
and "reduce" functions for tables/matrices. Unless there is some large
efficiency gain possible, I'd rather see these implemented in python than C.
-Jim Hugunin hugunin@mit.edu
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 12:32:25 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id MAA09881 for matrix-sig-people; Wed, 13 Sep 1995 12:32:25 -0400
Received: from mama-bear.LCS.MIT.EDU (mama-bear.lcs.mit.edu [18.27.0.167]) by python.org (8.6.12/8.6.12) with SMTP id MAA09877 for ; Wed, 13 Sep 1995 12:32:23 -0400
Received: from nineveh.LCS.MIT.EDU by mama-bear.LCS.MIT.EDU (4.1/SLS-4.1.1)
id AA00875; Wed, 13 Sep 95 12:32:42 EDT
Received: by nineveh.LCS.MIT.EDU (NX5.67e/SMI-4.1)
id AA04160; Wed, 13 Sep 95 12:39:14 -0400
Message-Id: <9509131639.AA04160@nineveh.LCS.MIT.EDU>
Content-Type: text/plain
Mime-Version: 1.0 (NeXT Mail 3.3 v118.2)
Received: by NeXT.Mailer (1.118.2)
From: James Hugunin
Date: Wed, 13 Sep 95 12:39:13 -0400
To: "Jim Fulton, U.S. Geological Survey"
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
Cc: matrix-sig@python.org
References: <199509112304.XAA13015@servrcolkr.cr.usgs.gov>
Sender: owner-matrix-sig@python.org
Precedence: bulk
In my previous post I comment on what appears to be the primary source of
controversy for the matrix object, whether it should be treated as a table
of numbers, or as a true mathematical matrix. I'm afraid that that issue is
likely to be the source of substantial more discussion before anything is
decided.
I would also like to comment on some of the less controversial points
raised by Jim Fulton in his first post regarding basic methods for matrix
objects.
>> transpose - The transpose of the matrix (not needed if "~" is used for
>> transpose)
> I'm thinking that this (or ~) should return by reference.
I agree completely. In fact, when I went about implementing matrix slices
(so that m[((1,3),)] can be properly returned by reference, I noticed that
transposing a matrix was trivially implemented by converting it to a matrix
slice. This of course raises the issue of how to properly deal with these
matrix slices.
For example, the way my code is written now, if I try to add a slice to a
matrix, I can perform the operation quickly without needing to convert the
slice to a matrix, as long as the slice meets a fairly obscure list of
requirements. Basically these can be summarized as the fact that there must
be a constant offset between the indices for each dimensions. ie.
M[((1,3,5),)] would be easy to deal with (as fast as a matrix) whereas:
M[((1,3,6),)] would not be
What bothers me about this gets back to your earlier complaints regarding
automatic coercion as being a possible source of errors (which I've come
around to agreeing with in general). On the other hand, conceptually, it
would be nice to be able to treat a slice exactly the same as a matrix
whenever possible.
I see three options, none of which seems perfect, but I am inclined towards
the third.
1) Don't allow slices to be passed to functions which require a matrix
argument, and don't allow numeric operations on slices
2) Treat a slice just like any other non-matrix sequence, and allow numeric
operations, and allow it to be passed to matrix functions using the generic
sequence to matrix functions (there is an interesting issue here having to
do with copying data back from the matrix to the input sequence if it turns
out that the function modifies it's input data. I'm curious as to how you
deal with this, or whether you just don't allow this to happen).
3) Whenever a slice is passed to a function requiring a matrix argument,
automatically copy the data from the slice to a contiguous block of memory.
Allow slices to be used in numeric operations, doing this efficiently where
possible, and copying the slice to a contiguous block of memeory where not
possible. This would be the most efficient solution.
>> byteswap - Useful for reading data from a file written on a machine with a
>> different byte order
> Hm. Does this operate in-place or return a value?
In-place. In fact, I am tempted to try and make all of the methods on
matrices operate "in-place" in order to be in line with list objects.
>> typecast(t) - Returns a new matrix of the same dimensions where each item
>> is cast (using C typecasting rules) from the matrix's type to the new type
>> t.
>Our constructor already gives you this:
> new_matrix=Matrix(old_matrix,new_type)
True. My problem is that I am frequently having to cast between types (I
deal with samples of sound that usually have to come in and go out as
shorts, but which I usually want to operate on as floats in the meantime).
So, I agree that this should be done using the matrix constructor, and all
that I'd add is that the matrix constructor should be written so that this
typecasting is done efficiently.
-Jim Hugunin hugunin@mit.edu
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 12:34:33 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id MAA09881 for matrix-sig-people; Wed, 13 Sep 1995 12:32:25 -0400
Received: from mama-bear.LCS.MIT.EDU (mama-bear.lcs.mit.edu [18.27.0.167]) by python.org (8.6.12/8.6.12) with SMTP id MAA09877 for ; Wed, 13 Sep 1995 12:32:23 -0400
Received: from nineveh.LCS.MIT.EDU by mama-bear.LCS.MIT.EDU (4.1/SLS-4.1.1)
id AA00875; Wed, 13 Sep 95 12:32:42 EDT
Received: by nineveh.LCS.MIT.EDU (NX5.67e/SMI-4.1)
id AA04160; Wed, 13 Sep 95 12:39:14 -0400
Message-Id: <9509131639.AA04160@nineveh.LCS.MIT.EDU>
Content-Type: text/plain
Mime-Version: 1.0 (NeXT Mail 3.3 v118.2)
Received: by NeXT.Mailer (1.118.2)
From: James Hugunin
Date: Wed, 13 Sep 95 12:39:13 -0400
To: "Jim Fulton, U.S. Geological Survey"
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
Cc: matrix-sig@python.org
References: <199509112304.XAA13015@servrcolkr.cr.usgs.gov>
Sender: owner-matrix-sig@python.org
Precedence: bulk
In my previous post I comment on what appears to be the primary source of
controversy for the matrix object, whether it should be treated as a table
of numbers, or as a true mathematical matrix. I'm afraid that that issue is
likely to be the source of substantial more discussion before anything is
decided.
I would also like to comment on some of the less controversial points
raised by Jim Fulton in his first post regarding basic methods for matrix
objects.
>> transpose - The transpose of the matrix (not needed if "~" is used for
>> transpose)
> I'm thinking that this (or ~) should return by reference.
I agree completely. In fact, when I went about implementing matrix slices
(so that m[((1,3),)] can be properly returned by reference, I noticed that
transposing a matrix was trivially implemented by converting it to a matrix
slice. This of course raises the issue of how to properly deal with these
matrix slices.
For example, the way my code is written now, if I try to add a slice to a
matrix, I can perform the operation quickly without needing to convert the
slice to a matrix, as long as the slice meets a fairly obscure list of
requirements. Basically these can be summarized as the fact that there must
be a constant offset between the indices for each dimensions. ie.
M[((1,3,5),)] would be easy to deal with (as fast as a matrix) whereas:
M[((1,3,6),)] would not be
What bothers me about this gets back to your earlier complaints regarding
automatic coercion as being a possible source of errors (which I've come
around to agreeing with in general). On the other hand, conceptually, it
would be nice to be able to treat a slice exactly the same as a matrix
whenever possible.
I see three options, none of which seems perfect, but I am inclined towards
the third.
1) Don't allow slices to be passed to functions which require a matrix
argument, and don't allow numeric operations on slices
2) Treat a slice just like any other non-matrix sequence, and allow numeric
operations, and allow it to be passed to matrix functions using the generic
sequence to matrix functions (there is an interesting issue here having to
do with copying data back from the matrix to the input sequence if it turns
out that the function modifies it's input data. I'm curious as to how you
deal with this, or whether you just don't allow this to happen).
3) Whenever a slice is passed to a function requiring a matrix argument,
automatically copy the data from the slice to a contiguous block of memory.
Allow slices to be used in numeric operations, doing this efficiently where
possible, and copying the slice to a contiguous block of memeory where not
possible. This would be the most efficient solution.
>> byteswap - Useful for reading data from a file written on a machine with a
>> different byte order
> Hm. Does this operate in-place or return a value?
In-place. In fact, I am tempted to try and make all of the methods on
matrices operate "in-place" in order to be in line with list objects.
>> typecast(t) - Returns a new matrix of the same dimensions where each item
>> is cast (using C typecasting rules) from the matrix's type to the new type
>> t.
>Our constructor already gives you this:
> new_matrix=Matrix(old_matrix,new_type)
True. My problem is that I am frequently having to cast between types (I
deal with samples of sound that usually have to come in and go out as
shorts, but which I usually want to operate on as floats in the meantime).
So, I agree that this should be done using the matrix constructor, and all
that I'd add is that the matrix constructor should be written so that this
typecasting is done efficiently.
-Jim Hugunin hugunin@mit.edu
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 12:35:42 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id MAA09902 for matrix-sig-people; Wed, 13 Sep 1995 12:35:42 -0400
Received: from rnet.rose.rsoc.rockwell.com (rose.rsoc.rockwell.com [161.40.39.100]) by python.org (8.6.12/8.6.12) with SMTP id MAA09898 for ; Wed, 13 Sep 1995 12:35:39 -0400
Received: by rnet.rose.rsoc.rockwell.com (4.1/SMI-4.1)
id AA22321; Wed, 13 Sep 95 11:36:36 CDT
Received: from sunrise(161.40.50.11) by rnet via smap (V1.3)
id sma022319; Wed Sep 13 11:36:07 1995
Received: from feynman.rsoc.rockwell.com by rose.rsoc.rockwell.com (5.0/SMI-SVR4)
id AA05890; Wed, 13 Sep 1995 11:36:05 -0500
Received: by feynman.rsoc.rockwell.com (5.0/SMI-SVR4)
id AA00821; Wed, 13 Sep 1995 11:36:03 -0500
Date: Wed, 13 Sep 1995 11:36:03 -0500
From: forrest@rose.rsoc.rockwell.com (Dave Forrest)
Message-Id: <9509131636.AA00821@feynman.rsoc.rockwell.com>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Are we talking about one thing or two?
X-Sun-Charset: US-ASCII
Content-Length: 1555
Sender: owner-matrix-sig@python.org
Precedence: bulk
When we started talking about the use of operator* being elementwise or
"mathematical", I thought that was the extent of it - a disagreement
over one operator. But now I think we may actually be asking for two
completely different animals. Mr. Hugunin suggested that "matrix" is,
in effect, just a name. Well, maybe not. For what we do here we have
definite semantics assigned to the word "matrix" and they are the
"mathematical" semantics described earlier. The "elementwise" stuff is
more like what we would call an "array" or "table" - although "tensor"
sounds good to me.
So, I'd like to suggest that we pursue not one, but two new entities.
A "matrix" would be limited to two dimensions and support the
"mathematical" notion that most of us engineers had beat into our
brains in college. A "tensor" or "table" or even "array" would fit the
more general interpretation and have element-wise operations.
Why? For practical reasons. We here - and I would submit that many
other organizations are like this - do more Matlab-like work. The way
Matlab defines a matrix is not a problem but a huge advantage for us.
Several of the rest of you apparently deal with more abstract notions
either as data storage elements, set theoretic elements, or
mathematical elements. You need the elementwise stuff just as badly as
we need our "mathematical" stuff.
Rather than trying to make a one-size-fits all that isn't exactly what
anyone wants, why not make two interfaces (maybe one implementation?)
that give each camp what they want to work with?
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 13:44:53 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id NAA10214 for matrix-sig-people; Wed, 13 Sep 1995 13:44:53 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id NAA10210 for ; Wed, 13 Sep 1995 13:44:48 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id NAA17425
(8.6.11/IDA-1.6); Wed, 13 Sep 1995 13:43:30 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id NAA02220; Wed, 13 Sep 1995 13:43:29 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id NAA09394; Wed, 13 Sep 1995 13:43:37 -0400
Date: Wed, 13 Sep 1995 13:43:37 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509131743.NAA09394@cyclone.ERE.UMontreal.CA>
To: forrest@rose.rsoc.rockwell.com
CC: matrix-sig@python.org
In-reply-to: <9509131636.AA00821@feynman.rsoc.rockwell.com> (forrest@rose.rsoc.rockwell.com)
Subject: Re: [PYTHON MATRIX-SIG] Are we talking about one thing or two?
Sender: owner-matrix-sig@python.org
Precedence: bulk
When we started talking about the use of operator* being elementwise or
"mathematical", I thought that was the extent of it - a disagreement
over one operator. But now I think we may actually be asking for two
completely different animals. Mr. Hugunin suggested that "matrix" is,
in effect, just a name. Well, maybe not. For what we do here we have
But the one operator is the only difference - apart from the interpretation
of multiplication, "mathematical" matrices are just a special case
of general arrays.
"mathematical" semantics described earlier. The "elementwise" stuff is
more like what we would call an "array" or "table" - although "tensor"
sounds good to me.
Not to me though. A tensor is a mathematical object representing a
physical quantity in space that exists indepently of a choice of a
coordinate system. A tensor is thus not just a table of numbers, but a
set of numbers with a well-defined transformation property when
changing from one coordinate system to another.
I vote for "array" as the name for what we are trying to do.
Why? For practical reasons. We here - and I would submit that many
other organizations are like this - do more Matlab-like work. The way
Matlab defines a matrix is not a problem but a huge advantage for us.
But in what way would a more general concept be a disadvantage?
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 14:07:42 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA10335 for matrix-sig-people; Wed, 13 Sep 1995 14:07:42 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id OAA10330 for ; Wed, 13 Sep 1995 14:07:37 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id OAA18220
(8.6.11/IDA-1.6); Wed, 13 Sep 1995 14:07:16 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id OAA08197; Wed, 13 Sep 1995 14:07:14 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id OAA10603; Wed, 13 Sep 1995 14:07:22 -0400
Date: Wed, 13 Sep 1995 14:07:22 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509131807.OAA10603@cyclone.ERE.UMontreal.CA>
To: jjh@mama-bear.lcs.mit.edu
CC: matrix-sig@python.org
In-reply-to: <9509131639.AA04160@nineveh.LCS.MIT.EDU> (message from James Hugunin on Wed, 13 Sep 95 12:39:13 -0400)
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
Sender: owner-matrix-sig@python.org
Precedence: bulk
3) Whenever a slice is passed to a function requiring a matrix argument,
automatically copy the data from the slice to a contiguous block of memory.
Allow slices to be used in numeric operations, doing this efficiently where
possible, and copying the slice to a contiguous block of memeory where not
possible. This would be the most efficient solution.
Why would it have to be copied whenever it is passed to a function?
It would be sufficient to make a copy whenever the matrix is
changed.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 14:23:38 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA10388 for matrix-sig-people; Wed, 13 Sep 1995 14:23:38 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id OAA10383 for ; Wed, 13 Sep 1995 14:23:26 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id OAA18709
(8.6.11/IDA-1.6); Wed, 13 Sep 1995 14:23:22 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id OAA13737; Wed, 13 Sep 1995 14:23:21 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id OAA11504; Wed, 13 Sep 1995 14:23:29 -0400
Date: Wed, 13 Sep 1995 14:23:29 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509131823.OAA11504@cyclone.ERE.UMontreal.CA>
To: jjh@mama-bear.lcs.mit.edu
CC: matrix-sig@python.org
In-reply-to: <9509131604.AA04152@nineveh.LCS.MIT.EDU> (message from James Hugunin on Wed, 13 Sep 95 12:04:44 -0400)
Subject: Re: [PYTHON MATRIX-SIG] Vector-style operations vs. matrix operations
Sender: owner-matrix-sig@python.org
Precedence: bulk
Chric Chase suggests:
Generalized inner and outer products (taking user specified binary
operators) would also be very helpful.
I'm curious to better understand this proposal. If it means defining an
outer product that can take python binary functions as it's arguments, I
would argue that it would be incredibly inefficient (compared to native C),
and therefore might as well be implemented in python as a set of "helper"
functions.
If it means specifying only primitive binary arithmetic operations (like
"+" and "*") then I think that it could be implemented efficiently, but I
would really like to see some examples of where this would be useful (other
than matrix multiplication which will probably be done with special purpose
code for efficiency reasons anyway).
Of course general inner and outer products, as well as reduction,
should work with any binary operation, including user-defined
functions. And I agree that this is best done in Python. However, the
most important applications use only the built-in operations
(arithmetic, comparison, logical), so it would make sense to provide a
more efficient solution for them.
There are lots of applications for these things, as any APLer will
confirm. Unfortunately I can't include any APL idioms here due to
character set restrictions :-( But let me give a simple example:
suppose you have an array of data values x (one-dimensional). You
want to make a cumulative histogram, i.e. an array of integers
n in which each entry n[i] indicates the number of values in x
that are larger than b[i], where b[i] is the value of each "bin"
in the histogram. This is just an outer product using >, followed
by a reduction with +:
n = reduce(lambda a,b: a+b, 1, outer_product(lambda a,b: a>b, x, b))
in Pseudo-Python, where the second argument of "reduce" indicates
the rank of the cells to be reduced.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 14:23:44 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA10394 for matrix-sig-people; Wed, 13 Sep 1995 14:23:44 -0400
Received: from servrcolkr.cr.usgs.gov (servrcolkr.cr.usgs.gov [136.177.112.5]) by python.org (8.6.12/8.6.12) with ESMTP id OAA10390 for ; Wed, 13 Sep 1995 14:23:40 -0400
Received: from localhost (dsjfqvarsa.er.usgs.gov [130.11.51.73]) by servrcolkr.cr.usgs.gov (EMAIL 1.2.1) with ESMTP id SAA17108; Wed, 13 Sep 1995 18:24:21 GMT
Message-Id: <199509131824.SAA17108@servrcolkr.cr.usgs.gov>
To: hinsenk@ere.umontreal.ca (Hinsen Konrad)
cc: jjh@mama-bear.lcs.mit.edu
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
In-reply-to: <199509131807.OAA10603@cyclone.ERE.UMontreal.CA>
Date: Wed, 13 Sep 1995 14:24:18 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Wed, 13 Sep 1995 14:07:22 -0400
Hinsen Konrad said:
>
> 3) Whenever a slice is passed to a function requiring a matrix argument,
> automatically copy the data from the slice to a contiguous block of memory.
> Allow slices to be used in numeric operations, doing this efficiently where
> possible, and copying the slice to a contiguous block of memeory where not
> possible. This would be the most efficient solution.
>
> Why would it have to be copied whenever it is passed to a function?
> It would be sufficient to make a copy whenever the matrix is
> changed.
This gets back to my earlier comment that slices, as proposed cannot
be matrices because they store a reference to the data from which they
were created and a bunch of offsets for accessing the orinal data
properly. When passed to math/sci libraries, they'll have to be
converted to real matrices so that their data are contiguous.
JIm
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 14:28:26 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA10439 for matrix-sig-people; Wed, 13 Sep 1995 14:28:26 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id OAA10435 for ; Wed, 13 Sep 1995 14:28:22 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id OAA18842
(8.6.11/IDA-1.6); Wed, 13 Sep 1995 14:28:19 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id OAA14777; Wed, 13 Sep 1995 14:28:18 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id OAA11695; Wed, 13 Sep 1995 14:28:26 -0400
Date: Wed, 13 Sep 1995 14:28:26 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509131828.OAA11695@cyclone.ERE.UMontreal.CA>
To: jfulton@usgs.gov
CC: matrix-sig@python.org
In-reply-to: <199509131824.SAA17108@servrcolkr.cr.usgs.gov> (jfulton@wrdmail.er.usgs.gov)
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
Sender: owner-matrix-sig@python.org
Precedence: bulk
> Why would it have to be copied whenever it is passed to a function?
> It would be sufficient to make a copy whenever the matrix is
> changed.
This gets back to my earlier comment that slices, as proposed cannot
be matrices because they store a reference to the data from which they
were created and a bunch of offsets for accessing the orinal data
properly. When passed to math/sci libraries, they'll have to be
converted to real matrices so that their data are contiguous.
I see. "Function" means "function in an external library", not
"Python function" as I had assumed.
BTW, there is a good book called "Scientific and Engineering C++"
that appeared recently, unfortunately I don't remember the names
of the authors (but I can check at home). It contains a
very good discussion of how a matrix class in C++ could be
designed, and this covers many of the topics we are discussing
here. Unfortunately they treat only one- and two-dimensional
matrices.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 14:33:36 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA10492 for matrix-sig-people; Wed, 13 Sep 1995 14:33:36 -0400
Received: from servrcolkr.cr.usgs.gov (servrcolkr.cr.usgs.gov [136.177.112.5]) by python.org (8.6.12/8.6.12) with ESMTP id OAA10487 for ; Wed, 13 Sep 1995 14:33:31 -0400
Received: from localhost (dsjfqvarsa.er.usgs.gov [130.11.51.73]) by servrcolkr.cr.usgs.gov (EMAIL 1.2.1) with ESMTP id SAA17250; Wed, 13 Sep 1995 18:34:14 GMT
Message-Id: <199509131834.SAA17250@servrcolkr.cr.usgs.gov>
To: hinsenk@ere.umontreal.ca (Hinsen Konrad)
cc: "James L Fulton, Hydrologist, Reston, VA "
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
In-reply-to: <199509131828.OAA11695@cyclone.ERE.UMontreal.CA>
Date: Wed, 13 Sep 1995 14:34:11 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Wed, 13 Sep 1995 14:28:26 -0400
Hinsen Konrad said:
>
> > Why would it have to be copied whenever it is passed to a function?
> > It would be sufficient to make a copy whenever the matrix is
> > changed.
>
> This gets back to my earlier comment that slices, as proposed cannot
> be matrices because they store a reference to the data from which they
> were created and a bunch of offsets for accessing the orinal data
> properly. When passed to math/sci libraries, they'll have to be
> converted to real matrices so that their data are contiguous.
>
> I see. "Function" means "function in an external library", not
> "Python function" as I had assumed.
Python functions won't really care whether they get a "true" matrix,
or a sequence of sequences, as they will use []s to access data in
either case.
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 15:44:48 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id PAA10861 for matrix-sig-people; Wed, 13 Sep 1995 15:44:48 -0400
Received: from rnet.rose.rsoc.rockwell.com (rose.rsoc.rockwell.com [161.40.39.100]) by python.org (8.6.12/8.6.12) with SMTP id PAA10857 for ; Wed, 13 Sep 1995 15:44:45 -0400
Received: by rnet.rose.rsoc.rockwell.com (4.1/SMI-4.1)
id AA23046; Wed, 13 Sep 95 14:45:44 CDT
Received: from sunrise(161.40.50.11) by rnet via smap (V1.3)
id sma023042; Wed Sep 13 14:45:13 1995
Received: from feynman.rsoc.rockwell.com by rose.rsoc.rockwell.com (5.0/SMI-SVR4)
id AA06888; Wed, 13 Sep 1995 14:45:10 -0500
Received: by feynman.rsoc.rockwell.com (5.0/SMI-SVR4)
id AA01068; Wed, 13 Sep 1995 14:45:10 -0500
Date: Wed, 13 Sep 1995 14:45:10 -0500
From: forrest@rose.rsoc.rockwell.com (Dave Forrest)
Message-Id: <9509131945.AA01068@feynman.rsoc.rockwell.com>
To: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Are we talking about one thing or two?
X-Sun-Charset: US-ASCII
Content-Length: 951
Sender: owner-matrix-sig@python.org
Precedence: bulk
> From owner-matrix-sig@python.org Wed Sep 13 12:46 CDT 1995
[snip]
>
> Why? For practical reasons. We here - and I would submit that many
> other organizations are like this - do more Matlab-like work. The way
> Matlab defines a matrix is not a problem but a huge advantage for us.
>
> But in what way would a more general concept be a disadvantage?
Easy - a more general concept might require me to worry about extra
things that I'm not interested in (like the number of dimensions, or
the "rank" that J uses). These things are not interesting and can only
cause problems by being there to make mistakes on. We will end up
writing a wrapper that gives us the interface we want and hides the
things that we're not interested in - but since we're not the only ones
who want this and it would be more efficiently done as an intrinsic
part of the language I think that's the right thing to do.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 16:38:20 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id QAA11112 for matrix-sig-people; Wed, 13 Sep 1995 16:38:20 -0400
Received: from servrcolkr.cr.usgs.gov (servrcolkr.cr.usgs.gov [136.177.112.5]) by python.org (8.6.12/8.6.12) with ESMTP id QAA11108 for ; Wed, 13 Sep 1995 16:38:17 -0400
Received: from localhost (dsjfqvarsa.er.usgs.gov [130.11.51.73]) by servrcolkr.cr.usgs.gov (EMAIL 1.2.1) with ESMTP id UAA22390; Wed, 13 Sep 1995 20:38:58 GMT
Message-Id: <199509132038.UAA22390@servrcolkr.cr.usgs.gov>
To: James Hugunin
cc: "James L Fulton, Hydrologist, Reston, VA "
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
In-reply-to: <9509131639.AA04160@nineveh.LCS.MIT.EDU>
Date: Wed, 13 Sep 1995 16:38:56 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Wed, 13 Sep 95 12:39:13 -0400
James Hugunin said:
>
(snip)
>
> I would also like to comment on some of the less controversial points
> raised by Jim Fulton in his first post regarding basic methods for matrix
> objects.
>
> >> transpose - The transpose of the matrix (not needed if "~" is used for
> >> transpose)
> > I'm thinking that this (or ~) should return by reference.
>
> I agree completely. In fact, when I went about implementing matrix slices
> (so that m[((1,3),)] can be properly returned by reference, I noticed that
> transposing a matrix was trivially implemented by converting it to a matrix
> slice. This of course raises the issue of how to properly deal with these
> matrix slices.
>
> For example, the way my code is written now, if I try to add a slice to a
> matrix, I can perform the operation quickly without needing to convert the
> slice to a matrix, as long as the slice meets a fairly obscure list of
> requirements. Basically these can be summarized as the fact that there must
> be a constant offset between the indices for each dimensions. ie.
>
> M[((1,3,5),)] would be easy to deal with (as fast as a matrix) whereas:
> M[((1,3,6),)] would not be
I think this is too strange.
> What bothers me about this gets back to your earlier complaints regarding
> automatic coercion as being a possible source of errors (which I've come
> around to agreeing with in general). On the other hand, conceptually, it
> would be nice to be able to treat a slice exactly the same as a matrix
> whenever possible.
Right. This is polymorphism, not coercion.
> I see three options, none of which seems perfect, but I am inclined towards
> the third.
>
> 1) Don't allow slices to be passed to functions which require a matrix
> argument, and don't allow numeric operations on slices
Yuck.
> 2) Treat a slice just like any other non-matrix sequence, and allow numeric
> operations, and allow it to be passed to matrix functions using the generic
> sequence to matrix functions (there is an interesting issue here having to
> do with copying data back from the matrix to the input sequence if it turns
> out that the function modifies it's input data. I'm curious as to how you
> deal with this, or whether you just don't allow this to happen).
> 3) Whenever a slice is passed to a function requiring a matrix argument,
> automatically copy the data from the slice to a contiguous block of memory.
> Allow slices to be used in numeric operations, doing this efficiently where
> possible, and copying the slice to a contiguous block of memeory where not
> possible. This would be the most efficient solution.
I can't tell 2 and three apart.
Currently, if FIDL calls a function that modifies one of it's
arguments, the modified value is returned as an element of the return
list. Non-scalar output/modify values are always matrices. If a
modify argument *is* a matrix, then it gets modified, otherwise it
gets copied. This is a bit gross but it handles most cases.
>
> >> byteswap - Useful for reading data from a file written on a machine with a
> >> different byte order
>
> > Hm. Does this operate in-place or return a value?
>
> In-place. In fact, I am tempted to try and make all of the methods on
> matrices operate "in-place" in order to be in line with list objects.
I'm not sure what you mean by this. Surely, you aren't trying to
make:
m=[[1,2,3],[4,5,6],[7,8,9]]
b=[11,22,33]
m=[1]=b
b[1]=99
cause m[1][1] to equal 99? Are you?
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 16:46:28 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id QAA11173 for matrix-sig-people; Wed, 13 Sep 1995 16:46:28 -0400
Received: from retro.jhuapl.edu (retro.jhuapl.edu [128.244.146.239]) by python.org (8.6.12/8.6.12) with ESMTP id QAA11169 for ; Wed, 13 Sep 1995 16:46:25 -0400
Message-Id: <199509132046.QAA11169@python.org>
Received: by retro.jhuapl.edu
(1.37.109.16/16.2) id AA180845239; Wed, 13 Sep 1995 16:47:19 -0400
Date: Wed, 13 Sep 1995 16:47:19 -0400
From: Chris Chase S1A
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Are we talking about one thing or two?
In-Reply-To: <9509131636.AA00821@feynman.rsoc.rockwell.com>
References: <9509131636.AA00821@feynman.rsoc.rockwell.com>
Reply-To: chris.chase@jhuapl.edu
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Sender: owner-matrix-sig@python.org
Precedence: bulk
>>>>> "Dave" == Dave Forrest writes:
Dave> Rather than trying to make a one-size-fits all that isn't exactly what
Dave> anyone wants, why not make two interfaces (maybe one implementation?)
Dave> that give each camp what they want to work with?
By two interfaces do you mean having different meanings for an
operator such as "*" depending on the arguments classes?
I think it would be too confusing to have an operation "A*B" that
performs matrix multiplication when A and B are matrices (two
dimensional arrays) but performs elementwise multiplication for the
a general class of arrays (whatever you want to call them - arrays,
tensors, tables - I prefer arrays).
I think that supporting the Matlab-like matrix linear algebra such as
matrix multiplication and matrix division is a high priority for many
people. One question is can the new operators be added to the
language without difficulty, in a logical fashion, and without
cluttering the language with operators that are only used for this
"matrix" class?
The Matlab approach uses
"+", "-", ".*", "./" ".^" for elementwise operations (called array
operations in Matlab. I call them vector operations.)
"*", "/", "\", "^" for matrix operations. ("^" is a Matrix power operator).
Python is much more general than a matrix language, so I think that
using all of these would give Python operator bloat.
Is adding new operators to the language even a possibility?
New methods or functions would have to be defined anyway to implement
the operators, e.g. matrixmultiply(a,b), vectormultiply(a,b),
leftdivision(a,b), etc. Without operators that call these functions we
would probably want shorter names.
>>
>> Why? For practical reasons. We here - and I would submit that many
>> other organizations are like this - do more Matlab-like work. The way
>> Matlab defines a matrix is not a problem but a huge advantage for us.
>>
>> But in what way would a more general concept be a disadvantage?
Dave> Easy - a more general concept might require me to worry about extra
Dave> things that I'm not interested in (like the number of dimensions, or
Dave> the "rank" that J uses). These things are not interesting and can only
Dave> cause problems by being there to make mistakes on. We will end up
Dave> writing a wrapper that gives us the interface we want and hides the
Dave> things that we're not interested in - but since we're not the only ones
Dave> who want this and it would be more efficiently done as an intrinsic
Dave> part of the language I think that's the right thing to do.
Even if you limit yourself to only two dimensions you still have to
worry about rank. In Matlab you can make a mistake using
one-dimensional vectors when a matrix is required. Tela is a perfect
example of a language that took the Matlab syntax and added higher
dimensional arrays without breaking any of the basic Matlab matrix
features. IDL and APL are additional examples with higher dimensional
arrays that don't limit their capability to do matrix linear algebra.
Matlab is two-dimensional more because of its limited scope and
computer resources from historical beginnings. I even seem to recall
being informed by Mathworks representatives at a tradeshow that higher
dimensional arrays would be added in a future release to Matlab.
The natural implemenation would be a multi-dimensional array class as
Jim is proposing with a matrix linear algebra module to specialize
this class [for implementing matrix multiplication, matrix
inverses/adjoints (square and non-square "matrix division") , linear
equation solvers, column rank, factorization, spectral/eigenvalue
computations, special matrices (diagonal, Hankel, toeplitz, Hilbert,
sparse, etc.), norms, matrix exponential, and on and on]. This would
easily be a whole additional SIG - the Matrix Linear Algebra (Matlab
clone) SIG. Such a SIG wouldn't have to implement another object type
and external interface except for subclassing to special matrices
(sparse in particular).
Is the purpose of this SIG only for implementing a two-dimensional
matrix linear algebra system within Python or for implementing a more
general purpose multi-dimensional array class with homogeneous
elements for easy interface to external scientific and mathematical
libraries?
Chris
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 16:54:44 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id QAA11209 for matrix-sig-people; Wed, 13 Sep 1995 16:54:44 -0400
Received: from mama-bear.LCS.MIT.EDU (mama-bear.lcs.mit.edu [18.27.0.167]) by python.org (8.6.12/8.6.12) with SMTP id QAA11205 for ; Wed, 13 Sep 1995 16:54:42 -0400
Received: from nineveh.LCS.MIT.EDU by mama-bear.LCS.MIT.EDU (4.1/SLS-4.1.1)
id AA02516; Wed, 13 Sep 95 16:54:59 EDT
Received: by nineveh.LCS.MIT.EDU (NX5.67e/SMI-4.1)
id AA04255; Wed, 13 Sep 95 17:01:32 -0400
Message-Id: <9509132101.AA04255@nineveh.LCS.MIT.EDU>
Content-Type: text/plain
Mime-Version: 1.0 (NeXT Mail 3.3 v118.2)
Received: by NeXT.Mailer (1.118.2)
From: James Hugunin
Date: Wed, 13 Sep 95 17:01:31 -0400
To: "Jim Fulton, U.S. Geological Survey"
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
Cc: matrix-sig@python.org
References: <199509132038.UAA22390@servrcolkr.cr.usgs.gov>
Sender: owner-matrix-sig@python.org
Precedence: bulk
I've got to run to a meeting, but I wanted to reply to your points.
I'm not at all sure that we disagree on the implementation of slices. Let
me ask two simple questions. Let's say A is a slice taken from a matrix,
and B is a contiguous matrix.
1) Should I be able to say C = A+B, and have the obvious thing happen
(assuming that dimensions line up, etc.)?
I'd say yes, and I have a few performance optimizations to make this fast
in certain cases and nobody needs to worry about those.
2) Should I be able to say fft(A) where fft is an in-place FFT routine?
Should it be expected to modify the memory referred to by A, or only to
return a brand-new matrix which corresponds to the fft of A?
I'm not sure what the right answer to this one is.
> > > Hm. Does this operate in-place or return a value?
> >
> > In-place. In fact, I am tempted to try and make all of the methods on
> > matrices operate "in-place" in order to be in line with list objects.
>
> I'm not sure what you mean by this. Surely, you aren't trying to
> make:
>
> m=[[1,2,3],[4,5,6],[7,8,9]]
> b=[11,22,33]
> m=[1]=b
> b[1]=99
>
> cause m[1][1] to equal 99? Are you?
Not at all! All I meant by this is that methods on list objects (like
insert or append) actually change the list object that they are operating
on, whereas operations like concatenation return a new object. In that
vein, I feel that m.byteswap() should operate in-place on m and cause the
memory associated with m to be byte-swapped. This is as opposed to having
it return a new matrix in with the same dimensions as m, but with all values
byte-swapped.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 16:54:49 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id QAA11220 for matrix-sig-people; Wed, 13 Sep 1995 16:54:49 -0400
Received: from servrcolkr.cr.usgs.gov (servrcolkr.cr.usgs.gov [136.177.112.5]) by python.org (8.6.12/8.6.12) with ESMTP id QAA11215 for ; Wed, 13 Sep 1995 16:54:46 -0400
Received: from localhost (dsjfqvarsa.er.usgs.gov [130.11.51.73]) by servrcolkr.cr.usgs.gov (EMAIL 1.2.1) with ESMTP id UAA23220; Wed, 13 Sep 1995 20:55:30 GMT
Message-Id: <199509132055.UAA23220@servrcolkr.cr.usgs.gov>
To: chris.chase@jhuapl.edu
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Are we talking about one thing or two?
In-reply-to: <199509132046.QAA11169@python.org>
Date: Wed, 13 Sep 1995 16:55:28 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Wed, 13 Sep 1995 16:47:19 -0400
Chris Chase S1A said:
> >>>>> "Dave" == Dave Forrest writes:
>
> Dave> Rather than trying to make a one-size-fits all that isn't exactly what
> Dave> anyone wants, why not make two interfaces (maybe one implementation?)
> Dave> that give each camp what they want to work with?
>
> By two interfaces do you mean having different meanings for an
> operator such as "*" depending on the arguments classes?
>
> I think it would be too confusing to have an operation "A*B" that
> performs matrix multiplication when A and B are matrices (two
> dimensional arrays) but performs elementwise multiplication for the
> a general class of arrays (whatever you want to call them - arrays,
> tensors, tables - I prefer arrays).
Hm. Well this is what happens now with numbers. Math on integers is
different from math on numbers.
> I think that supporting the Matlab-like matrix linear algebra such as
> matrix multiplication and matrix division is a high priority for many
> people. One question is can the new operators be added to the
> language without difficulty, in a logical fashion, and without
> cluttering the language with operators that are only used for this
> "matrix" class?
>
>
> The Matlab approach uses
>
> "+", "-", ".*", "./" ".^" for elementwise operations (called array
> operations in Matlab. I call them vector operations.)
>
> "*", "/", "\", "^" for matrix operations. ("^" is a Matrix power operator).
>
> Python is much more general than a matrix language, so I think that
> using all of these would give Python operator bloat.
>
> Is adding new operators to the language even a possibility?
I'm 99% sure that the answer is no.
I think that we should try to keep this proposal to things that can be
implemented with a new module withion the bounds of the existing
language.
> New methods or functions would have to be defined anyway to implement
> the operators, e.g. matrixmultiply(a,b), vectormultiply(a,b),
> leftdivision(a,b), etc. Without operators that call these functions we
> would probably want shorter names.
>
> >>
> >> Why? For practical reasons. We here - and I would submit that many
> >> other organizations are like this - do more Matlab-like work. The way
> >> Matlab defines a matrix is not a problem but a huge advantage for us.
> >>
> >> But in what way would a more general concept be a disadvantage?
>
> Dave> Easy - a more general concept might require me to worry about extra
> Dave> things that I'm not interested in (like the number of dimensions, or
> Dave> the "rank" that J uses). These things are not interesting and can only
> Dave> cause problems by being there to make mistakes on. We will end up
> Dave> writing a wrapper that gives us the interface we want and hides the
> Dave> things that we're not interested in - but since we're not the only ones
> Dave> who want this and it would be more efficiently done as an intrinsic
> Dave> part of the language I think that's the right thing to do.
>
> Even if you limit yourself to only two dimensions you still have to
> worry about rank. In Matlab you can make a mistake using
> one-dimensional vectors when a matrix is required. Tela is a perfect
> example of a language that took the Matlab syntax and added higher
> dimensional arrays without breaking any of the basic Matlab matrix
> features. IDL and APL are additional examples with higher dimensional
> arrays that don't limit their capability to do matrix linear algebra.
> Matlab is two-dimensional more because of its limited scope and
> computer resources from historical beginnings. I even seem to recall
> being informed by Mathworks representatives at a tradeshow that higher
> dimensional arrays would be added in a future release to Matlab.
>
> The natural implemenation would be a multi-dimensional array class as
> Jim is proposing with a matrix linear algebra module to specialize
> this class [for implementing matrix multiplication, matrix
> inverses/adjoints (square and non-square "matrix division") , linear
> equation solvers, column rank, factorization, spectral/eigenvalue
> computations, special matrices (diagonal, Hankel, toeplitz, Hilbert,
> sparse, etc.), norms, matrix exponential, and on and on]. This would
> easily be a whole additional SIG - the Matrix Linear Algebra (Matlab
> clone) SIG. Such a SIG wouldn't have to implement another object type
> and external interface except for subclassing to special matrices
> (sparse in particular).
>
> Is the purpose of this SIG only for implementing a two-dimensional
> matrix linear algebra system within Python or for implementing a more
> general purpose multi-dimensional array class with homogeneous
> elements for easy interface to external scientific and mathematical
> libraries?
The latter.
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 17:18:20 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id RAA11353 for matrix-sig-people; Wed, 13 Sep 1995 17:18:20 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id RAA11349 for ; Wed, 13 Sep 1995 17:18:14 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id RAA24718
(8.6.11/IDA-1.6); Wed, 13 Sep 1995 17:17:46 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id RAA08973; Wed, 13 Sep 1995 17:17:45 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id RAA20989; Wed, 13 Sep 1995 17:17:54 -0400
Date: Wed, 13 Sep 1995 17:17:54 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509132117.RAA20989@cyclone.ERE.UMontreal.CA>
To: chris.chase@jhuapl.edu
CC: matrix-sig@python.org
In-reply-to: <199509132046.QAA11169@python.org> (message from Chris Chase S1A on Wed, 13 Sep 1995 16:47:19 -0400)
Subject: Re: [PYTHON MATRIX-SIG] Are we talking about one thing or two?
Sender: owner-matrix-sig@python.org
Precedence: bulk
The natural implemenation would be a multi-dimensional array class as
Jim is proposing with a matrix linear algebra module to specialize
this class [for implementing matrix multiplication, matrix
inverses/adjoints (square and non-square "matrix division") , linear
...
Why specialise? That only limits the flexibility of the whole
package. Why shouldn't it be possible to have "inverse" invert
each two-dimensional slice of a three-dimensional array? There
are applications for it, it doesn't cost any extra effort to
do it, so why not do it? Why invent artificial restrictions?
Is the purpose of this SIG only for implementing a two-dimensional
matrix linear algebra system within Python or for implementing a more
general purpose multi-dimensional array class with homogeneous
elements for easy interface to external scientific and mathematical
libraries?
I'd say both.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 17:23:26 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id RAA11380 for matrix-sig-people; Wed, 13 Sep 1995 17:23:26 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id RAA11376 for ; Wed, 13 Sep 1995 17:23:23 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id QAA22452
(8.6.11/IDA-1.6); Wed, 13 Sep 1995 16:11:20 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id QAA09390; Wed, 13 Sep 1995 16:11:19 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id QAA17017; Wed, 13 Sep 1995 16:10:49 -0400
Date: Wed, 13 Sep 1995 16:10:49 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509132010.QAA17017@cyclone.ERE.UMontreal.CA>
To: forrest@rose.rsoc.rockwell.com
CC: matrix-sig@python.org
In-reply-to: <9509131945.AA01068@feynman.rsoc.rockwell.com> (forrest@rose.rsoc.rockwell.com)
Subject: Re: [PYTHON MATRIX-SIG] Are we talking about one thing or two?
Sender: owner-matrix-sig@python.org
Precedence: bulk
Easy - a more general concept might require me to worry about extra
things that I'm not interested in (like the number of dimensions, or
the "rank" that J uses). These things are not interesting and can only
cause problems by being there to make mistakes on. We will end u
writing a wrapper that gives us the interface we want and hides the
But it doesn't, as several decades of APL experience have shown. I
know several APL users who are doing linear-algebra type things and
are not even aware of the existence of higher-dimensional arrays.
Your "wrapper" would only map some operations on "matrices" on
exactly the same operations on "arrays" and leave out some others.
That's just an unnecessary complication.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 17:30:18 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id RAA11429 for matrix-sig-people; Wed, 13 Sep 1995 17:30:18 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id RAA11425 for ; Wed, 13 Sep 1995 17:30:14 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id RAA25097
(8.6.11/IDA-1.6); Wed, 13 Sep 1995 17:30:13 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id RAA14823; Wed, 13 Sep 1995 17:30:12 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id RAA21754; Wed, 13 Sep 1995 17:30:21 -0400
Date: Wed, 13 Sep 1995 17:30:21 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509132130.RAA21754@cyclone.ERE.UMontreal.CA>
To: jjh@mama-bear.lcs.mit.edu
CC: matrix-sig@python.org
In-reply-to: <9509132101.AA04255@nineveh.LCS.MIT.EDU> (message from James Hugunin on Wed, 13 Sep 95 17:01:31 -0400)
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
Sender: owner-matrix-sig@python.org
Precedence: bulk
2) Should I be able to say fft(A) where fft is an in-place FFT routine?
Should it be expected to modify the memory referred to by A, or only to
return a brand-new matrix which corresponds to the fft of A?
I'm not sure what the right answer to this one is.
Both operations are useful in different contexts, so why not
have both? One possibility would be to have a function fft(A) that
returns a new array, and a method a.fft() to do an in-place
FFT. Implementationally the function would just copy A and call
the method. The same applies to inversion, factorization etc.
It would be nice to have some consistency here - always functions
for copies, always methods for in-place modifications.
> I'm not sure what you mean by this. Surely, you aren't trying to
> make:
>
> m=[[1,2,3],[4,5,6],[7,8,9]]
> b=[11,22,33]
> m=[1]=b
> b[1]=99
>
> cause m[1][1] to equal 99? Are you?
Actually this is not such a silly question, because Python's lists
behave exactly in this way. So the question is whether we want
arrays to have reference semantice (like Python lists) or value
semantics (like Python integers, floats, and complex numbers).
I strongly recommend the latter - reference semantics for
arrays quickly produce confusion, as I had to find out while
playing with such an implementation in Smalltalk.
on, whereas operations like concatenation return a new object. In that
vein, I feel that m.byteswap() should operate in-place on m and cause the
memory associated with m to be byte-swapped. This is as opposed to having
it return a new matrix in with the same dimensions as m, but with all values
byte-swapped.
This depends on what you would use byte swapping for. In fact, I am
not at all convinced of its utility. What would happen when such
a byte-swapped matrix is accessed? Would all operations on it
still be allowed? That would create an enormous implementation effort.
If not, then a byte-swapped matrix would no longer be a matrix,
because none of the matrix operations could be used on it.
The only reason I can see for a byte swapping operation is during
I/O, so that's where it should go (as a flag to I/O functions).
In general, I have the impression that we get too much lost in
implementational details. Let's first define arrays as an abstract
data type in terms of the operations allowed on it from a
user's point of view and *then* worry about implementational
details.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 17:39:59 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id RAA11481 for matrix-sig-people; Wed, 13 Sep 1995 17:39:59 -0400
Received: from servrcolkr.cr.usgs.gov (servrcolkr.cr.usgs.gov [136.177.112.5]) by python.org (8.6.12/8.6.12) with ESMTP id RAA11477 for ; Wed, 13 Sep 1995 17:39:53 -0400
Received: from localhost (dsjfqvarsa.er.usgs.gov [130.11.51.73]) by servrcolkr.cr.usgs.gov (EMAIL 1.2.1) with ESMTP id VAA24360; Wed, 13 Sep 1995 21:40:43 GMT
Message-Id: <199509132140.VAA24360@servrcolkr.cr.usgs.gov>
To: James Hugunin
cc: "James L Fulton, Hydrologist, Reston, VA "
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
In-reply-to: <9509132101.AA04255@nineveh.LCS.MIT.EDU>
Date: Wed, 13 Sep 1995 17:40:42 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Wed, 13 Sep 95 17:01:31 -0400
James Hugunin said:
>
> I've got to run to a meeting, but I wanted to reply to your points.
>
> I'm not at all sure that we disagree on the implementation of slices. Let
> me ask two simple questions. Let's say A is a slice taken from a matrix,
> and B is a contiguous matrix.
>
> 1) Should I be able to say C = A+B, and have the obvious thing happen
> (assuming that dimensions line up, etc.)?
Yes.
> I'd say yes, and I have a few performance optimizations to make this fast
> in certain cases and nobody needs to worry about those.
Cool.
>
> 2) Should I be able to say fft(A) where fft is an in-place FFT routine?
> Should it be expected to modify the memory referred to by A, or only to
> return a brand-new matrix which corresponds to the fft of A?
>
> I'm not sure what the right answer to this one is.
My opinion, in general is that functions should return new objects,
however, reasonable performance arguments can be made for at least
some cases of having functions modify large matrices rather than
creating new ones.
In the case above, even if I had fft modify it's argument, I would
still have it return the argument as a return value.
The problem is that you want to be able to call routines that expect
contigous data. Slice data, even for the cases you mentioned aren't
like that. Slices could be implemented with their own contiguous
data area that they keep in sync with the original matrices data, but
I think this would complicate the implementation of slices beyound
their benefit. Of course, this is only a problem when calling Fortran
or C. Python functions (or C functions written for Python, which are
much faster than python functions, but have many of the benefits)
should nor present a problem.
I suppose when calling a library function that wants to modify it's
data, one could (within the glue code) do something equivalent to:
def spam_glue(m):
if type(m) is MatrixType:
return spam(m)
else:
t=Matrix(m)
r=spam(t)
m[:]=t
return r
That is the (automagically generated) glue code could create a
temporrary matrix as copy of the original data, call the function with
the copy, and then slice-assign the modified copy back to the
argument. Note this should work work with any sequence, such as a
slice, that allows assignment from any arbitrary sequence. (Grrrrrr.
Unfortunately, this won't work with list arguments, as the following
code fails:
a=[1,2,3]
a[:]=(7,8,9) # This fails, but shouldn't, IMHO
This should work!)
I think I could work this behavior into FIDL, without much trouble.
Hm. So perhaps allowing slices to be used as modifyable arguments
might not be too bad, as long as the implementor is willing to do a
little extra work. (In my case, the implementor is a program, so I
don't mind if it works hard. ;) You still pay the performance penalty
of making a copy, but at least functions that provide performance wins
when used with matrices can still function correctly with slices.
>
> > > > Hm. Does this operate in-place or return a value?
> > >
> > > In-place. In fact, I am tempted to try and make all of the methods on
> > > matrices operate "in-place" in order to be in line with list objects.
> >
> > I'm not sure what you mean by this. Surely, you aren't trying to
> > make:
> >
> > m=[[1,2,3],[4,5,6],[7,8,9]]
> > b=[11,22,33]
> > m=[1]=b
> > b[1]=99
> >
> > cause m[1][1] to equal 99? Are you?
>
> Not at all! All I meant by this is that methods on list objects (like
> insert or append) actually change the list object that they are operating
> on, whereas operations like concatenation return a new object. In that
> vein, I feel that m.byteswap() should operate in-place on m and cause the
> memory associated with m to be byte-swapped. This is as opposed to having
> it return a new matrix in with the same dimensions as m, but with all values
> byte-swapped.
Cool. Actually, I would use a naming convention to make this clear,
so I might have both "byteswap" which modifies in place, and
"byteswapped" which returns a new objects.
(BTW Guido, I wish lists has a "sorted" operation that returned a new
sorted list.)
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 17:52:21 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id RAA11516 for matrix-sig-people; Wed, 13 Sep 1995 17:52:21 -0400
Received: from servrcolkr.cr.usgs.gov (servrcolkr.cr.usgs.gov [136.177.112.5]) by python.org (8.6.12/8.6.12) with ESMTP id RAA11512 for ; Wed, 13 Sep 1995 17:52:18 -0400
Received: from localhost (dsjfqvarsa.er.usgs.gov [130.11.51.73]) by servrcolkr.cr.usgs.gov (EMAIL 1.2.1) with ESMTP id VAA25125; Wed, 13 Sep 1995 21:52:18 GMT
Message-Id: <199509132152.VAA25125@servrcolkr.cr.usgs.gov>
To: hinsenk@ere.umontreal.ca (Hinsen Konrad)
cc: jjh@mama-bear.lcs.mit.edu
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
In-reply-to: <199509132130.RAA21754@cyclone.ERE.UMontreal.CA>
Date: Wed, 13 Sep 1995 17:52:16 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Wed, 13 Sep 1995 17:30:21 -0400
Hinsen Konrad said:
>
> 2) Should I be able to say fft(A) where fft is an in-place FFT routine?
> Should it be expected to modify the memory referred to by A, or only to
> return a brand-new matrix which corresponds to the fft of A?
>
> I'm not sure what the right answer to this one is.
>
> Both operations are useful in different contexts, so why not
> have both? One possibility would be to have a function fft(A) that
> returns a new array, and a method a.fft() to do an in-place
> FFT. Implementationally the function would just copy A and call
> the method. The same applies to inversion, factorization etc.
> It would be nice to have some consistency here - always functions
> for copies, always methods for in-place modifications.
>
> > I'm not sure what you mean by this. Surely, you aren't trying to
> > make:
> >
> > m=[[1,2,3],[4,5,6],[7,8,9]]
> > b=[11,22,33]
> > m=[1]=b
> > b[1]=99
> >
> > cause m[1][1] to equal 99? Are you?
>
> Actually this is not such a silly question, because Python's lists
> behave exactly in this way.
I didn't say it was a silly question. I know that lists behave this
way, but making matrices behave this way complicates things alot.
> So the question is whether we want
> arrays to have reference semantice (like Python lists) or value
> semantics (like Python integers, floats, and complex numbers).
> I strongly recommend the latter - reference semantics for
> arrays quickly produce confusion, as I had to find out while
> playing with such an implementation in Smalltalk.
You have to have some reference semantics to make m[i][j] work.
Actuall, the proposal has reference semantics for reference (ie
__getitem__) and copy semantics for element/slice assignment
(__setitem__).
> on, whereas operations like concatenation return a new object. In that
> vein, I feel that m.byteswap() should operate in-place on m and cause the
> memory associated with m to be byte-swapped. This is as opposed to having
> it return a new matrix in with the same dimensions as m, but with all values
> byte-swapped.
>
> This depends on what you would use byte swapping for. In fact, I am
> not at all convinced of its utility. What would happen when such
> a byte-swapped matrix is accessed? Would all operations on it
> still be allowed? That would create an enormous implementation effort.
> If not, then a byte-swapped matrix would no longer be a matrix,
> because none of the matrix operations could be used on it.
> The only reason I can see for a byte swapping operation is during
> I/O, so that's where it should go (as a flag to I/O functions).
>
>
> In general, I have the impression that we get too much lost in
> implementational details. Let's first define arrays as an abstract
> data type in terms of the operations allowed on it from a
> user's point of view and *then* worry about implementational
> details.
I agree that we should try to focus on requirements, however, a very
basic requirement of this type is it's ability to support interfacing
with existing numerical routines, which imposes some significant
restrictions on it's implementation.
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 18:33:19 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id SAA11754 for matrix-sig-people; Wed, 13 Sep 1995 18:33:19 -0400
Received: from retro.jhuapl.edu (retro.jhuapl.edu [128.244.146.239]) by python.org (8.6.12/8.6.12) with ESMTP id SAA11749 for ; Wed, 13 Sep 1995 18:33:16 -0400
Message-Id: <199509132233.SAA11749@python.org>
Received: by retro.jhuapl.edu
(1.37.109.16/16.2) id AA186081648; Wed, 13 Sep 1995 18:34:08 -0400
Date: Wed, 13 Sep 1995 18:34:08 -0400
From: Chris Chase S1A
To: matrix-sig@python.org
Cc: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Subject: [PYTHON MATRIX-SIG] Re: Are we talking about one thing or two?
In-Reply-To: <199509132117.RAA20989@cyclone.ERE.UMontreal.CA>
References: <199509132046.QAA11169@python.org>
<199509132117.RAA20989@cyclone.ERE.UMontreal.CA>
Reply-To: chris.chase@jhuapl.edu
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Sender: owner-matrix-sig@python.org
Precedence: bulk
Hinsen> Why specialise? That only limits the flexibility of the whole
Hinsen> package. Why shouldn't it be possible to have "inverse" invert
Hinsen> each two-dimensional slice of a three-dimensional array? There
Hinsen> are applications for it, it doesn't cost any extra effort to
Hinsen> do it, so why not do it? Why invent artificial restrictions?
I did not mean that this could not be done. Inverse() in this context
is an operation on square matrices (R^NxN). It is not a concept that
I have experience generalizing to higher dimensions. Your example
maps the inverse() function onto the two-dimensional slices. I think
of this as a mapping operator that is applied to an indexed set of
projections followed by application of inverse().
In general, you could apply any matrix function in this manner. You
could do this for spectral factorization, diagonalization, etc. There
is no need to redefine your matrix linear algebra functions. Instead
you use a projection mapping/iterator (rank selector or whatever you
want to call it) to apply the matrix function to each two-dimensional
slice. This would seem the natural way to implement it. Many matrix
functions such as inverse() would be best implemented by a call to an
already existing and efficient C or FORTRAN library function.
Perhaps it would be more elegant (and like APL) if the rank selection
was a part of the inverse() parameter list but it can just as easily
be a part of a projection mapping function that does the rank
selection and then applies inverse(), i.e., a variation of the builtin
map():
map(function, array, rank selection parameters)
This avoids having to recode the same mechanism for each matrix
function. It also provides a method for applying already coded lower
dimensional operators (1D, 2D, 3D, whatever) to higher dimensional
arrays without having to recode.
James Hugunin made a comment about this - many of the mapping and
reduction operations are generalizations that can be implemented in
Python with calls to the appropriate external matrix-based function
(or whatever the lower dimensional function is).
Chris Chase
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 21:15:56 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id VAA12404 for matrix-sig-people; Wed, 13 Sep 1995 21:15:56 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id VAA12400 for ; Wed, 13 Sep 1995 21:15:52 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id VAA29547
(8.6.11/IDA-1.6); Wed, 13 Sep 1995 21:15:49 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id VAA15104; Wed, 13 Sep 1995 21:15:47 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id VAA03573; Wed, 13 Sep 1995 21:15:57 -0400
Date: Wed, 13 Sep 1995 21:15:57 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509140115.VAA03573@cyclone.ERE.UMontreal.CA>
To: jfulton@usgs.gov
CC: matrix-sig@python.org
In-reply-to: <199509132152.VAA25125@servrcolkr.cr.usgs.gov> (jfulton@usgs.gov)
Subject: Re: [PYTHON MATRIX-SIG] Let's get going
Sender: owner-matrix-sig@python.org
Precedence: bulk
You have to have some reference semantics to make m[i][j] work.
Actuall, the proposal has reference semantics for reference (ie
__getitem__) and copy semantics for element/slice assignment
(__setitem__).
Indexing is really just syntactic sugar for getitem() and setitem(),
so I wouldn't call that reference semantics.
I agree that we should try to focus on requirements, however, a very
basic requirement of this type is it's ability to support interfacing
with existing numerical routines, which imposes some significant
restrictions on it's implementation.
Sure. But that's just one point on the list of requirements ;-)
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 13 21:46:18 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id VAA12507 for matrix-sig-people; Wed, 13 Sep 1995 21:46:18 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id VAA12503 for ; Wed, 13 Sep 1995 21:46:12 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id VAA00006
(8.6.11/IDA-1.6); Wed, 13 Sep 1995 21:46:06 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id VAA20831; Wed, 13 Sep 1995 21:46:05 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id VAA04451; Wed, 13 Sep 1995 21:46:15 -0400
Date: Wed, 13 Sep 1995 21:46:15 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509140146.VAA04451@cyclone.ERE.UMontreal.CA>
To: chris.chase@jhuapl.edu
CC: matrix-sig@python.org
In-reply-to: <199509132233.SAA27339@cyclone.ERE.UMontreal.CA> (message from Chris Chase S1A on Wed, 13 Sep 1995 18:34:08 -0400)
Subject: [PYTHON MATRIX-SIG] Re: Are we talking about one thing or two?
Sender: owner-matrix-sig@python.org
Precedence: bulk
I did not mean that this could not be done. Inverse() in this context
is an operation on square matrices (R^NxN). It is not a concept that
I have experience generalizing to higher dimensions. Your example
maps the inverse() function onto the two-dimensional slices. I think
of this as a mapping operator that is applied to an indexed set of
projections followed by application of inverse().
In the rank concept that I outlined before, matrix inversion (and
similar operations) is an operation with an intrinsic rank of 2. Its
meaning for rank 3 arrays is thus an automatic consequence of the
general rank rules and *not* an additional rule. You could specify an
explicit rank of 1, reducing matrix inversion to scalar
inversion. Specifying a higher rank would lead to an error
message. Nevertheless, the possibility of specifying explicit ranks is
important, since they can also be variables to be determined at
runtime. There are applications where this is useful. And I would
like to stress again that this generalization comes at no disadvantage
to "two-dimensional only" users.
slice. This would seem the natural way to implement it. Many matrix
functions such as inverse() would be best implemented by a call to an
already existing and efficient C or FORTRAN library function.
Implmentation is a different story. I agree that using existing
libraries makes sense. I would nevertheless prefer C libraries to
Fortran libraries, as otherwise a Fortran compiler would become
necessary to install Python, and not every Unix system comes with a
Fortran compiler.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 08:34:59 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id IAA14107 for matrix-sig-people; Thu, 14 Sep 1995 08:34:59 -0400
Received: from CNRI.Reston.VA.US (cnri.reston.va.us [132.151.1.1]) by python.org (8.6.12/8.6.12) with SMTP id IAA14100 for ; Thu, 14 Sep 1995 08:34:56 -0400
Received: from monty.cnri.reston.va.us by CNRI.Reston.VA.US id aa18624;
14 Sep 95 8:27 EDT
Received: from localhost by monty via SMTP (940816.SGI.8.6.9/940406.SGI)
for id IAA01106; Thu, 14 Sep 1995 08:27:04 -0400
Message-Id: <199509141227.IAA01106@monty>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Why I won't add complex numbers to the base language
From: Guido van Rossum
X-Face: S#(=*/yxQ]{^7V>yr9ElReWJZvi^x!]tR6g^m5q?|f@/ChslBZm*tWH=*aicc{$>4c/M:$.
\@@o)psQitBQa!z()VjNsj+aqtDI/J0jX'a/=Qhn|gv6coKW}06%6gyuTn)oc6
Date: Thu, 14 Sep 1995 08:27:04 -0400
Sender: owner-matrix-sig@python.org
Precedence: bulk
[This is the first of a series of small essays that treat several
issues that have been brought up in the matrix sig. I'd like to make
an effort to at least separate the threads by subject, if not putting
some issues to rest altogether.]
It's been proposed to add complex numbers to the base language, and
when I muttered I didn't like that the response was "there's no
mathematical reason not to." Let me rebutt.
1. Mathematically, there is at least one difference: complex numbers
can't be compared (e.g. z1 < z2 is undefined when either has a nonzero
imaginary party). Python will have to define this operation anyhow,
since deep inside it requires that each object type defines comparison
-- but it leads me to believe that there should be plenty of
algorithms out there that wouldn't work if some of their parameters
were complex numbers. (At least if the designer of the code didn't
think about the possibility of complex numbers, the correctness of the
code can't be guaranteed in general.) As long as real and complex are
distinct types in Python, there is no fear that complex numbers will
be introduced into the computation accidentally (e.g. by taking the
sqrt() of a negative number or by a rounding error) -- they are passed
in or used explicitly.
2. The available of GNU or other pseudo-free packages for
transcendental functions on complex numbers is still a big restriction
for Python's portability. Everything that is part of the *core*
language has to be portable to every new platform. Since some of
these platforms are non Unix, portability of code written by a crowd
who assume gcc is available "everywhere" is questionable. There's
another problem with GNU code that's only relevant for a certain group
but which I care about anyway: people want to be able to embed Python
(at least it's core) in commercial applications, and if there's a GNU
licence attached to any part of that core, this is a problem. (Don't
get me wrong -- I don't mind having Python extensions that use GNU
stuff, as long as they can be taken out cleanly and easily by those
who can't use GNU stuff, for whatever reason.)
3. The implementation of complex numbers as the only floating-point
type in Python would add considerable complexity (no pun intended :-).
I take that this should mean that if x is a real number (e.g. 0.0) and
z is a complex number (e.g. sqrt(-1.0)), type(x) == type(z). This
means that the representation in memory of Python complex number
objects either has to contain the imaginary part at all times, or it
has to contain a flag telling whether there's an imaginary part or
not. Since there's no space in the standard object header for such a
flag (at least not without changes that would have repercussions at
many other places), it would have to be an additional byte (or more)
in the complex number object itself. Say an object header plus malloc
overhead is 12 bytes and a double precision float is 8 bytes. If we
choose to have an additional flag it gets rounded up to 4 bytes (these
are typical and optimistic numbers -- on some platforms your mileage
may vary). So we have two choices: either add a flag, so numbers
without an imaginary part are 12 (header) + 8(real) + 4 (flag) == 24
bytes and numbers with one are 12 + 8 + 4 + 8(imaginary) == 32 bytes,
or all numbers are 12 + 8 + 8 == 28 bytes. Since most Python programs
won't be using numbers with imaginary parts (Python being a general
programming language), we may want to choose the version with a flag
-- but this add at least one if statement to each C function operating
on complex numbers (at least two for binary operators). Timewise, the
fastest solution would be to always have the imaginary part there --
this would also make the code considerably cleaner.
Conclusion: there's a big price to be paid by every Python user for
having complex numbers as the only floating point type.
Consider the alternative: write an extension module ("complex") that
defines arithmetic on complex numbers (there are already examples of
how to do this in Python; a C version is totally plausible) and
another extensions module ("cmath") that defines transcendental
functions on complex number. Now the price is only paid by those who
use the feature. I think this solution can still lead to very clean
code. I would even sanction a coding style where one writes "from
math import *" to use the standard math functions, so it would require
only a one-line change to user the complex versions instead ("from
cmath import *").
--Guido van Rossum
URL:
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 08:46:38 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id IAA14155 for matrix-sig-people; Thu, 14 Sep 1995 08:46:38 -0400
Received: from CNRI.Reston.VA.US (cnri.reston.va.us [132.151.1.1]) by python.org (8.6.12/8.6.12) with SMTP id IAA14151 for ; Thu, 14 Sep 1995 08:46:35 -0400
Received: from monty.cnri.reston.va.us by CNRI.Reston.VA.US id aa19003;
14 Sep 95 8:44 EDT
Received: from localhost by monty via SMTP (940816.SGI.8.6.9/940406.SGI)
for id IAA01172; Thu, 14 Sep 1995 08:44:21 -0400
Message-Id: <199509141244.IAA01172@monty>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Forget adding new operators to Python
From: Guido van Rossum
X-Face: S#(=*/yxQ]{^7V>yr9ElReWJZvi^x!]tR6g^m5q?|f@/ChslBZm*tWH=*aicc{$>4c/M:$.
\@@o)psQitBQa!z()VjNsj+aqtDI/J0jX'a/=Qhn|gv6coKW}06%6gyuTn)oc6
Date: Thu, 14 Sep 1995 08:44:20 -0400
Sender: owner-matrix-sig@python.org
Precedence: bulk
[The second in a series of short essays on subjects raised in the
Matrix discussion.]
Let me be brief on this one: adding new operators (like ".*", "./" or
"\") to the language is no-no. I'm very fond of the fact that nearly
all graphical elements of the Python language correspond pretty
closely to their use in "everyday life" (with the C language
considered to be part of everyday life :-).
I should point out that even though the semantics of operators are
(almost) entirely defined by their operands, their syntax (including
priorities) is not -- the parser doesn't know the operand types.
Another, more practical reason is that adding a new operator requires
changes to many components of the language implementation -- e.g. if
".*" were to be added as a new numeric operator, I'd have to make
changes to every module that implements numbers, if only to add a NULL
pointer.
The only thing I regret is not having added "**" as an exponentiation
operator -- this may happen someday (contributions accepted!).
Oh, and there's also agreement that operators like "+=", "*=" should
eventually be added; though not "++" and "--".
--Guido van Rossum
URL:
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 09:27:08 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id JAA14334 for matrix-sig-people; Thu, 14 Sep 1995 09:27:08 -0400
Received: from CNRI.Reston.VA.US (cnri.reston.va.us [132.151.1.1]) by python.org (8.6.12/8.6.12) with SMTP id JAA14330 for ; Thu, 14 Sep 1995 09:27:05 -0400
Received: from monty.cnri.reston.va.us by CNRI.Reston.VA.US id aa19772;
14 Sep 95 9:20 EDT
Received: from localhost by monty via SMTP (940816.SGI.8.6.9/940406.SGI)
for id JAA01256; Thu, 14 Sep 1995 09:20:28 -0400
Message-Id: <199509141320.JAA01256@monty>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] A problem with slicing
From: Guido van Rossum
X-Face: S#(=*/yxQ]{^7V>yr9ElReWJZvi^x!]tR6g^m5q?|f@/ChslBZm*tWH=*aicc{$>4c/M:$.
\@@o)psQitBQa!z()VjNsj+aqtDI/J0jX'a/=Qhn|gv6coKW}06%6gyuTn)oc6
Date: Thu, 14 Sep 1995 09:20:28 -0400
Sender: owner-matrix-sig@python.org
Precedence: bulk
[The third in a series of short essays on subjects raised in the
Matrix discussion.]
Here's a problem where I have neither a strong opinion nor a perfect
solution...
Jim Fulton proposes an elegant indexing syntax for matrix objects
which doesn't require any changes to the language:
M[i][j]
references the element at column i and row j (or was that column j and
row i? Never mind...).
This nicely generalizes to slicing, so you can write:
M[i][j1:j2]
meaning the column vector at column i with row indices j1...j2-1.
Unfortunately, the analogous expression for a row vector won't work:
M[i1:i2][j]
The reason for this is that it works by interpreting M as a sequence
of columns (and it's all evaluated one thing at a time -- M[i][j]
means (M[i])[j], and so on). M[i] is column i, so M[i][j] is the
element at row j thereof. But slice semantics imply that of M is a
sequence of X'es, then M[i1:j1] is still a sequence of X'es -- just
shorter. So M[p:q][r] is really the same as M[p+r] (assuming r
URL:
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 09:39:19 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id JAA14406 for matrix-sig-people; Thu, 14 Sep 1995 09:39:19 -0400
Received: from rnet.rose.rsoc.rockwell.com (rose.rsoc.rockwell.com [161.40.39.100]) by python.org (8.6.12/8.6.12) with SMTP id JAA14402 for ; Thu, 14 Sep 1995 09:39:16 -0400
Received: by rnet.rose.rsoc.rockwell.com (4.1/SMI-4.1)
id AA24882; Thu, 14 Sep 95 08:40:05 CDT
Received: from sunrise(161.40.50.11) by rnet via smap (V1.3)
id sma024875; Thu Sep 14 08:39:43 1995
Received: from feynman.rsoc.rockwell.com by rose.rsoc.rockwell.com (5.0/SMI-SVR4)
id AA10685; Thu, 14 Sep 1995 08:39:40 -0500
Received: by feynman.rsoc.rockwell.com (5.0/SMI-SVR4)
id AA04618; Thu, 14 Sep 1995 08:39:37 -0500
Date: Thu, 14 Sep 1995 08:39:37 -0500
From: forrest@rose.rsoc.rockwell.com (Dave Forrest)
Message-Id: <9509141339.AA04618@feynman.rsoc.rockwell.com>
To: matrix-sig@python.org, guido@CNRI.Reston.VA.US
Subject: Re: [PYTHON MATRIX-SIG] Forget adding new operators to Python
X-Sun-Charset: US-ASCII
Content-Length: 525
Sender: owner-matrix-sig@python.org
Precedence: bulk
This is a strong reason to provide two different interfaces - matrix
and array. You can implement them both with the same code, but just
provide a simple interface to matrix that restricts itself to two
dimensions and does matrix multiplication with operator *. Array then
acts "element-wise" and generalizes to any dimension,etc.
Result: People who want stuff like my folks want are happy, people who
want the more general stuff are happy, it's only implemented once, and
no new operators are added to the language.
dF
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 10:15:25 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id KAA14570 for matrix-sig-people; Thu, 14 Sep 1995 10:15:25 -0400
Received: from servrcolkr.cr.usgs.gov (servrcolkr.cr.usgs.gov [136.177.112.5]) by python.org (8.6.12/8.6.12) with ESMTP id KAA14561 for ; Thu, 14 Sep 1995 10:15:22 -0400
Received: from localhost (dsjfqvarsa.er.usgs.gov [130.11.51.73]) by servrcolkr.cr.usgs.gov (EMAIL 1.2.1) with ESMTP id OAA16059; Thu, 14 Sep 1995 14:15:44 GMT
Message-Id: <199509141415.OAA16059@servrcolkr.cr.usgs.gov>
To: Guido van Rossum
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] A problem with slicing
In-reply-to: <199509141320.JAA01256@monty>
Date: Thu, 14 Sep 1995 10:15:42 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Thu, 14 Sep 1995 09:20:28 -0400
Guido van Rossum said:
> [The third in a series of short essays on subjects raised in the
> Matrix discussion.]
>
> Here's a problem where I have neither a strong opinion nor a perfect
> solution...
>
> Jim Fulton proposes an elegant indexing syntax for matrix objects
> which doesn't require any changes to the language:
>
> M[i][j]
>
> references the element at column i and row j (or was that column j and
> row i? Never mind...).
Actually, it's element j of sub-matrix i. If M is a 2-d matrix, then
you may choose to call submatrices either "rows" or "columns". I
prefer "columns".
> This nicely generalizes to slicing, so you can write:
>
> M[i][j1:j2]
>
> meaning the column vector at column i with row indices j1...j2-1.
>
> Unfortunately, the analogous expression for a row vector won't work:
>
> M[i1:i2][j]
>
> The reason for this is that it works by interpreting M as a sequence
> of columns (and it's all evaluated one thing at a time -- M[i][j]
> means (M[i])[j], and so on). M[i] is column i, so M[i][j] is the
> element at row j thereof. But slice semantics imply that of M is a
> sequence of X'es, then M[i1:j1] is still a sequence of X'es -- just
> shorter. So M[p:q][r] is really the same as M[p+r] (assuming r
>
> One way out of this is to adopt the syntax
>
> M[i, j]
>
> for simple indexing. This would require only a minor tweaking of the
> grammar I believe.
In fact, this could be as simple as saying that the comma operator
generates tuples inside of []s. This is:
M[i,j] is equivalent to M[(i,j)].
or even:
M[i,] is equivalent to M[(i,)]
> This could be extended to support
>
> M[i1:i2, j]
> M[i1:i2, j1:j2]
> M[i, j1:j2]
>
> (and of course higher-dimensional equivalents).
>
> This would require considerable changes of the run-time architecture
> of slicing and indexing, and since currently everything is geared
> towards one-dimensional indexing/slicing, but I suppose it would be
> doable.
I agree.
> (Funny how I'm accepting this possibility of changing the language
> here, while I'm violently opposed to it for operator definitions. I
Yeah. Strange even! ;-)
> guess with adding operators there is no end to the number of new
> operators you could dream up, so there would be no end to the change;
> while here there's a clear-cut one-time change.)
Hm.
I really don't think this is a good idea. I don't really think we
need M[i1:i2, j1:j2]. M[range(i1,i2),range(j1,j2)] is fine for me.
Plus, it also allows: M[(1,3,5),(2,4,6)], in other words, we can
simply allow a sequence of indexes for a dimension and then let range
generate the desired sequence when we want a range.
>
> Of course adopting such a change would completely ruin any possbility
> of using things like
>
> M[3, 4, 7] = [1, 10, 100]
>
> as roughly equivalent to
>
> M[3] = 1
> M[4] = 10
> M[7] = 100
>
> but then again I'm not too fond of that anyway (as a matter of fact,
> I'd oppose it strongly).
>
>
> Some other things that I haven't completely followed through, and that
> may cause complications for the theoretical foundation of it all:
>
> - Allowing M[i, j] for (multidimensional) sequence types would also
> meaning that D[i, j] would be equivalent to D[(i, j)] for
> dictionaries.
I see no reason to support M[i,j] for arbitrary sequence types. I'd
say that if a type wants to support multiple arguments to [], then it
should provide mapping behavior and have the mapping implementation
sniff for either an integer or a tuple argument and do the right
thing.
I am *vary much* against a language change to support this.
> - Should M[i][j] still be equivalent to M[i, j]?
Yes. M[i,j] is really a compact form of M[((i),(j))].
> - Now we have multidimensional sequence types, should be have a
> multidimensional equivalent of len()? Some ideas:
I'm against multi-dimension sequence types. 8->
> - len(a, i) would return a's length in dimension i; len(a, i) == len(a)
>
> - dim(a) (or rank(a)?) would return the number of dimensions
>
> - shape(a) would return a tuple giving a's dimensions, e.g. for a
> 3x4 matrix it would return (3, 4), and for a one-dimensional
> sequence such as a string or list, it would return a singleton
> tuple: (len(a),).
Unnecessary. Matrices can provide special methods for this.
> - How about multidimensional map(), filter(), reduce()?
>
> - map() seems easy (except there seems to be no easy way to specify
> the rank of the operator): it returns a similarly shaped
> multidimensional object whose elements are the function results for
> the corresponding elements of the input matrix
>
> - filter() is problematic since the columns won't be of the same
> length
>
> - reduce()??? -- someone who knows APL tell me what it should mean
There have been a number of proposals for generic functions that
operate over matrices in some fashion. I have not had time to digest
them yet. Stay tuned. :-) (Geez, I really need to get back to my day
job.)
> - Multidimensional for loops? Or should for iterate over the first
> dimension only?
What is wrong with nested for loops.
Ee-gads, what's gotten into you? :-]
> One sees there are many potential consequences of a seemingly simple
> change --
Simple?
I agree that enabling the tuplefication opertor, ",", in []s is
simple, but not adding mult-dimensional behavior to sequences.
> that's why I insist that language changes be thought through
> in extreme detail before being introduced...
I really don't see any reason why the matrix type should require
language changes (aside from the minor impact of the tuplefication
operator).
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 10:40:28 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id KAA14734 for matrix-sig-people; Thu, 14 Sep 1995 10:40:28 -0400
Received: from CNRI.Reston.VA.US (cnri.reston.va.us [132.151.1.1]) by python.org (8.6.12/8.6.12) with SMTP id KAA14728 for ; Thu, 14 Sep 1995 10:40:24 -0400
Received: from monty.cnri.reston.va.us by CNRI.Reston.VA.US id aa21971;
14 Sep 95 10:39 EDT
Received: from localhost by monty via SMTP (940816.SGI.8.6.9/940406.SGI)
id KAA01464; Thu, 14 Sep 1995 10:39:00 -0400
Message-Id: <199509141439.KAA01464@monty>
To: "Jim Fulton, U.S. Geological Survey"
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] A problem with slicing
In-reply-to: Your message of "Thu, 14 Sep 1995 10:15:42 EDT."
<199509141415.OAA16059@servrcolkr.cr.usgs.gov>
References: <199509141415.OAA16059@servrcolkr.cr.usgs.gov>
From: Guido van Rossum
X-Face: S#(=*/yxQ]{^7V>yr9ElReWJZvi^x!]tR6g^m5q?|f@/ChslBZm*tWH=*aicc{$>4c/M:$.
\@@o)psQitBQa!z()VjNsj+aqtDI/J0jX'a/=Qhn|gv6coKW}06%6gyuTn)oc6
Date: Thu, 14 Sep 1995 10:39:00 -0400
Sender: owner-matrix-sig@python.org
Precedence: bulk
> I really don't see any reason why the matrix type should require
> language changes (aside from the minor impact of the tuplefication
> operator).
I guess one of my points was that, given the desire for a fairly
consistent design, allowing tuples as indices is *not* a minor
change...
(More later.)
--Guido van Rossum
URL:
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 10:49:51 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id KAA14797 for matrix-sig-people; Thu, 14 Sep 1995 10:49:51 -0400
Received: from servrcolkr.cr.usgs.gov (servrcolkr.cr.usgs.gov [136.177.112.5]) by python.org (8.6.12/8.6.12) with ESMTP id KAA14793 for ; Thu, 14 Sep 1995 10:49:47 -0400
Received: from localhost (dsjfqvarsa.er.usgs.gov [130.11.51.73]) by servrcolkr.cr.usgs.gov (EMAIL 1.2.1) with ESMTP id OAA17703; Thu, 14 Sep 1995 14:50:27 GMT
Message-Id: <199509141450.OAA17703@servrcolkr.cr.usgs.gov>
To: Guido van Rossum
cc: "James L Fulton, Hydrologist, Reston, VA "
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] A problem with slicing
In-reply-to: <199509141439.KAA01464@monty>
Date: Thu, 14 Sep 1995 10:50:25 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Thu, 14 Sep 1995 10:39:00 -0400
Guido van Rossum said:
> > I really don't see any reason why the matrix type should require
> > language changes (aside from the minor impact of the tuplefication
> > operator).
>
> I guess one of my points was that, given the desire for a fairly
> consistent design, allowing tuples as indices is *not* a minor
> change...
I'm not suggesting that tuples should be allowed as indexes to
sequence types. Tuples are *already* allowed as indexes to mapping
types. I propose that matrices should provide *both* sequence and
mapping behavior, where the mapping behavior is used to support matrix
slicing. This requires *no* change to the language.
The only *minor* change proposed, which I could live without is to
allow the "," to generate tuples inside of []s, just as it does
outside of []. In fact, I view the current non-recognition of tuples
in []s as an inconsistency. For example:
a=1,2,3
is equivalent to:
a=(1,2,3)
and:
for spam in 1,2,3:
...
is equivalent to:
for spam in (1,2,3):
...
so why isn't:
a[1,2,3]
equivalent to:
a[(1,2,3)
Note that this change is cosmetic (like making modules callable ;-),
and is not *needed* for the matrix proposal.
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 11:00:34 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id LAA14891 for matrix-sig-people; Thu, 14 Sep 1995 11:00:34 -0400
Received: from CNRI.Reston.VA.US (cnri.reston.va.us [132.151.1.1]) by python.org (8.6.12/8.6.12) with SMTP id LAA14887 for ; Thu, 14 Sep 1995 11:00:32 -0400
Received: from monty.cnri.reston.va.us by CNRI.Reston.VA.US id aa22339;
14 Sep 95 10:55 EDT
Received: from localhost by monty via SMTP (940816.SGI.8.6.9/940406.SGI)
id KAA01555; Thu, 14 Sep 1995 10:55:32 -0400
Message-Id: <199509141455.KAA01555@monty>
To: "Jim Fulton, U.S. Geological Survey"
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] A problem with slicing
In-reply-to: Your message of "Thu, 14 Sep 1995 10:50:25 EDT."
<199509141450.OAA17703@servrcolkr.cr.usgs.gov>
References: <199509141450.OAA17703@servrcolkr.cr.usgs.gov>
From: Guido van Rossum
X-Face: S#(=*/yxQ]{^7V>yr9ElReWJZvi^x!]tR6g^m5q?|f@/ChslBZm*tWH=*aicc{$>4c/M:$.
\@@o)psQitBQa!z()VjNsj+aqtDI/J0jX'a/=Qhn|gv6coKW}06%6gyuTn)oc6
Date: Thu, 14 Sep 1995 10:55:32 -0400
Sender: owner-matrix-sig@python.org
Precedence: bulk
> The only *minor* change proposed, which I could live without is to
> allow the "," to generate tuples inside of []s, just as it does
> outside of []. In fact, I view the current non-recognition of tuples
> in []s as an inconsistency. For example:
[...]
> so why isn't:
>
> a[1,2,3]
>
> equivalent to:
>
> a[(1,2,3)]
Because there's also
a[1:2]
while there is no equivalent
a(1:2)
I could either tweak the priorities so that
a[1,2:3,4]
is parsed as
a[(1,2) : (3,4)]
or so that it is parsed as
a[1, (2:3), 4]
but neither appears very natural to me.
I guess my problem is that ":" and "," have "fuzzy" priorities, and
while everybody agrees that e.g. "*" binds tighter than "+", if you
ask a few people in the street, or even computer programmers, you'd
get confused answers.
--Guido van Rossum
URL:
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 11:12:30 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id LAA14951 for matrix-sig-people; Thu, 14 Sep 1995 11:12:30 -0400
Received: from servrcolkr.cr.usgs.gov (servrcolkr.cr.usgs.gov [136.177.112.5]) by python.org (8.6.12/8.6.12) with ESMTP id LAA14947 for ; Thu, 14 Sep 1995 11:12:25 -0400
Received: from localhost (dsjfqvarsa.er.usgs.gov [130.11.51.73]) by servrcolkr.cr.usgs.gov (EMAIL 1.2.1) with ESMTP id PAA18008; Thu, 14 Sep 1995 15:13:06 GMT
Message-Id: <199509141513.PAA18008@servrcolkr.cr.usgs.gov>
To: Guido van Rossum
cc: "James L Fulton, Hydrologist, Reston, VA "
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] A problem with slicing
In-reply-to: <199509141455.KAA01555@monty>
Date: Thu, 14 Sep 1995 11:13:03 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Thu, 14 Sep 1995 10:55:32 -0400
Guido van Rossum said:
> > The only *minor* change proposed, which I could live without is to
> > allow the "," to generate tuples inside of []s, just as it does
> > outside of []. In fact, I view the current non-recognition of tuples
> > in []s as an inconsistency. For example:
> [...]
> > so why isn't:
> >
> > a[1,2,3]
> >
> > equivalent to:
> >
> > a[(1,2,3)]
>
> Because there's also
>
> a[1:2]
>
> while there is no equivalent
>
> a(1:2)
>
> I could either tweak the priorities so that
>
> a[1,2:3,4]
>
> is parsed as
>
> a[(1,2) : (3,4)]
Can't be, ":" wants integers.
> or so that it is parsed as
>
> a[1, (2:3), 4]
Can't be, (2:3) is not a valid expression, so it can't yield a valid
element of the tuple.
> but neither appears very natural to me.
Good. :-)
> I guess my problem is that ":" and "," have "fuzzy" priorities, and
> while everybody agrees that e.g. "*" binds tighter than "+", if you
> ask a few people in the street, or even computer programmers, you'd
> get confused answers.
But ":" only makes sense for sequences and "," only makes sense for
mappings, so ":" and "," should never appear together in []s. I don't
think this is a precedence issue. I see your point that ":"
complicates things a bit. You not only have to recognize ",", but you
have to make sure that it is not used in conjunction with ":".
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 12:43:14 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id MAA15313 for matrix-sig-people; Thu, 14 Sep 1995 12:43:14 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id MAA15305 for ; Thu, 14 Sep 1995 12:43:04 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id MAA15050
(8.6.11/IDA-1.6); Thu, 14 Sep 1995 12:42:08 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id MAA12527; Thu, 14 Sep 1995 12:42:07 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id MAA06980; Thu, 14 Sep 1995 12:42:21 -0400
Date: Thu, 14 Sep 1995 12:42:21 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509141642.MAA06980@cyclone.ERE.UMontreal.CA>
To: guido@CNRI.Reston.VA.US
CC: matrix-sig@python.org
In-reply-to: <199509141227.IAA01106@monty> (message from Guido van Rossum on Thu, 14 Sep 1995 08:27:04 -0400)
Subject: Re: [PYTHON MATRIX-SIG] Why I won't add complex numbers to the base language
Sender: owner-matrix-sig@python.org
Precedence: bulk
1. Mathematically, there is at least one difference: complex numbers
can't be compared (e.g. z1 < z2 is undefined when either has a nonzero
imaginary party). Python will have to define this operation anyhow,
So that would lead to an exception. Nothing exceptional ;-), since
there are other operations that produce exceptions (like division
by zero).
-- but it leads me to believe that there should be plenty of
algorithms out there that wouldn't work if some of their parameters
were complex numbers. (At least if the designer of the code didn't
think about the possibility of complex numbers, the correctness of the
code can't be guaranteed in general.) As long as real and complex are
True, but the same problem occurs with complex numbers defined in
packages. Since Python's variables and function parameters are not
types, nothing prevents me from passing complex numbers to an
algorithm not designed for them.
2. The available of GNU or other pseudo-free packages for
transcendental functions on complex numbers is still a big restriction
for Python's portability. Everything that is part of the *core*
language has to be portable to every new platform. Since some of
That should not be a problem. All codes for complex numbers that
I am aware of handle complex-number arithmetic in terms of (portable)
real arithmetic. An implementation of complex numbers in portable C
is just as possible as in portable Python.
another problem with GNU code that's only relevant for a certain group
but which I care about anyway: people want to be able to embed Python
(at least it's core) in commercial applications, and if there's a GNU
licence attached to any part of that core, this is a problem. (Don't
There are non-GNU complex libraries, and even writing a new one is
only a small task.
3. The implementation of complex numbers as the only floating-point
type in Python would add considerable complexity (no pun intended :-).
I take that this should mean that if x is a real number (e.g. 0.0) and
z is a complex number (e.g. sqrt(-1.0)), type(x) == type(z). This
Indeed.
means that the representation in memory of Python complex number
objects either has to contain the imaginary part at all times, or it
has to contain a flag telling whether there's an imaginary part or
Maybe. I don't know much about the internals of Python, I am just a
simple user... But I know that most APL implementation use different
internal types, but don't make this distinction visible to the user.
In fact, they don't even distinguish between integers and reals.
APL has only two types: character and numeric, although numeric
has four internal representations (bits, integers, reals, and complex
numbers).
Consider the alternative: write an extension module ("complex") that
defines arithmetic on complex numbers (there are already examples of
how to do this in Python; a C version is totally plausible) and
another extensions module ("cmath") that defines transcendental
functions on complex number. Now the price is only paid by those who
That would indeed be a good solution (I come to like Python's module
system more and more every day). I'll explore that when I find some
time... I don't see any real problem, everything necessary for
a convenient implementation should be there (i.e. coercion from
realfloat to complex, such that real constants can be entered
conveniently). It would be nice to have a more convenient notation
for complex constants, but that is not really essential.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 13:37:51 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id NAA15610 for matrix-sig-people; Thu, 14 Sep 1995 13:37:51 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id NAA15605 for ; Thu, 14 Sep 1995 13:37:46 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id NAA16541
(8.6.11/IDA-1.6); Thu, 14 Sep 1995 13:37:31 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id NAA24105; Thu, 14 Sep 1995 13:37:30 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id NAA09446; Thu, 14 Sep 1995 13:37:44 -0400
Date: Thu, 14 Sep 1995 13:37:44 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509141737.NAA09446@cyclone.ERE.UMontreal.CA>
To: guido@CNRI.Reston.VA.US
CC: matrix-sig@python.org
In-reply-to: <199509141244.IAA01172@monty> (message from Guido van Rossum on Thu, 14 Sep 1995 08:44:20 -0400)
Subject: Re: [PYTHON MATRIX-SIG] Forget adding new operators to Python
Sender: owner-matrix-sig@python.org
Precedence: bulk
The only thing I regret is not having added "**" as an exponentiation
operator -- this may happen someday (contributions accepted!).
Oh, and there's also agreement that operators like "+=", "*=" should
eventually be added; though not "++" and "--".
Great. I am waiting ;-)
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 14:21:15 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA15992 for matrix-sig-people; Thu, 14 Sep 1995 14:21:15 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id OAA15988 for ; Thu, 14 Sep 1995 14:21:11 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id OAA18142
(8.6.11/IDA-1.6); Thu, 14 Sep 1995 14:20:49 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id OAA03723; Thu, 14 Sep 1995 14:20:48 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id OAA11773; Thu, 14 Sep 1995 14:21:02 -0400
Date: Thu, 14 Sep 1995 14:21:02 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509141821.OAA11773@cyclone.ERE.UMontreal.CA>
To: guido@CNRI.Reston.VA.US
CC: matrix-sig@python.org
In-reply-to: <199509141320.JAA01256@monty> (message from Guido van Rossum on Thu, 14 Sep 1995 09:20:28 -0400)
Subject: Re: [PYTHON MATRIX-SIG] A problem with slicing
Sender: owner-matrix-sig@python.org
Precedence: bulk
One way out of this is to adopt the syntax
M[i, j]
for simple indexing. This would require only a minor tweaking of the
grammar I believe. This could be extended to support
M[i1:i2, j]
M[i1:i2, j1:j2]
M[i, j1:j2]
(and of course higher-dimensional equivalents).
How about allowing each of the index expressions to be an array
(of integers) itself? That gives an enormous flexibility (consider
that the indexing array could be a very complicated expression!).
Of course adopting such a change would completely ruin any possbility
of using things like
M[3, 4, 7] = [1, 10, 100]
as roughly equivalent to
M[3] = 1
M[4] = 10
M[7] = 100
but then again I'm not too fond of that anyway (as a matter of fact,
I'd oppose it strongly).
According to my proposal, that could be done as
M[[3,4,7]] = [1, 10, 100]
- Should M[i][j] still be equivalent to M[i, j]?
Basically the question is whether M[i] should be allowed (and with
what meaning) if M has more than one dimension. Maybe it is better
not to allow it at all, as it creates some confusion due to the
imperfect analogy of arrays with (nested) sequences.
- Now we have multidimensional sequence types, should be have a
multidimensional equivalent of len()? Some ideas:
- len(a, i) would return a's length in dimension i; len(a, i) == len(a)
- dim(a) (or rank(a)?) would return the number of dimensions
- shape(a) would return a tuple giving a's dimensions, e.g. for a
3x4 matrix it would return (3, 4), and for a one-dimensional
sequence such as a string or list, it would return a singleton
tuple: (len(a),).
How about having len() return the total number of elements? shape()
would be necessary anyway, and your proposed len(a,i) would
just be equivalent to shape(a)[i]. Similarly, dim(a) would
be len(shape(a)).
APL actually has only shape, which returns a one-dimensional
array of the lengths along each axis, i.e. a zero-length
vector in case of a scalar. Then dim(a) is just shape(shape(a)).
- How about multidimensional map(), filter(), reduce()?
map() is no problem, as you pointed out. Actually, if one adopts
a J-style rank concept, it wouldn't even be necessary. filter()
and reduce() should operate along a specified axis. filter()
would thereby return an array of equal dimension, but shorter
along one axis, and reduce() would return an array with the
dimension reduced by one.
- Multidimensional for loops? Or should for iterate over the first
dimension only?
Again, iteration over an arbitrary axis would be useful. Iteration
over all elements could be handled by map().
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 14:35:00 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA16145 for matrix-sig-people; Thu, 14 Sep 1995 14:35:00 -0400
Received: from mama-bear.LCS.MIT.EDU (mama-bear.lcs.mit.edu [18.27.0.167]) by python.org (8.6.12/8.6.12) with SMTP id OAA16141 for ; Thu, 14 Sep 1995 14:34:57 -0400
Received: from nineveh.LCS.MIT.EDU by mama-bear.LCS.MIT.EDU (4.1/SLS-4.1.1)
id AA09007; Thu, 14 Sep 95 14:35:03 EDT
Received: by nineveh.LCS.MIT.EDU (NX5.67e/SMI-4.1)
id AA04493; Thu, 14 Sep 95 14:41:40 -0400
Message-Id: <9509141841.AA04493@nineveh.LCS.MIT.EDU>
Content-Type: text/plain
Mime-Version: 1.0 (NeXT Mail 3.3 v118.2)
Received: by NeXT.Mailer (1.118.2)
From: James Hugunin
Date: Thu, 14 Sep 95 14:41:39 -0400
To: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] A problem with slicing
References: <199509141513.PAA18008@servrcolkr.cr.usgs.gov>
Sender: owner-matrix-sig@python.org
Precedence: bulk
I'm eager to see what Guido has to add in his "(More later.)", because at
the moment I'm slightly confused by the trend in this discussion. If the
point is that the proposed slicing semantics are a bad idea because they'd
require basic changes to the language, then I'd answer that no changes are
in fact necessary. If instead he is offering to open up this portion of the
language to change in the hopes of creating a more "consistent design",
then I think there are some interesting possibilities.
In my opinion, the slicing semantics for matrices as currently proposed
seem reasonable and can be implemented with zero changes to the core python
language by using getitem and setitem for mapping objects (I know this
because I've implemented them within the matrix module that way).
However, there is a good argument to be made saying that
- a[:3, :4]
is a lot more consistent with the current implementation of lists than
- a[(range(3), range(4))]
In addition, something like
- a[3:, 4:]
is a lot clearer than
- a[(range(3,shape(a[0]), range(4, shape(a)[1]))]
However, one thing that I really like about the proposed indexing scheme is
that I can say
a[((1,3,5), (2,4,6))]
and get back the desired non-contiguous chunk of the array. This is
occasionally very useful from an efficiency point of view, and
unfortunately, efficiency is something that needs to be kept in mind for
large numeric arrays.
If I had my wish, I would change the syntax of python so that
start:step:stop, or start:stop was a shorthand for creating a range object
(with some reasonable way of specifying start or stop as defaults). This
would not need to change the semantics of basic sequence indexing, as these
could be handled as a special case. I think that ":" should obviously be of
higher precedence than ",". There are no cases that I can think of where
it would be a reasonable thing to have a tuple as one element in a range.
With this change, and the I feel completely reasonable proposal that "," be
allowed for tuple creation within an index, then multidimensional arrays
could be made to behave in a manner completely consistent with lists. And
this would require minimal changes to the run-time architecture of slicing
and indexing.
On a slightly different track:
I've been playing with another technique for indexing a matrix, borrowed
from matlab. I've implemented indexing matrices with a matrix of booleans
(integers) that is the same size as the matrix being indexed (this only
makes sense for a setvalue). This is trivially done using the mapping
semantics, and combined with some matrix comparision operators I've found
this quite useful.
ie. a.gt(x) is a matrix less than operator. (I'd prefer to use "a < x",
but the point of this is to explore what can be done without changing the
core python language that all of us love so much).
a = matrix([1,2,3,4,5,4,3,2,1])
a[a.gt(3)] = 3
print a
--> [1,2,3,3,3,3,3,2,1]
I don't think that this is a substitute for any of the indexing methods
currently being discussed, but I want to make sure that all candidate
indexing methods are brought up as early on in the discussion as possible in
order to ultimately create a coherent (rather than haphazard) indexing
system.
-Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Sep 14 18:47:09 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id SAA17414 for matrix-sig-people; Thu, 14 Sep 1995 18:47:09 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id SAA17410 for ; Thu, 14 Sep 1995 18:47:06 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id SAA26236
(8.6.11/IDA-1.6); Thu, 14 Sep 1995 18:46:43 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id SAA06103; Thu, 14 Sep 1995 18:46:42 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id SAA26506; Thu, 14 Sep 1995 18:46:58 -0400
Date: Thu, 14 Sep 1995 18:46:58 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509142246.SAA26506@cyclone.ERE.UMontreal.CA>
To: jjh@mama-bear.lcs.mit.edu
CC: matrix-sig@python.org
In-reply-to: <9509141841.AA04493@nineveh.LCS.MIT.EDU> (message from James Hugunin on Thu, 14 Sep 95 14:41:39 -0400)
Subject: Re: [PYTHON MATRIX-SIG] A problem with slicing
Sender: owner-matrix-sig@python.org
Precedence: bulk
I've been playing with another technique for indexing a matrix, borrowed
from matlab. I've implemented indexing matrices with a matrix of booleans
(integers) that is the same size as the matrix being indexed (this only
makes sense for a setvalue). This is trivially done using the mapping
semantics, and combined with some matrix comparision operators I've found
this quite useful.
Indeed. But how does it work for higher-dimensional arrays?
APL provides a similar functionality using an operator called
"compress". Its boolean argument is always one-dimensional and it a
works along a specified axis. If used on the right-hand side of an
expression, it is simply filter(). In APL2 it may also be used on
the left hand side of an assignment, as btw can all expressions
that evaluate to a subset of elements of an array. This is an
extremely powerful feature, but so complicated to implement that
to my knowledge only IBM's mainframe version does it without
restrictions.
In summary, I like your proposal, adding that it ought to
work along one (arbitrary) axis for higher-dimensional arrays.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Fri Sep 15 11:29:23 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id LAA19907 for matrix-sig-people; Fri, 15 Sep 1995 11:29:23 -0400
Received: from mama-bear.LCS.MIT.EDU (mama-bear.lcs.mit.edu [18.27.0.167]) by python.org (8.6.12/8.6.12) with SMTP id LAA19903 for ; Fri, 15 Sep 1995 11:29:18 -0400
Received: from nineveh.LCS.MIT.EDU by mama-bear.LCS.MIT.EDU (4.1/SLS-4.1.1)
id AA03305; Fri, 15 Sep 95 11:29:08 EDT
Received: by nineveh.LCS.MIT.EDU (NX5.67e/SMI-4.1)
id AA01177; Fri, 15 Sep 95 11:29:10 -0400
Message-Id: <9509151529.AA01177@nineveh.LCS.MIT.EDU>
Content-Type: text/plain
Mime-Version: 1.0 (NeXT Mail 3.3 v118.2)
Received: by NeXT.Mailer (1.118.2)
From: James Hugunin
Date: Fri, 15 Sep 95 11:29:09 -0400
To: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Subject: Re: [PYTHON MATRIX-SIG] A problem with slicing
Cc: matrix-sig@python.org
References: <199509142246.SAA26506@cyclone.ERE.UMontreal.CA>
Sender: owner-matrix-sig@python.org
Precedence: bulk
> I've been playing with another technique for indexing a matrix, borrowed
> from matlab. I've implemented indexing matrices with a matrix of booleans
> (integers) that is the same size as the matrix being indexed (this only
> makes sense for a setvalue). This is trivially done using the mapping
> semantics, and combined with some matrix comparision operators I've found
> this quite useful.
>
> Indeed. But how does it work for higher-dimensional arrays?
>
> APL provides a similar functionality using an operator called
> "compress". Its boolean argument is always one-dimensional and it a
> works along a specified axis. If used on the right-hand side of an
> expression, it is simply filter(). In APL2 it may also be used on
> the left hand side of an assignment, as btw can all expressions
> that evaluate to a subset of elements of an array. This is an
> extremely powerful feature, but so complicated to implement that
> to my knowledge only IBM's mainframe version does it without
> restrictions.
My initial idea for higher-dimensional arrays was to use a matrix of
booleans that was the size of the entire higher-d array being indexed, this
fits naturally with the outputs from matrix comparision operators. ie.
a = [[1,2,3], [4,5,6]]
--> a.gt(2) == [[0,0,1], [1,1,1]]
a[a.gt(2)] = 99
--> a == [[1,2,99], [99,99,99]]
However, I agree that it should be possible to rationally apply this to a
subset of the matrix. Something like:
a = [[1,2,3], [4,5,6]]
--> sum(a).gt(7) == [0,1]
a[sum(a).gt(7), :] = 99
--> a == [[1,2,3], [99,99,99]]
This example suggests that it should be possible to mix these boolean
indices with range indices, and with single integer indices. While this all
sounds reasonable (and extremely powerful) to me, I have to confess that a
few of my feeping creaturism warning lights go off when I sit back and look
at it. I'd love to hear more impressions on this, pro and con.
-Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Fri Sep 15 12:29:35 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id MAA20234 for matrix-sig-people; Fri, 15 Sep 1995 12:29:35 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id MAA20230 for ; Fri, 15 Sep 1995 12:29:24 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id MAA15149
(8.6.11/IDA-1.6); Fri, 15 Sep 1995 12:28:23 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id MAA05616; Fri, 15 Sep 1995 12:28:22 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id MAA13937; Fri, 15 Sep 1995 12:28:42 -0400
Date: Fri, 15 Sep 1995 12:28:42 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509151628.MAA13937@cyclone.ERE.UMontreal.CA>
To: jjh@mama-bear.lcs.mit.edu
CC: matrix-sig@python.org
In-reply-to: <9509151529.AA01177@nineveh.LCS.MIT.EDU> (message from James Hugunin on Fri, 15 Sep 95 11:29:09 -0400)
Subject: Re: [PYTHON MATRIX-SIG] A problem with slicing
Sender: owner-matrix-sig@python.org
Precedence: bulk
My initial idea for higher-dimensional arrays was to use a matrix of
booleans that was the size of the entire higher-d array being indexed, this
fits naturally with the outputs from matrix comparision operators. ie.
a = [[1,2,3], [4,5,6]]
--> a.gt(2) == [[0,0,1], [1,1,1]]
a[a.gt(2)] = 99
--> a == [[1,2,99], [99,99,99]]
That's nice on the left hand side of an assignment, but what is the value
of a[a.gt(2)] in an expression? It can't be an array!
If all you want is some form of selective assignment, that can be done
with mapping, although you have to type a bit more. You could also
achieve the above result (admittedly less efficiently) with
a = (not a > 2)*a + (a > 2)*99. Therefore I am not so sure whether
your proposed feature is that important, except if it were very
common.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Fri Sep 15 13:26:07 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id NAA20422 for matrix-sig-people; Fri, 15 Sep 1995 13:26:07 -0400
Received: from retro.jhuapl.edu (retro.jhuapl.edu [128.244.146.239]) by python.org (8.6.12/8.6.12) with ESMTP id NAA20418 for ; Fri, 15 Sep 1995 13:25:59 -0400
Message-Id: <199509151725.NAA20418@python.org>
Received: by retro.jhuapl.edu
(1.37.109.16/16.2) id AA255275977; Fri, 15 Sep 1995 13:26:17 -0400
Date: Fri, 15 Sep 1995 13:26:17 -0400
From: Chris Chase S1A
Cc: matrix-sig@python.org
To: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Subject: Re: [PYTHON MATRIX-SIG] A problem with slicing
In-Reply-To: <199509151628.MAA13937@cyclone.ERE.UMontreal.CA>
References: <9509151529.AA01177@nineveh.LCS.MIT.EDU>
<199509151628.MAA13937@cyclone.ERE.UMontreal.CA>
Reply-To: chris.chase@jhuapl.edu
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Sender: owner-matrix-sig@python.org
Precedence: bulk
Hinsen> a = [[1,2,3], [4,5,6]]
--> a.gt(2) == [[0,0,1], [1,1,1]]
Hinsen> a[a.gt(2)] = 99
--> a == [[1,2,99], [99,99,99]]
Hinsen> That's nice on the left hand side of an assignment, but what
Hinsen> is the value of a[a.gt(2)] in an expression? It can't be an
Hinsen> array!
It be an array if you define a selection type indexing, e.g.,
a.select(a.gt(2))
where select is a method that whose argument has the same
dimensions as "a" and returns elements of "a" that correspond to
"true" elements of a.gt(2).
The language Octave allows for this type of selection indexing.
There are several types of indexing schemes that I have seen for
multi-demensional arrays. Without additions to the syntax, only one
could be used with "[...]" and the others would have to be explicit
method calls.
I have a number of possible suggestions about different types of
array indexing which I will post later.
Chris Chase
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Fri Sep 15 14:25:58 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA20703 for matrix-sig-people; Fri, 15 Sep 1995 14:25:58 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id OAA20699 for ; Fri, 15 Sep 1995 14:25:51 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id OAA19270
(8.6.11/IDA-1.6); Fri, 15 Sep 1995 14:24:55 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id OAA29081; Fri, 15 Sep 1995 14:24:53 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id OAA21099; Fri, 15 Sep 1995 14:25:10 -0400
Date: Fri, 15 Sep 1995 14:25:10 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509151825.OAA21099@cyclone.ERE.UMontreal.CA>
To: chris.chase@jhuapl.edu
CC: matrix-sig@python.org
In-reply-to: <199509151725.NAA20418@python.org> (message from Chris Chase S1A on Fri, 15 Sep 1995 13:26:17 -0400)
Subject: Re: [PYTHON MATRIX-SIG] A problem with slicing
Sender: owner-matrix-sig@python.org
Precedence: bulk
Hinsen> That's nice on the left hand side of an assignment, but what
Hinsen> is the value of a[a.gt(2)] in an expression? It can't be an
Hinsen> array!
It be an array if you define a selection type indexing, e.g.,
a.select(a.gt(2))
where select is a method that whose argument has the same
dimensions as "a" and returns elements of "a" that correspond to
"true" elements of a.gt(2).
But *how* does it return the elements that correspong to "true"?
Specifically, what is the shape of the array returned?
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 19 20:29:06 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id UAA08091 for matrix-sig-people; Tue, 19 Sep 1995 20:29:06 -0400
Received: from big.fishnet.net (root@big.fishnet.net [205.216.133.3]) by python.org (8.6.12/8.6.12) with ESMTP id UAA08087 for ; Tue, 19 Sep 1995 20:29:02 -0400
Received: from port50.fishnet.net (port50.fishnet.net [205.216.133.250]) by big.fishnet.net (8.6.12/8.6.9) with SMTP id RAA19637 for ; Tue, 19 Sep 1995 17:30:03 GMT
Message-Id: <199509191730.RAA19637@big.fishnet.net>
X-Sender: graham@fishnet.net
X-Mailer: Windows Eudora Pro Version 2.1.2
Mime-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Date: Tue, 19 Sep 1995 17:31:31 -0700
To: matrix-sig@python.org
From: Graham Hughes
Subject: [PYTHON MATRIX-SIG] Assorted remarks
Sender: owner-matrix-sig@python.org
Precedence: bulk
Another compendium of responses to various messages.
--- Guido van Rossum ---
>- Allowing M[i, j] for (multidimensional) sequence types would also
>meaning that D[i, j] would be equivalent to D[(i, j)] for
>dictionaries.
Sounds good to me. Alternatively, we could interpret D[i,j] as D[i][j]...
After all, basically that's what M[i,j] is. Might warrant consideration.
>- Should M[i][j] still be equivalent to M[i, j]?
Probably. While slicing would be difficult as your earlier remarks point
out, it seems that allowing this will allow the use of sequences of
sequences. Actually, I'm not entirely convinced that M[i:j][k] is *not*
impossible; give me a week or so to decide on this, and I may come up with
something that doesn't require any changing of the core language...
>- Now we have multidimensional sequence types, should be have a
>multidimensional equivalent of len()? Some ideas:
Actually, I feel that for a multidimensional sequence, len() should behave
the same way it always had. Allowing for greater dimensions will give
greater flexibility, yes, but it may also break `older' functions that
assume that what's being passed to them is a 2D array. This is mainly
because len() is defined to return an integer, sadly, and not one of our
magic sequences; if it did return the magic sequence, we would be able to
totally ignore how many dimensions we have.
> - len(a, i) would return a's length in dimension i; len(a, i) == len(a)
As to extending len(), I don't think it's even necessary. Just do len(a[1]).
Short, simple, requires nothing out of the ordinary, *and* will even return
a 1 or something like that *if* [] returns an array of the proper rank (a 3D
will return a rank 2 array, 2 will return 1, etc.). This will not in fact
cause problems, because all operations on arrays should be defined to be
'elementwise' by default. This means we can pass a 3D array to something
expecting a matrix, and it won't make a difference. APL/J ranking does this
automatically, and given a little time I can probably work up an example
matrix that will do that too.
> - dim(a) (or rank(a)?) would return the number of dimensions
If we have shape(), that's not terribly useful either. Just do len(shape(a)).
> - shape(a) would return a tuple giving a's dimensions, e.g. for a
> 3x4 matrix it would return (3, 4), and for a one-dimensional
> sequence such as a string or list, it would return a singleton
> tuple: (len(a),).
Why a tuple, and not an array of rank 1? The array can be manipulated more
effectively than the tuple...
>- How about multidimensional map(), filter(), reduce()?
Not needed, really; see following remarks.
> - map() seems easy (except there seems to be no easy way to specify
> the rank of the operator): it returns a similarly shaped
> multidimensional object whose elements are the function results for
> the corresponding elements of the input matrix
If we use APL/J ranks, this is mostly taken care of automagically. The
function will request what it wants from the matrix, and the matrix can do
whatever it needs to with that.
Ah, but let's suppose you want the dimensions in a different order; let's
suppose you've got [ [ [1,2], [2,3] ] , [ [3,4], [4,5] ] ] (2x2x2 array).
Normally, map would invert over [ [1,2], [2,3] ] and [ [3,4], [4,5] ]. But
suppose you want it to behave differently; you want to 'rotate' the matrix.
I argue that this is an important enough operation that it should be
divorced from map, reduce, and filter, and brought into its own (notably
unlike APL, BTW; APL had a special modifier on *every* sequence operation to
do this. That is inefficient :).
> - filter() is problematic since the columns won't be of the same
> length
filter() should work just like map(); i.e. just like it already does. The
function that filter calls should know exactly what it wants out of the
array passed; there's no reason for filter() to have this information.
> - reduce()??? -- someone who knows APL tell me what it should mean
Oh, ok; this follows right along the lines of the above stuff; it should
work like it always did. Reducing a 2d array will simply have a grouping of
1d arrays being passed to the function. Since the function knows what it
wants (sense a common theme here?), the overall effect will be transparent.
I really *need* to get that sample matrix written up to illustrate this...
>- Multidimensional for loops? Or should for iterate over the first
>dimension only?
Same as above, i.e. first dimension only. If you want a different dimension,
rotate() the matrix.
BTW, notice that if (very strong if) I can get slicing to work properly,
*none* of this would require altering the language. Even if I can't, they
shouldn't require any alterations beyond the original [i,j] change.
Secret bonus; we can use [i,j] to signify non-continguous slices. While you
said you're not fond of this earlier (not quoted), and I agree that the
example given was a bit strange, this can give a great deal of additional
power to the matrix class. More on this later.
--- forrest@rose.rsoc.rockwell.com (Dave Forrest) ---
>This is a strong reason to provide two different interfaces - matrix
>and array. You can implement them both with the same code, but just
>provide a simple interface to matrix that restricts itself to two
>dimensions and does matrix multiplication with operator *. Array then
>acts "element-wise" and generalizes to any dimension,etc.
Not really. I'll reiterate what I've said before, that you don't need it
because all your functions will work fine with whatever dimension thingy we
pass it, with a bumper; *if* we do two different things, and you discover
that your original code needs to be modified somehow, or someone gets
confused and passes mixed arguments (matrix to array, or vice versa), all of
a sudden everything gets thoroughly confused. This is not a problem in
standard Python, since you can't add different types, but it might crop up
here, particularly if we write most of the interface in Python (which is
entirely possible, and possibly desirable).
Basically, my main argument against splitting it up is that it adds unneeded
complexity for a relatively small bonus (just write a matrix_multiply()
function that expects rank 2 arrays, and everything works peachy), and that
it would segregate the two entirely too much. For example, I would have no
way of taking my array and doing parallel matrix multiplications on it (i.e.
multiplying three sets of matrices at the same time), because while I can do
the parallelism in 'array' class and the matrix multiplication in the
'matrix' class, I would have no way to fold them together. As a result, I
would likely write a matrix_multiply() for the general matrix, and
_entirely_ sidestep the submatrix class. If you ever need to multiply
several matricies together (and you probably do) this parallelism is much
more efficient (because you spend more time in C code multiplying than in
Python figuring out which one to do next) than swapping matrices around.
Examine your code; you probably do something like (dropping into C for a while)
matrix m[4];
int i;
for (i = 0; i < 4; i++)
load_matrix(m[i]);
matrix_multiply(m[0],m[1]);
matrix_multiply(m[2],m[3]);
Notice that you're doing two multiplications. In C, there is no reason to do
parallelism (for efficiency sake) because you're not kicking back up into an
interpreter every time you multiply something. The parallel matrix would
work exactly the same way (would look a little different, I think, but not a
real problem), but would only kick in the interpreter *once*. This, for 5,
10, 200 matrices is a *real* timesaver. This point is why APL is so fast,
even though it is interpreted; it spends most of its time in C, not an
interpreter.
---
Graham
Graham Hughes Home page http://www.fishnet.net/~graham/
``I think it would be a good idea.'' -- Mahatma Ghandi, when asked what he
thought of Western civilization
finger for PGP public key.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 20 01:16:15 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id BAA08901 for matrix-sig-people; Wed, 20 Sep 1995 01:16:15 -0400
Received: from big.fishnet.net (root@big.fishnet.net [205.216.133.3]) by python.org (8.6.12/8.6.12) with ESMTP id BAA08893 for ; Wed, 20 Sep 1995 01:16:09 -0400
Received: from port27.fishnet.net (port27.fishnet.net [205.216.133.227]) by big.fishnet.net (8.6.12/8.6.9) with SMTP id WAA29411 for ; Tue, 19 Sep 1995 22:17:13 GMT
Message-Id: <199509192217.WAA29411@big.fishnet.net>
X-Sender: graham@fishnet.net
X-Mailer: Windows Eudora Pro Version 2.1.2
Mime-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Date: Tue, 19 Sep 1995 22:18:43 -0700
To: matrix-sig@python.org
From: Graham Hughes
Subject: [PYTHON MATRIX-SIG] Slicing
Sender: owner-matrix-sig@python.org
Precedence: bulk
I think I figured out how to get the slicing without any modification of the
existing Python base to work.
Earlier it was said that m[i:j][k] wouldn't work because of the precedence
rules. I think one way of avoiding these problems is to look at the slicing
this way:
Assume for the moment that sequences of sequences are stored by row, i.e.
like C. To get the slicing to work properly, we have to slice by *columns*.
As an example, suppose we have [[1,2,3],[2,3,4]], or
1 2 3
2 3 4
if we accept the original premise. The matrix class will store it this way
internally. However, every interaction with the user *must* make the matrix
look like this:
1 2
2 3
3 4
Given this, slicing is relatively easy, and [:] will return a transpose of
the internal storage. So m[0:1] will return in original form [[1],[2],[3]] or
1
2
3
This works great for slices. However, assigning to individual elements is a
tad tricker... Note that a special case for single dimension arrays will
simply do standard slicing, as the effect is the same.
Graham
Graham Hughes Home page http://www.fishnet.net/~graham/
``I think it would be a good idea.'' -- Mahatma Ghandi, when asked what he
thought of Western civilization
finger for PGP public key.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 20 09:18:49 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id JAA09918 for matrix-sig-people; Wed, 20 Sep 1995 09:18:49 -0400
Received: from servrcolkr.cr.usgs.gov (servrcolkr.cr.usgs.gov [136.177.112.5]) by python.org (8.6.12/8.6.12) with ESMTP id JAA09914 for ; Wed, 20 Sep 1995 09:18:46 -0400
Received: from localhost (dsjfqvarsa.er.usgs.gov [130.11.51.73]) by servrcolkr.cr.usgs.gov (EMAIL 1.2.1) with ESMTP id NAA29865; Wed, 20 Sep 1995 13:18:11 GMT
Message-Id: <199509201318.NAA29865@servrcolkr.cr.usgs.gov>
To: Graham Hughes
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Slicing
In-reply-to: <199509192217.WAA29411@big.fishnet.net>
Date: Wed, 20 Sep 1995 09:18:08 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Tue, 19 Sep 1995 22:18:43 -0700
Graham Hughes said:
> I think I figured out how to get the slicing without any modification of the
> existing Python base to work.
The original proposal *already* says how to get slicing without any
modifications to the core language.
> Earlier it was said that m[i:j][k] wouldn't work because of the precedence
> rules. I think one way of avoiding these problems is to look at the slicing
> this way:
>
> Assume for the moment that sequences of sequences are stored by row, i.e.
> like C.
See my earlier post. Matrices are stored by sub-matrix. Storage by
"rows" or by "columns" is a question of interpretation. Under my
system of interpretation, matrices are stored by column (in Fortran,
C, and the proposed Python extension).
> To get the slicing to work properly, we have to slice by *columns*.
> As an example, suppose we have [[1,2,3],[2,3,4]], or
>
> 1 2 3
>
> 2 3 4
What does this mean?
> if we accept the original premise. The matrix class will store it this way
> internally. However, every interaction with the user *must* make the matrix
> look like this:
>
> 1 2
>
> 2 3
>
> 3 4
Do you mean like: [[1,2],[2,3],[3,4]]?
> Given this, slicing is relatively easy,
How?
> and [:] will return a transpose of
> the internal storage.
Why? This would be inconsistent with other sequence types.
> So m[0:1] will return in original form [[1],[2],[3]] or
>
> 1
>
> 2
>
> 3
What has this gained? (Perhaps an example with greater than two rows
and columns would be better?)
Let's be clear about the goal. Goven a matrix that looks (by rows or
by columns, take your pick) like this:
0 10 20 30
1 11 21 31
2 12 22 32
3 13 23 33
one wants to be able to access a matrix that looks like this:
11 21
12 22
Some even want to be able to access a matrix that looks like this:
1 21
3 23
or even this:
0 30
3 33
and so on. And, of course, this needs to generalize to higher dimensions.
Also, modification to this access should be reflected in the matrix
being accessed.
I don't see how switching indexes solves this problem.
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 26 21:40:32 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id VAA06839 for matrix-sig-people; Tue, 26 Sep 1995 21:40:32 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id VAA06835 for ; Tue, 26 Sep 1995 21:40:27 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id VAA14168
(8.6.11/IDA-1.6 for ); Tue, 26 Sep 1995 21:37:37 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id VAA03450; Tue, 26 Sep 1995 21:37:37 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id VAA15833; Tue, 26 Sep 1995 21:38:49 -0400
Date: Tue, 26 Sep 1995 21:38:49 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509270138.VAA15833@cyclone.ERE.UMontreal.CA>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] J-style arrays in Python
Sender: owner-matrix-sig@python.org
Precedence: bulk
To bring back some life into this discussion group, I'll distribute a
Python implementation of J-like arrays, to give those unfamiliar with
J a chance to become familiar with its array and rank system. And of
course it is also usable, as long as the arrays don't become too big.
This is a first test version; many important functions are still
missing, others (like output formatting) need substantial improvement,
and many could probably be made faster with some thought.
To make (my) life simpler, I tried to stick to J's conventions as much
as possible (but hopefully without violating Python traditions). I am
not claiming that this is semantically the best way to implement arrays,
but it is a start.
Some general remarks:
- Since my implementation uses nested lists to represent arrays,
the elements can be arbitrary objects.
- Like arrays in J, my arrays are immutable, i.e. there is no
provision for changing individual elements. The reason for
making arrays immutable in J was that J is half-way to being
a functional language (it is not a pure functional language,
but many substantial problems can easily be solved in a
functional way). I have never missed element assignment, but
probably there are some good applications...
- All functions are implemented as external functions, not as
methods. The main reason is that at first I could not think of
a way to implement methods with variable rank, although later
I figured out how to do this (in the same way as I implemented
reduction).
I'll send two Python files; the first is the Array module itself,
and the second a kind of simple tutorial. Let me know if
anything is unclear. And let me know what you think of the
whole implementation!
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 26 21:40:56 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id VAA06845 for matrix-sig-people; Tue, 26 Sep 1995 21:40:56 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id VAA06841 for ; Tue, 26 Sep 1995 21:40:53 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id VAA14175
(8.6.11/IDA-1.6 for ); Tue, 26 Sep 1995 21:37:59 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id VAA03454; Tue, 26 Sep 1995 21:37:58 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id VAA15839; Tue, 26 Sep 1995 21:39:11 -0400
Date: Tue, 26 Sep 1995 21:39:11 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509270139.VAA15839@cyclone.ERE.UMontreal.CA>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Array.py
Sender: owner-matrix-sig@python.org
Precedence: bulk
# J-style array class
# Arrays are represented by a scalar or a list, possibly containing
# other lists in case of higher-rank arrays. Array rank is limited
# only by the user's patience.
# Send comments to Konrad Hinsen
import math, string
######################################################################
# Error type
ArrayError = 'ArrayError'
######################################################################
# Various functions that do the real work. Classes follow.
# Construct string representation of array
def _toString(data, dimension):
s = ''
if dimension == 0:
s = s + `data`
elif dimension == 1:
for e in data:
s = s + `e` + ' '
else:
separator = (dimension-1)*'\n'
for e in data:
s = s + _toString(e,dimension-1) + separator
return string.strip(s)
# Find the shape of an array and check for consistency
def _shape(data):
if type(data) == type([]):
if data and type(data[0]) == type([]):
shapes = map(lambda x:_shape(x),data)
for i in range(1,len(shapes)):
if shapes[i] != shapes[0]:
raise ArrayError, 'Inconsistent shapes'
shape = [len(data)]
shape = shape + shapes[0]
return shape
else:
return [len(data)]
else:
return []
# Construct a one-dimensional list of all array elements
def __ravel(data):
if type(data) == type([]):
if type(data[0]) == type([]):
return reduce(lambda a,b: a+b,
map(lambda x: __ravel(x), data))
else:
return data
else:
return [data]
def _ravel(array):
return Array(__ravel(array._data), reduce(lambda a,b: a*b, array._shape, 1))
# Reshape an array
def _reshape(array, shape):
array = ravel(array)
if len(shape._data) == 0:
return take(array,0)
else:
array = array._data
shape = shape._data
n = reduce(lambda a,b: a*b, shape)
if n > len(array):
nr = (n+len(array)-1)/len(array)
array = (nr*array)[:n]
elif n < len(array):
array = array[:n]
for i in range(len(shape)-1, 0, -1):
d = shape[i]
n = n/d
for j in range(n):
array[j:j+d] = [array[j:j+d]]
return Array(array,shape)
# Map a function on the first dimensions of an array
def _extract(a, index, dimension):
if len(a[1]) < dimension:
return a
else:
return (a[0][index], a[1][1:], a[2])
def _map(function, arglist, max_frame, scalar_flag):
result = []
if len(max_frame) == 0:
if scalar_flag:
result = apply(function,tuple(map(lambda a: a[0], arglist)))
else:
result = apply(function,tuple(map(lambda a: Array(a[0],a[2]),
arglist)))._data
else:
for index in range(max_frame[0]):
result.append(_map(function,
map(lambda a,i=index,d=len(max_frame):
_extract(a,i,d), arglist),
max_frame[1:], scalar_flag))
return result
# Reduce an array with a given binary function
def _reduce(function, array):
function = function[0]
array = array[0]
if len(array._shape) == 0:
return array
elif array._shape[0] == 0:
return reshape(function._neutral, array._shape[1:])
else:
result = Array(array._data[0], array._shape[1:])
for i in range(1,array._shape[0]):
result = function(result, Array(array._data[i], array._shape[1:]))
return result
# Find the higher of two ranks
def _maxrank(a,b):
if a == None or b == None:
return None
else:
return max(a,b)
######################################################################
# Array class definition
class Array:
def __init__(self, scalar_or_list, shape = None):
self._data = scalar_or_list
if shape == None:
self._shape = _shape(self._data)
else:
self._shape = shape
def __str__(self):
return _toString(self._data,len(self._shape))
__repr__ = __str__
def __len__(self):
if type(self._data) == type([]):
return len(self._data)
else:
return 1
def __getitem__(self, index):
return take(self, index)
def __getslice__(self, i, j):
return take(self, range(i,j))
def __add__(self, other):
return sum(self, other)
__radd__ = __add__
def __sub__(self, other):
return difference(self, other)
def __rsub__(self, other):
return difference(other, self)
def __mul__(self, other):
return product(self, other)
__rmul__ = __mul__
def __div__(self, other):
return quotient(self, other)
def __rdiv__(self, other):
return quotient(other, self)
def __pow__(self,other):
return power(self, other)
def __rpow__(self,other):
return power(other, self)
def __neg__(self):
return 0-self
# Check for arrayness
def isArray(x):
return hasattr(x,'_shape')
######################################################################
# Array function class
class ArrayFunction:
def __init__(self, function, ranks, intrinsic_ranks=None):
self._function = function
if isArray(ranks):
self._ranks = ranks._data
elif type(ranks) == type([]):
self._ranks = ranks
else:
self._ranks = [ranks]
if intrinsic_ranks == None:
self._intrinsic_ranks = self._ranks
else:
self._intrinsic_ranks = intrinsic_ranks
if len(self._ranks) == 1:
self._ranks = len(self._intrinsic_ranks)*self._ranks
def __call__(self, *args):
if len(self._ranks) != len(args):
raise ArrayError, 'Wrong number of arguments for an array function'
arglist = []
framelist = []
shapelist = []
for i in range(len(args)):
if isArray(args[i]):
arglist.append(args[i])
else:
arglist.append(Array(args[i]))
shape = arglist[i]._shape
rank = self._ranks[i]
intrinsic_rank = self._intrinsic_ranks[i]
if rank == None:
cell = 0
elif rank < 0:
cell = min(-rank,len(shape))
else:
cell = max(0,len(shape)-rank)
if intrinsic_rank != None:
cell = max(cell,len(shape)-intrinsic_rank)
framelist.append(shape[:cell])
shapelist.append(shape[cell:])
max_frame = []
for frame in framelist:
if len(frame) > len(max_frame):
max_frame = frame
for i in range(len(framelist)):
if framelist[i] != max_frame[len(max_frame)-len(framelist[i]):]:
raise ArrayError, 'Incompatible arguments'
scalar_function = reduce(lambda a,b:_maxrank(a,b),
self._intrinsic_ranks) == 0
return Array(_map(self._function, map(lambda a,b,c: (a._data,b,c),
arglist, framelist, shapelist),
max_frame, scalar_function))
def __getitem__(self, ranks):
return ArrayFunction(self._function,ranks,self._intrinsic_ranks)
class BinaryArrayFunction(ArrayFunction):
def __init__(self, function, neutral_element, ranks, intrinsic_ranks=None):
ArrayFunction.__init__(self, function, ranks, intrinsic_ranks)
self._neutral = neutral_element
self.over = ArrayFunction(ArrayOperator(_reduce, [self]), [None])
def __getitem__(self, ranks):
return BinaryArrayFunction(self._function, self._neutral,
ranks, self._intrinsic_ranks)
class ArrayOperator:
def __init__(self, operator, function_list):
self._operator = operator
self._functions = function_list
def __call__(self, *args):
return apply(self._operator, (self._functions, args))
######################################################################
# Array functions
# Structural functions
shape = ArrayFunction(lambda a: Array(a._shape), [None])
reshape = ArrayFunction(_reshape, [None, 1])
ravel = ArrayFunction(_ravel, [None])
take = ArrayFunction(lambda a,i: Array(a._data[i._data], a._shape[1:]),
[None, 0])
# Elementwise binary functions
_sum = ArrayFunction(lambda a,b: a+b, [0, 0])
_difference = ArrayFunction(lambda a,b: a-b, [0, 0])
_product = ArrayFunction(lambda a,b: a*b, [0, 0])
_quotient = ArrayFunction(lambda a,b: a/b, [0, 0])
_power = ArrayFunction(pow, [0, 0])
sum = BinaryArrayFunction(_sum, 0, [None, None])
difference = BinaryArrayFunction(_difference, 0, [None, None])
product = BinaryArrayFunction(_product, 1, [None, None])
quotient = BinaryArrayFunction(_quotient, 1, [None, None])
power = BinaryArrayFunction(_power, 1, [None, None])
# Scalar functions of one variable
sqrt = ArrayFunction(math.sqrt, [0])
exp = ArrayFunction(math.exp, [0])
log = ArrayFunction(math.log, [0])
log10 = ArrayFunction(math.log10, [0])
sin = ArrayFunction(math.sin, [0])
cos = ArrayFunction(math.cos, [0])
tan = ArrayFunction(math.tan, [0])
asin = ArrayFunction(math.asin, [0])
acos = ArrayFunction(math.acos, [0])
atan = ArrayFunction(math.atan, [0])
sinh = ArrayFunction(math.sinh, [0])
cosh = ArrayFunction(math.cosh, [0])
tanh = ArrayFunction(math.tanh, [0])
floor = ArrayFunction(math.floor, [0])
ceil = ArrayFunction(math.ceil, [0])
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Sep 26 21:41:19 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id VAA06862 for matrix-sig-people; Tue, 26 Sep 1995 21:41:19 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id VAA06857 for ; Tue, 26 Sep 1995 21:41:14 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id VAA14191
(8.6.11/IDA-1.6 for ); Tue, 26 Sep 1995 21:38:23 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id VAA03460; Tue, 26 Sep 1995 21:38:22 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id VAA15853; Tue, 26 Sep 1995 21:39:35 -0400
Date: Tue, 26 Sep 1995 21:39:35 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509270139.VAA15853@cyclone.ERE.UMontreal.CA>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] ArrayTutorial.py
Sender: owner-matrix-sig@python.org
Precedence: bulk
# This file illustrates the use of the the Array class.
#
# Send comments to Konrad Hinsen
#
from Array import *
######################################################################
# Arrays are constructed from (nested) lists:
a = Array(range(10))
b = Array([ [2,3,7], [9,8,2] ])
c = Array([ [ [4,5,6], [0,4,5] ], [ [1,6,5], [8,5,2] ] ])
# Scalars make arrays of rank 0:
s = Array(42)
# Array elements can be anything:
text_array = Array(['Hello', 'world'])
# Arrays can be printed:
print 'a:\n', a
print 'b:\n', b
print 'c:\n', c
print 's:\n', s
# shape() returns an array containing the dimensions of another array:
print 'shape(a):\n', shape(a)
print 'shape(b):\n', shape(b)
print 'shape(c):\n', shape(c)
print 'shape(s):\n', shape(s)
# Scalar functions act on each element of an array:
print 'sqrt(b):\n',sqrt(b)
# Binary operators likewise work elementwise:
print 'c+c:\n',c+c
# But you can also add a scalar:
print 'c+s:\n',c+s
# To understand the general rule for combining arrays of different
# shapes in a function, we need some more technical terms:
# The length of the shape vector of an array is called its rank.
# The elements of an array along the first axis are called items.
# The items of an array of rank N have rank N-1. More generally,
# the shape vector is divided into frames and cells. Frames and
# cells are not properties of an array, but describe ways of looking
# at an array. For example, a rank-3 array can be regarded as
# as just that - a single rank-3 array. It can also be regarded
# as a rank-1 frame of rank-2 cells, or as a rank-2 frame of
# rank-1 cells. Or even as a rank-3 array of rank-0 cells, i.e.
# scalar cells.
#
# When two arrays are to be added (or multiplied, or...), their
# shapes need not equal, but the lower-rank array must match
# an equal-rank cell of the higher-rank array. The lower-rank
# array will then be combined with each corresponding cell, and
# the result will have the shape of the higher-rank array.
print 'b+c:\n',b+c
# The addition of a scalar is just a special case: it has rank 0
# and therefore matches the rank-0 cells (scalar elements) of any array!
print 'b+s:\n',b+s
# All operators are also available as normal binary function,
# e.g. addition can be written as
print 'sum(a,s):\n',sum(a,s)
# You'll need this form to perform reductions, e.g. a sum
# of all items of an array:
print 'sum.over(a):\n',sum.over(a)
# Let's do it with a higher-rank array:
print 'product.over(b):\n',product.over(b)
# But how do you get the product along the second axis
# of b? Easy:
print 'product.over[1](b):\n',product.over[1](b)
# The [1] after the function name product.over modifies
# the functions rank. Function ranks are related to array
# ranks, in that a function of rank N operates on the
# N-cells of its argument. If the argument has a higher
# rank, the function is applied to each N-cell and the
# result is combined with the frame of the argument.
# In the last example, product.over will be
# called for each of the 1-cells of b, returning a
# rank-0 result for each, and the results will be
# collected in the 1-frame of b, producing as a net
# result an array of rank 1.
#
# All functions have ranks; if no rank is explicitly
# given, the default rank will be used. The default
# rank of all reductions is 'unbounded', which means
# that the function will operate on the highest-level
# cells possible. Many functions have unbounded rank,
# for example shape():
print 'shape(c):\n',shape(c)
# But of course you can modify the rank of shape():
print 'shape[1](c):\n',shape[1](c)
print 'shape[2](c):\n',shape[2](c)
# Functions with more than one argument can have a different
# rank for each. The rank is applied to each argument,
# defining its frames and cells for this purpose. The frames
# must match in the same way as indicated above for
# addition of two arrays. The function is then applied
# to the cells, and if appropriate, the same matching
# procedure is applied once again. This may seem confusing
# at first, but it is really just the application of a
# single principle everywhere!
#
# For example, let's take a (our rank-1 array) and add
# b (our rank-2 array) to each of a's 0-cells:
print 'sum[[0,None]](a,b):\n',sum[[0,None]](a,b)
# 'None' stands for 'unbounded'. Since the rank of sum is
# 0 for its first argument, a is divided into 1-frames
# and 0-cells. For b the rank is unbounded, so it is
# treated as a 0-frame with 2-cells. b's 0-frame matches
# a's 1-frame (a 0-frame matches everything!), and
# the result gets the 1-frame. The cells of the result
# are sums of a rank-0 array (element of a) and a rank-2
# array (b), i.e. rank-2 arrays by the matching rules
# given above. So the net total is a rank-3 array,
# as you have seen.
# From now on we will specify the default rank of each function.
# It should be noted that specifying a higher rank than the
# default rank has no effect on the function's behaviour. Only
# lower ranks make a difference.
#
# All the scalar mathematical functions (sqrt, sin, ...) have
# rank 0. The binary arithmetic functions (sum, product, ...)
# have unbounded rank for both argument. For structural functions
# (i.e. those that modify an array's shape rather than its
# elements), the rank varies. As we have seen, shape() is
# unbounded. The other structural functions are yet to be
# introduced:
# ravel() produces a rank-1 array containing all elements
# of its argument. It has unbounded rank:
print 'ravel(c):\n',ravel(c)
# reshape() allows you to change the shape of an array.
# It first obtains a rank-1 list of elements of its first
# argument (like ravel()) and then reassembles the
# elements into an array with the shape given by the
# second argument:
print 'reshape(a,[2,2]):\n',reshape(a,[2,2])
print 'reshape(b,[10]):\n',reshape(b,[10])
# As you have seen in the second case, reshape() reuses
# the elements of its arguments all over if they get
# exhausted.
# You may have noticed that in some examples, a nested list
# has been used instead of an array as a function argument.
# In general, all array functions will convert its arguments
# to arrays if necessary.
# Now we need a way to select parts of an array. You can
# use standard indexing and slicing to obtain items
# (i.e. N-1 cells for a rank-N array):
print 'c[1]:\n',c[1]
print 'a[3:7]:\n',a[3:7]
# You can also specify an array as the index and obtain an
# array of the corresponding items:
print 'a[[2,6,1]]:\n',a[[2,6,1]]
# The function take() does exactly the same:
print 'take(c,1):\n',take(c,1)
# You will have to use take() if you want to modify its
# ranks, which are [None,0] by default. There is little
# point in changing the second rank (it wouldn't make
# any difference), but changing the first rank lets you
# select along other dimensions than the first:
print 'take[1](c,0):\n',take[1](c,0)
print 'take[2](c,1):\n',take[2](c,1)
# Isn't there something wrong here? take() takes
# two arguments and therefore needs two ranks. But for
# convenience, only one rank must be given if all ranks
# are to be the same.
# And that's the end of this introduction. Stay tuned for
# updates of the array package that will provide some
# important still missing in this version. In the meantime,
# play round with what there is to get a feeling for
# how things work.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 27 05:39:14 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id FAA08092 for matrix-sig-people; Wed, 27 Sep 1995 05:39:14 -0400
Received: from qvarsx.er.usgs.GOV (qvarsx.er.usgs.gov [130.11.51.82]) by python.org (8.6.12/8.6.12) with ESMTP id FAA08088 for ; Wed, 27 Sep 1995 05:39:09 -0400
Received: from localhost (dsjfqvarsa [130.11.51.73]) by qvarsx.er.usgs.GOV (EMAIL 1.2.1) with ESMTP id JAA29752; Wed, 27 Sep 1995 09:31:59 GMT
Message-Id: <199509270931.JAA29752@qvarsx.er.usgs.GOV>
To: hinsenk@ere.umontreal.ca (Hinsen Konrad)
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] J-style arrays in Python
In-reply-to: <199509270138.VAA15833@cyclone.ERE.UMontreal.CA>
Date: Wed, 27 Sep 1995 05:37:12 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Tue, 26 Sep 1995 21:38:49 -0400
Hinsen Konrad said:
> To bring back some life into this discussion group, I'll distribute a
> Python implementation of J-like arrays, to give those unfamiliar with
> J a chance to become familiar with its array and rank system.
Cool. This will help me become more familiar with some of the generic
processing operators that have been discussed. I for one have not had
much to say lately because I'm waiting for a chance to study some of the
suggestions for map-ish operators.
> And of
> course it is also usable, as long as the arrays don't become too big.
This is perfectly appropriate for a prototype. A real implementation
will need to handle large arrays well.
> This is a first test version; many important functions are still
> missing, others (like output formatting) need substantial improvement,
> and many could probably be made faster with some thought.
>
> To make (my) life simpler, I tried to stick to J's conventions as much
> as possible (but hopefully without violating Python traditions). I am
> not claiming that this is semantically the best way to implement arrays,
> but it is a start.
Please keep in mind that:
- If there is a standard matrix class, it will be implemented in C,
for performance reasons,
- There is already a base implementation, currently being worked on
by James Hugunin.
- Much of the base implementation was dictated by the *stated* goal
that the implementation should hold the data in an homogenous and
contingous block of data suitable for passing directly to existing
Fortran and C libraries.
> Some general remarks:
>
> - Since my implementation uses nested lists to represent arrays,
> the elements can be arbitrary objects.
Which violates one of the basic goals of this effort. I realize that
you may not agree with the goal, but this was clearly stated in the
announcement for *this* SIG.
> - Like arrays in J, my arrays are immutable, i.e. there is no
> provision for changing individual elements. The reason for
> making arrays immutable in J was that J is half-way to being
> a functional language (it is not a pure functional language,
> but many substantial problems can easily be solved in a
> functional way). I have never missed element assignment, but
> probably there are some good applications...
Gee. I would miss element assignment.
> - All functions are implemented as external functions, not as
> methods. The main reason is that at first I could not think of
> a way to implement methods with variable rank, although later
> I figured out how to do this (in the same way as I implemented
> reduction).
This brings up a good point. I think that whatever we come up with
should adhere to the KISS (Keep It Simple Stupid) rule as much as
possible. I'm in favor of a fairly lean matrix module with auxilary
modules to provide support for specific application areas.
> I'll send two Python files; the first is the Array module itself,
> and the second a kind of simple tutorial. Let me know if
> anything is unclear. And let me know what you think of the
> whole implementation!
I look forward to studying what you sent. (When I have a chance. :)
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 27 09:37:22 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id JAA08793 for matrix-sig-people; Wed, 27 Sep 1995 09:37:22 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id JAA08789 for ; Wed, 27 Sep 1995 09:37:16 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id JAA23343
(8.6.11/IDA-1.6); Wed, 27 Sep 1995 09:34:18 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id JAA07132; Wed, 27 Sep 1995 09:34:17 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id JAA06038; Wed, 27 Sep 1995 09:35:32 -0400
Date: Wed, 27 Sep 1995 09:35:32 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509271335.JAA06038@cyclone.ERE.UMontreal.CA>
To: jfulton@usgs.gov
CC: matrix-sig@python.org
In-reply-to: <199509270931.JAA29752@qvarsx.er.usgs.GOV> (jfulton@usgs.gov)
Subject: Re: [PYTHON MATRIX-SIG] J-style arrays in Python
Sender: owner-matrix-sig@python.org
Precedence: bulk
Please keep in mind that:
- If there is a standard matrix class, it will be implemented in C,
for performance reasons,
I hope so!
- Much of the base implementation was dictated by the *stated* goal
that the implementation should hold the data in an homogenous and
contingous block of data suitable for passing directly to existing
Fortran and C libraries.
Actually I can't think of any reason not to keep the data in a
continous block...
> Some general remarks:
>
> - Since my implementation uses nested lists to represent arrays,
> the elements can be arbitrary objects.
Which violates one of the basic goals of this effort. I realize that
you may not agree with the goal, but this was clearly stated in the
announcement for *this* SIG.
I do not disagree with that goal at all; in fact I seriously
considered adding type checking (or rather consistency checking) to my
Python implementation. But it would have slowed down everything
without producing much of an advantage (I assume no one will produce
mixed arrays by accident), so I left it out.
Again, I do not claim in the least that any future array module
should resemble my implementation in any respect. On the contrary,
I expect that both could be used in parallel for different
applications. I started writing this because I had a need for
flexible (but small) arrays, and then polished it up a bit to
make it usable as a demonstration for people in this SIG.
Gee. I would miss element assignment.
Really? I realize that element assignment is necessary to implement
many of the standard linear algebra algorithms, but these would be
implemented in C/Fortran/whatever anyway. I have never missed element
assignment in J; in fact, I only noticed its absence while working on
my Python implementation!
This brings up a good point. I think that whatever we come up with
should adhere to the KISS (Keep It Simple Stupid) rule as much as
possible. I'm in favor of a fairly lean matrix module with auxilary
modules to provide support for specific application areas.
So am I. But we must make sure that the auxiliary modules can
be used together conveniently. For example, the function names
should be distinct, to make it possible to import them all into
a single namespace (important for calculator-style use).
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 27 10:15:58 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id KAA09012 for matrix-sig-people; Wed, 27 Sep 1995 10:15:58 -0400
Received: from qvarsx.er.usgs.GOV (qvarsx.er.usgs.gov [130.11.51.82]) by python.org (8.6.12/8.6.12) with ESMTP id KAA09008 for ; Wed, 27 Sep 1995 10:15:56 -0400
Received: from localhost (dsjfqvarsa [130.11.51.73]) by qvarsx.er.usgs.GOV (EMAIL 1.2.1) with ESMTP id OAA04669; Wed, 27 Sep 1995 14:08:38 GMT
Message-Id: <199509271408.OAA04669@qvarsx.er.usgs.GOV>
To: hinsenk@ere.umontreal.ca (Hinsen Konrad)
cc: "James L Fulton, Hydrologist, Reston, VA "
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] J-style arrays in Python
In-reply-to: <199509271335.JAA06038@cyclone.ERE.UMontreal.CA>
Date: Wed, 27 Sep 1995 10:13:50 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Wed, 27 Sep 1995 09:35:32 -0400
Hinsen Konrad said:
>
> Please keep in mind that:
>
> - If there is a standard matrix class, it will be implemented in C,
> for performance reasons,
>
> I hope so!
>
> - Much of the base implementation was dictated by the *stated* goal
> that the implementation should hold the data in an homogenous and
> contingous block of data suitable for passing directly to existing
> Fortran and C libraries.
>
> Actually I can't think of any reason not to keep the data in a
> continous block...
>
> > Some general remarks:
> >
> > - Since my implementation uses nested lists to represent arrays,
> > the elements can be arbitrary objects.
>
> Which violates one of the basic goals of this effort. I realize that
> you may not agree with the goal, but this was clearly stated in the
> announcement for *this* SIG.
>
> I do not disagree with that goal at all; in fact I seriously
> considered adding type checking (or rather consistency checking) to my
> Python implementation. But it would have slowed down everything
> without producing much of an advantage (I assume no one will produce
> mixed arrays by accident), so I left it out.
>
> Again, I do not claim in the least that any future array module
> should resemble my implementation in any respect. On the contrary,
> I expect that both could be used in parallel for different
> applications. I started writing this because I had a need for
> flexible (but small) arrays, and then polished it up a bit to
> make it usable as a demonstration for people in this SIG.
Sounds like we're on the same wave-length then.
> Gee. I would miss element assignment.
>
> Really? I realize that element assignment is necessary to implement
> many of the standard linear algebra algorithms, but these would be
> implemented in C/Fortran/whatever anyway. I have never missed element
> assignment in J; in fact, I only noticed its absence while working on
> my Python implementation!
>
> This brings up a good point. I think that whatever we come up with
> should adhere to the KISS (Keep It Simple Stupid) rule as much as
> possible. I'm in favor of a fairly lean matrix module with auxilary
> modules to provide support for specific application areas.
>
> So am I. But we must make sure that the auxiliary modules can
> be used together conveniently. For example, the function names
> should be distinct, to make it possible to import them all into
> a single namespace
I don't agree with this. In general, I'm not a fan of
"from spam import *".
> (important for calculator-style use).
Why?
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Sep 27 11:03:33 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id LAA09219 for matrix-sig-people; Wed, 27 Sep 1995 11:03:33 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id LAA09215 for ; Wed, 27 Sep 1995 11:03:30 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id LAA26916
(8.6.11/IDA-1.6); Wed, 27 Sep 1995 11:00:30 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id LAA24095; Wed, 27 Sep 1995 11:00:29 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id LAA09849; Wed, 27 Sep 1995 11:01:45 -0400
Date: Wed, 27 Sep 1995 11:01:45 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509271501.LAA09849@cyclone.ERE.UMontreal.CA>
To: jfulton@usgs.gov, matrix-sig@python.org
In-reply-to: <199509271408.OAA04669@qvarsx.er.usgs.GOV> (jfulton@usgs.gov)
Subject: Re: [PYTHON MATRIX-SIG] J-style arrays in Python
Sender: owner-matrix-sig@python.org
Precedence: bulk
I don't agree with this. In general, I'm not a fan of
"from spam import *".
Neither am I, in general. But...
> (important for calculator-style use).
Why?
To avoid having to type the module name all over again, of course. I
am using my array module mostly to work (interactively) on simulation
results read in from files (I didn't include the I/O in the version I
sent around, because it still works only for a few special cases). So
I am typing things like
sqrt(Array("file1"))*Array("file2)
which I prefer a lot to
Array.sqrt(Array.Array("file1"))*Array.Array("file2)
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Fri Sep 29 08:36:05 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id IAA20328 for matrix-sig-people; Fri, 29 Sep 1995 08:36:05 -0400
Received: from CNRI.Reston.VA.US (cnri.reston.va.us [132.151.1.1]) by python.org (8.6.12/8.6.12) with SMTP id IAA20324 for ; Fri, 29 Sep 1995 08:36:02 -0400
Received: from monty.cnri.reston.va.us by CNRI.Reston.VA.US id aa08365;
29 Sep 95 8:17 EDT
Received: from localhost by monty via SMTP (940816.SGI.8.6.9/940406.SGI)
for id IAA19851; Fri, 29 Sep 1995 08:21:01 -0400
Message-Id: <199509291221.IAA19851@monty>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Time for recap?
From: Guido van Rossum
X-Face: S#(=*/yxQ]{^7V>yr9ElReWJZvi^x!]tR6g^m5q?|f@/ChslBZm*tWH=*aicc{$>4c/M:$.
\@@o)psQitBQa!z()VjNsj+aqtDI/J0jX'a/=Qhn|gv6coKW}06%6gyuTn)oc6
Date: Fri, 29 Sep 1995 08:21:01 -0400
Sender: owner-matrix-sig@python.org
Precedence: bulk
I'll try to be short.
I like the general idea of going with APL/J style multidimensional
objects.
I liked Konrad's sample implementation even though I agree an actual
implementation would have to have a contiguous representation.
I'm not sure about Konrad's particular notation for changing the rank
of an operator (though it is very cool that that is possible in
Python!). More thought needs to go in details like this.
I think the best way to proceed is to use the built-in operators for
APL/J style elementwise operations and to have the specific 2D linear
algebra operations as methods.
Likewise, instead of my earlier wild ideas about multidimensional
slices, I propose to use a slice() method that can produce arbitrary
slices; e.g. a[k][i:j] would be equivalent to a.slice(k, (i,j)).
I'm not against dropping the parentheses in a[(i,j)] but I'm not sure
if I can give it the proper thought and testing before October 13, the
scheduled release of Python 1.3 (at 13:13:13 hours :-). We can work
on a standard patch shortly after that date though.
- - -
I have one idea I would like to float by this group. How about
separating out the representation and the structure?
I believe I've seen C/Fortran matrix packages that made quite good use
of this. The representation would be a simple 1-dimensional sequence.
You'd normally not see or use this, but it would be there if you
needed access to it (e.g. for passing to C/Fortran code).
There's a simple way to map an index in an N-dim array into an index
in the 1-dim representation array (Fortran compilers use it all the
time :-).
To make efficient use of this, I propose that slicing and indexing, as
long as they return an object of rank >= 1, return an object that
points into the same representation sequence.
I propose one additional feature (which I believe I saw in Algol-68;
some matrix packages may also use it): add a "stride" to each
dimension (including the last). This makes it possible to have slices
reference discontiguous parts of the underlying representation, and
even to represent a transposed version!
If we do things just right, it may be possible to pass in the sequence
to be used as the representation -- it could be a Python list, tuple
or array (from the array module).
Again, all this can be prototyped in Python!
I would give an example, but I have to run.
--Guido van Rossum
URL:
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Fri Sep 29 09:47:32 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id JAA20640 for matrix-sig-people; Fri, 29 Sep 1995 09:47:32 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id JAA20636 for ; Fri, 29 Sep 1995 09:47:27 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id JAA26509
(8.6.11/IDA-1.6); Fri, 29 Sep 1995 09:43:43 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id JAA22407; Fri, 29 Sep 1995 09:43:42 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id JAA27953; Fri, 29 Sep 1995 09:45:05 -0400
Date: Fri, 29 Sep 1995 09:45:05 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199509291345.JAA27953@cyclone.ERE.UMontreal.CA>
To: guido@CNRI.Reston.VA.US
CC: matrix-sig@python.org
In-reply-to: <199509291221.IAA19851@monty> (message from Guido van Rossum on Fri, 29 Sep 1995 08:21:01 -0400)
Subject: Re: [PYTHON MATRIX-SIG] Time for recap?
Sender: owner-matrix-sig@python.org
Precedence: bulk
I liked Konrad's sample implementation even though I agree an actual
implementation would have to have a contiguous representation.
The two main reasons for choosing the nested list implementation
(remember, that was before I even knew about the Matrix SIG) were:
1) the possibility of using nested lists as a notation for
array constants; this means I rarely have to type Array().
2) many of the array operations can be written concisely as
recursive functions.
The second reason is less important for our purposes, but we do
have to think about a convenient notation for array constants.
We might actually use nested lists and provide a coercion function
that transforms them into a contiguous representation.
I'm not sure about Konrad's particular notation for changing the rank
of an operator (though it is very cool that that is possible in
Python!). More thought needs to go in details like this.
Definitely. I am not too pleased with it myself, mostly because the
specification of ranks for functions with more than one argument
is cumbersome, requiring two sets of square brackets. But I couldn't
find another way to specify rank in Python!
I think the best way to proceed is to use the built-in operators for
APL/J style elementwise operations and to have the specific 2D linear
algebra operations as methods.
Why such a division? There is no clear borderline between "elementwise"
and "linear algebra". If we should distinguish at all between methods
and functions, I'd propose to use methods for procedures that change
an array in place and functions for those that return a new array.
I have one idea I would like to float by this group. How about
separating out the representation and the structure?
I believe I've seen C/Fortran matrix packages that made quite good use
of this. The representation would be a simple 1-dimensional sequence.
You'd normally not see or use this, but it would be there if you
needed access to it (e.g. for passing to C/Fortran code).
I have also seen C++ packages that make the representation available
in this way, and it makes sense.
To make efficient use of this, I propose that slicing and indexing, as
long as they return an object of rank >= 1, return an object that
points into the same representation sequence.
Again, there are C++ classes that do this, and of course this makes
slicing very efficient. On the other hand, it makes element assignment
more complicated, since the array may have to be copied first.
I propose one additional feature (which I believe I saw in Algol-68;
some matrix packages may also use it): add a "stride" to each
dimension (including the last). This makes it possible to have slices
reference discontiguous parts of the underlying representation, and
even to represent a transposed version!
Again, this is used in C++ classes... There's a book that I have
recommended before (Scientific end Engineering C++, by John J. Barton
and Lee R. Nackman) that describes a matrix class with all the
implementation features you just proposed.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Fri Sep 29 15:20:56 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id PAA22181 for matrix-sig-people; Fri, 29 Sep 1995 15:20:56 -0400
Received: from mama-bear.LCS.MIT.EDU (mama-bear.lcs.mit.edu [18.27.0.167]) by python.org (8.6.12/8.6.12) with SMTP id PAA22177 for ; Fri, 29 Sep 1995 15:20:51 -0400
Received: from nineveh.LCS.MIT.EDU by mama-bear.LCS.MIT.EDU (4.1/SLS-4.1.1)
id AA25738; Fri, 29 Sep 95 15:17:47 EDT
Received: by nineveh.LCS.MIT.EDU (NX5.67e/SMI-4.1)
id AA07305; Fri, 29 Sep 95 15:18:03 -0400
Message-Id: <9509291918.AA07305@nineveh.LCS.MIT.EDU>
Content-Type: text/plain
Mime-Version: 1.0 (NeXT Mail 3.3 v118.2)
Received: by NeXT.Mailer (1.118.2)
From: James Hugunin
Date: Fri, 29 Sep 95 15:18:02 -0400
To: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Time for recap?
References: <199509291345.JAA27953@cyclone.ERE.UMontreal.CA>
Sender: owner-matrix-sig@python.org
Precedence: bulk
> I like the general idea of going with APL/J style multidimensional
> objects.
>
> I liked Konrad's sample implementation even though I agree an actual
> implementation would have to have a contiguous representation.
>
> I'm not sure about Konrad's particular notation for changing the rank
> of an operator (though it is very cool that that is possible in
> Python!). More thought needs to go in details like this.
I found Konrad's implementation enlightening regarding the ways of APL. I
have a different notation for many of the things he does. Some of this
notation should be discussed ASAP, but I'll save that for a later message.
> I think the best way to proceed is to use the built-in operators for
> APL/J style elementwise operations and to have the specific 2D linear
> algebra operations as methods.
I agree completely!
> Likewise, instead of my earlier wild ideas about multidimensional
> slices, I propose to use a slice() method that can produce arbitrary
> slices; e.g. a[k][i:j] would be equivalent to a.slice(k, (i,j)).
I disagree slightly here. I still prefer a[(k, range(i,j))] for Guido's
example. This makes a[(k, [x,y,z])] possible.
> I have one idea I would like to float by this group. How about
> separating out the representation and the structure?
>
> I believe I've seen C/Fortran matrix packages that made quite good use
> of this. The representation would be a simple 1-dimensional sequence.
> You'd normally not see or use this, but it would be there if you
> needed access to it (e.g. for passing to C/Fortran code).
>
> There's a simple way to map an index in an N-dim array into an index
> in the 1-dim representation array (Fortran compilers use it all the
> time :-).
>
> To make efficient use of this, I propose that slicing and indexing, as
> long as they return an object of rank >= 1, return an object that
> points into the same representation sequence.
>
> I propose one additional feature (which I believe I saw in Algol-68;
> some matrix packages may also use it): add a "stride" to each
> dimension (including the last). This makes it possible to have slices
> reference discontiguous parts of the underlying representation, and
> even to represent a transposed version!
I agree almost completely with this specification. In fact, in many ways
this is exactly what I have been implementing on top of Jim Fulton's
original matrix object. (btw - the real or the imaginary part of a complex
matrix are also particularly easy to represent using this style).
The one thing that I've been doing differently is that slices (ie. a[1:5])
are returned by value rather than by reference. This was Jim Fulton's
original implementation and I kept it because it was similar to the notion
of slicing a list. Conceptually, I have no problems with treating slices
the same as any other discontinuous index (ie. a[(range(1,5), range(4,6))])
and returning them by reference. Actually, I like the simplicity of being
able to think of every index into a matrix returning by reference.
I would be interested in hearing other opinions on this issue.
> If we do things just right, it may be possible to pass in the sequence
> to be used as the representation -- it could be a Python list, tuple
> or array (from the array module).
This is an interesting notion, but I can't see what it would gain over
using a 1d C-array of basic types as the representation. It wouldn't be too
hard to expand the definition of basic type to include (PyObject *)'s if
you'd like to have the possibility of a matrix of "real" python objects.
I should have time to finalize some of these things this weekend so that I
can make an alpha version of a matrix object written in C available to
interested members of this group on Monday. It is surprisingly similar to
Konrad's sample implementation in python (though a lot bigger, uglier and
yet faster owing to it's C-implementation).
-Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Sat Sep 30 14:15:42 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA25684 for matrix-sig-people; Sat, 30 Sep 1995 14:15:42 -0400
Received: from CNRI.Reston.VA.US (cnri.reston.va.us [132.151.1.1]) by python.org (8.6.12/8.6.12) with SMTP id OAA25680 for ; Sat, 30 Sep 1995 14:15:39 -0400
Received: from monty.cnri.reston.va.us by CNRI.Reston.VA.US id aa12822;
30 Sep 95 14:03 EDT
Received: from localhost by monty via SMTP (940816.SGI.8.6.9/940406.SGI)
id OAA22282; Sat, 30 Sep 1995 14:07:40 -0400
Message-Id: <199509301807.OAA22282@monty>
To: James Hugunin
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Time for recap?
In-reply-to: Your message of "Fri, 29 Sep 1995 15:18:02 EDT."
<9509291918.AA07305@nineveh.LCS.MIT.EDU>
References: <199509291345.JAA27953@cyclone.ERE.UMontreal.CA> <9509291918.AA07305@nineveh.LCS.MIT.EDU>
From: Guido van Rossum
X-Face: S#(=*/yxQ]{^7V>yr9ElReWJZvi^x!]tR6g^m5q?|f@/ChslBZm*tWH=*aicc{$>4c/M:$.
\@@o)psQitBQa!z()VjNsj+aqtDI/J0jX'a/=Qhn|gv6coKW}06%6gyuTn)oc6
Date: Sat, 30 Sep 1995 14:07:39 -0400
Sender: owner-matrix-sig@python.org
Precedence: bulk
> > Likewise, instead of my earlier wild ideas about multidimensional
> > slices, I propose to use a slice() method that can produce arbitrary
> > slices; e.g. a[k][i:j] would be equivalent to a.slice(k, (i,j)).
>
> I disagree slightly here. I still prefer a[(k, range(i,j))] for Guido's
> example. This makes a[(k, [x,y,z])] possible.
Ah. I was writing from memory, and forgot this feature. I don't like
it. How many times do you have to select a more or less arbitrary
group of rows/columns from a matrix? It makes the slice representation
bulkier -- contiguous slices can be stored (in C) as 4 ints per
dimension, while random selections will require a variable-length
list of indices per dimension. (It is also wasteful to have to
generate the full range of numbers when what you mean is a contiguous
slice.)
Perhaps a more important reason why I don't like this is that I see
problems with various notations. In the current version of the
language, you can't write a[2,3] -- you have to write a[(2,3)] and it
will be used as a mapping index (not as a sequence index!). The
proposal is to make this mean a[2][3]. That's fine with me.
There are certain ambiguities with the use of parentheses in Python
that I can't completely get rid of without breaking the type
structure: (1) means the same as 1 -- a scalar, while (1,2) is a tuple
of length 2. To write a tuple of length one, you can't write (1) --
you have to write (1,). (A tuple of length 0 is () -- easy.) For
consistency, I think I'll have to make a[(1,)] mean the same as a[1],
if a[(1,2)] is the same as a[1,2]. Finally, notice that to the object
being indexed, there is no way to tell a[(1,2)] from x=(1,2); a[x].
A tuple is just one example of a sequence in Python. Lists are
another example. In many situations, any sequence is acceptable and
the results are the same (e.g. for loops). (And in situations where
only lists or only tuples are accepted by the current version of the
language, Steve Majewski has often made the point that there is no
inherent reason why only one type should be accepted and that this
should be fixed. I agree in most cases.)
If we extend this rule to N-dimensional indexing, a[sequence], for
some sequence of integers whose elements are i, j, k, ..., should be
equivalent to a[i][j][k]..., and we can't make a[(1,2,3)] mean
a[1][2][3] while at the same time interpreting a[[1,2,3]] as a[1:4].
(Sooner or later, you'll be passing a vector to the index. Then the
question will arise, should this have the same meaning as a tuple or
as a list. It's better if they all three mean the same.)
Instead of supporting a[[2,3,5]] to select elements 2, 3 and 5 from a,
I would propose to use filter() or a multi-dimensional extension
thereof if you want to access selected subarrays. Or perhaps just a
method a.select() where each argument is either an index (meaning a
reduction of dimensionality in this dimension by picking just the
sub-array with this index) or a sequence of indices (meaning selecting
the set of sub-arrays with the indices in the sequence).
How about it? Is this acceptable?
> The one thing that I've been doing differently is that slices (ie. a[1:5])
> are returned by value rather than by reference. This was Jim Fulton's
> original implementation and I kept it because it was similar to the notion
> of slicing a list. Conceptually, I have no problems with treating slices
> the same as any other discontinuous index (ie. a[(range(1,5), range(4,6))])
> and returning them by reference. Actually, I like the simplicity of being
> able to think of every index into a matrix returning by reference.
It's somewhat odd that slices are returned by reference (since they
return a new value for lists), but not against the rules or silent
assumptions of the language, and I think it is necessary to make the
most of the flexibility that you get by using the notion of separating
the indexing and the representation object.
> I would be interested in hearing other opinions on this issue.
Me too.
> > If we do things just right, it may be possible to pass in the sequence
> > to be used as the representation -- it could be a Python list, tuple
> > or array (from the array module).
>
> This is an interesting notion, but I can't see what it would gain
> over using a 1d C-array of basic types as the representation.
I like this idea because the list or array containing the
representation may already be available in memory -- so why copy it?
Also by using an immutable underlying sequence (e.g. a tuple) it is
easy to create immutable N-dimensional arrays without the need for a
read-only flag. Finally it makes it possible to use a representation
where the actual values are stored in disk and only fetched into
memory when needed, using a cache -- this way you can implement your
own virtual memory system, persistent matrices, etc.
There could still be a "default" underlying representation that is
highly optimized and that the indexing object knows about, for speedier
access.
> It wouldn't be too hard to expand the definition of basic type to
> include (PyObject *)'s if you'd like to have the possibility of a
> matrix of "real" python objects.
Yes, the latter is definitely something that should be possible even
if my idea doesn't find the acceptance I hope it will get.
> I should have time to finalize some of these things this weekend so that I
> can make an alpha version of a matrix object written in C available to
> interested members of this group on Monday. It is surprisingly similar to
> Konrad's sample implementation in python (though a lot bigger, uglier and
> yet faster owing to it's C-implementation).
Cool!
--Guido van Rossum
URL:
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Sat Sep 30 14:36:36 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA25744 for matrix-sig-people; Sat, 30 Sep 1995 14:36:36 -0400
Received: from CNRI.Reston.VA.US (cnri.reston.va.us [132.151.1.1]) by python.org (8.6.12/8.6.12) with SMTP id OAA25740 for ; Sat, 30 Sep 1995 14:36:33 -0400
Received: from monty.cnri.reston.va.us by CNRI.Reston.VA.US id aa13152;
30 Sep 95 14:26 EDT
Received: from localhost by monty via SMTP (940816.SGI.8.6.9/940406.SGI)
id OAA22332; Sat, 30 Sep 1995 14:30:25 -0400
Message-Id: <199509301830.OAA22332@monty>
To: Hinsen Konrad
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Time for recap?
In-reply-to: Your message of "Fri, 29 Sep 1995 09:45:05 EDT."
<199509291345.JAA27953@cyclone.ERE.UMontreal.CA>
References: <199509291345.JAA27953@cyclone.ERE.UMontreal.CA>
From: Guido van Rossum
X-Face: S#(=*/yxQ]{^7V>yr9ElReWJZvi^x!]tR6g^m5q?|f@/ChslBZm*tWH=*aicc{$>4c/M:$.
\@@o)psQitBQa!z()VjNsj+aqtDI/J0jX'a/=Qhn|gv6coKW}06%6gyuTn)oc6
Date: Sat, 30 Sep 1995 14:30:24 -0400
Sender: owner-matrix-sig@python.org
Precedence: bulk
(Me:)
> I think the best way to proceed is to use the built-in operators for
> APL/J style elementwise operations and to have the specific 2D linear
> algebra operations as methods.
(Konrad:)
> Why such a division? There is no clear borderline between "elementwise"
> and "linear algebra". If we should distinguish at all between methods
> and functions, I'd propose to use methods for procedures that change
> an array in place and functions for those that return a new array.
I just meant to end the debate about whether a*b should mean matrix
multiplication in the LA sense or elementwise multiplication like
APL/J. This only an issue for * and /. For / most people agree that
"matrix division" is numerically ill-defined and you should have
specified the particular decomposition you need instead. This leaves
*, and, in spite of the elegance of linear algebra matrix
multiplication, I vote for the consistency of elementwise
multiplication. This means that we need to define a method that
implements LA matrix multiply, e.g. a.mul(b). Functions are out of
the question (since they would have to live in the namespace reserved
for built-in functions.)
(Me:)
> To make efficient use of this, I propose that slicing and indexing, as
> long as they return an object of rank >= 1, return an object that
> points into the same representation sequence.
(Konrad:)
> Again, there are C++ classes that do this, and of course this makes
> slicing very efficient. On the other hand, it makes element assignment
> more complicated, since the array may have to be copied first.
I don't see much of a problem with that. Functions/methods that take
an array and return a like-shaped array should always copy their
argument before modifying it. Methods that are supposed to modify an
array in place should not also return a reference to the array.
Functions/methods that wish to modify the array only as a means of
calculating some property of the array should come in to flavors: a
"low-level" version whose name ends in "_inplace", which works on the
array in place, and a "high-level" version which copies the array and
then invokes the low-level version. The user can then decide to use
the low-level version if performance so dictates, in which case the
consequences of course must be considerd first. (I'd hate to see
optional "in-place" flag arguments -- in general, I don't like to see
flag arguments where the calls will always contain a constant argument
value.)
--Guido van Rossum
URL:
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Sat Sep 30 14:57:12 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA25851 for matrix-sig-people; Sat, 30 Sep 1995 14:57:12 -0400
Received: from mama-bear.LCS.MIT.EDU (mama-bear.lcs.mit.edu [18.27.0.167]) by python.org (8.6.12/8.6.12) with SMTP id OAA25847 for ; Sat, 30 Sep 1995 14:57:09 -0400
Received: from nineveh.LCS.MIT.EDU by mama-bear.LCS.MIT.EDU (4.1/SLS-4.1.1)
id AA01106; Sat, 30 Sep 95 14:53:53 EDT
Received: by nineveh.LCS.MIT.EDU (NX5.67e/SMI-4.1)
id AA00377; Sat, 30 Sep 95 14:53:50 -0400
Message-Id: <9509301853.AA00377@nineveh.LCS.MIT.EDU>
Content-Type: text/plain
Mime-Version: 1.0 (NeXT Mail 3.3 v118.2)
Received: by NeXT.Mailer (1.118.2)
From: James Hugunin
Date: Sat, 30 Sep 95 14:53:50 -0400
To: Guido van Rossum
Subject: Re: [PYTHON MATRIX-SIG] Time for recap?
Cc: matrix-sig@python.org
References: <199509291345.JAA27953@cyclone.ERE.UMontreal.CA>
<9509291918.AA07305@nineveh.LCS.MIT.EDU>
<199509301807.OAA22282@monty>
Sender: owner-matrix-sig@python.org
Precedence: bulk
> > > Likewise, instead of my earlier wild ideas about multidimensional
> > > slices, I propose to use a slice() method that can produce arbitrary
> > > slices; e.g. a[k][i:j] would be equivalent to a.slice(k, (i,j)).
> >
> > I disagree slightly here. I still prefer a[(k, range(i,j))] for Guido's
> > example. This makes a[(k, [x,y,z])] possible.
>
> Ah. I was writing from memory, and forgot this feature. I don't like
> it. How many times do you have to select a more or less arbitrary
> group of rows/columns from a matrix? It makes the slice representation
> bulkier -- contiguous slices can be stored (in C) as 4 ints per
> dimension, while random selections will require a variable-length
> list of indices per dimension. (It is also wasteful to have to
> generate the full range of numbers when what you mean is a contiguous
> slice.)
As far as implementation efficiency is concerned, I have implemented things
so that each dimension requires 2 ints and an int pointer. If the
dimension corresponds to a quasi-contiguous slice then the pointer is set to
NULL, and the ints give the number of indices and a stride. If the
dimension corresponds to a random slice, then the pointer points to an array
of indices. So, very little efficiency is lost for quasi-contiguous
slices, and yet it is still possible to represent random slices.
My current favorite use for arbitrary indexing is for image zooming. ie:
img = [1,2,3,4]
a = img[((1,1,2,2,2,3,3,3,4,4),)]
a = [1,1,2,2,2,3,3,3,4,4]
This is a reasonably efficient approach for coarse-zooming in to even large
2d images, and it will work for on any arbitrary matrix (unlike C-code
which I must write a special case for each matrix).
My other reason for really liking this feature is that matlab supports it,
and I would really like to see this object support as large a subset of
matlab code as possible.
> To write a tuple of length one, you can't write (1) --
> you have to write (1,). (A tuple of length 0 is () -- easy.) For
> consistency, I think I'll have to make a[(1,)] mean the same as a[1],
> if a[(1,2)] is the same as a[1,2]. Finally, notice that to the object
> being indexed, there is no way to tell a[(1,2)] from x=(1,2); a[x].
I disagree completely with this. The proposal to allow tuplizing inside of
[]'s I feel should be completely compatible with "tupleizing" outside of
brackets. Just as you can right in python "a = 1," and a will be a 1d
tuple, you should be able to write a[1,] and have the index be a 1d tuple.
Of course, this will ultimately be your decision.
> If we extend this rule to N-dimensional indexing, a[sequence], for
> some sequence of integers whose elements are i, j, k, ..., should be
> equivalent to a[i][j][k]..., and we can't make a[(1,2,3)] mean
> a[1][2][3] while at the same time interpreting a[[1,2,3]] as a[1:4].
> (Sooner or later, you'll be passing a vector to the index. Then the
> question will arise, should this have the same meaning as a tuple or
> as a list. It's better if they all three mean the same.)
I actually agree with you completely on this. I have used Jim Fulton's
generic sequence indexing functions in order to be sure that the index can
happily be either a tuple or a list or a matrix (which actually should be
special cased and optimized at some point).
Either I missed a point somewhere, or you have a different interpretation
of my indexing scheme than I do. As some examples:
a[1,2,3] == a[(1,2,3)] == a[[1,2,3]] == a[1][2][3]
^ if this becomes allowed
a[(1,2,3),] == a[((1,2,3),)] == [a[1], a[2], a[3]]
However, if there is sufficient disapproval of this method of indexing,
a.select() is a reasonable alternative.
Just my two cents worth.
-Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Mon Oct 2 12:31:41 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id MAA00550 for matrix-sig-people; Mon, 2 Oct 1995 12:31:41 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id MAA00546 for ; Mon, 2 Oct 1995 12:31:35 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id MAA18714
(8.6.11/IDA-1.6); Mon, 2 Oct 1995 12:27:24 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id MAA19570; Mon, 2 Oct 1995 12:27:24 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id MAA14222; Mon, 2 Oct 1995 12:27:22 -0400
Date: Mon, 2 Oct 1995 12:27:22 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199510021627.MAA14222@cyclone.ERE.UMontreal.CA>
To: guido@CNRI.Reston.VA.US
CC: matrix-sig@python.org
In-reply-to: <199509301807.OAA22282@monty> (message from Guido van Rossum on Sat, 30 Sep 1995 14:07:39 -0400)
Subject: Re: [PYTHON MATRIX-SIG] Time for recap?
Sender: owner-matrix-sig@python.org
Precedence: bulk
Ah. I was writing from memory, and forgot this feature. I don't like
it. How many times do you have to select a more or less arbitrary
group of rows/columns from a matrix? It makes the slice representation
Quite often, if you use matrices as tables of numbers.
bulkier -- contiguous slices can be stored (in C) as 4 ints per
dimension, while random selections will require a variable-length
list of indices per dimension. (It is also wasteful to have to
generate the full range of numbers when what you mean is a contiguous
slice.)
Maybe there should be two internal representations, one for
contigous blocks (i.e. submatrices) and one for arbitrary
slices.
A tuple is just one example of a sequence in Python. Lists are
another example. In many situations, any sequence is acceptable and
the results are the same (e.g. for loops). (And in situations where
only lists or only tuples are accepted by the current version of the
language, Steve Majewski has often made the point that there is no
inherent reason why only one type should be accepted and that this
should be fixed. I agree in most cases.)
Actually, I have never really understood why Python needs both
tuples and lists. Anything you can with tuples you can do with
lists, so why have tuples?
Instead of supporting a[[2,3,5]] to select elements 2, 3 and 5 from a,
I would propose to use filter() or a multi-dimensional extension
thereof if you want to access selected subarrays. Or perhaps just a
You mean actually having to iterate over the whole array just to
pick some element? That sounds a bit wasteful.
One problem I am seeing in this discussion is that indexing is being
treated separately from other array operations, although it is really
only one structural function among many (reshaping, transposing,
etc.). We should rather discuss the complete set of structural
functions together; they should behave consistently and together allow
all array manipulations that might occur, no matter which can be done
by "indexing" and which by something with another name.
If you look at my Array implementation, you will see that there
indexing is just syntactic sugar for a structural function "take" that
selects an arbitrary set of items from its argument. It has the useful
property that the shape of the result is the combination of the shape
of the "index" argument and the shape of the items of the "data"
argument. That gives great flexibility in selecting subarrays, and
provides an easy-to-understand behaviour even in complicated cases.
In my implementation, indexing itself is limited, since you can't
specify rank, but I don't really care, since "take" lets me do
whatever I want.
> It wouldn't be too hard to expand the definition of basic type to
> include (PyObject *)'s if you'd like to have the possibility of a
> matrix of "real" python objects.
Yes, the latter is definitely something that should be possible even
if my idea doesn't find the acceptance I hope it will get.
You can add support from me ;-)
General arrays would be a useful feature without causing much
effort. The main problem I see is how to specify whether e.g. a
constant array of integers is supposed to be an array of integers or a
general array whose initial values happen to be integers. But this
can be overcome with explicit conversion, if necessary.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Mon Oct 2 13:12:44 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id NAA00732 for matrix-sig-people; Mon, 2 Oct 1995 13:12:44 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id NAA00728 for ; Mon, 2 Oct 1995 13:12:41 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id NAA19702
(8.6.11/IDA-1.6); Mon, 2 Oct 1995 13:08:13 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id NAA26008; Mon, 2 Oct 1995 13:08:12 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id NAA15986; Mon, 2 Oct 1995 13:08:11 -0400
Date: Mon, 2 Oct 1995 13:08:11 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199510021708.NAA15986@cyclone.ERE.UMontreal.CA>
To: guido@CNRI.Reston.VA.US
CC: matrix-sig@python.org
In-reply-to: <199509301830.OAA22332@monty> (message from Guido van Rossum on Sat, 30 Sep 1995 14:30:24 -0400)
Subject: Re: [PYTHON MATRIX-SIG] Time for recap?
Sender: owner-matrix-sig@python.org
Precedence: bulk
I just meant to end the debate about whether a*b should mean matrix
multiplication in the LA sense or elementwise multiplication like
APL/J. This only an issue for * and /. For / most people agree that
...
OK, on that I agree, of course. I thought you were referring to
functions like matrix inversion.
I don't see much of a problem with that. Functions/methods that take
an array and return a like-shaped array should always copy their
argument before modifying it. Methods that are supposed to modify an
array in place should not also return a reference to the array.
That seems a good way to make the distinction clear.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. A | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Oct 3 12:45:12 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id MAA06254 for matrix-sig-people; Tue, 3 Oct 1995 12:45:12 -0400
Received: from alfven.jhuapl.edu (alfven.jhuapl.edu [128.244.146.147]) by python.org (8.6.12/8.6.12) with SMTP id MAA06249 for ; Tue, 3 Oct 1995 12:45:05 -0400
Message-Id: <199510031645.MAA06249@python.org>
Received: by alfven.jhuapl.edu
(1.38.193.4/16.2) id AA12935; Tue, 3 Oct 1995 12:47:47 -0400
Date: Tue, 3 Oct 1995 12:47:47 -0400
From: Chris Chase S1A
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Mutli-dimensional indexing and other comments
Reply-To: chris.chase@jhuapl.edu
Sender: owner-matrix-sig@python.org
Precedence: bulk
This is a long note.
I have tried to go back and read through the various matrix/array
proposals from the start of this list. I have some comments on
various proposals and opinions. I also would like to make some
suggestions about multi-dimensional indexing.
----
Hinsen Konrad's proposal:
- Like arrays in J, my arrays are immutable, i.e. there is no
provision for changing individual elements.
I find that changinng individual elements a necessity in almost all
the computations that I do. For example, this kind of operation is
pervasive in imaging applications (e.g. segmentation and
region-of-interest manipulations) and block-matrix linear algebra
computations. For me, setting elements is a must.
-----
Array functions:
Hinsen Konrad supports calculator sytle array usage, i.e. interactive
computation:
I am typing things like
sqrt(Array("file1"))*Array("file2)
which I prefer a lot to
Array.sqrt(Array.Array("file1"))*Array.Array("file2)
I definitely prefer the first sytle for interactive computations.
I also like Konrad's array function prototype that allows rank
specification, e.g. product.over[1](b). Guido did not like the rank
specification via an index argument. Could the rank be overriden
using a keyword argument instead of an index argument,
e.g. product.over(b,rank=1)? Since keywords are commonly used to
override default arguments this would seem a natural possibility.
------
Heirarchical (list-like) indexing:
Jim Fulton's proposal thinks of multi-dimensional arrays as being
heirarchical so that a[i] returns a N-1 dimensioned array. This
allows him to use list style indexing, e.g. a[i][j]. This approach
would seem to rule out flatten indexing (one-dimensional indexing) and
multi-dimensional slicing, e.g. would a[2:9][1:5] just end up being
equivalent to a[3:7]? Perhaps that is what is wanted. If the
heirarchical approach is used then a different mechanism would be
necessary for more general multi-dimensional indexing. I could see
that heirarchical indexing like list indexing, would be useful. My
opinion is that arbitrary multi-dimensional slicing is more useful for
the homogeneous arrays we are discussing (they are not lists).
Perhaps both could be supported.
----
I am confused about proposals for array slices to be references. In
Jim's proposal, assignment is by copy and access is by reference.
Then am I correct in understanding that for b=a[i] (a is
two-dimensional) changing elements of b will not change a?
-----
General Multi-dimensional indexing:
In general, I have not been able to keep straight the various
proposals. It seems that most issues have been concerned with
implementation issues or with limiting extensions so they will not
break the current language.
Instead of diving into the implementation questions I would like to
present my views on multi-dimensional indexing for arrays. Then I
would like to know how the current proposals fit into this view.
I have worked with or investigated a number of interactive
array-oriented computational languages: IDL, Matlab, Tela, scilab,
Octave, rlab, Mathematica, APL. I have seen the following indexing
concepts all of which are very useful and completely generalize array
access.
Like Konrad, I will use the term "rank" to refer to the number of
dimensions of an array and "shape" to refer to the ordered list of
dimension sizes of an array.
The arrays are homogeneous and can be viewed as one-dimensional (as in
C or Fortran) or multi-dimensional.
index vector: a scalar, slice (with optional stride), or array. An
array index could be our new array (matrix) object, tuple or list.
Let A be a multi-dimensional array (rank is 3 in the examples) to be
indexed and let a1, a2, a3 be arbitrary index vectors.
one-dimensional indexing:
a) flattened indexing. Takes a single index vector and the array is
viewed like contiguously-stored arrays in Fortran or C (one or the
other, but consistent in that always the first or last dimension
changes most rapidly.) The shape of the result is the same as the
shape of the index vector.
b (not really 1-D?) heirachical indexing (Jim Fulton's list-like indexing):
Mentioned above. The index vector is used only for the first
dimension. I have never used this type of indexing for
homogeneous numeric arrays (because it is a special case of the
product indexing), but I see that it could be useful when viewing
the array as a list. For a scalar index the result has rank one
less than A. If a non-scalar is used as an index vector then the
index vector would be treated in flattened form and the result
would have have the same rank as A.
Multi-dimensional indexing:
The number of index vectors is equal to the rank A with one index
vector for each dimension. There are two types of indexing that I
have seen:
a) product indexing: You might call this arbitrary slice indexing. I
sometimes call this Cartesian product indexing because all
combinations of the index vector elements are used for indexing.
All index vectors are used in flattened form. The result has the
same rank but the size of each dimension is equal to the length of
the corresponding index vector. The elements of the result are
taken from all possible ordered combinations of the index vector
elements, e.g. the result element at index i,j,k is taken from A at
index a1(i),a2(j),a3(k).
b) mapped indexing (as in Tela): The index vectors all have the same
shape and are used in flattened form. Indexes into array A are
generated from ordered elementwise grouping, i.e. the result at 1-D
index i is taken from A at index a1(i),a2(i),a3(i). The shape of
the result is the same as the shape of the index vectors.
Selection indexing (as in Octave or APL):
A type of 1-D indexing where the index vector is a {0,1} vector that
has the same number of elements as A. The result contains only
those elements of A where the corresponding index vector is
non-zero.
Rather than support this directly, many languages, e.g. IDL and
Matlab, support this indirectly with a "where" or "find" function.
where(A) returns a rank 1 array containing in increasing order the
one-dimensional indexes where A is nonzero. The result of where(A)
can then be used for 1-D indexing. This is more powerful then
supporting selection directly.
Insertion indexing (as in IDL):
Used in setting blocks of items. Takes a 1-D or multi-dimensional
scalar index that specifies the starting position for the object
insertion, overwriting existing items. For a 1-D scalar index the
object inserted is used in flattened form. For a multi-dim index
the object inserted must have the same rank as A. When the inserted
object would extend beyond the dimension bounds of A there are
several possible behaviors: signal error, index wrap-around, or
truncation of A to fit.
In most of the languages that I have used, the syntax A[] is used for
both 1-D and multi-dimensional product/slicing indexing. Sometimes,
1-D indexing is used when only a single index vector is given. Konrad
suggests that this is just syntactic sugar in place of function calls
like "take" and "ravel". But it is extremely useful for writing
clear, readable code (even APL acknowledges this by supporting "[]"
syntax for indexing).
A natural solution syntactically would be:
1. "[]" subscripting to support 1-D indexing and product indexing
depending on the number of index vectors given.
2. allowing ":" slice notation for index vectors.
Mapped indexing, heirarchical indexing, insertion and where/selection
could be access methods.
It does not seem possible without major internal Python changes to
implement both 1) and 2) because of the ingrained and non-extensible
1-D indexing built-in to Python.
Someone (?) suggested that arbitrary (product) indexing did not seem
useful and slicing was sufficient. For my personal interests, product
indexing is necessary for a huge number of applications. I use mapped
indexing less often, but it is similarly necessary in many
applications. Of course all indexing can be converted manually into
1-D indexing, but this would make an array extension almost unuseable
for interactive use.
------
Question: Do any of the proposals completely support these types of
indexing in some form?
The closest I have seen to supporting product indexing is
James Hugunin proposal of using a mapping type:
M[(range(1,3),range(2,4))] -> [[13,14],[23,24]]
The idea suggests wrapping the index arguments of [] into a tuple.
However, this would not allow both 1-D and multi-dimensional indexing
for the same array without possible ambiguity.
To avoid the extra parentheses Guido suggests:
- Allowing M[i, j] for (multidimensional) sequence types would
also mean that D[i, j] would be equivalent to D[(i, j)] for
dictionaries.
But Jim Fulton:
I see no reason to support M[i,j] for arbitrary sequence types. I'd
say that if a type wants to support multiple arguments to [], then it
should provide mapping behavior and have the mapping implementation
sniff for either an integer or a tuple argument and do the right
thing.
I am *very much* against a language change to support this.
One complex solution that preserves the current Python language:
Treat M[i,j] as multiple dimension indexing and M[(i,j)] as 1-D
indexing. Think of M[i,j] as a function call with 2 arguments and
M[(i,j)] as a function call with a single argument. As specified by
the current language, M[(i,j)] is a mapping type, but (i,j) is treated
as a one-dimensional index vector selecting items i and j. On the
other hand, when multiple index arguments are used, e.g. M[i,j], a
different multi-dimension index method taking a variable length
argument list is called, e.g. __msetitem__ or __mgetitem__. This
allows both 1-D and multi-dim indexing for the same object that
_preserves_ existing language behavior.
This also overcomes the discussion of about bundling the index
arguments into a tuple, e.g. a[1,], which I think could be a source of
errors and confusion.
Of course, this solution requires large changes to Python internals.
-------------
Multi-dimensional slicing:
If possible I would prefer slice notation over range().
Using range() is certainly not as clean as ":". ":" is more than
syntaic sugar since it will use dimension length information for the
object that is not available to range(). With range() how do I
specify the entire dimension or dimension length for the upper bound?
For large array expressions the repetitive range() can generate a lot
of clutter.
For example:
a[1:3,:,5:] == a[range(1:3),range(0,shape(a)[1])),range(5,shape(a)[2])]
The left side is much more readable at a glance. Then imagine if this
was part of a larger expression.
Of course this could have been made a little simpler by storing the
shape in an auxillary variable:
s = shape(a)
then:
a[1:3,:,5:] == a[range(1:3),range(0,s[1])),range(5,len(s[2]))]
To support slice ":" for multi-dimensions Guido suggests an expression
like (2:3). But Jim Fulton replies:
Can't be, (2:3) is not a valid expression, so it can't yield a valid
element of the tuple.
Additionally, allowing (2:3) expressions in place of range() expressions
would break old slice usage.
A possible complex solution to allow ":" multi-dimensional expressions
with backwards compatibility:
To make slice expressions valid there would have to be some kind of
built-in slice type. For example, it could just be a tuple subclass
with the only difference being a type name of "slice". When used for
multi-dim indexing the indexing function would have to support
scalars, arrays or slices for the indexes by checking the type of the
index. For backwards compatibility, when using one-dimensional
indexing M[2:3] would call __getslice__(2,3) by unpacking the slice
tuple.
When using slices for multi-dimensional indexes, supporting negative
upper bounds would have to be a built-in dimension length function so
that the dimension upper bound would be available, e.g. when computing
M[2,3:-1]. If negative upper bounds could be forfeited for multi-dim
slices then the dimension length function would not be necessary ("None"
could be use for the upper bound in "2:").
If such additions to Python are just too much work, then I think Jim's
range() workaround is the next best thing.
------
The solutions above to allow ":" and 1-D plus multi-dimensional
indexing are not simple to implement. They would require major
internal changes to the compiler in addition to other areas. But they
would _preserve_ backwards compatibility with the current language.
They are complex because the original language was not designed with
extensibility to completely general multi-dimensional indexing in
mind.
General opinion about possible Python array/matrix extensions:
Trying to implement multi-dimensional arrays without any changes to
Python internals could end up being cumbersome, inelegant workarounds
that may be confusing and discouraging to end users (especially those
coming from numeric array languages like APL, Matlab, IDL, Tela, etc).
The extensions would most likely not be completely general. Why
should we limit the hamstring the capabilities of an array extension
just to avoid internal Python changes? Additionally, while they might
be useable in scripts, they will be inefficient for extensive
interactive use.
Chris Chase
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Oct 3 22:08:50 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id WAA09719 for matrix-sig-people; Tue, 3 Oct 1995 22:08:50 -0400
Received: from CNRI.Reston.VA.US (cnri.reston.va.us [132.151.1.1]) by python.org (8.6.12/8.6.12) with SMTP id WAA09714 for ; Tue, 3 Oct 1995 22:08:45 -0400
Received: from monty.cnri.reston.va.us by CNRI.Reston.VA.US id aa24939;
3 Oct 95 21:54 EDT
Received: from localhost by monty via SMTP (940816.SGI.8.6.9/940406.SGI)
id VAA05077; Tue, 3 Oct 1995 21:58:07 -0400
Message-Id: <199510040158.VAA05077@monty>
To: chris.chase@jhuapl.edu
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Mutli-dimensional indexing and other comments
In-reply-to: Your message of "Tue, 03 Oct 1995 12:47:47 EDT."
<199510031645.MAA06249@python.org>
References: <199510031645.MAA06249@python.org>
From: Guido van Rossum
X-Face: S#(=*/yxQ]{^7V>yr9ElReWJZvi^x!]tR6g^m5q?|f@/ChslBZm*tWH=*aicc{$>4c/M:$.
\@@o)psQitBQa!z()VjNsj+aqtDI/J0jX'a/=Qhn|gv6coKW}06%6gyuTn)oc6
Date: Tue, 03 Oct 1995 21:58:07 -0400
Sender: owner-matrix-sig@python.org
Precedence: bulk
> I also like Konrad's array function prototype that allows rank
> specification, e.g. product.over[1](b). Guido did not like the rank
> specification via an index argument. Could the rank be overriden
> using a keyword argument instead of an index argument,
> e.g. product.over(b,rank=1)? Since keywords are commonly used to
> override default arguments this would seem a natural possibility.
Sounds good to me, though I'm still struggling with the concent of the
rank of an operator...
> Heirarchical (list-like) indexing:
>
> Jim Fulton's proposal thinks of multi-dimensional arrays as being
> heirarchical so that a[i] returns a N-1 dimensioned array. This
> allows him to use list style indexing, e.g. a[i][j]. This approach
> would seem to rule out flatten indexing (one-dimensional indexing) and
> multi-dimensional slicing, e.g. would a[2:9][1:5] just end up being
> equivalent to a[3:7]?
Yes.
> Perhaps that is what is wanted.
Not necessarily, but it's the only thing that's possible given the
various constraints and the current language implementation. While
a[i,j] could easily be added (meaning a[i][j]), a[i:j,k:l] would
require major re-engineering of a complicated piece of code. I
proposed a.slice((i,j), (k,l)) to express this.
> I am confused about proposals for array slices to be references. In
> Jim's proposal, assignment is by copy and access is by reference.
> Then am I correct in understanding that for b=a[i] (a is
> two-dimensional) changing elements of b will not change a?
No. Assignment to a simple name (e.g. "b") will always be by
reference, this is fundamental in the language. However, *slice*
assignment has to copy (e.g. b[i:-1j] = a[i-1:j]), and I have proposed
that slice references (a[i:j] used in an expression) of arrays return
a reference to the original elements of a. E.g. after b = a[i:j],
assignment to elements of b will change the corresponding elements of
a. This is different from slice assignments for Python lists, but
seems to be the most useful rslice semantics for arrays.
> In general, I have not been able to keep straight the various
> proposals. It seems that most issues have been concerned with
> implementation issues or with limiting extensions so they will not
> break the current language.
Indeed, such are the practicalities of extending a language that has
been used and developed extensively by/for thousands of users over the
past five years.
> Instead of diving into the implementation questions I would like to
> present my views on multi-dimensional indexing for arrays. Then I
> would like to know how the current proposals fit into this view.
An approach that I like, at least in theory.
[What followed was too long for me to digest completely.]
> Why should we limit the hamstring the capabilities of an array
> extension just to avoid internal Python changes?
Because nobody wants to do the work.
> Additionally, while they might be useable in scripts, they will be
> inefficient for extensive interactive use.
Surely you mean "too much to type" and not "inefficient to execute".
Even though Python is quite usable interactively I don't think its its
real strengths lie there, and I don't think that interactive use
should be used as an argument in favor or against certain solutions.
--Guido van Rossum
URL:
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Oct 4 15:05:18 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id PAA01483 for matrix-sig-people; Wed, 4 Oct 1995 15:05:18 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id PAA01479 for ; Wed, 4 Oct 1995 15:05:12 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id PAA21349
(8.6.11/IDA-1.6); Wed, 4 Oct 1995 15:03:43 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id PAA02184; Wed, 4 Oct 1995 15:03:42 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id PAA18613; Wed, 4 Oct 1995 15:03:40 -0400
Date: Wed, 4 Oct 1995 15:03:40 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199510041903.PAA18613@cyclone.ERE.UMontreal.CA>
To: chris.chase@jhuapl.edu
CC: matrix-sig@python.org
In-reply-to: <199510031645.MAA06249@python.org> (message from Chris Chase S1A on Tue, 3 Oct 1995 12:47:47 -0400)
Subject: Re: [PYTHON MATRIX-SIG] Mutli-dimensional indexing and other comments
Sender: owner-matrix-sig@python.org
Precedence: bulk
specification via an index argument. Could the rank be overriden
using a keyword argument instead of an index argument,
e.g. product.over(b,rank=1)? Since keywords are commonly used to
override default arguments this would seem a natural possibility.
I have never used keyword arguments, but that's no reason not
to use them ;-) Basically the idea is not bad; however, it lacks
one useful feature of my implementation: it is not possible to
create function objects with modified ranks if the rank is only
supplied in the function call.
Whenever I need a particular function/rank combination often,
I just define a new function, e.g.
reshape_items = reshape[[-1,1]]
reshape_items(range(5),[2,2])
gives the result
0 0
0 0
1 1
1 1
2 2
2 2
3 3
3 3
4 4
4 4
This is useful not just to save typing, but also to give meaningful
names to nonobvious array functions, which helps to make the code
more readable.
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. Centre-Ville | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Oct 12 19:28:39 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id TAA15707 for matrix-sig-people; Thu, 12 Oct 1995 19:28:39 -0400
Received: from icf.llnl.gov (icf.llnl.gov [128.115.45.2]) by python.org (8.6.12/8.6.12) with SMTP id TAA15703; Thu, 12 Oct 1995 19:28:33 -0400
Received: from kristen.llnl.gov by icf.llnl.gov (4.1/LLNL-1.19)
id AA00469; Thu, 12 Oct 95 16:26:44 PDT
Received: by kristen.llnl.gov (5.0/SMI-SVR4)
id AA13225; Thu, 12 Oct 1995 16:26:42 +0800
Date: Thu, 12 Oct 1995 16:26:42 +0800
Message-Id: <9510122326.AA13225@kristen.llnl.gov>
From: "P. Dubois"
To: matrix-sig@python.org
Cc: guido@python.org
Subject: [PYTHON MATRIX-SIG] Hello from LLNL
Content-Length: 1881
Sender: owner-matrix-sig@python.org
Precedence: bulk
Greetings,
I head the computer science effort at Lawrence Livermore National Laboratory
in X-Division, where we do plasma physics and the design of targets for
laser fusion experiments. We have many large numerical applications in
Fortran and are starting newer codes in C++ and Eiffel.
For the last ten years we have used a programmable applications shell for
Fortran which I wrote. This is reaching the end of its useful lifetime as
we move further and further away from the world for which it was designed,
the monolithic (one CPU, one process, one language = Fortran) large code.
I had been trying to design a replacement system for us but I found Python
already is very close to what I was designing (except it is better done
than I would have managed). We have decided that building on Python is
the way to go.
So, we'd like to know what is going on in the Matrix SIG, and other
items of interest to those of us who are numerically and graphically
intensive. We bring a big pile of experienced and eager manpower to the
table and hope we can give a hand to this effort.
I designed the EiffelMath numerical library for the Eiffel language, so
I have considerable experience in both numerical mathematics and
object-oriented technology. And of course having run my own extension
language for 10 years I and the members of my team can do things of a
compiler sort. We have some special expertise in MPI and things parallel/
vector, too.
So: what do you have already? and how can we help?
A bunch of us will be there in December to get trained properly...
(My Fortran system is at http://www-phys.llnl.gov/X_Div/htdocs/basis.html)
Paul F. Dubois, L-472 (510)-422-5426
Lawrence Livermore National Laboratory FAX (510)-423-9969
Livermore, CA 94550 dubois1@llnl.gov
Consulting: PaulDubois@aol.com
Editor, Scientific Programming Department
Computers in Physics
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Oct 12 22:08:29 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id WAA16504 for matrix-sig-people; Thu, 12 Oct 1995 22:08:29 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id WAA16500 for ; Thu, 12 Oct 1995 22:08:25 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id WAA13096
(8.6.11/IDA-1.6 for ); Thu, 12 Oct 1995 22:05:02 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id WAA20076; Thu, 12 Oct 1995 22:05:00 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id WAA14506; Thu, 12 Oct 1995 22:04:59 -0400
Date: Thu, 12 Oct 1995 22:04:59 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199510130204.WAA14506@cyclone.ERE.UMontreal.CA>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] J-style arrays in Python, second edition
Sender: owner-matrix-sig@python.org
Precedence: bulk
In the meantime I have debugged and extended the Python
implementation of J-style arrays that I distributed earlier,
so it is time for an update. The main new features: file I/O,
better output formatting, more functions (cumulative
reduction, comparison, and more mathematical functions).
And hopefully fewer bugs; reshape() was actually quite
destructive in the first version...
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. Centre-Ville | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Oct 12 22:08:46 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id WAA16516 for matrix-sig-people; Thu, 12 Oct 1995 22:08:46 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id WAA16512 for ; Thu, 12 Oct 1995 22:08:39 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id WAA13112
(8.6.11/IDA-1.6 for ); Thu, 12 Oct 1995 22:05:23 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id WAA20234; Thu, 12 Oct 1995 22:05:22 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id WAA14547; Thu, 12 Oct 1995 22:05:22 -0400
Date: Thu, 12 Oct 1995 22:05:22 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199510130205.WAA14547@cyclone.ERE.UMontreal.CA>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Array.py
Sender: owner-matrix-sig@python.org
Precedence: bulk
# J-style array class
# Arrays are represented by a scalar or a list, possibly containing
# other lists in case of higher-rank arrays. Array rank is limited
# only by the user's patience.
# Send comments to Konrad Hinsen
import types, math, string, regexp, copy
######################################################################
# Error type
ArrayError = 'ArrayError'
######################################################################
# Various functions that do the real work. Classes follow.
# Construct string representation of array
def _output(data, dimension, maxlen):
s = ''
if dimension == 0:
s = s + string.rjust(data,maxlen)
elif dimension == 1:
for e in data:
s = s + string.rjust(e,maxlen) + ' '
else:
separator = (dimension-1)*'\n'
for e in data:
s = s + _output(e,dimension-1,maxlen) + separator
i = len(s)-1
while i > 0 and s[i] == '\n':
i = i-1
return s[:i+1]
# Find the shape of an array and check for consistency
def _shape(data):
if type(data) == types.ListType:
if data and type(data[0]) == types.ListType:
shapes = map(lambda x:_shape(x),data)
for i in range(1,len(shapes)):
if shapes[i] != shapes[0]:
raise ArrayError, 'Inconsistent shapes'
shape = [len(data)]
shape = shape + shapes[0]
return shape
else:
return [len(data)]
else:
return []
# Copy the data structure of an array
def _copy(data, dimension):
if (dimension == 0):
return data
else:
c = copy.copy(data)
for i in range(len(c)):
c[i] = _copy(c[i], dimension-1)
return c
# Construct a one-dimensional list of all array elements
def __ravel(data):
if type(data) == types.ListType:
if len(data) and type(data[0]) == types.ListType:
return reduce(lambda a,b: a+b,
map(lambda x: __ravel(x), data))
else:
return data
else:
return [data]
def _ravel(array):
return Array(__ravel(array._data),
[reduce(lambda a,b: a*b, array._shape, 1)])
# Reshape an array
def _reshape(array, shape):
array = _ravel(array)
if len(shape._data) == 0:
return take(array,0)
else:
array = _copy(array._data, len(array._shape))
shape = shape._data
n = reduce(lambda a,b: a*b, shape)
if n > len(array):
nr = (n+len(array)-1)/len(array)
array = (nr*array)[:n]
elif n < len(array):
array = array[:n]
for i in range(len(shape)-1, 0, -1):
d = shape[i]
n = n/d
for j in range(n):
array[j:j+d] = [array[j:j+d]]
return Array(array,shape)
# Map a function on the first dimensions of an array
def _extract(a, index, dimension):
if len(a[1]) < dimension:
return a
else:
return (a[0][index], a[1][1:], a[2])
def _map(function, arglist, max_frame, scalar_flag):
result = []
if len(max_frame) == 0:
if scalar_flag:
result = apply(function,tuple(map(lambda a: a[0], arglist)))
else:
result = apply(function,tuple(map(lambda a: Array(a[0],a[2]),
arglist)))._data
else:
for index in range(max_frame[0]):
result.append(_map(function,
map(lambda a,i=index,d=len(max_frame):
_extract(a,i,d), arglist),
max_frame[1:], scalar_flag))
return result
# Reduce an array with a given binary function
def _reduce(function, array):
function = function[0]
array = array[0]
if len(array._shape) == 0:
return array
elif array._shape[0] == 0:
return reshape(function._neutral, array._shape[1:])
else:
result = Array(array._data[0], array._shape[1:])
for i in range(1,array._shape[0]):
result = function(result, Array(array._data[i], array._shape[1:]))
return result
def _cumulative(function, array):
function = function[0]
array = array[0]
if len(array._shape) == 0:
return array
elif array._shape[0] == 0:
return array
else:
shape = array._shape
last_result = array._data[0]
result = [last_result]
for i in range(1,array._shape[0]):
last_result = function(last_result, Array(array._data[i],
array._shape[1:]))._data
result.append(last_result)
return Array(result, shape)
# Find the higher of two ranks
def _maxrank(a,b):
if a == None or b == None:
return None
else:
return max(a,b)
######################################################################
# Array class definition
class Array:
def __init__(self, scalar_or_list, shape = None):
self._data = scalar_or_list
if shape == None:
self._shape = _shape(self._data)
else:
self._shape = shape
def __copy__(self):
return Array(_copy(self._data, len(self._shape)),
copy.copy(self._shape))
def __str__(self):
s = tostr(self)
maxstrlen = maximum.over(_strlen(_ravel(s)))._data
return _output(s._data,len(s._shape),maxstrlen)
__repr__ = __str__
def __len__(self):
if type(self._data) == types.ListType:
return len(self._data)
else:
return 1
def __getitem__(self, index):
return take(self, index)
def __getslice__(self, i, j):
return take(self, range(i,j))
def __add__(self, other):
return sum(self, other)
__radd__ = __add__
def __sub__(self, other):
return difference(self, other)
def __rsub__(self, other):
return difference(other, self)
def __mul__(self, other):
return product(self, other)
__rmul__ = __mul__
def __div__(self, other):
return quotient(self, other)
def __rdiv__(self, other):
return quotient(other, self)
def __pow__(self,other):
return power(self, other)
def __rpow__(self,other):
return power(other, self)
def __neg__(self):
return 0-self
def writeToFile(self, filename):
file = open(filename, 'w')
file.write(str(self)+'\n')
file.close
# Check for arrayness
def isArray(x):
return hasattr(x,'_shape')
# Read array from file
_int_pattern = regexp.compile('-?[0-9]+')
_float_pattern = regexp.compile('-?[0-9]*\\.[0-9]*([eE][+-]?[0-9]+)*')
def _match(pattern,string):
r = pattern.match(string)
for i in r:
if i == (0, len(string)):
return 1
return 0
def _convertEntry(s):
if _match(_int_pattern,s):
return string.atoi(s)
elif _match(_float_pattern,s):
return string.atof(s)
else:
return s
def readArray(filename):
list = a = []
stack = []
blanks = 0
file = open(filename)
line = file.readline()
while line:
if line[0] != '#':
elements = map(_convertEntry, string.split(line))
if len(elements):
if blanks:
while blanks > len(stack):
a = [a]
stack.append(a)
list = copy.copy([])
stack[blanks-1].append(list)
for i in range(blanks-2,-1,-1):
list.append(copy.copy([]))
stack[i] = list
list = list[0]
list.append(elements)
blanks = 0
else:
blanks = blanks + 1
line = file.readline()
file.close()
while type(a) == types.ListType and len(a) == 1:
a = a[0]
return Array(a)
######################################################################
# Array function class
class ArrayFunction:
def __init__(self, function, ranks, intrinsic_ranks=None):
self._function = function
if isArray(ranks):
self._ranks = ranks._data
elif type(ranks) == types.ListType:
self._ranks = ranks
else:
self._ranks = [ranks]
if intrinsic_ranks == None:
self._intrinsic_ranks = self._ranks
else:
self._intrinsic_ranks = intrinsic_ranks
if len(self._ranks) == 1:
self._ranks = len(self._intrinsic_ranks)*self._ranks
def __call__(self, *args):
if len(self._ranks) != len(args):
raise ArrayError, 'Wrong number of arguments for an array function'
arglist = []
framelist = []
shapelist = []
for i in range(len(args)):
if isArray(args[i]):
arglist.append(args[i])
else:
arglist.append(Array(args[i]))
shape = arglist[i]._shape
rank = self._ranks[i]
intrinsic_rank = self._intrinsic_ranks[i]
if rank == None:
cell = 0
elif rank < 0:
cell = min(-rank,len(shape))
else:
cell = max(0,len(shape)-rank)
if intrinsic_rank != None:
cell = max(cell,len(shape)-intrinsic_rank)
framelist.append(shape[:cell])
shapelist.append(shape[cell:])
max_frame = []
for frame in framelist:
if len(frame) > len(max_frame):
max_frame = frame
for i in range(len(framelist)):
if framelist[i] != max_frame[len(max_frame)-len(framelist[i]):]:
raise ArrayError, 'Incompatible arguments'
scalar_function = reduce(lambda a,b:_maxrank(a,b),
self._intrinsic_ranks) == 0
return Array(_map(self._function, map(lambda a,b,c: (a._data,b,c),
arglist, framelist, shapelist),
max_frame, scalar_function))
def __getitem__(self, ranks):
return ArrayFunction(self._function,ranks,self._intrinsic_ranks)
class BinaryArrayFunction(ArrayFunction):
def __init__(self, function, neutral_element, ranks, intrinsic_ranks=None):
ArrayFunction.__init__(self, function, ranks, intrinsic_ranks)
self._neutral = neutral_element
self.over = ArrayFunction(ArrayOperator(_reduce, [self]), [None])
self.cumulative = ArrayFunction(ArrayOperator(_cumulative, [self]),
[None])
def __getitem__(self, ranks):
return BinaryArrayFunction(self._function, self._neutral,
ranks, self._intrinsic_ranks)
class ArrayOperator:
def __init__(self, operator, function_list):
self._operator = operator
self._functions = function_list
def __call__(self, *args):
return apply(self._operator, (self._functions, args))
######################################################################
# Array functions
# Functions for internal use
_strlen = ArrayFunction(len, [0])
# Structural functions
shape = ArrayFunction(lambda a: Array(a._shape,[len(a._shape)]), [None])
reshape = ArrayFunction(_reshape, [None, 1])
ravel = ArrayFunction(_ravel, [None])
take = ArrayFunction(lambda a,i: Array(a._data[i._data], a._shape[1:]),
[None, 0])
# Elementwise binary functions
_sum = ArrayFunction(lambda a,b: a+b, [0, 0])
_difference = ArrayFunction(lambda a,b: a-b, [0, 0])
_product = ArrayFunction(lambda a,b: a*b, [0, 0])
_quotient = ArrayFunction(lambda a,b: a/b, [0, 0])
_power = ArrayFunction(pow, [0, 0])
_max = ArrayFunction(max, [0, 0])
_min = ArrayFunction(min, [0, 0])
_smaller = ArrayFunction(lambda a,b: a**b, [0, 0])
_equal = ArrayFunction(lambda a,b: a==b, [0, 0])
sum = BinaryArrayFunction(_sum, 0, [None, None])
difference = BinaryArrayFunction(_difference, 0, [None, None])
product = BinaryArrayFunction(_product, 1, [None, None])
quotient = BinaryArrayFunction(_quotient, 1, [None, None])
power = BinaryArrayFunction(_power, 1, [None, None])
maximum = BinaryArrayFunction(_max, 0, [None, None])
minimum = BinaryArrayFunction(_min, 0, [None, None])
smaller = BinaryArrayFunction(_smaller, 1, [None, None])
greater = BinaryArrayFunction(_greater, 1, [None, None])
equal = BinaryArrayFunction(_equal, 1, [None, None])
# Scalar functions of one variable
tostr = ArrayFunction(str, [0])
sqrt = ArrayFunction(math.sqrt, [0])
exp = ArrayFunction(math.exp, [0])
log = ArrayFunction(math.log, [0])
log10 = ArrayFunction(math.log10, [0])
sin = ArrayFunction(math.sin, [0])
cos = ArrayFunction(math.cos, [0])
tan = ArrayFunction(math.tan, [0])
asin = ArrayFunction(math.asin, [0])
acos = ArrayFunction(math.acos, [0])
atan = ArrayFunction(math.atan, [0])
sinh = ArrayFunction(math.sinh, [0])
cosh = ArrayFunction(math.cosh, [0])
tanh = ArrayFunction(math.tanh, [0])
floor = ArrayFunction(math.floor, [0])
ceil = ArrayFunction(math.ceil, [0])
# Nasty hack to make fix max and min safe to use.
# Without this, they would return an array as most users
# would expect, but it would not be the correct answer.
# I know I shouldn't do this, but it seems the lesser of
# two evils.
builtin_max = max
builtin_min = min
def max(*args):
return apply(builtin_max,args)
def min(*args):
return apply(builtin_min,args)
# test data
x = Array(range(10))
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Oct 12 22:09:04 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id WAA16530 for matrix-sig-people; Thu, 12 Oct 1995 22:09:04 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id WAA16525 for ; Thu, 12 Oct 1995 22:09:01 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id WAA13120
(8.6.11/IDA-1.6 for ); Thu, 12 Oct 1995 22:05:49 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id WAA20350; Thu, 12 Oct 1995 22:05:48 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id WAA14571; Thu, 12 Oct 1995 22:05:48 -0400
Date: Thu, 12 Oct 1995 22:05:48 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199510130205.WAA14571@cyclone.ERE.UMontreal.CA>
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] ArrayExamples.py
Sender: owner-matrix-sig@python.org
Precedence: bulk
# This file illustrates the use of the the Array class.
#
# Send comments to Konrad Hinsen
#
from Array import *
######################################################################
# Arrays are constructed from (nested) lists:
a = Array(range(10))
b = Array([ [2,3,7], [9,8,2] ])
c = Array([ [ [4,5,6], [0,4,5] ], [ [1,6,5], [8,5,2] ] ])
# Scalars make arrays of rank 0:
s = Array(42)
# Array elements can be anything:
text_array = Array(['Hello', 'world'])
# Arrays can be printed:
print 'a:\n', a
print 'b:\n', b
print 'c:\n', c
print 's:\n', s
# shape() returns an array containing the dimensions of another array:
print 'shape(a):\n', shape(a)
print 'shape(b):\n', shape(b)
print 'shape(c):\n', shape(c)
print 'shape(s):\n', shape(s)
# Scalar functions act on each element of an array:
print 'sqrt(b):\n',sqrt(b)
# Binary operators likewise work elementwise:
print 'c+c:\n',c+c
# But you can also add a scalar:
print 'c+s:\n',c+s
# To understand the general rule for combining arrays of different
# shapes in a function, we need some more technical terms:
# The length of the shape vector of an array is called its rank.
# The elements of an array along the first axis are called items.
# The items of an array of rank N have rank N-1. More generally,
# the shape vector is divided into frames and cells. Frames and
# cells are not properties of an array, but describe ways of looking
# at an array. For example, a rank-3 array can be regarded as
# as just that - a single rank-3 array. It can also be regarded
# as a rank-1 frame of rank-2 cells, or as a rank-2 frame of
# rank-1 cells. Or even as a rank-3 array of rank-0 cells, i.e.
# scalar cells.
#
# When two arrays are to be added (or multiplied, or...), their
# shapes need not equal, but the lower-rank array must match
# an equal-rank cell of the higher-rank array. The lower-rank
# array will then be combined with each corresponding cell, and
# the result will have the shape of the higher-rank array.
print 'b+c:\n',b+c
# The addition of a scalar is just a special case: it has rank 0
# and therefore matches the rank-0 cells (scalar elements) of any array!
print 'b+s:\n',b+s
# All operators are also available as normal binary function,
# e.g. addition can be written as
print 'sum(a,s):\n',sum(a,s)
# You'll need this form to perform reductions, e.g. a sum
# of all items of an array:
print 'sum.over(a):\n',sum.over(a)
# Let's do it with a higher-rank array:
print 'product.over(b):\n',product.over(b)
# But how do you get the product along the second axis
# of b? Easy:
print 'product.over[1](b):\n',product.over[1](b)
# The [1] after the function name product.over modifies
# the functions rank. Function ranks are related to array
# ranks, in that a function of rank N operates on the
# N-cells of its argument. If the argument has a higher
# rank, the function is applied to each N-cell and the
# result is combined with the frame of the argument.
# In the last example, product.over will be
# called for each of the 1-cells of b, returning a
# rank-0 result for each, and the results will be
# collected in the 1-frame of b, producing as a net
# result an array of rank 1.
#
# All functions have ranks; if no rank is explicitly
# given, the default rank will be used. The default
# rank of all reductions is 'unbounded', which means
# that the function will operate on the highest-level
# cells possible. Many functions have unbounded rank,
# for example shape():
print 'shape(c):\n',shape(c)
# But of course you can modify the rank of shape():
print 'shape[1](c):\n',shape[1](c)
print 'shape[2](c):\n',shape[2](c)
# Functions with more than one argument can have a different
# rank for each. The rank is applied to each argument,
# defining its frames and cells for this purpose. The frames
# must match in the same way as indicated above for
# addition of two arrays. The function is then applied
# to the cells, and if appropriate, the same matching
# procedure is applied once again. This may seem confusing
# at first, but it is really just the application of a
# single principle everywhere!
#
# For example, let's take a (our rank-1 array) and add
# b (our rank-2 array) to each of a's 0-cells:
print 'sum[[0,None]](a,b):\n',sum[[0,None]](a,b)
# 'None' stands for 'unbounded'. Since the rank of sum is
# 0 for its first argument, a is divided into 1-frames
# and 0-cells. For b the rank is unbounded, so it is
# treated as a 0-frame with 2-cells. b's 0-frame matches
# a's 1-frame (a 0-frame matches everything!), and
# the result gets the 1-frame. The cells of the result
# are sums of a rank-0 array (element of a) and a rank-2
# array (b), i.e. rank-2 arrays by the matching rules
# given above. So the net total is a rank-3 array,
# as you have seen.
# From now on we will specify the default rank of each function.
# It should be noted that specifying a higher rank than the
# default rank has no effect on the function's behaviour. Only
# lower ranks make a difference.
#
# All the scalar mathematical functions (sqrt, sin, ...) have
# rank 0. The binary arithmetic functions (sum, product, ...)
# have unbounded rank for both argument. For structural functions
# (i.e. those that modify an array's shape rather than its
# elements), the rank varies. As we have seen, shape() is
# unbounded. The other structural functions are yet to be
# introduced:
# ravel() produces a rank-1 array containing all elements
# of its argument. It has unbounded rank:
print 'ravel(c):\n',ravel(c)
# reshape() allows you to change the shape of an array.
# It first obtains a rank-1 list of elements of its first
# argument (like ravel()) and then reassembles the
# elements into an array with the shape given by the
# second argument:
print 'reshape(a,[2,2]):\n',reshape(a,[2,2])
print 'reshape(b,[10]):\n',reshape(b,[10])
# As you have seen in the second case, reshape() reuses
# the elements of its arguments all over if they get
# exhausted.
# You may have noticed that in some examples, a nested list
# has been used instead of an array as a function argument.
# In general, all array functions will convert its arguments
# to arrays if necessary.
# Now we need a way to select parts of an array. You can
# use standard indexing and slicing to obtain items
# (i.e. N-1 cells for a rank-N array):
print 'c[1]:\n',c[1]
print 'a[3:7]:\n',a[3:7]
# You can also specify an array as the index and obtain an
# array of the corresponding items:
print 'a[[2,6,1]]:\n',a[[2,6,1]]
# The function take() does exactly the same:
print 'take(c,1):\n',take(c,1)
# You will have to use take() if you want to modify its
# ranks, which are [None,0] by default. There is little
# point in changing the second rank (it wouldn't make
# any difference), but changing the first rank lets you
# select along other dimensions than the first:
print 'take[1](c,0):\n',take[1](c,0)
print 'take[2](c,1):\n',take[2](c,1)
# Isn't there something wrong here? take() takes
# two arguments and therefore needs two ranks. But for
# convenience, only one rank must be given if all ranks
# are to be the same.
# Now the hard part is over. You are supposed to know
# how data and function ranks work together. What
# remains to be done is to show some more array functions.
# First of all, unary functions that act on each element
# of a matrix. These are: tostr, sqrt, exp, log, log10,
# sin, cos, tan, asin, acos, atan, sinh, cosh, tanh,
# floor, and ceil.
#
# Of course you expect the standard arithmetic operations,
# + - * / and pow(). The first four also exist as named functions
# which allow you to change rank; the names are sum, difference,
# product, and quotient. They work as expected. But there are
# some more binary functions that work in the same way.
# maximum() and minimum select the larger/smaller one of
# their arguments. With .over they find the maximum/minimum
# of an arbitrary list:
print 'maximum.over(a):\n',maximum.over(a)
# Then there are the comparison operations: smaller, greater,
# and equal. They exist only as functions of that name, not in
# the form of the familiar operators. It is simply not possible
# with the current Python implementation to assign such a meaning
# to the comparison operators, so you'll have to live with that
# for a while.
# Sometimes you want not just the sum over some list
# of items, but all the intermediate partial sums along
# the way. There is a special function for that:
print 'sum.cumulative(a):\n',sum.cumulative(a)
# Finally, you might want to read arrays from disk files,
# or write an array to a file. The second problem is handled
# by a method, so you write something like
c.writeToFile('just_a_test')
# You can read this back using
c_from_file = readArray('just_a_test')
# and then you should check whether the two are indeed equal:
print 'equal(c,c_from_file):\n',equal(c,c_from_file)
# And that's the end of this introduction. Stay tuned for
# updates of the array package that will provide some
# important still missing in this version. In the meantime,
# play round with what there is to get a feeling for
# how things work.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Oct 19 13:38:31 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id NAA06948 for matrix-sig-people; Thu, 19 Oct 1995 13:38:31 -0400
Received: from icf.llnl.gov (icf.llnl.gov [128.115.45.2]) by python.org (8.6.12/8.6.12) with SMTP id NAA06943 for ; Thu, 19 Oct 1995 13:38:26 -0400
Received: from kristen.llnl.gov by icf.llnl.gov (4.1/LLNL-1.19)
id AA18689; Thu, 19 Oct 95 10:38:11 PDT
Received: by kristen.llnl.gov (5.0/SMI-SVR4)
id AA25244; Thu, 19 Oct 1995 10:38:09 +0800
Date: Thu, 19 Oct 1995 10:38:09 +0800
Message-Id: <9510191738.AA25244@kristen.llnl.gov>
From: "P. Dubois"
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] a few thoughts
Content-Length: 5327
Sender: owner-matrix-sig@python.org
Precedence: bulk
I have only begun to peruse the archive but I did see a couple of discussions
worth some comments which I would like to make. For ten years I have been
in charge of a Fortran extension language called Basis, which has now been
used in perhaps 150-200 codes. I designed this language to look like an
array Fortran. It has been extremely popular with users. So what follows is
a synthesis of a whole lot of experience.
Let me begin with a short synopsis of Basis' array rules:
Arrays can be of any of the standard Fortran types, including complex.
They consist of a single block of storage with auxillary "shape" information.
They can be up to seven dimensional (the Fortran limit) but in practice
five is the highest I have seen in use. Had I been implementing them
in a modern language of course no limit would have been necessary.
Operations are element-wise but we have a separate operator for matrix
multiply and matrix divide (the latter means, a /! b is that x such
that b *! x = a), where /! and *! are the matrix operators). All
functions such as sin(x) operate element wise and do the right thing
depending on the type of x. It is important to be able to pass the
address of the storage area off to compiled code. Scalars broadcast
but we have an explicit function for "dup'ing" something to make it
match something higher dimensional, rather than do this automagically
Now some comments:
1. Usage of the matrix operators is perhaps 1% or less of the usage of
the element-wise numbers. This is because when two-dimensional
matrices do arise, they usually represent the spatial or other type of
discretization, far more often than they represent operators. If I
had it to do over again, I would not have special operators, just a
function call, since the light usage is not worth the trouble.
2. Speed is crucial. The basic operations must take place in compiled
loops without function calls, with as little overhead as possible.
In other words, x + y must be lead to a compiled loop doing the
corresponding operation on the blocks of storage. Yes, maybe one has
broadcast/type coercion/shape checks or operations first, but if both
x and y are really double arrays of length n we want to be into a
C loop doing xa[i] + ya[i] where xa and ya point to the storage areas.
This is because when a code is written as a programmable application
with an interpreter over compiled routines, the codes tend to evolve
to having more and more parts of the code in the interpreter. Also,
the interpreter is used to calculate information that is derivable
from the compiled state variables rather than add new compiled code,
but sometimes these calculations are quite intensive.
3. I think it is mistaken to try to reduce the implementor's job by
doing many types in one like the "array" built-in object does.
Having basic double/integer/complex stuff work fast should be the
primary consideration, even if it means some tedious and not terribly
elegant coding. A completely Python class like the J-array posting is
beautiful and suitable for cases where uniformity of representation is
of value, but it is a completely different question than having something
fast that can talk to C or Fortran. I timed this class doing x+x where
x was 100,000 elements long, and it was about 1000 times slower than
a simple C extension to Python and even ten times slower than doing a for
loop in Python (but boy, I learned a lot about Python from it!).
4. In Basis, sqrt(-1.) is an error not a complex; we actually went
through a time when it was complex and found it to be a disadvantage.
One must balance the need to avoid/find errors against the need for easy
expression.
5. One might want to consider having a very fast, very raw vector class
on which to base higher level classes that have concepts like shape, etc.
6. Banging malloc too hard can be a problem in these beasts. You probably
couldn't afford to represent a big matrix with independently malloc'ed
pointers for each row.
Some historical notes:
a. I noticed some discussion of representing things always as complex numbers.
The original Matlab (when Cleve Moler wrote it to
teach students linear algebra) represented everything as a complex number.
There was a limit of I think 5000 numbers in a system, period, because it
used a fixed array. When I ported it to a Cray I replaced that part of it
with a memory manager so that you could have a lot of complex numbers.
But of course having all the numbers be secretly complex is completely
crazy from an efficiency point of view, not to mention storage.
b. Yes, I implemented an extension language in Fortran. It was 1984 and
it was the only language available on a Cray. The computer center manager
said that I didn't need C, you can do everything in Fortran.
c. The documentation for the Basis language is available on line
at http://www-phys.llnl.gov/X_Div/htdocs/basis.html. I have decided to base
my next generation system on Python.
d. Reference for some philosophy: Dubois, P.F.,
"Making Applications Programmable", Computers in Physics Jan/Feb 1994.
Paul F. Dubois, L-472 (510)-422-5426
Lawrence Livermore National Laboratory FAX (510)-423-9969
Livermore, CA 94550 dubois1@llnl.gov
Consulting: PaulDubois@aol.com
Editor, Scientific Programming Department, Computers in Physics
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Oct 19 14:39:57 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA07408 for matrix-sig-people; Thu, 19 Oct 1995 14:39:57 -0400
Received: from drew.cog.brown.edu (drew.cog.brown.edu [128.148.208.10]) by python.org (8.6.12/8.6.12) with SMTP id OAA07404 for ; Thu, 19 Oct 1995 14:39:50 -0400
Received: by drew.cog.brown.edu (5.57/Ultrix3.0-C)
id AA05846; Thu, 19 Oct 95 14:27:49 -0400
Received: by maigret (950215.SGI.8.6.10/940406.SGI)
id OAA09461; Thu, 19 Oct 1995 14:43:29 -0400
From: da@maigret.cog.brown.edu (David Ascher)
Message-Id: <199510191843.OAA09461@maigret>
Subject: Re: [PYTHON MATRIX-SIG] a few thoughts
To: dubois@kristen.llnl.gov (P. Dubois)
Date: Thu, 19 Oct 1995 14:43:29 -0400 (EDT)
Cc: matrix-sig@python.org
In-Reply-To: <9510191738.AA25244@kristen.llnl.gov> from "P. Dubois" at Oct 19, 95 10:38:09 am
X-Mailer: ELM [version 2.4 PL24]
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
Content-Length: 659
Sender: owner-matrix-sig@python.org
Precedence: bulk
Paul Dubois' comments struck a chord with me. While I appreciate the
cleanliness of python's generic containers, I am painfully aware of the
slowness of python's operations on, say, sound files with thousands of
elements, to take a mid-size example.
1. How fervently are people opposed to splitting the array/tensors in
terms of element type? One array for any python object, and one for
numbers, all of the same type (one type per array). The two array
types could have the same interface, but one could use the uniformity
of its elements' types for optimization.
2. Am I right that this dichotomy would allow massive optimization?
-david
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Oct 19 15:24:09 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id PAA07697 for matrix-sig-people; Thu, 19 Oct 1995 15:24:09 -0400
Received: from qvarsx.er.usgs.GOV (qvarsx.er.usgs.gov [130.11.51.82]) by python.org (8.6.12/8.6.12) with ESMTP id PAA07693 for ; Thu, 19 Oct 1995 15:24:03 -0400
Received: from localhost (dsjfqvarsa [130.11.51.73]) by qvarsx.er.usgs.GOV (EMAIL 1.2.1) with ESMTP id TAA00071; Thu, 19 Oct 1995 19:18:07 GMT
Message-Id: <199510191918.TAA00071@qvarsx.er.usgs.GOV>
To: da@maigret.cog.brown.edu (David Ascher)
cc: dubois@kristen.llnl.gov (P. Dubois)
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] a few thoughts
In-reply-to: <199510191843.OAA09461@maigret>
Date: Thu, 19 Oct 1995 15:23:40 -0400
From: "Jim Fulton, U.S. Geological Survey"
Sender: owner-matrix-sig@python.org
Precedence: bulk
On Thu, 19 Oct 1995 14:43:29 -0400 (EDT)
David Ascher said:
> Paul Dubois' comments struck a chord with me. While I appreciate the
> cleanliness of python's generic containers, I am painfully aware of the
> slowness of python's operations on, say, sound files with thousands of
> elements, to take a mid-size example.
>
> 1. How fervently are people opposed to splitting the array/tensors in
> terms of element type? One array for any python object, and one for
> numbers, all of the same type (one type per array). The two array
> types could have the same interface, but one could use the uniformity
> of its elements' types for optimization.
>
> 2. Am I right that this dichotomy would allow massive optimization?
The current proposal is for homogenous matrices. So also supporting
heterogenous matrices would not allow any additional optimizaton.
Note also that the current proposal also allows homogenous matrices of
characters, to aid in interfacing to Fortran routines with character
arguments.
Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Oct 19 18:07:50 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id SAA08692 for matrix-sig-people; Thu, 19 Oct 1995 18:07:50 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id SAA08688 for ; Thu, 19 Oct 1995 18:07:46 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id SAA24708
(8.6.11/IDA-1.6); Thu, 19 Oct 1995 18:05:39 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id SAA04266; Thu, 19 Oct 1995 18:05:38 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id SAA12015; Thu, 19 Oct 1995 18:05:37 -0400
Date: Thu, 19 Oct 1995 18:05:37 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199510192205.SAA12015@cyclone.ERE.UMontreal.CA>
To: dubois@kristen.llnl.gov
CC: matrix-sig@python.org
In-reply-to: <9510191738.AA25244@kristen.llnl.gov> (dubois@kristen.llnl.gov)
Subject: Re: [PYTHON MATRIX-SIG] a few thoughts
Sender: owner-matrix-sig@python.org
Precedence: bulk
1. Usage of the matrix operators is perhaps 1% or less of the usage of
the element-wise numbers. This is because when two-dimensional
That raises the question of what kinds of applications were typically
involved. I can think of several that would make heavy use of
arrays as operators (e.g. all kinds of quantum mechanics). Maybe
they were just underrepresented among your users.
fast that can talk to C or Fortran. I timed this class doing x+x where
x was 100,000 elements long, and it was about 1000 times slower than
a simple C extension to Python and even ten times slower than doing a for
loop in Python (but boy, I learned a lot about Python from it!).
I agree that speed is a real problem. In fact, I openly admit that I
keep a "stripped-down" version that does only one-dimensional lists
for the many cases where I need long one-dimensional arrays. But this
is purely an implementation problem, not a problem in principle.
Much of the efficiency problems probably comes from the decision
to use nested lists as an internal representation, and this decision
was mostly based on laziness; it let me use simple recursive
functions to handle higher-rank arrays.
But of course having all the numbers be secretly complex is completely
crazy from an efficiency point of view, not to mention storage.
When I made this suggestion, I referred to modern implementations of
APL (including J), which in fact have many internal representations
for numbers, for efficiency reasons. A typical APL implementation
has
1) Bits
2) small integers (i.e. bytes)
3) long integers (4 bytes)
4) real numbers
5) complex numbers
But to the user all this looks like a single number type, since all
conversions happen automatically. The price to pay is not in efficiency
(internal APL operations tend to outperform Fortran), but in a
rather complex implementation, which has to decide the optimal
data type based on various criteria. For example, the high cost
of unpacking bit arrays means that they will be used only for
very large objects and/or when memory runs down.
The advantage of this is that numbers behave like you would expect
from mathematics, e.g. 1/3 equals 1./3., not 0. This prevents many
errors. There are actually more user-friendly features like this in
APL, e.g. non-zero comparison tolerance.
Actually, my dream language would also handle symbolic operations
in the style of Mathematica or Maple...
b. Yes, I implemented an extension language in Fortran. It was 1984 and
it was the only language available on a Cray. The computer center manager
said that I didn't need C, you can do everything in Fortran.
Isn't it great to have someone who always knows your real needs? ;-)
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. Centre-Ville | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Thu Oct 19 18:11:04 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id SAA08738 for matrix-sig-people; Thu, 19 Oct 1995 18:11:04 -0400
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id SAA08734 for ; Thu, 19 Oct 1995 18:11:00 -0400
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id SAA24758
(8.6.11/IDA-1.6); Thu, 19 Oct 1995 18:08:34 -0400
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id SAA04817; Thu, 19 Oct 1995 18:08:32 -0400
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id SAA12155; Thu, 19 Oct 1995 18:08:31 -0400
Date: Thu, 19 Oct 1995 18:08:31 -0400
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199510192208.SAA12155@cyclone.ERE.UMontreal.CA>
To: da@maigret.cog.brown.edu
CC: matrix-sig@python.org
In-reply-to: <199510191843.OAA09461@maigret> (da@maigret.cog.brown.edu)
Subject: Re: [PYTHON MATRIX-SIG] a few thoughts
Sender: owner-matrix-sig@python.org
Precedence: bulk
1. How fervently are people opposed to splitting the array/tensors in
terms of element type? One array for any python object, and one for
numbers, all of the same type (one type per array). The two array
types could have the same interface, but one could use the uniformity
of its elements' types for optimization.
Isn't that what Guido already proposed? I see nothing that speaks
agains that. The implementation of "general" arrays wouldn't
even be much different from the others; a general array would
simply be an array of object pointers. Only the application
of non-structural functions and operators has be handled differently.
2. Am I right that this dichotomy would allow massive optimization?
Of what?
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. Centre-Ville | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Fri Oct 20 12:18:59 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id MAA12457 for matrix-sig-people; Fri, 20 Oct 1995 12:18:59 -0400
Received: from eeel.nist.gov (sparky.eeel.nist.gov [129.6.64.163]) by python.org (8.6.12/8.6.12) with SMTP id MAA12453 for ; Fri, 20 Oct 1995 12:18:54 -0400
Received: from acdc.eeel.nist.gov by eeel.nist.gov (4.1/SMI-3.2-del.6)
id AA29059; Fri, 20 Oct 95 12:18:43 EDT
Received: by acdc.eeel.nist.gov (4.1/SMI-3.2-del.5)
id AA21381; Fri, 20 Oct 95 12:15:01 EDT
Date: Fri, 20 Oct 95 12:15:01 EDT
Message-Id: <9510201615.AA21381@acdc.eeel.nist.gov>
From: Michael McLay
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Comments on 'a few thoughts'
In-Reply-To: <9510191738.AA25244@kristen.llnl.gov>
References: <9510191738.AA25244@kristen.llnl.gov>
Sender: owner-matrix-sig@python.org
Precedence: bulk
P. Dubois writes:
> 1. Usage of the matrix operators is perhaps 1% or less of the usage of
> the element-wise numbers. This is because when two-dimensional
> matrices do arise, they usually represent the spatial or other type of
> discretization, far more often than they represent operators. If I
> had it to do over again, I would not have special operators, just a
> function call, since the light usage is not worth the trouble.
Wouldn't it be appropriate to make them methods instead of functions?
I agree that a good design rule would be to not implement symbolic
operators for rarely used operations. Unfortunately convincing
someone who is creating an operator that it is truly rare may not be
easy. The goal of this design rule is to ensure code is readable.
Readability is dependent on the vocabulary of the audience. If code
is only to be read by a small group who have agreed upon a
standardized shorthand then overloading symbols is Ok. However, to
reach the broadest audience the use of functions or methods is
necessary in order for the code to be unambiguous to the human parsing
the source. Granted, it will make the application source code a
little wordy, but that is the price to be paid for clarity. Giving
programmers the unrestricted ability to overload operators can be
dangerous since it tends to lead to the development of obscure
dialects in notation.
While I'm on the subject of design rules... Perhaps the Mathematica
convention of spelling out the full name of everything instead of
choosing arbitrary abbreviations should also be adopted. This would
be consistent with the Python tradition of making the source code
readable to others. A simple example will illustrate. How would you
interpret the following expression.
speed = m/hr
In scanning the text can you be sure if this is miles/hour or
meters/hour? I'd also suggest that only SI units be used in setting
up libraries. It is simple enough to do unit conversion at the user
interface.
> 3. I think it is mistaken to try to reduce the implementor's job by
> doing many types in one like the "array" built-in object does.
> Having basic double/integer/complex stuff work fast should be the
> primary consideration, even if it means some tedious and not terribly
> elegant coding.
> 5. One might want to consider having a very fast, very raw vector class
> on which to base higher level classes that have concepts like shape, etc.
This proposal suggests adding many specialized numerical types to
Python. Each type would be tuned for efficient performance in solving
a specific problem. From an implantation viewpoint this should not be
difficult to put in place. Each new numeric type would be implemented
as a dynamically linked module. This solution may be inevitable.
Each application domain will by necessity build an efficient set of
types required for the application domain's calculations.
This approach to implementation be a pragmatic necessity since at
the implementation level the computational requirements will demand
that all operations on large data sets run at the speed of compiled
code. Dividing the problem into discrete, importable modules
compartmentalizes the work. Each numeric type module can
independently be implement. All the operations needed for a numeric
type would be incorporated into the module.
This proposal just solves the easy part of the problem.
Hinsen Konrad writes:
> When I made this suggestion, I referred to modern implementations of
> APL (including J), which in fact have many internal representations
> for numbers, for efficiency reasons. A typical APL implementation
> has
> 1) Bits
> 2) small integers (i.e. bytes)
> 3) long integers (4 bytes)
> 4) real numbers
> 5) complex numbers
> But to the user all this looks like a single number type, since all
> conversions happen automatically. The price to pay is not in efficiency
> (internal APL operations tend to outperform Fortran), but in a
> rather complex implementation, which has to decide the optimal
> data type based on various criteria. For example, the high cost
> of unpacking bit arrays means that they will be used only for
> very large objects and/or when memory runs down.
>
> The advantage of this is that numbers behave like you would expect
> from mathematics, e.g. 1/3 equals 1./3., not 0. This prevents many
> errors. There are actually more user-friendly features like this in
> APL, e.g. non-zero comparison tolerance.
This is the hard part of the problem to solve.
Providing automatic type conversion would be a great feature and would
help reduce the number of bugs and the complexity of applications.
The only hitch is that it may take a significant effort to create a
working implementation. Assuming that the first solution is
inevitable. That is, people will write point solutions to solve their
problems. Is there something that would prevent the addition of
automatic type conversion from being implemented as a layer on top of
the numeric type modules that will be created independently? What
rules need to be established be ensure that the initially independent
numeric types can be integrated into the more elegant solution?
> Actually, my dream language would also handle symbolic operations
> in the style of Mathematica or Maple...
Yes, and one of the features from Mathematica that is missing is the
ability to tag objects with symbols that represent units of measure.
In Mathematica you can do the following:
In[1]: 12 meters
The meters symbol tags the integer 12 as its unit of measure.
Michael
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Fri Oct 27 10:39:29 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id KAA23280 for matrix-sig-people; Fri, 27 Oct 1995 10:39:29 -0400
Received: from rnet.rose.rsoc.rockwell.com (rose.rsoc.rockwell.com [161.40.39.100]) by python.org (8.6.12/8.6.12) with SMTP id KAA23276 for ; Fri, 27 Oct 1995 10:39:24 -0400
Received: by rnet.rose.rsoc.rockwell.com (4.1/SMI-4.1)
id AA16105; Fri, 27 Oct 95 09:38:33 CDT
Received: from sunrise(161.40.50.11) by rnet via smap (V1.3)
id sma016097; Fri Oct 27 09:38:09 1995
Received: from feynman.rsoc.rockwell.com by rose.rsoc.rockwell.com (5.0/SMI-SVR4)
id AA24224; Fri, 27 Oct 1995 09:38:08 -0500
Received: by feynman.rsoc.rockwell.com (5.0/SMI-SVR4)
id AA17917; Fri, 27 Oct 1995 09:38:07 -0500
Date: Fri, 27 Oct 1995 09:38:07 -0500
From: forrest@rose.rsoc.rockwell.com (Dave Forrest)
Message-Id: <9510271438.AA17917@feynman.rsoc.rockwell.com>
To: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Comments on 'a few thoughts'
X-Sun-Charset: US-ASCII
Content-Length: 1608
Sender: owner-matrix-sig@python.org
Precedence: bulk
> From owner-matrix-sig@python.org Fri Oct 20 11:20 CDT 1995
[snip]
> P. Dubois writes:
>
> > 1. Usage of the matrix operators is perhaps 1% or less of the usage of
> > the element-wise numbers. This is because when two-dimensional
Not in my office. Here, when we implemented a Matrix class in C++, we
didn't even supply elementwise multiplication or _any_ division
operator because _we_never_use_those_operations (well, we did
implement division by a scalar - but that's it! :-).
I made this plea once before but I'm going to make it again - please
consider providing two interfaces into the matrix implementation - one
that acts like a two-dimensional, linear-systems-type matrix and one
that acts like a tensor. Even in C you can have the same code
underlying it (reuse is good). I wouldn't try to deny you
tensor-types the elementwise behaviour that you obviously need, but we
need linear algebra - it's at the core of all our math models - please
take this need seriously.
> > matrices do arise, they usually represent the spatial or other type of
> > discretization, far more often than they represent operators. If I
> > had it to do over again, I would not have special operators, just a
> > function call, since the light usage is not worth the trouble.
>
> Wouldn't it be appropriate to make them methods instead of functions?
Absolutely - failing making them operators (as above) - methods are
definitely the way to go. Actually, I think that that is what everyone
meant, it's just a matter of OO semantics.
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Mon Oct 30 12:28:02 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id MAA01490 for matrix-sig-people; Mon, 30 Oct 1995 12:28:02 -0500
Received: from mama-bear.LCS.MIT.EDU (mama-bear.lcs.mit.edu [18.27.0.167]) by python.org (8.6.12/8.6.12) with SMTP id MAA01485 for ; Mon, 30 Oct 1995 12:27:54 -0500
Received: from nineveh.lcs.mit.edu.LCS.MIT.EDU by mama-bear.LCS.MIT.EDU (4.1/SLS-4.1.1)
id AA02555; Mon, 30 Oct 95 12:26:42 EST
Received: by nineveh.lcs.mit.edu.LCS.MIT.EDU (NX5.67e/SMI-4.1)
id AA09927; Mon, 30 Oct 95 12:28:39 -0500
Message-Id: <9510301728.AA09927@nineveh.lcs.mit.edu.LCS.MIT.EDU>
Content-Type: text/plain
Mime-Version: 1.0 (NeXT Mail 3.3 v118.2)
Received: by NeXT.Mailer (1.118.2)
From: James Hugunin
Date: Mon, 30 Oct 95 12:28:37 -0500
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Alpha Matrix Object in C available - Guinea Pigs wanted
Cc: jjh@mama-bear.lcs.mit.edu
Sender: owner-matrix-sig@python.org
Precedence: bulk
I finally have an alpha version of a working, reasonably efficient matrix
object implemented in C. It compiles sucessfully on a Sparc10 running
SUNOS, and on a P5(and P6) running NeXTStep.
I think that this object should satisfy many of the features requested by
members of this group.
1) Extremely fast basic arithmetic functions
To me, this is sine qua non of a useful matrix object. If I can't do
arithmetic at close to the speed of hand coded C, then I have no use for the
object. As a sanity check that things are reasonably efficient, I
performed a very rough comparision of the basic speeds of matlab, octave,
python, and C for vector arithmetic.
The operation was to multiply a 10000 length vector of doubles with itself
1000 times (10 M floating point multiplies). The tests were all run on an
unloaded Sun Sparc 10.
tool speed(in MFlops)
Array.py in python 0.002
for loop in python 0.03
octave 0.2
matlab 1.6
matrixobject in python w/-O 2.1
C w/-O 2.4
C w/-O4 2.6
So, the python object is within 10% of the speed of the hand-coded C. This
is good enough for me.
2) Fancy arithmetic operations borrowing concepts from J. Every operator
can be given a rank, and can be used to perform direct, outer, and inner
products as well as reductions and accumulations. Much of the style of this
part of the code is borrowed from Konrad's Array.py object. Thanks Konrad
for the introduction to J!
3) Very general multidimensional slicing operation. This is based on Jim
Fulton's proposal for multidimensional slicing, which those of you who have
been following this list have seen. This is a superset of most other
slicing approaches that I've seen. (Though I think that I need to go over
Chris Chase's post on slices a little bit more carefully).
4) reshaping, general transposition, byteswaping, interface to/from strings, ...
What's obviously missing:
1) Complex numbers don't work right yet. My basic problem is that I need a
good complex number class in C to interface with, and I just haven't felt
like writing this myself yet.
2) outer, inner, reduce, and accumulate style arithmetic functions could be
optimized by a factor of 2-4 for simple cases.
3) inner product specfication is not completely clear to me when applied to
operators with non-zero rank.
4) Documentation and comments in the code are minimal.
5) I'm sure that there must be a couple of memory leaks left hanging around.
What remains to be argued about (in my opinion at least)
1) Matrix-style multiplication as an option. I really hate to leave the
linear-algebra folks out in the cold. On the other hand, I don't like the
idea of setting a flag to determine how the "*" operator will act on any
given object. I'm open to suggestions.
2) Return by-value vs. by-reference. I finally made the decision that
every matrix slicing function returns by-value, and that indexing by a
single value returns by-reference. The by-value decision was based on a
couple of bad experiences with the complexity of doing by-reference returns
"right", and my observation that even in code that relied heavily on slicing
a matrix I couldn't identify a performance difference that was ever greater
than 20%. Having a single index return by-reference is necessary to allow
typical python style "multi-dimensional" indexing (ie. a[0][0][0]) to remain
efficient (this great hack is Jim Fulton's idea).
3) Type-coercion. At the moment, if I try to add a matrix of ints to a
matrix of floats, I'll get an exception. I'm not at all sure that it
wouldn't be better to just coerce the matrix of ints to floats and then do
the add.
4) Default type of a matrix. At the moment, if I say "Matrix(1,2,3)", I'll
get back a matrix of doubles. This should probably be setable with a
matrix module function, so that if I'm working a lot with complex numbers, I
can make this return a complex matrix by default instead, or whatever other
type I like to work in.
5) Rank-system. I've really grown to like the J-style rank operators. One
thing that they provide is that I can say
Matrix(1,2,3)+Matrix([1,2,3],[11,12,13]) -> Matrix([2,4,6], [12,14,16]).
There are those who might believe that it would be safer for this to return
an error that the two matrices aren't the same size.
6) There are a couple of possible patches to "ceval.c" to modify python to
deal a little bit better with an object that is a sequence type, but that
doesn't implement the sequence copy method when multiplied by a scalar.
These would general-purpose changes and should be completely compatible with
all existing python code. At the moment, I've left this patch out.
7) Many other things that I'm sure I'm missing, which leads me to...
I'd really like to have people play around with this object a bit and offer
me as harsh feedback as you wish. This code is all based on Jim Fulton's
implementation of a matrix object for using to interface to FORTRAN
libraries, Unfortunately, his employer has a small restriction on the
distribution of this code (until it becomes officially released).
THIS SOFTWARE HAS *NOT* BEEN APPROVED FOR RELEASE TO THE PUBLIC.
THIS SOFTWARE MAY ONLY BE PROVIDED TO INDIVIDUALS WHO ARE
CO-DEVELOPERS, REVIEWERS, OR TESTERS OF THE SOFTWARE, OR TO
PERSONNEL OF THE U.S. GEOLOGICAL SURVEY.
So, anybody who wants to play around with this object, send me a message
(at hugunin@mit.edu) saying that you agree to test and review it, I'll then
point you to the appropriate FTP site.
If I get some positive feedback from people saying that they like the
overall design of the object, then I'll go in and write up documentation and
comment the code better. 'til then I'm not sure that I wouldn't just be
wasting my time.
-Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Oct 31 12:52:48 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id MAA00809 for matrix-sig-people; Tue, 31 Oct 1995 12:52:48 -0500
Received: from mama-bear.LCS.MIT.EDU (mama-bear.lcs.mit.edu [18.27.0.167]) by python.org (8.6.12/8.6.12) with SMTP id MAA00800 for ; Tue, 31 Oct 1995 12:52:43 -0500
Received: from nineveh.lcs.mit.edu.LCS.MIT.EDU by mama-bear.LCS.MIT.EDU (4.1/SLS-4.1.1)
id AA10495; Tue, 31 Oct 95 12:51:41 EST
Received: by nineveh.lcs.mit.edu.LCS.MIT.EDU (NX5.67e/SMI-4.1)
id AA11479; Tue, 31 Oct 95 12:53:44 -0500
Message-Id: <9510311753.AA11479@nineveh.lcs.mit.edu.LCS.MIT.EDU>
Content-Type: text/plain
Mime-Version: 1.0 (NeXT Mail 3.3 v118.2)
Received: by NeXT.Mailer (1.118.2)
From: James Hugunin
Date: Tue, 31 Oct 95 12:53:43 -0500
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Ranks of operators
Sender: owner-matrix-sig@python.org
Precedence: bulk
Yesterday I announced an alpha version of my C-based matrix object, and I
already have gotten significant feedback on it from a number of folks on the
list. One topic was brought up by Konrad Hinsen that I think merits
discussion by the full list. This is the issue of ranks of functions, and
generalizations of functions to outer, inner, reduce, etc. And yes, I know
that we've been over this before.
Here's what I'm pretty sure of:
1) We need an object that can hold a basic mathematical function in a form
that it can be efficiently (nearly as fast as hand-coded C) applied to a
matrix of raw basic types (ints and floats).
2) Many of these are binary functions (add, subtract, etc.). These
functions should be able to be applied as outer products, reductions and
accumulations also efficiently (inner products as well, but these involve
two binary functions, so I'll leave them out for now).
3) The basic notion of rank in J is a sound one. A n-dimensional matrix is
well viewed as a k-dimensional frame of (n-k)-dimensional cells. Thus a
"matrix" can be a 2d cell, or a 1d frame of 1d cells (vector of vectors) or
a 2d array of 0d numbers.
My new plan is the following. I want to throw this out to the list to see
if there are any significant complaints about this approach before I spend a
couple of days modifying my code to match this spec.
add is a python object of type ofunc. This means that the add object knows
how to add matrices together efficiently.
add(a,b) <--> a+b
Every basic object of type ofunc has the members "reduce", "outer",
"accumulate", ("inner"?). These members are themselves ofuncs, however,
they don't have the members given above.
[This is in contrast to these being methods of the ofunc object, my initial
implementation.]
ie. add.reduce is an ofunc where reduce(add, m) <-> add.reduce(m) (except
that the second form is a couple orders of magnitude faster).
Note: add.reduce.reduce is an error
Every object of type ofunc can have its rank specified using [] notation.
[This is in contrast to using a keyword specifier for rank during the call.]
ie. add.reduce[1] is an ofunc with rank 1.
Note: add.reduce[1][0] is an ofunc with rank 0
Negative ranks are allowed (as in J). These are similar to negative
indices in slices, and specify an offset from the rank of there argument.
ie. add[-1]([1,2,3],[[1,2],[4,5]]) <--> add[(0,1)]([1,2,3],[[1,2],[4,5]])
I'm open to suggestions as to how the rank of inner product should be treated.
Well, that's it for now. Let me know if this makes sense, or seems like a
bad idea.
-Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Tue Oct 31 15:45:31 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id PAA02206 for matrix-sig-people; Tue, 31 Oct 1995 15:45:31 -0500
Received: from condor.CC.UMontreal.CA (condor.CC.UMontreal.CA [132.204.2.103]) by python.org (8.6.12/8.6.12) with ESMTP id PAA02202 for ; Tue, 31 Oct 1995 15:45:24 -0500
Received: from eole.ERE.UMontreal.CA (eole.ERE.UMontreal.CA [132.204.10.20]) by condor.CC.UMontreal.CA with ESMTP id PAA12764
(8.6.11/IDA-1.6); Tue, 31 Oct 1995 15:43:11 -0500
Received: from cyclone.ERE.UMontreal.CA by eole.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id PAA21404; Tue, 31 Oct 1995 15:43:05 -0500
Received: by cyclone.ERE.UMontreal.CA (950221.405.SGI.8.6.10/5.17)
id PAA00284; Tue, 31 Oct 1995 15:43:04 -0500
Date: Tue, 31 Oct 1995 15:43:04 -0500
From: hinsenk@ere.umontreal.ca (Hinsen Konrad)
Message-Id: <199510312043.PAA00284@cyclone.ERE.UMontreal.CA>
To: jjh@mama-bear.lcs.mit.edu
CC: matrix-sig@python.org
In-reply-to: <9510311753.AA11479@nineveh.lcs.mit.edu.LCS.MIT.EDU> (message from James Hugunin on Tue, 31 Oct 95 12:53:43 -0500)
Subject: Re: [PYTHON MATRIX-SIG] Ranks of operators
Sender: owner-matrix-sig@python.org
Precedence: bulk
1) We need an object that can hold a basic mathematical function in a form
that it can be efficiently (nearly as fast as hand-coded C) applied to a
matrix of raw basic types (ints and floats).
Also to complex numbers...
Every basic object of type ofunc has the members "reduce", "outer",
"accumulate", ("inner"?). These members are themselves ofuncs, however,
they don't have the members given above.
OK. For "inner" I'd prefer a notation of the form
add.inner.multiply(a,b)
with the usual possibility of specifying ranks. It seems that this
requires an explicit list of all possible combinations of binary
functions somewhere, but for the built-in functions that list is
not too long. Another possibility would be to make the second
binary function an argument, i.e. add.inner(multiply,a,b), but I'd
rather not have functions as arguments.
I'm open to suggestions as to how the rank of inner product should be treated.
No different from other operations. The inner product imposes a
restriction on its arguments in that the dimension of the rank-1 cells
of the first argument must be equal to the dimension of the rank-1
frames of the second argument, but this doesn't affect the general
principle of applying ranks. If the arguments do not fit, an exception
should be raised.
Well, that's it for now. Let me know if this makes sense, or seems like a
bad idea.
It seems OK to me!
-------------------------------------------------------------------------------
Konrad Hinsen | E-Mail: hinsenk@ere.umontreal.ca
Departement de chimie | Tel.: +1-514-343-6111 ext. 3953
Universite de Montreal | Fax: +1-514-343-7586
C.P. 6128, succ. Centre-Ville | Deutsch/Esperanto/English/Nederlands/
Montreal (QC) H3C 3J7 | Francais (phase experimentale)
-------------------------------------------------------------------------------
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Nov 1 13:56:41 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id NAA04623 for matrix-sig-people; Wed, 1 Nov 1995 13:56:41 -0500
Received: from icf.llnl.gov (icf.llnl.gov [128.115.45.2]) by python.org (8.6.12/8.6.12) with SMTP id NAA04614 for ; Wed, 1 Nov 1995 13:56:35 -0500
Received: from kristen.llnl.gov by icf.llnl.gov (4.1/LLNL-1.19)
id AA12580; Wed, 1 Nov 95 10:56:21 PST
Received: by kristen.llnl.gov (5.0/SMI-SVR4)
id AA29129; Wed, 1 Nov 1995 10:56:19 +0800
Date: Wed, 1 Nov 1995 10:56:19 +0800
Message-Id: <9511011856.AA29129@kristen.llnl.gov>
From: "P. Dubois"
To: matrix-sig@python.org
Subject: [PYTHON MATRIX-SIG] Some notes on the matrix class
Content-Length: 3363
Sender: owner-matrix-sig@python.org
Precedence: bulk
This is a very nice facility already and has an amazing amount of
functionality. What follows are some notes I made on it.
Other members of the MATRIX-SIG have probably played enough before with the
idea to realize what the examples don't show: the subscript notation
for multiple dimensions works perfectly...I'm stunned.
a. The already implemented list of functions/reduction operators is
impressive. We have also found useful:
1. A function equal to max(m) - min(m), often used as a reduction operator
in higher dimensions. We call it ptp (peak-to-peak).
2. ranf(m) fills m with uniform random numbers on (0.0, 1.0)
3. A function reshape(x,n,m,...) equal to a copy of x reshaped as (n,m,...)
The decision to make reshape a status-altering method is fine, but
often the need for a reshape is in the context of some temporary
value needed in an expression. Some of the clever rank stuff
mitigates this need but I bet this still comes in very useful.
Actually, our function of this kind also duplicates copies of
x as needed provided length(x) divides the new size.
4. The existing ones(n,m) will return a matrix of shape (n,m)
filled with 1.0; equally useful would be one where the i,j element is
1.0 iff i==j, 0.0 otherwise.
b. compress(condition, x) = those elements of x corresponding to those
elements of condition that are "true". condition and x must have
the same length (or x scalar), result is a one-dimensional vector with
length equal to the number of true elements in condition. Naturally this
is most useful if x < y returns a vector instead of a scalar.
Most used form however is with a condition of the form x > 0. or some
similar comparison to a scalar, which are expressible already in Python
if we take "true" in this sense to be the normal Python meaning.
c. My Basis users love:
where(condition,x,y) is shaped like condition and has elements of x and
y where condition is respectively true or false; we allow broadcast of
scalars x or y, so some thought about ranks should reveal the equivalent
notion here. Often used in the form where(z, x/z, 1.e99); in this case
we rely on a convention that something/0.0 = Infinity, where Infinity is
some variable the users can set to suit them, but anyway the idea is that
the computation x/z goes at compiled speed and does not cause an exception.
We haven't found this perfect and of course would prefer that the
computation is simply not done at components where z is false. Given
the genius you guys have already displayed ... (Yes, you can do this
with a for loop, this is just a speed-freak need).
d. One curiosity is the notation something[(a,b,c)]. This is required
because something[a,b,c] is a syntax error. But perhaps Python could
allow the latter interpreted as the former. For if you do x=[1.,2.,3.]
then trying x[(0,1,2)] produces a perfectly sensible error message.
And it would be in the spirit of tuples not always needing the parentheses.
(That sound you hear is the shot of a large caliber weapon as Guido
justifiably shoots a newbie for making a suggested language change before
he can even use half the language yet; in fact, I'm tempted to shoot
myself, but I'm enjoying Python too much to die yet.)
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Nov 1 14:13:08 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id OAA04756 for matrix-sig-people; Wed, 1 Nov 1995 14:13:08 -0500
Received: from CNRI.Reston.VA.US (cnri.reston.va.us [132.151.1.1]) by python.org (8.6.12/8.6.12) with SMTP id OAA04752 for ; Wed, 1 Nov 1995 14:13:05 -0500
Received: from monty.cnri.reston.va.us by CNRI.Reston.VA.US id aa14624;
1 Nov 95 14:10 EST
Received: from localhost by monty via SMTP (940816.SGI.8.6.9/940406.SGI)
id OAA04346; Wed, 1 Nov 1995 14:09:27 -0500
Message-Id: <199511011909.OAA04346@monty>
To: "P. Dubois"
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Some notes on the matrix class
In-reply-to: Your message of "Wed, 01 Nov 1995 10:56:19 +0800."
<9511011856.AA29129@kristen.llnl.gov>
References: <9511011856.AA29129@kristen.llnl.gov>
From: Guido van Rossum
X-Face: S#(=*/yxQ]{^7V>yr9ElReWJZvi^x!]tR6g^m5q?|f@/ChslBZm*tWH=*aicc{$>4c/M:$.
\@@o)psQitBQa!z()VjNsj+aqtDI/J0jX'a/=Qhn|gv6coKW}06%6gyuTn)oc6
Date: Wed, 01 Nov 1995 14:09:26 -0500
Sender: owner-matrix-sig@python.org
Precedence: bulk
> d. One curiosity is the notation something[(a,b,c)]. This is required
> because something[a,b,c] is a syntax error. But perhaps Python could
> allow the latter interpreted as the former. For if you do x=[1.,2.,3.]
> then trying x[(0,1,2)] produces a perfectly sensible error message.
> And it would be in the spirit of tuples not always needing the parentheses.
> (That sound you hear is the shot of a large caliber weapon as Guido
> justifiably shoots a newbie for making a suggested language change before
> he can even use half the language yet; in fact, I'm tempted to shoot
> myself, but I'm enjoying Python too much to die yet.)
Am I known to shoot newbies? Only in my dreams :-)
This is actually a very reasonable request that has come up in this
context before. It's a tad complicated to implement because there
some interference with the syntax for x[i:j], but should be possible.
If someone could contribute patches I'd be willing to review them and
add them to the next release; otherwise it may happen slower. I
expect that changes are needed only to Grammar and compile.c...
--Guido van Rossum
URL:
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Nov 1 15:28:20 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id PAA05271 for matrix-sig-people; Wed, 1 Nov 1995 15:28:20 -0500
Received: from mama-bear.LCS.MIT.EDU (mama-bear.lcs.mit.edu [18.27.0.167]) by python.org (8.6.12/8.6.12) with SMTP id PAA05265 for ; Wed, 1 Nov 1995 15:28:15 -0500
Received: from nineveh.lcs.mit.edu.LCS.MIT.EDU by mama-bear.LCS.MIT.EDU (4.1/SLS-4.1.1)
id AA20086; Wed, 1 Nov 95 15:27:03 EST
Received: by nineveh.lcs.mit.edu.LCS.MIT.EDU (NX5.67e/SMI-4.1)
id AA00472; Wed, 1 Nov 95 15:28:59 -0500
Message-Id: <9511012028.AA00472@nineveh.lcs.mit.edu.LCS.MIT.EDU>
Content-Type: text/plain
Mime-Version: 1.0 (NeXT Mail 3.3 v118.2)
Received: by NeXT.Mailer (1.118.2)
From: James Hugunin
Date: Wed, 1 Nov 95 15:28:58 -0500
To: "P. Dubois"
Subject: Re: [PYTHON MATRIX-SIG] Some notes on the matrix class
Cc: matrix-sig@python.org
References: <9511011856.AA29129@kristen.llnl.gov>
Sender: owner-matrix-sig@python.org
Precedence: bulk
> Date: Wed, 1 Nov 1995 10:56:19 +0800
> From: "P. Dubois"
>
> This is a very nice facility already and has an amazing amount of
> functionality.
Thanks for the compliment and the feedback.
> 1. A function equal to max(m) - min(m), often used as a reduction operator
> in higher dimensions. We call it ptp (peak-to-peak).
I'll add the following to the standard functions
def ptp(m): return max(m)-min(m)
where within Matrix.py, max(m) <--> maximum.reduce(m)
> 2. ranf(m) fills m with uniform random numbers on (0.0, 1.0)
I just added a function for this, (I needed to initialize a weight matrix
for a NN) it has the form, m = rand(d1,...,dn). where m will be a new
matrix with the given dimensions. It uses whrandom.py for getting its
random numbers so is slow. If somebody with a good random number generator
in C would like to add this to matrixmodule.c as a new instantiation method
in C, I'd use it.
> 3. A function reshape(x,n,m,...) equal to a copy of x reshaped as (n,m,...)
Already in my working copy of Matrix.py (requested by Konrad Hinsen). I
use the syntax reshape(x, (d1,...dn)) so that reshape(a, b.shape) works. I
don't like the idea of automatically making copies of the matrix in calls to
reshape, but I have added the method m.copy(n=1) to make an arbitray number
of copies of a matrix (this is much like the multiply operator for lists).
(I also added a concat method noticing that I was missing this other
functionality of lists).
> 4. The existing ones(n,m) will return a matrix of shape (n,m)
> filled with 1.0; equally useful would be one where the i,j element is
> 1.0 iff i==j, 0.0 otherwise.
This is now given by the eye(n,m,k=0) function (stolen from matlab), where
the i,j element is 1.0 iff i-j==k, 0.0 otherwise. This is easy enough to
contruct yourself out of outer products (which is what eye does).
ie.
identity = equal.outer(mrange(n), mrange(m))
Aren't outer products cool?
> b. compress(condition, x) = those elements of x corresponding to those
> elements of condition that are "true". condition and x must have
> the same length (or x scalar), result is a one-dimensional vector with
> length equal to the number of true elements in condition. Naturally this
> is most useful if x < y returns a vector instead of a scalar.
> Most used form however is with a condition of the form x > 0. or some
> similar comparison to a scalar, which are expressible already in Python
> if we take "true" in this sense to be the normal Python meaning.
This can be expressed as reshape(x, None)[condition.nonzero()].
My personal take on this is that if condition is not a vector, then this
function is meaningless. If it is a vector, it should be able to be applied
to any dimension of x.
I'll add the following to Matrix.py to support this:
def compress(condition, m, dimension=-1):
if dimension < 0: dimension = len(m.shape)+dimension
if len(condition.shape)!=1 or len(condition)!=m.shape[dimension]:
raise ValueError
index = [All]*len(m.shape)
index[dimension] = condition.nonzero()
print index
return m[index]
BTW - while x < y returns a (meaningless) scalar, Matrix.less(x, y) returns
a matrix of booleans.
There has been a small amount of talk about the possibilty of having
"boolean types" in addition to number, sequence, and mapping types that
would support >, <, ==, etc. After writing a lot of code with matrices, I
feel that many things could be expressed a lot more elegantly if this was
allowed. Unfortunately, Guido appears to be skeptical of this proposal, so
don't hold your breath.
I've run into particular collisions with and, or, and not. These functions
when applied to matrices are renamed to the ugly "Matrix.andBoolean", etc.
The problem is that the former are reserved words in python, so you can't
create functions with those names (even in the "safe" context of a module).
> c. My Basis users love:
> where(condition,x,y) is shaped like condition and has elements of x and
> y where condition is respectively true or false; we allow broadcast of
> scalars x or y, so some thought about ranks should reveal the equivalent
> notion here. Often used in the form where(z, x/z, 1.e99); in this case
> we rely on a convention that something/0.0 = Infinity, where Infinity is
> some variable the users can set to suit them, but anyway the idea is that
> the computation x/z goes at compiled speed and does not cause an exception.
I had something like this in my original matrix proposal, but was advised
that it could be expressed as without any extra C-code:
def where(condition, x, y):
c = notEqual(condition, 0)
return c*x+(1-c)*y
where the I am relying on the fact that all comparision operators return 1
for true.
This is in fact about 1/5th the speed of writing a where function in C, so
it should be dropped to compiled code at some time. I'll add this to the
standard set of functions in Matrix.py.
> We haven't found this perfect and of course would prefer that the
> computation is simply not done at components where z is false. Given
> the genius you guys have already displayed ... (Yes, you can do this
> with a for loop, this is just a speed-freak need).
I'm a speed freak too, if you ever have to use a for loop to iterate over
the contents of a matrix, feel free to report it as a bug in the matrix
package.
--
You raise another interesting issue here that I've completely ignored in
the matrix object so far. Numeric exceptions are not handled at all; in
return, matrix math executes at the speed of compiled code. However, it
wouldn't be too hard to add functions like safeDivide to the ofunc
collection that would signal exceptions if someone considers this important.
I don't even have the hack that x/0.0 = some user settable Infinity. x/0.0
is set to the IEEE value for Infinity.
> d. One curiosity is the notation something[(a,b,c)]. This is required
> because something[a,b,c] is a syntax error. But perhaps Python could
> allow the latter interpreted as the former.
Guido beat me to this one. I'd like to add that if anybody decides to go
rummaging around in Grammer and compile.c, Guido also mentioned that he'd be
willing to let a**b <--> pow(a, b) into the syntax. This also seems like
an easy hack for someone with the right know-how, and would be a nice bonus
for numerical code.
There have been so many things added to the matrix object this week, that
it seems I should get a new version out to those interested testers (so you
won't need to ask me for features that are already there). I'll put out a
new object on Friday (if all goes well). It will have all of the above
additions (and many more), slightly different syntax for specifying the rank
of an operator, working complex numbers, possibly working generic Python
Objects as matrix elements and almost all of the memory leaks that I
identified as clearly belonging to my code squashed (thanks to Purify).
There are still a few memory leaks that appear to be in the python
internals, and not due to my code (I think). I'll check those out when I
have more time.
-Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Nov 1 15:35:01 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id PAA05369 for matrix-sig-people; Wed, 1 Nov 1995 15:35:01 -0500
Received: from CNRI.Reston.VA.US (cnri.reston.va.us [132.151.1.1]) by python.org (8.6.12/8.6.12) with SMTP id PAA05365 for ; Wed, 1 Nov 1995 15:34:58 -0500
Received: from monty.cnri.reston.va.us by CNRI.Reston.VA.US id aa16169;
1 Nov 95 15:32 EST
Received: from localhost by monty via SMTP (940816.SGI.8.6.9/940406.SGI)
id PAA04571; Wed, 1 Nov 1995 15:31:19 -0500
Message-Id: <199511012031.PAA04571@monty>
To: James Hugunin
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Some notes on the matrix class
In-reply-to: Your message of "Wed, 01 Nov 1995 15:28:58 EST."
<9511012028.AA00472@nineveh.lcs.mit.edu.LCS.MIT.EDU>
References: <9511011856.AA29129@kristen.llnl.gov> <9511012028.AA00472@nineveh.lcs.mit.edu.LCS.MIT.EDU>
From: Guido van Rossum
X-Face: S#(=*/yxQ]{^7V>yr9ElReWJZvi^x!]tR6g^m5q?|f@/ChslBZm*tWH=*aicc{$>4c/M:$.
\@@o)psQitBQa!z()VjNsj+aqtDI/J0jX'a/=Qhn|gv6coKW}06%6gyuTn)oc6
Date: Wed, 01 Nov 1995 15:31:19 -0500
Sender: owner-matrix-sig@python.org
Precedence: bulk
> There has been a small amount of talk about the possibilty of having
> "boolean types" in addition to number, sequence, and mapping types that
> would support >, <, ==, etc. After writing a lot of code with matrices, I
> feel that many things could be expressed a lot more elegantly if this was
> allowed. Unfortunately, Guido appears to be skeptical of this proposal, so
> don't hold your breath.
Couldn't this be done by implementing a "numeric" type (at the C
level) that has a range of [0..1]? It could have two objects...
Also, what exactly is the advantage of having a Boolean type? (Am I
showing my age here? :-)
Re a**b for pow(a, b): yes, that's also an acceptable addition.
--Guido van Rossum
URL:
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Nov 1 16:01:56 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id QAA05599 for matrix-sig-people; Wed, 1 Nov 1995 16:01:56 -0500
Received: from mama-bear.LCS.MIT.EDU (mama-bear.lcs.mit.edu [18.27.0.167]) by python.org (8.6.12/8.6.12) with SMTP id QAA05595 for ; Wed, 1 Nov 1995 16:01:54 -0500
Received: from nineveh.lcs.mit.edu.LCS.MIT.EDU by mama-bear.LCS.MIT.EDU (4.1/SLS-4.1.1)
id AA20373; Wed, 1 Nov 95 16:00:42 EST
Received: by nineveh.lcs.mit.edu.LCS.MIT.EDU (NX5.67e/SMI-4.1)
id AA00485; Wed, 1 Nov 95 16:02:31 -0500
Message-Id: <9511012102.AA00485@nineveh.lcs.mit.edu.LCS.MIT.EDU>
Content-Type: text/plain
Mime-Version: 1.0 (NeXT Mail 3.3 v118.2)
Received: by NeXT.Mailer (1.118.2)
From: James Hugunin
Date: Wed, 1 Nov 95 16:02:31 -0500
To: Guido van Rossum
Subject: Re: [PYTHON MATRIX-SIG] Some notes on the matrix class
Cc: matrix-sig@python.org
References: <9511011856.AA29129@kristen.llnl.gov>
<9511012028.AA00472@nineveh.lcs.mit.edu.LCS.MIT.EDU>
<199511012031.PAA04571@monty>
Sender: owner-matrix-sig@python.org
Precedence: bulk
> Couldn't this be done by implementing a "numeric" type (at the C
> level) that has a range of [0..1]? It could have two objects...
>
> Also, what exactly is the advantage of having a Boolean type? (Am I
> showing my age here? :-)
What I'm asking for is to be able to create a new python object in C, and
to include boolean methods for that object. So, just like I can implement
my matrix_add function for the syntax "a+b" when a and b are matrices
(modulo coercion), I'd really like to be able to implement a matrix_greater
function for the syntax "a>b" when a and b are matrices (still modulo
coercion). This seems completely in the original spirit of the language.
The reason for this is that if a = [1,2,3], and b = [2,2,2], I'd like to
define a>b = [0,0,1]. (where all of the above are matrices). This is
useful in an astounding range of matrix computations, and there is no
sensible definition of a>b for matrices that returns a scalar boolean value.
ie. the traditional discrete delta function is elegantly expressed as
def delta(i): return i==0
delta([-2,-1,0,1,2,3,4]) -> [0,0,1,0,0,0,0]
It's also useful for "vectorizing" traditional operations like filter. If
x is a vector,
filter(lambda x: x<2 and x>0, x) <--> x.filter(x<2 and x>0)
except the second is orders of magnitude faster for matrices.
I understand that this is a major change to some of the more complicated
parts of python, so there are good practical reasons that it won't be done
anytime soon. Nevertheless, I would love though to have you agree that this
would be a good feature if somebody ever has the time to add it.
-Jim
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================
From owner-matrix-sig Wed Nov 1 16:15:46 1995
Received: (from majordom@localhost) by python.org (8.6.12/8.6.12) id QAA05699 for matrix-sig-people; Wed, 1 Nov 1995 16:15:46 -0500
Received: from CNRI.Reston.VA.US (cnri.reston.va.us [132.151.1.1]) by python.org (8.6.12/8.6.12) with SMTP id QAA05695 for ; Wed, 1 Nov 1995 16:15:44 -0500
Received: from monty.cnri.reston.va.us by CNRI.Reston.VA.US id aa16962;
1 Nov 95 16:09 EST
Received: from localhost by monty via SMTP (940816.SGI.8.6.9/940406.SGI)
id QAA06690; Wed, 1 Nov 1995 16:08:06 -0500
Message-Id: <199511012108.QAA06690@monty>
To: James Hugunin
cc: matrix-sig@python.org
Subject: Re: [PYTHON MATRIX-SIG] Some notes on the matrix class
In-reply-to: Your message of "Wed, 01 Nov 1995 16:02:31 EST."
<9511012102.AA00485@nineveh.lcs.mit.edu.LCS.MIT.EDU>
References: <9511011856.AA29129@kristen.llnl.gov> <9511012028.AA00472@nineveh.lcs.mit.edu.LCS.MIT.EDU> <199511012031.PAA04571@monty> <9511012102.AA00485@nineveh.lcs.mit.edu.LCS.MIT.EDU>
From: Guido van Rossum **