All Downloads are FREE. Search and download functionalities are using the official Maven repository.

expr.LinAlg.scala Maven / Gradle / Ivy

The newest version!
package galileo.linalg

// Linear algebra stuff, matrices and vectors
import galileo.expr._
import galileo.environment._
import galileo.selectable.Selectable

import scala.collection.mutable.{ListBuffer}

trait Matrix extends Expr with Selectable {
	val numRows:Int
	val numCols:Int
	def *(that:Expr):Expr // dotProduct
	def *(that:DenseMatrix):DenseMatrix
	
	val lu_lup:(Matrix,Matrix) 
	val lup_lup:(Matrix,Matrix,Matrix) 
	def solve( rhs:DenseMatrix ):DenseMatrix 
	val toDenseMatrix:DenseMatrix
	val inverse:Matrix
	def visit(env:Option[Environment]):Expr
	// elementwise operation
	def operate(env:Option[Environment],operator:Expr=>Expr):Matrix
	def norm(p:Int):Expr = this.toDenseMatrix.norm( p )
	//def tensorProduct(that:Expr):Matrix
	def transpose:Matrix //= this.toDenseMatrix.transpose
	// This can be optimized for subtypes - todo
	def apply(i:Int,j:Int):Expr = this.toDenseMatrix.rows(i)(j)
	def select(indices:List[Expr]):Expr = {
		indices match {
			case Number( i ) :: Number( j ) :: Nil => this.apply(i.toInt,j.toInt)
			case Number( i ) :: Nil => DenseMatrix( List( this.toDenseMatrix.rows( i.toInt ) ) )
			case _ => ErrorExpr( "Unhandled selection" )
		}
	}
	//def components:List[Expr] = toDenseMatrix.rows.flatMap
}

// for statements like 1:2:11
object LinSpace { 
	def apply( start:Double, end:Double, n:Int=100) =  {
		val incr = ( end - start ) / ( n - 1 ) // todo, use Fraction here
		var i = 0;
		var l:List[Expr] = List()
		while( i < n ) {
			l = l :+ Number( start + incr * i )
			i = i + 1
		}
		DenseMatrix( List( l ) ) //l.map( elem => List( elem ) ) )
	}
}

// to handle b:e and b:i:end
case class RowVector( begin:Expr, end:Expr, incr:Expr ) extends Expr {
	override def visit(env:Option[Environment]=None) = (begin.visit(env), end.visit(env), incr.visit(env)) match {
		case (Number(b),Number(e),Number(i)) => DenseMatrix( List( ( b to e by i ).map( n => Number( n ) ).to[List] ) )
		case (b,e,i) => RowVector(b,e,i)
	}
	def info(env:Option[Environment]=None) = "RowVector(" + begin + "," + end + "," + incr + ")"
	def variables:List[Variable] = begin.variables ++ end.variables ++ incr.variables
}

object LogSpace {
	def apply( start:Double, end:Double, n:Int=50) =  {
		val incr = ( end - start ) / ( n - 1 ) // todo, use fraction here
		var i = 0;
		var l:List[Expr] = List()
		while( i < n ) {
			l = l :+ Power( Number( 10 ), Number( start + incr * i ) ) 
			i = i + 1 
		}
		DenseMatrix( List( l ) ) //l.map( elem => List( elem ) ) )
	}
}

// transpose( expr ) command in parser
case class Transpose(e:Expr) extends Expr with Statement {
	def info(env:Option[Environment]=None) = "Transpose(" + e + ")"
	override def visit( env:Option[Environment]=None) = e.visit( env ) match {
		case m:Matrix => m.transpose
		case other => ErrorExpr( "Can not apply transpose to " + e + "(" + other + ") " )
	}
}

// Tensor product
/*
case class MatKron( a:Expr, b:Expr ) extends Expr {
	override def visit(env:Option[Environment]=None) = ( a.visit(env), b.visit(env) ) match {
		case (l:Matrix,r:Matrix) => l.tensorProduct( r )
		case _ => ErrorExpr( "Invalid operands for kron(" + a + "," + b + ")" )
	}
	def info = "MatKron(" + a + "," + b + ")"
}
*/

