# HG changeset patch
# User Mike Hansen <mhansen@gmail.com>
# Date 1268769372 25200
# Node ID bbbcfefeffbdd740062b60764183313af1492e99
# Parent  4a4d3a353abb86ae14ecfb996ca8fcf79a311f4e
Trac #261: a new matrix constructor: add lambda support

diff --git a/sage/matrix/constructor.py b/sage/matrix/constructor.py
--- a/sage/matrix/constructor.py
+++ b/sage/matrix/constructor.py
@@ -17,7 +17,7 @@
 #
 #                  http://www.gnu.org/licenses/
 #*****************************************************************************
-
+import types
 import sage.rings.all as rings
 import sage.matrix.matrix_space as matrix_space
 from sage.structure.element import is_Vector
@@ -40,13 +40,15 @@
     
     The entries of a matrix can be specified as a flat list of
     elements, a list of lists (i.e., a list of rows), a list of Sage
-    vectors, or a dictionary having positions as keys and matrix
-    entries as values (see the examples). You can create a matrix of
-    zeros by passing an empty list or the integer zero for the entries.
-    To construct a multiple of the identity (`cI`), you can
-    specify square dimensions and pass in `c`. Calling matrix()
-    with a Sage object may return something that makes sense. Calling
-    matrix() with a NumPy array will convert the array to a matrix.
+    vectors, a callable object, or a dictionary having positions as
+    keys and matrix entries as values (see the examples). If you pass
+    in a callable object, then you must specify the number of rows and
+    columns. You can create a matrix of zeros by passing an empty list
+    or the integer zero for the entries.  To construct a multiple of
+    the identity (`cI`), you can specify square dimensions and pass in
+    `c`. Calling matrix() with a Sage object may return something that
+    makes sense. Calling matrix() with a NumPy array will convert the
+    array to a matrix.
     
     The ring, number of rows, and number of columns of the matrix can
     be specified by setting the ring, nrows, or ncols parameters or by
@@ -94,6 +96,17 @@
         [4 5 6]
         Full MatrixSpace of 2 by 3 dense matrices over Rational Field
     
+    :: 
+       
+        sage: m = matrix(QQ, 3, 3, lambda i, j: i+j); m
+        [0 1 2]
+        [1 2 3]
+        [2 3 4]
+        sage: m = matrix(3, lambda i,j: i-j)
+        [ 0 -1 -2]
+        [ 1  0 -1]
+        [ 2  1  0]
+
     ::
     
         sage: v1=vector((1,2,3))
@@ -390,6 +403,8 @@
         sage: c = matrix(a.numpy('float32')); c
         [1.0 2.0]
         [3.0 4.0]
+        sage: matrix(numpy.array([[5]]))
+        [5]
         sage: v = vector(ZZ, [1, 10, 100])
         sage: m=matrix(ZZ['x'], v); m; m.parent()
         [  1  10 100]
@@ -466,6 +481,9 @@
     if len(args) >= 1:
         # check to see if the number of rows is specified
         try:
+            import numpy
+            if isinstance(args[0], numpy.ndarray):
+                raise TypeError
             nrows = int(args[0])
             args.pop(0)
             if kwds.get('nrows', nrows) != nrows:
@@ -478,6 +496,9 @@
     if len(args) >= 1:
         # check to see if additionally, the number of columns is specified
         try:
+            import numpy
+            if isinstance(args[0], numpy.ndarray):
+                raise TypeError
             ncols = int(args[0])
             args.pop(0)
             if kwds.get('ncols', ncols) != ncols:
@@ -491,14 +512,24 @@
     # Now we've taken care of initial ring, nrows, and ncols arguments.
     # We've also taken care of the Sage object case.
     
-    # Now the rest of the arguments are a list of
-    # rows, a flat list of entries, a dict, a numpy array, or a single
-    # value.
+    # Now the rest of the arguments are a list of rows, a flat list of
+    # entries, a callable, a dict, a numpy array, or a single value.
     if len(args) == 0:
         # If no entries are specified, pass back a zero matrix
         entries = 0
         entry_ring = rings.ZZ
     elif len(args) == 1:
+        if isinstance(args[0], (types.FunctionType, types.LambdaType, types.MethodType)):
+            if ncols is None and nrows is None:
+                raise ValueError, "When passing in a callable, the dimensions of the matrix must be specified"
+            if ncols is None:
+                ncols = nrows
+            else:
+                nrows = ncols
+
+            f = args[0]
+            args[0] = [[f(i,j) for j in range(ncols)] for i in range(nrows)]
+
         if isinstance(args[0], (list, tuple)):
             if len(args[0]) == 0:
                 # no entries are specified, pass back the zero matrix
@@ -538,7 +569,7 @@
 
             if nrows > 0 and ncols > 0 and ring is None:
                 entries, ring = prepare(entries)
-            
+   
         elif isinstance(args[0], dict):
             # We have a dictionary
             # default to sparse