// U for unhandled - count of rows and cols are expressions, not int
case class OnesMatrixU( nrs:Expr, ncs:Expr,element:Expr=Number(1)) extends Expr with Statement {
	override def visit( env:Option[Environment]=None ) = ( nrs.visit(env), ncs.visit(env) ) match {
		case ( Number(nr), Number(nc) ) if (nr%1==0 && nc%1==0) => OnesMatrix( nr.toInt, nc.toInt, element )
		case _ => ErrorExpr( "Incorrect arguments in ones(" + nrs + "," + ncs + ")" ) 
	}
	def info(env:Option[Environment]=None) = "OnesMatrix(" + nrs + "," + ncs + ")"
}

// Matrix with all elements equal to element
case class OnesMatrix( numRows:Int, numCols:Int, element:Expr=Number( 1 ) ) extends Matrix {
	override def visit( environment:Option[Environment]=None) = 
		OnesMatrix( numRows, numCols, element.visit( environment ) )
	override def toString() = {
		var s = ""
		val elem = element.toString() + "\t"
		for( i <- 0 until numRows ) {
			for( k <- 0 until numCols )
				s = s + elem
			s += "\n"
		}
		s
	}
	def info(env:Option[Environment]=None) = "OnesMatrix todo"

	def *(that:Expr) = OnesMatrix( numRows, numCols, Product( element, that ) )
	//def tensorProduct(that:Expr) = OnesMatrix( numRows, numCols, Product( element, that ) )
	def *(that:DenseMatrix):DenseMatrix = {	//var rows:List[List[Expr]] = Nil
		var sums:List[Expr] = 
			that.rows.transpose.map( column => column.foldLeft( Number( 0 ):Expr ){ case (r:Expr,c:Expr) => Sum( r, c ) } ).map( sum => Product( sum, element ) )
		
		DenseMatrix( List.fill( numRows ){ sums } )
	}
	
	def operate(env:Option[Environment], operator:Expr=>Expr):Matrix = OnesMatrix( numRows, numCols, operator( element ) )
	def solve( rhs:DenseMatrix ) = DenseMatrix(List( List( ErrorExpr( "Indefinite matrix can not be solved" ) ) ) )
	
	DenseMatrix( List( List( Variable( "todo solve OnesMatrix") ) ) )
	val toDenseMatrix = DenseMatrix( List.fill(numRows){
		List.fill(numCols){element}
	} )
	lazy val inverse = this.toDenseMatrix.inverse // Is there a more optimal way?
	lazy val lu_lup:(Matrix,Matrix) = this.toDenseMatrix.lu_lup
	lazy val lup_lup:(Matrix,Matrix,Matrix) = this.toDenseMatrix.lup_lup

	def transpose = this
	def variables:List[Variable] = element.variables
}

case class EyeMatrixU(nrs:Expr,ncs:Expr) extends Expr with Statement {
	def info(env:Option[Environment]=None) = "EyeMatrixU(" + nrs + "," + ncs + ")"
	override def visit(env:Option[Environment]=None) = (nrs.visit(env), ncs.visit(env)) match {
		case ( Number( nr ), Number( nc ) ) if ( nr%1==0 && nc%1 == 0 && !(nc < nr )) => EyeMatrix( nr.toInt, nc.toInt )
		case _ => ErrorExpr( "Incorrect arguments in eye(" + nrs + "," + ncs + ")" )
	}
}

object EyeMatrix{
	def apply(n:Integer) = DiagMatrix( n, List.fill(n){ Number( 1 ) } )
	def apply(n:Integer, m:Integer) = { 
		require( m >= n )
		DiagMatrix( m, List.fill(n){ Number( 1 ) } )
	}
}

case class DiagMatrix( numCols:Int, diag:List[Expr] ) extends Matrix {
	override def visit( environment:Option[Environment]=None) = 
		DiagMatrix( numCols, diag.map( element => element.visit( environment ) ) )
	override def toString() = {
		var s = ""
		val zero = Number( 0 ).toString() + "\t"
		for( i <- 0 until diag.size ) {
			var r:List[Expr] = List.fill( numCols ){ Number(0) }
			r = r.updated( i, diag(i) )
			s += r.map( elem => elem.toString() ).mkString( "\t") 
			/*
			for( k <- 0 until numCols ) {
				if( k == i ) s = s + diag( i ).toString() + "\t" else s = s + zero
			}
			*/
			s += "\n"
		}
		s
	}
	def info(env:Option[Environment]=None) = "DiagMatrix todo"
	lazy val numRows = diag.size	
	def *(that:Expr) = DiagMatrix( numCols, diag.map( elem => Product( elem, that ) ) )
	def tensorProduct(that:Expr) = DiagMatrix( numCols, diag.map( elem => Product( elem, that ) ) )

	def *(that:DenseMatrix) = DenseMatrix( 
		that.rows.zip(diag).map( { case ( row, diagelem ) => row.map( elem => Product( elem, diagelem ).visit() ) } )
	)
	//val l_lup = Variable( "todo l_lup")
	override lazy val lu_lup = ( EyeMatrix( this.numRows ), this )
	override lazy val lup_lup = ( EyeMatrix( this.numRows ), this, EyeMatrix( this.numRows ) )

	def solve(rhs:DenseMatrix):DenseMatrix = {
		require( numCols == diag.size )
		var rr = rhs.rows
		for( i <- 0 until numCols )
			rr = rr.updated( i, rr(i).map( elem => Fraction( elem, diag( i ) ).visit() ) )
		DenseMatrix( rr )
	}
	lazy val toDenseMatrix:DenseMatrix = {
		var rows:List[List[Expr]]=Nil
		for( i <- 0 until numRows ) {
			var row:List[Expr] = List.fill(numCols){Number(0)}
			row = row.updated(i,diag(i))
			rows = rows :+ row
		}
		DenseMatrix( rows )
	}

	// todo: How does this work for non square matrices?
	lazy val inverse:DiagMatrix = DiagMatrix( numCols, diag.map( elem => Fraction( Number( 1 ), elem ).visit() ) )
	def transpose = this

	def operate(env:Option[Environment], operator:Expr=>Expr):Matrix = DiagMatrix( numCols, diag.map( elem => operator( elem ) ) )
	def variables:List[Variable] = diag.flatMap( elem => elem.variables )
}

// cols are only defined starting at the diag element
case class LowerTriangularMatrix( cols:List[List[Expr]] ) extends Expr with Matrix {
	override def toString() = {
		var s:String = ""
		val n = cols(0).size
		for( i <- 0 until n )
		{
			for( j <- 0 to i )
				s = s + cols(j)(i-j) + "\t"
			for( j <- i+1 until n )
				s = s + Number( 0 ) + "\t"
			s = s + "\n"
		}
		s
	}
	def info(env:Option[Environment]=None) = "LowerTriangularMatrix todo"

	override def visit(env:Option[Environment]=None):LowerTriangularMatrix = LowerTriangularMatrix(
		cols.map( col => col.map( elem => elem.visit( env ) ) )
	)

	override def eval() = LowerTriangularMatrix(
		cols.map( col => col.map( elem => elem.eval() ) )
	)
	
	lazy val numCols = cols.size
	lazy val numRows = cols.size

	def *(that:Expr) = {
		LowerTriangularMatrix( cols.map( col => { col.map( elem => Product( elem, that ) ) } ) )
	}

	def tensorProduct(that:Expr) = {
		LowerTriangularMatrix( cols.map( col => { col.map( elem => Product( elem, that ) ) } ) )
	}

	def *(that:DenseMatrix) = this.toDenseMatrix * that.toDenseMatrix

	lazy val toDenseMatrix:DenseMatrix = {
		var rows:List[List[Expr]] = Nil
		for( i <- 0 until numRows )
		{
			var row:List[Expr] = Nil
			for( j <- 0 to i )
				row = row :+ cols(j)(i-j)
			for( j <- i + 1 until numCols )
				row = row :+ Number( 0 )
			rows = rows :+ row //List( row ) :: Nil
		}
		DenseMatrix( rows )
	}

	//val lup = Variable( "todo")
	//val l_lup = Variable( "todo l_lup")
	lazy val lu_lup = ( this, EyeMatrix( this.numRows ) )
	lazy val lup_lup = ( this, EyeMatrix( this.numRows ), EyeMatrix( this.numRows ) )
	
	/*
	* | l11   0 | |x1|   |r1|
	* | l21 l22 | |x2| = |r2|
	*  ...
	*/
	def solve( rhs:DenseMatrix ):DenseMatrix = {
		var x:List[List[Expr]] = Nil//rhs.rows.map( row => row.to[ListBuffer] ).to[ListBuffer]
		// Forward substitution
		for( i <- 0 until this.numRows )
		{
			var xi = rhs.rows( i ).to[ListBuffer]
			for( k <- 0 until xi.size )
			{
				var xik:Expr=xi(k)
				for( j <- 0 until i )
				{
					xik = Sum( xik, Product( Number( -1 ), Product( x(j)(k), cols(j)(i-j) ) ) )
				}
				xi = xi.updated(k, xik)
					//println( "i: " + i + ", j: " + j )
				//x(i) = x(i).map( elem => Sum( elem, Product( Product( Number( -1 ), cols(j)(i-j) ), x(i)(j) ) ) )
			}
			// not strictly needed as diag is always 1...
			x = x :+ xi.map( elem => Fraction( elem, cols(i)(0) ).visit() ).to[List] 
		}
		DenseMatrix( x )
	}
	
	lazy val inverse = this.solve( EyeMatrix( this.numRows ).toDenseMatrix ) //Variable( "Todo")
	def transpose = UpperTriangularMatrix( this.cols )
	def operate(env:Option[Environment], operator:Expr=>Expr):Matrix = UpperTriangularMatrix( cols.map( col =>col.map( elem => operator( elem ) ) ) )
	def variables:List[Variable] = cols.flatMap( col => col.flatMap( elem => elem.variables ) )
}

// rows are only defined starting at the diag element
case class UpperTriangularMatrix( rows:List[List[Expr]] ) extends Matrix {
	override def toString() = {
		var s:String = ""
		val n = rows(0).size
		for( i <- 0 until n )
		{
			for( j <- 0 until i )
				s = s + Number( 0 ) + "\t"
			for( j <- i until n )
				s = s + rows(i)(j-i) + "\t"
			
			s = s + "\n"
		}
		s
/*
		def filler(n:Int):List[Expr] = n match{
			case a:Int if ( a <= 0 ) => Nil
			case _ => List.fill(n){ Number(0) }
		}
		var n = rows(0).size - 1
		rows.map( row => { ( filler( n ) :: row ).mkString( "\t" ); n = n - 1 } ).mkString( "\n")
		//rows.map( row => row.mkString( "\t" ) ).mkString( "\n")
		//"hi"
		*/
	}
	def info(env:Option[Environment]=None) = "UpperTriangularMatrix(" + rows.map( row => row.mkString( ",")).mkString( "\n" ) + ")"

	override def visit(env:Option[Environment]=None):UpperTriangularMatrix = UpperTriangularMatrix(
		rows.map( row => row.map( elem => elem.visit( env ) ) )
	)

	override def eval() = UpperTriangularMatrix(
		rows.map( row => row.map( elem => elem.eval() ) )
	)

	lazy val numCols = rows.size
	lazy val numRows = rows.size

	def *(that:Expr) = UpperTriangularMatrix( rows.map( row => { row.map( elem => Product( elem, that ) ) } ) )
	def tensorProduct(that:Expr) = UpperTriangularMatrix( rows.map( row => { row.map( elem => Product( elem, that ) ) } ) )

	//val lup = Variable( "todo")
	def *(that:DenseMatrix) = this.toDenseMatrix * that.toDenseMatrix

	//val l_lup = Variable( "todo l_lup")
	lazy val lu_lup = ( EyeMatrix( this.numRows ), this )
	lazy val lup_lup = ( EyeMatrix( this.numRows ), this, EyeMatrix( this.numRows ) )
	
	// TODO: Improve implementation
	def solve( rhs:DenseMatrix ):DenseMatrix = {
		var x:ListBuffer[List[Expr]] = ListBuffer.fill(rhs.numRows){ List.fill(rhs.numCols){ Number( 0 ) } } //rhs.rows.map( row => row.to[ListBuffer] ).to[ListBuffer]
		// Backward substitution
		for( i <- this.numRows - 1 to 0 by -1 ) //until this.numRows )
		{
			var xi = rhs.rows( i ).to[ListBuffer]
			for( k <- 0 until xi.size )
			{
				var xik:Expr = xi(k)
				for( j <- i + 1 until this.numRows )
					xik = Sum( xik, Product( Number( -1 ), Product( x(j)(k), rows(i)(j-i) ) ) )

				xi = xi.updated( k, xik )
			}
			x( i ) = xi.map( elem => Fraction( elem, rows(i)(0) ).visit() ).to[List] // ::: x :: Nil// divide by diagonal
		}
		DenseMatrix( x.to[List] )
	}

	lazy val toDenseMatrix:DenseMatrix = {
		var rs:List[List[Expr]] = Nil
		for( i <- 0 until numRows )
		{
			val pre:List[Expr] = List.fill( i ){ Number( 0 ) }
			var row = rows( i )
			row = pre ::: row // :: Nil
			rs = rs :+ row
		}
		DenseMatrix( rs )
	}
	
	lazy val inverse = this.solve( EyeMatrix( this.numRows ).toDenseMatrix ) //Variable( "Todo")

	def transpose = LowerTriangularMatrix( this.rows )
	def operate(env:Option[Environment], operator:Expr=>Expr):Matrix = UpperTriangularMatrix( rows.map( row => row.map( elem => operator( elem ) ) ) )
	def variables:List[Variable] = rows.flatMap( row => row.flatMap( elem => elem.variables ) )
}

/*
* ps is the list of permutation steps
* rowPositions has the final list of rearranged rows
*/
case class RowPermutationMatrixInverse( ps:ListBuffer[Int] ) extends Matrix {
	lazy val numCols = ps.size
	lazy val numRows = ps.size
	val l_lup = Variable( "todo l_lup")
	lazy val lu_lup = ( this, EyeMatrix( numRows ) )
	lazy val lup_lup = ( EyeMatrix( numRows ), EyeMatrix( numRows ), this.inverse )

	def *(that:Expr) = this.toDenseMatrix * that 
	def tensorProduct(that:Expr) = this.toDenseMatrix * that 


	def *(that:DenseMatrix):DenseMatrix = {
		var rows = that.rows
		for( i <- ps.size - 1 to 0 by -1) { 
			val p = ps(i)
				if( p != i )
					rows = rows.updated(i,rows(p)).updated(p,rows(i))
		}
		DenseMatrix( rows )
	}

	def solve( rhs:DenseMatrix ):DenseMatrix = {
		// apply permutation to rhs
		var x = rhs.rows
		for( i <- 0 until ps.size ) //- 1 to 0 by -1 )
			x = x.updated( i, x(ps(i))).updated( ps(i), x(i))
		DenseMatrix( x ) //List( List ( Variable( "todo RowPermutationMatrix solve") ) ) )
	}

	override def toString() = {
		var s = ""
		for( i <- 0 until numRows )
		{
			var r:List[Expr] = List.fill( numCols ){ Number(0) } //todo...
			r = r.updated( rowPositions( i ), Number( 1 ) ) // todo
			s += r.map( elem => elem.toString() ).mkString( "\t") + "\n"
		}
		s
	}
	def info(env:Option[Environment]=None) = "RowPermutationMatrixInverse todo"

	override def visit(env:Option[Environment]=None) = this

	lazy val rowPositions:ListBuffer[Int] = {
		var rp = (0 until ps.size ).to[ListBuffer]
		for( i <- ps.size - 1 to 0 by -1 ) { 
			val p = ps(i)
				if( p != i )
					rp = rp.updated(i,rp(p)).updated(p,rp(i))
		}
		rp
	}

	lazy val toDenseMatrix:DenseMatrix = {
		var rows:List[List[Expr]] = Nil
		for( i <- 0 until numRows )
		{
			var row:List[Expr] = List.fill(numCols){Number(0)}
			row = row.updated(rowPositions(i), Number( 1 ) )
			rows = rows :+ row
		}
		// somehow this creates empty rows!!!!
		DenseMatrix( rows )
	}

// NOT CORRECT
	lazy val inverse:RowPermutationMatrix = {
		//psi = 
		RowPermutationMatrix( ps )
	}

	def transpose = RowPermutationMatrix( ps )
	
	def operate(env:Option[Environment], operator:Expr=>Expr):Matrix = this.toDenseMatrix.operate(env,operator)
	def variables:List[Variable] = List() 
}

/*
* ps is the list of permutation steps
* rowPositions has the final list of rearranged rows
*/
case class RowPermutationMatrix( ps:ListBuffer[Int] ) extends Matrix {

	lazy val numCols = ps.size
	lazy val numRows = ps.size
	lazy val l_lup = Variable( "todo l_lup")
	lazy val lu_lup = ( this, EyeMatrix( numRows ) )
	lazy val lup_lup = ( EyeMatrix( numRows ), EyeMatrix( numRows ), this.inverse )

	def *(that:Expr) = this.toDenseMatrix * that 
	def tensorProduct(that:Expr) = this.toDenseMatrix * that 

	def *(that:DenseMatrix):DenseMatrix = {
		var rows = that.rows
		for( i <- 0 until ps.size ) { 
			val p = ps(i)
				if( p != i )
					rows = rows.updated(i,rows(p)).updated(p,rows(i))
		}
		DenseMatrix( rows )
	}

	def solve( rhs:DenseMatrix ):DenseMatrix = {
		// apply permutation to rhs
		var x = rhs.rows
		for( i <- ps.size - 1 to 0 by -1 )
			x = x.updated( i, x(ps(i))).updated( ps(i), x(i))
		DenseMatrix( x ) //List( List ( Variable( "todo RowPermutationMatrix solve") ) ) )
	}

	override def toString() = {
		//println( "ps: " + ps )
		var s = ""
		for( i <- 0 until numRows )
		{
			var r:List[Expr] = List.fill( numCols ){ Number(0) } //todo...
			r = r.updated( rowPositions( i ), Number( 1 ) ) // todo
			s += r.map( elem => elem.toString() ).mkString( "\t") + "\n"
		}
		s
	}
	def info(env:Option[Environment]=None) = "RowPermutationMatrix todo"

	override def visit(env:Option[Environment]=None) = this

	lazy val rowPositions:ListBuffer[Int] = {
		var rp = (0 until ps.size ).to[ListBuffer]
		for( i <- 0 until ps.size ) { 
			val p = ps(i)
				if( p != i )
					rp = rp.updated(i,rp(p)).updated(p,rp(i))
		}
		rp
	}

	lazy val toDenseMatrix:DenseMatrix = {
		var rows:List[List[Expr]] = Nil
		for( i <- 0 until numRows )
		{
			var row:List[Expr] = List.fill(numCols){Number(0)}
			row = row.updated(rowPositions(i), Number( 1 ) )
			rows = rows :+ row
		}
		// somehow this creates empty rows!!!!
		DenseMatrix( rows )
	}

/*
	def *(that:LowerTriangularMatrix):LowerTriangularMatrix = {
		that
	}
	*/

// NOT CORRECT
	lazy val inverse:RowPermutationMatrixInverse = {
		//psi = 
		RowPermutationMatrixInverse( ps )
	}

	def transpose = RowPermutationMatrixInverse( ps )
	def operate(env:Option[Environment], operator:Expr=>Expr):Matrix = this.toDenseMatrix.operate(env,operator)
	def variables:List[Variable] = List()
}

/*
** Main matrix class - use this for small matrix operations
*/
case class DenseMatrix( rows:List[List[Expr]]) extends Expr with Matrix {
	override def toString() = rows.map( l => l.mkString( "\t" ) ).mkString("\n")
	def info(env:Option[Environment]=None) = "DenseMatrix todo"
	def dotProduct( row:List[Expr], col:List[Expr] ):Expr =
		row.zip( col ).map( { case (a,b) => Product( a, b ) } ).foldLeft(Number(0):Expr)((c,d)=>Sum(c,d)) 
	
	// Matrix multiplication
	def *(that:DenseMatrix):DenseMatrix = {
		val rv = for( row <- this.rows )
			yield for( col <- that.rows.transpose )
				yield dotProduct( row, col ).visit()
		DenseMatrix( rv )
    }

    def *(that:RowPermutationMatrix):DenseMatrix = {
    	var rv = rows
    	for( i <- that.ps.size - 1 to 0 by -1 ) { //0 until ps.size ) { 
			val p = that.ps(i)
				if( p != i )
					rv = rv.updated(i,rv(p)).updated(p,rv(i))
		}
		DenseMatrix( rows )
    }

    def *(that:Expr) = DenseMatrix( rows.map( row => { row.map( elem => Product( elem, that ) ) } ) )
    def tensorProduct(that:Expr) = DenseMatrix( rows.map( row => { row.map( elem => Product( elem, that ) ) } ) )

    def -(that:DenseMatrix):DenseMatrix = {
		val rv = rows.zip( that.rows ).map( {
			case( thisrow, thatrow ) => thisrow.zip( thatrow ).map( {
				case (a,b) => Sum( a, Product( Number( -1 ), b ) )
			} )
		} )
    	DenseMatrix( rv )
    }
	def +(that:DenseMatrix):DenseMatrix = {
		val rv = rows.zip( that.rows ).map( {
			case( thisrow, thatrow ) => thisrow.zip( thatrow ).map( {
				case (a,b) => Sum( a, b )
			} )
		} )
    	DenseMatrix( rv )
    }
    override def +(that:Expr):DenseMatrix = {
    	DenseMatrix( rows.map( row => row.map( elem => Sum( elem, that ) ) ) )
    }

    def dotProduct(that:Number):DenseMatrix = {
		val rv = for( row <- this.rows )
			yield for( elem <- row )
				yield Product( elem, that )
		DenseMatrix( rv )
    }
    def dotProduct(that:Variable):DenseMatrix = {
		val rv = for( row <- this.rows )
			yield for( elem <- row )
				yield Product( elem, that )
		DenseMatrix( rv )
    }
	def dotProduct(that:DenseMatrix):DenseMatrix = DenseMatrix(
		this.rows.zip( that.rows ).map( { case( lc, rc) => lc.zip(rc).map( { case (le,re) => Product( le,re ) } ) } )
	)

	override def visit(env:Option[Environment]=None) = DenseMatrix( rows.map( row => row.map( elem => elem.visit( env ) ) ) )
	override def expand = DenseMatrix( rows.map( row => row.map( elem => elem.expand ) ) )
	override def simplify = DenseMatrix( rows.map( row => row.map( elem => elem.simplify ) ) )
	override def factor = DenseMatrix( rows.map( row => row.map( elem => elem.factor ) ) )

	override def eval() = DenseMatrix(
		rows.map( row => row.map( elem => elem.eval() ) )
	)

	lazy val numRows = rows.size
	lazy val numCols = if (rows.size > 0 ) rows(0).size else 0
	lazy val cols = rows.transpose

	// L*U = P * A, inv(P)*L*U=A, inv(inv(P)*L*U)=inv(A), inv(U)*inv(L)*P=inv(A)
	lazy val inverse = _lup match {
		case (lc,ur,ps) => ( UpperTriangularMatrix( ur ).visit().inverse * LowerTriangularMatrix( lc ).visit().inverse ).visit() * RowPermutationMatrix( ps ).toDenseMatrix
	}

	lazy val lu_lup = _lup match {
		case (lc,ur,ps) => (
			( RowPermutationMatrix( ps ).inverse * LowerTriangularMatrix( lc ).toDenseMatrix ).visit(),
			UpperTriangularMatrix( ur ).visit()
		)
	}

	lazy val lup_lup = _lup match {
		case (lc,ur,ps) => (
			LowerTriangularMatrix( lc ).visit(),
			UpperTriangularMatrix( ur ).visit(),
			RowPermutationMatrix( ps )//.visit() // No visit needed for RowPerm
		)
	}

	// Helper for LU factorization
	lazy val _lup = {
		var lc:List[List[Expr]] = Nil // List of list of expr, cols of L
		var ur:List[List[Expr]] = Nil // List of list of expr, rows of U
		val ps:ListBuffer[Int] = ( 0 until this.numRows ).to[ListBuffer] // P, permutatations
		//println( "ps: " + ps )
		var rs = rows
		var nr = rows
		
		for( i <- 0 until this.numRows )
		{
			// find row with max abs value
			var pi = i // pivot index
			
			var pv = math.abs( rs(0)(0).doubleValue )
			for( p <- i + 1  until this.numRows ) {
				if( math.abs( rs(p-i)(0).doubleValue ) > pv ) {
					pi = p; 
					pv = rs(p-i)(0).doubleValue
				}
			}
			
			ps(i)=pi // these are the indices to use later on, to build the external facing P matrix
			// move elements in rs around
			if( pi != i ){
				//println( "pi: " + pi + "i: " + i + "rs.size: " + rs.size )
				rs = rs.updated(0,rs(pi-i)).updated(pi-i,rs(0))
				nr = nr.updated(i,nr( pi)).updated(pi, nr( i ) )
				// tricky part... if you applied a pivot here, also updated l part that is already built up !!
				for( c <- 0 until lc.size ) {
					val pc = lc(c).updated( i - c, lc(c)(pi-c)).updated( pi - c, lc(c)(i-c))// Permuted Column
					lc = lc.updated( c, pc )
				}					
			}

			var pivot = rs(0)(0) // rs keeps shrinking...

			var cs = rs.transpose
			
			var col = cs( 0 ).takeRight( numRows - i -1 ).map( elem => Fraction( elem, pivot ) )
			lc = lc ::: ( Number( 1 )  :: col ) :: Nil

			ur = ur ::: rs(0).takeRight( numRows - i ) :: Nil
			
			// Now adjust rs to reflect a smaller set of of rows and cols
			rs = nr.takeRight( numRows - 1 - i ).map( row => row.takeRight( numRows - 1 - i ) )


			var l21 = lc.map( col => col.takeRight( numRows - 1 - i ) )//.takeRight( numRows - 1 - i )
			var u12 = ur.map( row => row.takeRight( numRows - 1 - i ) )//.takeRight( numRows - 1 - i )
			// rs <- r1 - L21*U11
			//println( "rs: " + rs + "l21: " + l21 + "u12: " + u12 )
			rs = ( DenseMatrix( rs ) - DenseMatrix( l21.transpose ) * DenseMatrix( u12 ) ).rows
			
			rs = rs.map( row => row.map( elem => elem.visit() ) )
		}

		(lc,ur,ps)
	}

	// Solves X in this * X = rhs
	// X = inv( this ) * rhs, but that's not how it's calculated
	def solve(rhs:DenseMatrix):DenseMatrix = this._lup match {
		case (lc,ur,ps) =>
			var sol:DenseMatrix = RowPermutationMatrix( ps ) * rhs
			sol = LowerTriangularMatrix( lc ).solve( sol )
			UpperTriangularMatrix( ur ).solve( sol ) 
	}

	override def norm( p:Int ):Expr = p match {
		case 0 => Number( 0 )
		case 1 => Max( rows.transpose.map( row => row.map( elem => Abs( elem ) ).foldLeft( Number( 0 ):Expr )( (a,b) => Sum( a, b ).visit() ) ):_* ).visit() //.foldLeft( Number( 0 ):Expr )( (a,b) => Max( a, b ).visit() )// max column abs sum
		case 2 => ErrorExpr( "Todo DenseMatrix.norm") // requires eigenvalues 
		//case Inf	
	}

	lazy val toDenseMatrix = this
	def transpose = DenseMatrix( rows.transpose ) 

	def operate(env:Option[Environment], operator:Expr=>Expr):Matrix = DenseMatrix( rows.map( row => row.map( elem => operator(elem) ) ) )
	def variables:List[Variable] = rows.flatMap( row => row.flatMap( elem => elem.variables ) )
}

// Handler for lu command
case class MatLU(expr:Expr) extends Expr with Statement {
	def info(env:Option[Environment]=None) = this.toString()
}

// Handler for inv command
case class MatInv(expr:Expr) extends Expr with Statement {
	def info(env:Option[Environment]=None) = this.toString()
	override def visit(env:Option[Environment]=None) = expr.visit( env ) match {	 
        case m:Matrix => m.inverse
        case _ => ErrorExpr( "inv(matrix) can only be applied to a matrix")
    }
}

// Handler for norm command
case class MatNorm(expr:Expr, p:Int = 2) extends Expr with Statement {
	def info(env:Option[Environment]=None) = this.toString()
	override def visit(env:Option[Environment]=None) = expr.visit( env ) match {
        case m:Matrix => m.norm(p)
        case _ => ErrorExpr( "norm(matrix) can only be applied to a matrix")
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy