diff --git a/help/errors.md b/help/errors.md index 55cd1b0b..8d7ce5e1 100644 --- a/help/errors.md +++ b/help/errors.md @@ -25,6 +25,14 @@ You can also use the `warning` method to alert the user of a potential issue tha myerr.warning() +To see the full list of morpho errors, look at the `errorlist` help entry. + +# Error list +[tagerrorrlist]: # (error list) +[tagerrorlist]: # (errorlist) + +A list of morpho errors: + [showsubtopics]: # (subtopics) ## Alloc @@ -195,3 +203,1712 @@ Or to be multiplied together, the number of columns of the left hand matrix must var b = Matrix([1,2]) print a*b // ok print b*a // generates a `MtrxIncmptbl` error. + +## DvZr +[tagdvzr]: # (dvzr) + +This error occurs when attempting to divide by zero: + + var a = 5 / 0 // Causes 'DvZr' + +## StckOvflw +[tagstckovflw]: # (stckovflw) + +This error occurs when the call stack exceeds its maximum depth, typically due to excessive recursion or deeply nested function calls. + +## ErrStckOvflw +[tagerrstckovflw]: # (errstckovflw) + +This error occurs when the error handler stack overflows, typically due to errors occurring within error handlers. + +## Exit +[tagexit]: # (exit) + +This error is generated when the virtual machine is halted, typically when the program exits normally. + +## MltplDsptchFld +[tagmltpldsptchfld]: # (mltpldsptchfld) + +This error occurs when multiple dispatch cannot find a method implementation that matches the provided arguments: + + class A { } + class B { } + fn method(A a) { } + fn method(B b) { } + method(1) // Causes 'MltplDsptchFld' - no matching method for integer + +## TypeChk +[tagtypechk]: # (typechk) + +This error occurs when there is a type violation, such as attempting to assign a value of one type to a variable declared with a different type: + + String x = 5 // Causes 'TypeChk' + +## NoOptArg +[tagnooptarg]: # (nooptarg) + +This error occurs when you try to pass optional arguments to a function that doesn't accept them: + + fn f(x) { return x } + f(1, y=2) // Causes 'NoOptArg' + +## UnkwnOptArg +[tagunkwnoptarg]: # (unkwnoptarg) + +This error occurs when you pass an unknown optional argument to a function: + + fn f(x, y=1) { return x + y } + f(1, z=2) // Causes 'UnkwnOptArg' + +## InvldArgsBltn +[taginvldargsbltn]: # (invldargsbltn) + +This error occurs when a built-in function is called with arguments of the wrong type: + + print(1, 2, 3) // If print expects a string, causes 'InvldArgsBltn' + +## ArrayArgs +[tagarrayargs]: # (arrayargs) + +This error occurs when creating an Array with invalid arguments. Arrays must be created with integer dimensions: + + var a = Array("invalid") // Causes 'ArrayArgs' + +## ArrayInit +[tagarrayinit]: # (arrayinit) + +This error occurs when an Array initializer is not an array or list: + + var a = Array(2, 2, "invalid") // Causes 'ArrayInit' + +## ArrayCmpt +[tagarraycmpt]: # (arraycmpt) + +This error occurs when an Array initializer has dimensions that don't match the requested dimensions: + + var a = Array(2, 2, [[1,2,3]]) // Causes 'ArrayCmpt' if dimensions don't match + +## ArrayIndx +[tagarrayindx]: # (arrayindx) + +This error occurs when indexing an Array with non-integer indices: + + var a[2,2] + a["x", "y"] // Causes 'ArrayIndx' + +## BrkOtsdLp +[tagbrkotsdlp]: # (brkotsdlp) + +This error occurs when a `break` statement is encountered outside of a loop: + + break // Causes 'BrkOtsdLp' + +## CntOtsdLp +[tagcntotsdlp]: # (cntotsdlp) + +This error occurs when a `continue` statement is encountered outside of a loop: + + continue // Causes 'CntOtsdLp' + +## ClssCrcRf +[tagclsscrcrf]: # (clsscrcrf) + +This error occurs when a class attempts to inherit from itself: + + class A < A { } // Causes 'ClssCrcRf' + +## ClssDplctImpl +[tagclssdplctimpl]: # (clssdplctimpl) + +This error occurs when a class has duplicate method implementations with the same signature: + + class A { + fn method() { } + fn method() { } // Causes 'ClssDplctImpl' + } + +## ClssLnrz +[tagclsslnrz]: # (clsslnrz) + +This error occurs when morpho cannot linearize a class hierarchy due to conflicting inheritance order. Check parent and ancestor classes for inheritance issues. + +## SlfOtsdClss +[tagslfotsdclss]: # (slfotsdclss) + +This error occurs when `self` is used outside of a class method: + + print self // Causes 'SlfOtsdClss' + +## SprOtsdClss +[tagsprotsdclss]: # (sprotsdclss) + +This error occurs when `super` is used outside of a class method: + + print super // Causes 'SprOtsdClss' + +## SprSelMthd +[tagsprselmthd]: # (sprselmthd) + +This error occurs when `super` is used incorrectly. It can only be used to select a method: + + super // Causes 'SprSelMthd' + super.method() // OK + +## SprNtFnd +[tagsprntfnd]: # (sprntfnd) + +This error occurs when a superclass cannot be found: + + class A < NonExistent { } // Causes 'SprNtFnd' + +## TooMnyArg +[tagtoomnyarg]: # (toomnyarg) + +This error occurs when too many arguments are passed to a function: + + fn f(x) { return x } + f(1, 2, 3) // Causes 'TooMnyArg' + +## TooMnyPrm +[tagtoomnyprm]: # (toomnyprm) + +This error occurs when a function is defined with too many parameters (exceeding the maximum allowed). + +## TooMnyCnst +[tagtoomnycnst]: # (toomnycnst) + +This error occurs when a program has too many constants (exceeding the maximum allowed). + +## VblDcl +[tagvbldcl]: # (vbldcl) + +This error occurs when a variable is declared multiple times in the same scope: + + var x = 1 + var x = 2 // Causes 'VblDcl' + +## FlNtFnd +[tagflntfnd]: # (flntfnd) + +This error occurs when a file cannot be found: + + import "nonexistent.morpho" // Causes 'FlNtFnd' + +## MdlNtFnd +[tagmdlntfnd]: # (mdlntfnd) + +This error occurs when a module cannot be found: + + import nonexistent // Causes 'MdlNtFnd' + +## ImprtFld +[tagimprtfld]: # (imprtfld) + +This error occurs when an import statement fails: + + import "broken.morpho" // Causes 'ImprtFld' if the file has errors + +## UnrslvdFrwdRf +[tagunrslvdfrwdrf]: # (unrslvdfrwdrf) + +This error occurs when a function is called before it is defined in the same scope: + + f() // Causes 'UnrslvdFrwdRf' + fn f() { } + +## MltVarPrmtr +[tagmltvarprmtr]: # (mltvarprmtr) + +This error occurs when a function has more than one variadic parameter: + + fn f(...args1, ...args2) { } // Causes 'MltVarPrmtr' + +## VarPrLst +[tagvarprlst]: # (varprlst) + +This error occurs when fixed parameters are placed after a variadic parameter: + + fn f(...args, x) { } // Causes 'VarPrLst' + +## OptPrmDflt +[tagoptprmdflt]: # (optprmdflt) + +This error occurs when an optional parameter's default value is not a constant: + + var x = 1 + fn f(y: x) { } // Causes 'OptPrmDflt' + +## MssngLoopBdy +[tagmssngloopbdy]: # (mssngloopbdy) + +This error occurs when a loop statement is missing its body: + + for (var i = 0; i < 10; i++) // Causes 'MssngLoopBdy' + +## NstdClss +[tagnstdclss]: # (nstdclss) + +This error occurs when attempting to define a class within another class: + + class A { + class B { } // Causes 'NstdClss' + } + +## InvldAssgn +[taginvldassgn]: # (invldassgn) + +This error occurs when attempting to assign to an invalid target: + + 5 = 10 // Causes 'InvldAssgn' + +## FnPrmSymb +[tagfnprmsymb]: # (fnprmsymb) + +This error occurs when function parameters are not symbols: + + fn f(5) { } // Causes 'FnPrmSymb' + +## PptyNmRqd +[tagpptynmrqd]: # (pptynmrqd) + +This error occurs when a property name is required but not provided. + +## InitRtn +[taginitrtn]: # (initrtn) + +This error occurs when attempting to return a value from an initializer method: + + class A { + init() { + return 5 // Causes 'InitRtn' + } + } + +## MssngIndx +[tagmssngindx]: # (mssngindx) + +This error occurs when indexing syntax is incomplete, missing required indices. + +## MssngIntlzr +[tagmssngintlzr]: # (mssngintlzr) + +This error occurs when a typed variable is declared without an initializer: + + var x: String // Causes 'MssngIntlzr' if initialization is required + +## TypeErr +[tagtypeerr]: # (typeerr) + +This error occurs when there is a type violation during assignment: + + var x: String + x = 5 // Causes 'TypeErr' + +## UnknwnType +[tagunknwntype]: # (unknwntype) + +This error occurs when an unknown type is referenced: + + var x: UnknownType // Causes 'UnknwnType' + +## UnknwnNmSpc +[tagunknwnnmspc]: # (unknwnnmspc) + +This error occurs when an unknown namespace is referenced: + + import unknown::module // Causes 'UnknwnNmSpc' + +## UnknwnTypeNmSpc +[tagunknwntypenmspc]: # (unknwntypenmspc) + +This error occurs when an unknown type is referenced in a namespace: + + var x: unknown::Type // Causes 'UnknwnTypeNmSpc' + +## SymblUndfNmSpc +[tagsymblundfnmspc]: # (symblundfnmspc) + +This error occurs when a symbol is not defined in the specified namespace: + + unknown::symbol // Causes 'SymblUndfNmSpc' + +## IncExp +[tagincexp]: # (incexp) + +This error occurs when an expression is incomplete: + + var x = 5 + // Causes 'IncExp' + +## MssngParen +[tagmssngparen]: # (mssngparen) + +This error occurs when a closing parenthesis is missing: + + fn f(x // Causes 'MssngParen' + +## ExpExpr +[tagexpexpr]: # (expexpr) + +This error occurs when an expression is expected but not found: + + var x = // Causes 'ExpExpr' + +## MssngExpTerm +[tagmssngexpterm]: # (mssngexpterm) + +This error occurs when an expression terminator (semicolon or newline) is missing after an expression. + +## VarExpct +[tagvarexpct]: # (varexpct) + +This error occurs when a variable name is expected after `var`: + + var // Causes 'VarExpct' + +## SymblExpct +[tagsymblexpct]: # (symblexpct) + +This error occurs when a symbol is expected but not found. + +## MssngBrc +[tagmssngbrc]: # (mssngbrc) + +This error occurs when a closing brace is missing: + + fn f() { // Causes 'MssngBrc' + +## MssngSqBrc +[tagmssngsqbrc]: # (mssngsqbrc) + +This error occurs when a closing square bracket is missing: + + var x = [1, 2 // Causes 'MssngSqBrc' + +## MssngComma +[tagmssngcomma]: # (mssngcomma) + +This error occurs when a comma is expected: + + var x = [1 2] // Causes 'MssngComma' + +## TrnryMssngColon +[tagtrnymssngcolon]: # (trnymssngcolon) + +This error occurs when a colon is missing in a ternary operator: + + var x = true ? 1 // Causes 'TrnryMssngColon' + +## IfMssngLftPrn +[tagifmssnglftprn]: # (ifmssnglftprn) + +This error occurs when a left parenthesis is missing after `if`: + + if x > 0 { } // Causes 'IfMssngLftPrn' + +## IfMssngRgtPrn +[tagifmssngrgtprn]: # (ifmssngrgtprn) + +This error occurs when a right parenthesis is missing after an if condition: + + if (x > 0 { } // Causes 'IfMssngRgtPrn' + +## WhlMssngLftPrn +[tagwhlmssnglftprn]: # (whlmssnglftprn) + +This error occurs when a left parenthesis is missing after `while`: + + while x > 0 { } // Causes 'WhlMssngLftPrn' + +## ForMssngLftPrn +[tagformssnglftprn]: # (formssnglftprn) + +This error occurs when a left parenthesis is missing after `for`: + + for var i = 0; i < 10; i++ { } // Causes 'ForMssngLftPrn' + +## ForMssngRgtPrn +[tagformssngrgtprn]: # (formssngrgtprn) + +This error occurs when a right parenthesis is missing after for clauses: + + for (var i = 0; i < 10; i++ { } // Causes 'ForMssngRgtPrn' + +## FnNoName +[tagfnoname]: # (fnoname) + +This error occurs when a function or method name is expected but not found: + + fn () { } // Causes 'FnNoName' + +## FnMssngLftPrn +[tagfnmssnglftprn]: # (fnmssnglftprn) + +This error occurs when a left parenthesis is missing after a function name: + + fn f { } // Causes 'FnMssngLftPrn' + +## FnMssngRgtPrn +[tagfnmssngrgtprn]: # (fnmssngrgtprn) + +This error occurs when a right parenthesis is missing after function parameters: + + fn f(x { } // Causes 'FnMssngRgtPrn' + +## FnMssngLftBrc +[tagfnmssnglftbrc]: # (fnmssnglftbrc) + +This error occurs when a left brace is missing before a function body: + + fn f() // Causes 'FnMssngLftBrc' + +## CllMssngRgtPrn +[tagcllmssngrgtprn]: # (cllmssngrgtprn) + +This error occurs when a right parenthesis is missing after function call arguments: + + f(x // Causes 'CllMssngRgtPrn' + +## ClsNmMssng +[tagclsnmssng]: # (clsnmssng) + +This error occurs when a class name is expected but not found: + + class { } // Causes 'ClsNmMssng' + +## ClsMssngLftBrc +[tagclsmssnglftbrc]: # (clsmssnglftbrc) + +This error occurs when a left brace is missing before a class body: + + class A // Causes 'ClsMssngLftBrc' + +## ClsMssngRgtBrc +[tagclsmssngrgtbrc]: # (clsmssngrgtbrc) + +This error occurs when a right brace is missing after a class body: + + class A { // Causes 'ClsMssngRgtBrc' + +## ExpctDtSpr +[tagexpctdtspr]: # (expctdtspr) + +This error occurs when a dot is expected after `super`: + + super method() // Causes 'ExpctDtSpr' + super.method() // OK + +## SprNmMssng +[tagsprnmssng]: # (sprnmssng) + +This error occurs when a superclass name is expected but not found: + + class A < { } // Causes 'SprNmMssng' + +## MxnNmMssng +[tagmxnnmssng]: # (mxnnmssng) + +This error occurs when a mixin class name is expected but not found. + +## IntrpIncmp +[tagintrpincmp]: # (intrpincmp) + +This error occurs when a string interpolation is incomplete: + + var x = "Hello ${" // Causes 'IntrpIncmp' + +## EmptyIndx +[tagemptyindx]: # (emptyindx) + +This error occurs when a variable declaration has an empty capacity: + + var x[] // Causes 'EmptyIndx' + +## ImprtMssngNm +[tagimprtmssngnm]: # (imprtmssngnm) + +This error occurs when an import statement is missing a module or file name: + + import // Causes 'ImprtMssngNm' + +## ImprtMltplAs +[tagimprtmltplas]: # (imprtmltplas) + +This error occurs when an import statement has multiple `as` clauses: + + import module as A as B // Causes 'ImprtMltplAs' + +## ImprtExpctFrAs +[tagimprtexpctfras]: # (imprtexpctfras) + +This error occurs when an import statement doesn't have the expected format: + + import module invalid // Causes 'ImprtExpctFrAs' + +## ExpctSymblAftrAs +[tagexpctsymblaftras]: # (expctsymblaftras) + +This error occurs when a symbol is expected after `as` in an import: + + import module as // Causes 'ExpctSymblAftrAs' + +## ExpctSymblAftrFr +[tagexpctsymblaftrfr]: # (expctsymblaftrfr) + +This error occurs when a symbol is expected after `for` in an import: + + import module for // Causes 'ExpctSymblAftrFr' + +## DctSprtr +[tagdctsprtr]: # (dctsprtr) + +This error occurs when a colon is missing in a dictionary key-value pair: + + var d = {"key" "value"} // Causes 'DctSprtr' + +## DctEntrySprtr +[tagdctentrysprtr]: # (dctentrysprtr) + +This error occurs when a comma is missing between dictionary entries: + + var d = {"a": 1 "b": 2} // Causes 'DctEntrySprtr' + +## DctTrmntr +[tagdcttrmntr]: # (dcttrmntr) + +This error occurs when a closing brace is missing in a dictionary: + + var d = {"a": 1 // Causes 'DctTrmntr' + +## SwtchSprtr +[tagswtchsprtr]: # (swtchsprtr) + +This error occurs when a colon is missing after a switch label: + + switch x { + case 1 // Causes 'SwtchSprtr' + } + +## ExpctWhl +[tagexpctwhl]: # (expctwhl) + +This error occurs when `while` is expected after a do-while loop body: + + do { + // body + } // Causes 'ExpctWhl' + +## ExpctCtch +[tagexpctctch]: # (expctctch) + +This error occurs when `catch` is expected after a `try` statement: + + try { + // code + } // Causes 'ExpctCtch' + +## ExpctHndlr +[tagexpcthndlr]: # (expcthndlr) + +This error occurs when an error handler block is expected after `catch`: + + try { + // code + } catch // Causes 'ExpctHndlr' + +## InvldLbl +[taginvldlbl]: # (invldlbl) + +This error occurs when an invalid label is used in a catch statement. + +## OneVarPr +[tagonevarpr]: # (onevarpr) + +This error occurs when a function has more than one variadic parameter (same as `MltVarPrmtr`). + +## ValRng +[tagvalrng]: # (valrng) + +This error occurs when a value is out of the expected range. + +## StrEsc +[tagstresc]: # (stresc) + +This error occurs when an unrecognized escape sequence is used in a string: + + var s = "\q" // Causes 'StrEsc' + +## RcrsnLmt +[tagrcrsnlmt]: # (rcrsnlmt) + +This error occurs when the parser recursion depth is exceeded, typically due to deeply nested expressions. + +## UnescpdCtrl +[tagunescpdctrl]: # (unescpdctrl) + +This error occurs when an unescaped control character is found in a string literal. + +## InvldUncd +[taginvlduncd]: # (invlduncd) + +This error occurs when an invalid unicode escape sequence is used in a string: + + var s = "\uZZZZ" // Causes 'InvldUncd' + +## UnrcgnzdTok +[tagunrcgnzdtok]: # (unrcgnzdtok) + +This error occurs when the parser encounters an unrecognized token. + +## UntrmComm +[taguntrmcomm]: # (untrmcomm) + +This error occurs when a multiline comment is not terminated: + + /* This comment // Causes 'UntrmComm' + +## UntrmStrng +[taguntrmstrng]: # (untrmstrng) + +This error occurs when a string literal is not terminated: + + var s = "This string // Causes 'UntrmStrng' + +## UnrgnzdTkn +[tagunrgnzdtkn]: # (unrgnzdtkn) + +This error occurs when the lexer encounters an unrecognized token. + +## MtrxBnds +[tagmtrxbnds]: # (mtrxbnds) + +This error occurs when attempting to access a matrix element with an index that is out of bounds: + + var m = Matrix([[1,2],[3,4]]) + print m[10, 10] // Causes 'MtrxBnds' + +## MtrxInvldIndx +[tagmtrxinvldindx]: # (mtrxinvldindx) + +This error occurs when matrix indices are not integers: + + var m = Matrix([[1,2],[3,4]]) + print m["x", "y"] // Causes 'MtrxInvldIndx' + +## MtrxInvldNumIndx +[tagmtrxinvldnumindx]: # (mtrxinvldnumindx) + +This error occurs when a matrix is indexed with the wrong number of indices: + + var m = Matrix([[1,2],[3,4]]) + print m[1] // Causes 'MtrxInvldNumIndx' (needs two indices) + +## MtrxCns +[tagmtrxcns]: # (mtrxcns) + +This error occurs when the Matrix constructor is called with invalid arguments. It should be called with dimensions or an array/list/matrix initializer: + + var m = Matrix("invalid") // Causes 'MtrxCns' + +## MtrxIdnttyCns +[tagmtrxidnttycns]: # (mtrxidnttycns) + +This error occurs when IdentityMatrix is called with invalid arguments. It expects a single dimension: + + var m = IdentityMatrix() // Causes 'MtrxIdnttyCns' + +## MtrxInvldInit +[tagmtrxinvldinit]: # (mtrxinvldinit) + +This error occurs when an invalid initializer is passed to the Matrix constructor: + + var m = Matrix([["invalid"]]) // Causes 'MtrxInvldInit' if incompatible + +## MtrxInvldArg +[tagmtrxinvldarg]: # (mtrxinvldarg) + +This error occurs when matrix arithmetic methods receive invalid arguments: + + var m = Matrix([[1,2],[3,4]]) + m + "string" // Causes 'MtrxInvldArg' + +## MtrxRShpArg +[tagmtrxrshparg]: # (mtrxrshparg) + +This error occurs when the reshape method is called with invalid arguments. It requires two integer arguments: + + var m = Matrix([[1,2],[3,4]]) + m.reshape("invalid") // Causes 'MtrxRShpArg' + +## MtrxIncmptbl +[tagmtrxincmptbl]: # (mtrxincmptbl) + +This error occurs when matrices have incompatible shapes for an operation. See the main documentation above for examples. + +## MtrxSnglr +[tagmtrxsnglr]: # (mtrxsnglr) + +This error occurs when attempting to invert a singular (non-invertible) matrix: + + var m = Matrix([[1,2],[2,4]]) // Singular matrix + m.inverse() // Causes 'MtrxSnglr' + +## MtrxNtSq +[tagmtrxntsq]: # (mtrxntsq) + +This error occurs when a matrix operation requires a square matrix but a non-square matrix is provided: + + var m = Matrix([[1,2,3],[4,5,6]]) // 2x3 matrix + m.inverse() // Causes 'MtrxNtSq' + +## MtrxOpFld +[tagmtrxopfld]: # (mtrxopfld) + +This error occurs when a matrix operation fails for an unspecified reason. + +## MtrxNrmArgs +[tagmtrxnrmargs]: # (mtrxnrmargs) + +This error occurs when the norm method is called with invalid arguments. It expects an optional numerical argument: + + var m = Matrix([[1,2],[3,4]]) + m.norm("invalid") // Causes 'MtrxNrmArgs' + +## MtrxStClArgs +[tagmtrxstclargs]: # (mtrxstclargs) + +This error occurs when setcolumn is called with invalid arguments. It expects an integer column index and a column matrix: + + var m = Matrix([[1,2],[3,4]]) + m.setcolumn("invalid", Matrix([1,2])) // Causes 'MtrxStClArgs' + +## LnAlgMtrxIncmptbl +[taglnalgmtrxincmptbl]: # (lnalgmtrxincmptbl) + +This error occurs when matrices have incompatible shapes in linear algebra operations. + +## LnAlgMtrxIndxBnds +[taglnalgmtrxindxbnds]: # (lnalgmtrxindxbnds) + +This error occurs when a matrix index is out of bounds in linear algebra operations. + +## LnAlgMtrxSnglr +[taglnalgmtrxsnglr]: # (lnalgmtrxsnglr) + +This error occurs when a matrix is singular in linear algebra operations. + +## LnAlgMtrxNtSq +[taglnalgmtrxntsq]: # (lnalgmtrxntsq) + +This error occurs when a matrix is not square in linear algebra operations. + +## LnAlgLapackArgs +[taglnalglapackargs]: # (lnalglapackargs) + +This error occurs when a LAPACK function is called with invalid arguments. + +## LnAlgMtrxOpFld +[taglnalgmtrxopfld]: # (lnalgmtrxopfld) + +This error occurs when a matrix operation fails in the linear algebra library. + +## LnAlgMtrxNtSpprtd +[taglnalgmtrxntspprtd]: # (lnalgmtrxntspprtd) + +This error occurs when an operation is not supported for a particular matrix type. + +## LnAlgMtrxInvldArg +[taglnalgmtrxinvldarg]: # (lnalgmtrxinvldarg) + +This error occurs when invalid arguments are passed to a matrix method in the linear algebra library. + +## LnAlgMtrxNnNmrclArg +[taglnalgmtrxnnnmrclarg]: # (lnalgmtrxnnnmrclarg) + +This error occurs when a matrix method requires numerical arguments but receives non-numerical ones. + +## LnAlgMtrxNrmArgs +[taglnalgmtrxnrmargs]: # (lnalgmtrxnrmargs) + +This error occurs when the norm method is called with an unsupported argument. It requires 1 or inf: + + var m = Matrix([[1,2],[3,4]]) + m.norm(2) // Causes 'LnAlgMtrxNrmArgs' if 2 is not supported + +## LnAlgInvldArg +[taglnalginvldarg]: # (lnalginvldarg) + +This error occurs when matrix arithmetic methods receive invalid arguments: + + var m = Matrix([[1,2],[3,4]]) + m + "string" // Causes 'LnAlgInvldArg' + +## SprsCns +[tagsprscns]: # (sprscns) + +This error occurs when the Sparse constructor is called with invalid arguments. It should be called with dimensions or an array initializer: + + var s = Sparse("invalid") // Causes 'SprsCns' + +## SprsInvldInit +[tagsprsinvldinit]: # (sprsinvldinit) + +This error occurs when an invalid initializer is passed to the Sparse constructor. + +## SprsSt +[tagsprsst]: # (sprsst) + +This error occurs when attempting to set a sparse matrix element fails. + +## SprsCnvFld +[tagsprscnvfld]: # (sprscnvfld) + +This error occurs when sparse format conversion fails. + +## SprsOpFld +[tagsprsopfld]: # (sprsopfld) + +This error occurs when a sparse matrix operation fails. + +## CmplxCns +[tagcmplxcns]: # (cmplxcns) + +This error occurs when the Complex constructor is called with invalid arguments. It should be called with two floats: + + var c = Complex(1) // Causes 'CmplxCns' + +## CmplxInvldArg +[tagcmplxinvldarg]: # (cmplxinvldarg) + +This error occurs when complex arithmetic methods receive invalid arguments: + + var c = Complex(1, 2) + c + "string" // Causes 'CmplxInvldArg' + +## CmpxArg +[tagcmpxarg]: # (cmpxarg) + +This error occurs when a complex operation receives unexpected arguments. + +## LstArgs +[taglstargs]: # (lstargs) + +This error occurs when a List is created with invalid arguments. Lists must be called with integer dimensions: + + var l = List("invalid") // Causes 'LstArgs' + +## LstNumArgs +[taglstnumargs]: # (lstnumargs) + +This error occurs when a List is indexed with more than one argument: + + var l = [1, 2, 3] + l[1, 2] // Causes 'LstNumArgs' + +## LstAddArgs +[taglstaddargs]: # (lstaddargs) + +This error occurs when the add method receives invalid arguments. It requires a list: + + var l = [1, 2, 3] + l.add("invalid") // Causes 'LstAddArgs' + +## LstSrtFn +[taglstsrtfn]: # (lstsrtfn) + +This error occurs when a list sort function doesn't return an integer: + + var l = [3, 1, 2] + l.sort(fn(a, b) { return "invalid" }) // Causes 'LstSrtFn' + +## EntryNtFnd +[tagentryntfnd]: # (entryntfnd) + +This error occurs when an entry is not found in a list: + + var l = [1, 2, 3] + l.remove(10) // Causes 'EntryNtFnd' + +## TplArgs +[tagtplargs]: # (tplargs) + +This error occurs when a Tuple is created with invalid arguments. Tuples must be called with integer dimensions: + + var t = Tuple("invalid") // Causes 'TplArgs' + +## TpmNumArgs +[tagtpmnumargs]: # (tpmnumargs) + +This error occurs when a Tuple is indexed with more than one argument: + + var t = (1, 2, 3) + t[1, 2] // Causes 'TpmNumArgs' + +## DctKyNtFnd +[tagdctkyntfnd]: # (dctkyntfnd) + +This error occurs when a key is not found in a dictionary: + + var d = {"a": 1} + print d["b"] // Causes 'DctKyNtFnd' + +## DctStArg +[tagdctstarg]: # (dctstarg) + +This error occurs when dictionary set methods (union, intersection, difference) receive invalid arguments. They expect a dictionary: + + var d1 = {"a": 1} + d1.union("invalid") // Causes 'DctStArg' + +## FlOpnFld +[tagflopnfld]: # (flopnfld) + +This error occurs when a file cannot be opened: + + var f = File("nonexistent.txt", "read") // Causes 'FlOpnFld' if file doesn't exist + +## FlNmMssng +[tagflnmssng]: # (flnmssng) + +This error occurs when a filename is missing in a File operation: + + var f = File() // Causes 'FlNmMssng' + +## FlNmArgs +[tagflnmargs]: # (flnmargs) + +This error occurs when the first argument to File is not a filename: + + var f = File(123, "read") // Causes 'FlNmArgs' + +## FlMode +[tagflmode]: # (flmode) + +This error occurs when the second argument to File is not a valid mode. It should be 'read', 'write', or 'append': + + var f = File("test.txt", "invalid") // Causes 'FlMode' + +## FlWrtArgs +[tagflwrtargs]: # (flwrtargs) + +This error occurs when File.write receives non-string arguments: + + var f = File("test.txt", "write") + f.write(123) // Causes 'FlWrtArgs' + +## FlWrtFld +[tagflwrtfld]: # (flwrtfld) + +This error occurs when writing to a file fails. + +## FldrExpctPth +[tagfldrexpctpth]: # (fldrexpctpth) + +This error occurs when folder methods receive invalid arguments. They expect a path: + + Folder.exists(123) // Causes 'FldrExpctPth' + +## NtFldr +[tagntfldr]: # (ntfldr) + +This error occurs when a path is not a folder: + + Folder.exists("file.txt") // May cause 'NtFldr' if it's a file, not a folder + +## FldrCrtFld +[tagfldrcrtfld]: # (fldrcrtfld) + +This error occurs when folder creation fails: + + Folder.create("/invalid/path") // Causes 'FldrCrtFld' + +## RngArgs +[tagrngargs]: # (rngargs) + +This error occurs when Range receives invalid arguments. It expects numerical arguments: a start, an end, and an optional stepsize: + + Range("invalid") // Causes 'RngArgs' + +## RngStpSz +[tagrngstpsz]: # (rngstpsz) + +This error occurs when a Range stepsize is too small: + + Range(0, 10, 0.0000001) // May cause 'RngStpSz' if too small + +## ExpctNmArgs +[tagexpctnmargs]: # (expctnmargs) + +This error occurs when a function expects numerical arguments but receives non-numerical ones: + + sqrt("string") // Causes 'ExpctNmArgs' + +## ExpctArgNm +[tagexpctargnm]: # (expctargnm) + +This error occurs when a function expects a single numerical argument but receives something else: + + abs() // Causes 'ExpctArgNm' + +## TypArgNm +[tagtypargnm]: # (typargnm) + +This error occurs when a function expects one argument but receives a different number: + + type() // May cause 'TypArgNm' if no arguments provided + +## MnMxArgs +[tagmnmxargs]: # (mnmxargs) + +This error occurs when min or max functions receive invalid arguments. They expect at least one numerical argument, list, or matrix: + + min() // Causes 'MnMxArgs' + +## ApplyArgs +[tagapplyargs]: # (applyargs) + +This error occurs when the apply function receives fewer than two arguments: + + apply() // Causes 'ApplyArgs' + +## ApplyNtCllble +[tagapplyntcllble]: # (applyntcllble) + +This error occurs when apply receives a non-callable object as its first argument: + + apply("not a function", [1, 2, 3]) // Causes 'ApplyNtCllble' + +## FrmtArg +[tagfrmtarg]: # (frmtarg) + +This error occurs when the format method receives invalid arguments. It requires a format string: + + "test".format(123) // Causes 'FrmtArg' if format string expected + +## InvldFrmt +[taginvldfrmt]: # (invldfrmt) + +This error occurs when an invalid format string is provided: + + "test".format("%Z") // May cause 'InvldFrmt' if %Z is invalid + +## ErrorArgs +[tagerrorargs]: # (errorargs) + +This error occurs when the Error constructor is called with invalid arguments. It must be called with a tag and a default message: + + Error("Tag") // Causes 'ErrorArgs' + +## Err +[tagerr]: # (err) + +This is a generic error tag used for general error conditions. + +## EnmrtArgs +[tagenmrtargs]: # (enmrtargs) + +This error occurs when the enumerate method receives invalid arguments. It expects a single integer argument: + + var obj = Object() + obj.enumerate("invalid") // Causes 'EnmrtArgs' + +## IndxArgs +[tagindxargs]: # (indxargs) + +This error occurs when the index method receives invalid arguments. It expects a String property name: + + var obj = Object() + obj.index(123) // Causes 'IndxArgs' + +## SetIndxArgs +[tagsetindxargs]: # (setindxargs) + +This error occurs when the setindex method receives invalid arguments. It expects an index and a value: + + var obj = Object() + obj.setindex(1) // Causes 'SetIndxArgs' (missing value) + +## RspndsToArg +[tagrspndstoarg]: # (rspndstoarg) + +This error occurs when the respondsto method receives invalid arguments. It expects a single string argument or no argument: + + var obj = Object() + obj.respondsto(123) // Causes 'RspndsToArg' + +## HasArg +[taghasarg]: # (hasarg) + +This error occurs when the has method receives invalid arguments. It expects a single string argument or no argument: + + var obj = Object() + obj.has(123) // Causes 'HasArg' + +## IsMmbrArg +[tagismmbrarg]: # (ismmbrarg) + +This error occurs when the ismember method receives invalid arguments. It expects a single argument: + + var obj = Object() + obj.ismember() // Causes 'IsMmbrArg' + +## ObjCantClone +[tagobjcantclone]: # (objcantclone) + +This error occurs when attempting to clone an object that cannot be cloned: + + var obj = Object() + obj.clone() // May cause 'ObjCantClone' if cloning not supported + +## ObjImmutable +[tagobjimmutable]: # (objimmutable) + +This error occurs when attempting to modify an immutable object: + + var obj = Object() + // If obj is immutable: + obj.property = "value" // Causes 'ObjImmutable' + +## ObjNoPrp +[tagobjnoprp]: # (objnoprp) + +This error occurs when an object does not provide properties: + + var obj = Object() + obj.property // May cause 'ObjNoPrp' if object doesn't support properties + +## InvocationArgs +[taginvocationargs]: # (invocationargs) + +This error occurs when Invocation is called with invalid arguments. It must be called with an object and a method name: + + Invocation() // Causes 'InvocationArgs' + +## SystmSlpArgs +[tagsystmslpargs]: # (systmslpargs) + +This error occurs when the sleep method receives invalid arguments. It expects a time in seconds: + + sleep("invalid") // Causes 'SystmSlpArgs' + +## SystmStWrkDr +[tagsystmstwrkdr]: # (systmstwrkdr) + +This error occurs when setting the working directory fails: + + System.setworkingdirectory("/invalid/path") // Causes 'SystmStWrkDr' + +## SystmStWrkDrArgs +[tagsystmstwrkdrargs]: # (systmstwrkdrargs) + +This error occurs when setworkingdirectory receives invalid arguments. It expects a path name: + + System.setworkingdirectory(123) // Causes 'SystmStWrkDrArgs' + +## JSONPrsArgs +[tagjsonprsargs]: # (jsonprsargs) + +This error occurs when JSON.parse receives invalid arguments. It requires a string: + + JSON.parse(123) // Causes 'JSONPrsArgs' + +## JSONObjctKey +[tagjsonobjctkey]: # (jsonobjctkey) + +This error occurs when a JSON object key is not a string: + + JSON.parse('{123: "value"}') // Causes 'JSONObjctKey' + +## JSONNmbrFrmt +[tagjsonnmbrfrmt]: # (jsonnmbrfrmt) + +This error occurs when a number in JSON is improperly formatted: + + JSON.parse('{"num": 1.2.3}') // Causes 'JSONNmbrFrmt' + +## JSONExtrnsTkn +[tagjsonextrnstkn]: # (jsonextrnstkn) + +This error occurs when there is an extraneous token after a JSON element: + + JSON.parse('{"a": 1} extra') // Causes 'JSONExtrnsTkn' + +## JSONBlnkElmnt +[tagjsonblnkelmnt]: # (jsonblnkelmnt) + +This error occurs when a blank element is found in JSON: + + JSON.parse('[,]') // Causes 'JSONBlnkElmnt' + +## MshFlNtFnd +[tagmshflntfnd]: # (mshflntfnd) + +This error occurs when a mesh file cannot be found: + + var m = Mesh("nonexistent.mesh") // Causes 'MshFlNtFnd' + +## MshArgs +[tagmshargs]: # (mshargs) + +This error occurs when Mesh receives invalid arguments. It expects either a single file name or no arguments: + + var m = Mesh(123) // Causes 'MshArgs' + +## MshVrtMtrxDim +[tagmshvrtmtrxdim]: # (mshvrtmtrxdim) + +This error occurs when vertex matrix dimensions are inconsistent with the mesh. + +## MshLdVrtDim +[tagmshldvrtdim]: # (mshldvrtdim) + +This error occurs when a vertex has inconsistent dimensions when loading a mesh file. + +## MshLdVrtCrd +[tagmshldvrtcrd]: # (mshldvrtcrd) + +This error occurs when a vertex has non-numerical coordinates when loading a mesh file. + +## MshLdPrsErr +[tagmshldprserr]: # (mshldprserr) + +This error occurs when there is a parse error in a mesh file. + +## MshLdVrtNm +[tagmshldvrtnm]: # (mshldvrtnm) + +This error occurs when an element has an incorrect number of vertices when loading a mesh file. + +## MshLdVrtId +[tagmshldvrtid]: # (mshldvrtid) + +This error occurs when a vertex id is not an integer when loading a mesh file. + +## MshLdVrtNtFnd +[tagmshldvrtntfnd]: # (mshldvrtntfnd) + +This error occurs when a vertex is not found when loading a mesh file. + +## MshStVrtPsnArgs +[tagmshstvrtpsnargs]: # (mshstvrtpsnargs) + +This error occurs when setvertexposition receives invalid arguments. It expects a vertex id and a position matrix: + + var m = Mesh() + m.setvertexposition("invalid") // Causes 'MshStVrtPsnArgs' + +## MshVrtPsnArgs +[tagmshvrtpsnargs]: # (mshvrtpsnargs) + +This error occurs when vertexposition receives invalid arguments. It expects a vertex id: + + var m = Mesh() + m.vertexposition() // Causes 'MshVrtPsnArgs' + +## MshInvldId +[tagmshinvldid]: # (mshinvldid) + +This error occurs when an invalid element id is used: + + var m = Mesh() + m.element(-1) // Causes 'MshInvldId' + +## MshCnnMtxArgs +[tagmshcnnmtxargs]: # (mshcnnmtxargs) + +This error occurs when connectivitymatrix receives invalid arguments. It expects integer arguments: + + var m = Mesh() + m.connectivitymatrix("invalid") // Causes 'MshCnnMtxArgs' + +## MshAddGrdArgs +[tagmshaddgrdargs]: # (mshaddgrdargs) + +This error occurs when addgrade receives invalid arguments. It expects either an integer grade and optionally a sparse connectivity matrix: + + var m = Mesh() + m.addgrade("invalid") // Causes 'MshAddGrdArgs' + +## MshAddGrdOutOfBnds +[tagmshaddgrdoutofbnds]: # (mshaddgrdoutofbnds) + +This error occurs when attempting to add elements of a grade that exceeds the mesh's maximum grade: + + var m = Mesh() + m.addgrade(10) // Causes 'MshAddGrdOutOfBnds' if max grade is lower + +## MshAddSymArgs +[tagmshaddsymargs]: # (mshaddsymargs) + +This error occurs when addsymmetry receives invalid arguments. It expects an object that provides a transform method and optionally a selection: + + var m = Mesh() + m.addsymmetry("invalid") // Causes 'MshAddSymArgs' + +## MshAddSymMsngTrnsfrm +[tagmshaddsymmsngtrnsfrm]: # (mshaddsymmsngtrnsfrm) + +This error occurs when addsymmetry receives an object that doesn't provide a transform method: + + var m = Mesh() + var obj = Object() + m.addsymmetry(obj) // Causes 'MshAddSymMsngTrnsfrm' + +## SlNoMsh +[tagslnomsh]: # (slnomsh) + +This error occurs when a Selection operation requires a Mesh object but doesn't receive one: + + var s = Selection("invalid") // Causes 'SlNoMsh' + +## SlIsSlArg +[tagslisslarg]: # (slisslarg) + +This error occurs when Selection.isselected receives invalid arguments. It requires a grade and element id: + + var s = Selection(mesh) + s.isselected(1) // Causes 'SlIsSlArg' (missing element id) + +## SlGrdArg +[tagslgrdarg]: # (slgrdarg) + +This error occurs when a Selection method requires a grade as an argument but doesn't receive one: + + var s = Selection(mesh) + s.method() // Causes 'SlGrdArg' if grade required + +## SlStArg +[tagslstarg]: # (slstarg) + +This error occurs when Selection set methods receive invalid arguments. They require a selection: + + var s = Selection(mesh) + s.union("invalid") // Causes 'SlStArg' + +## SlBnd +[tagslbnd]: # (slbnd) + +This error occurs when a mesh has no boundary elements: + + var m = Mesh() + m.boundary() // Causes 'SlBnd' if no boundary exists + +## FldMshArg +[tagfldmsharg]: # (fldmsharg) + +This error occurs when Field receives invalid arguments. It expects a mesh as its first argument: + + var f = Field("invalid") // Causes 'FldMshArg' + +## FldArgs +[tagfldargs]: # (fldargs) + +This error occurs when Field receives invalid optional arguments. It allows 'grade' as an optional argument. + +## FldBnds +[tagfldbnds]: # (fldbnds) + +This error occurs when a Field index is out of bounds: + + var f = Field(mesh) + f[100, 100, 100] // Causes 'FldBnds' if out of bounds + +## FldInvldIndx +[tagfldinvldindx]: # (fldinvldindx) + +This error occurs when Field indices are not numerical: + + var f = Field(mesh) + f["x", "y", "z"] // Causes 'FldInvldIndx' + +## FldInvldArg +[tagfldinvldarg]: # (fldinvldarg) + +This error occurs when Field arithmetic methods receive invalid arguments. They expect a field or number: + + var f = Field(mesh) + f + "string" // Causes 'FldInvldArg' + +## FldIncmptbl +[tagfldincmptbl]: # (fldincmptbl) + +This error occurs when fields have incompatible shapes: + + var f1 = Field(mesh1) + var f2 = Field(mesh2) + f1 + f2 // Causes 'FldIncmptbl' if shapes incompatible + +## FldIncmptblVal +[tagfldincmptblval]: # (fldincmptblval) + +This error occurs when an assignment value has an incompatible shape with field elements: + + var f = Field(mesh) + f[0, 0, 0] = Matrix([[1,2,3,4]]) // Causes 'FldIncmptblVal' if shape doesn't match + +## FldOp +[tagfldop]: # (fldop) + +This error occurs when Field.op receives invalid arguments. It requires a callable object as the first argument and fields of compatible shape as other arguments: + + var f = Field(mesh) + f.op("not callable", f) // Causes 'FldOp' + +## FldOpFn +[tagfldopfn]: # (fldopfn) + +This error occurs when Field.op cannot construct a Field from the return value of the function: + + var f = Field(mesh) + f.op(fn(x) { return "invalid" }, f) // Causes 'FldOpFn' + +## FnSpcArgs +[tagfnspcargs]: # (fnspcargs) + +This error occurs when a FunctionSpace is created with invalid arguments. It must be initialized with a label and a grade: + + FunctionSpace("invalid") // Causes 'FnSpcArgs' + +## FnSpcNtFnd +[tagfnspcntfnd]: # (fnspcntfnd) + +This error occurs when a function space cannot be found: + + FunctionSpace.find("nonexistent", 1) // Causes 'FnSpcNtFnd' + +## FnctlIntMsh +[tagfnctlintmsh]: # (fnctlintmsh) + +This error occurs when a functional's integrand method requires a mesh as an argument but doesn't receive one: + + var func = Length() + func.integrand() // Causes 'FnctlIntMsh' + +## FnctlELNtFnd +[tagfnctleltfnd]: # (fnctleltfnd) + +This error occurs when a mesh doesn't provide elements of the required grade: + + var func = Length() + func.integrand(mesh) // Causes 'FnctlELNtFnd' if mesh lacks required grade + +## FnctlArgs +[tagfnctlargs]: # (fnctlargs) + +This error occurs when invalid arguments are passed to a functional method. + +## VolEnclZero +[tagvolenclzero]: # (volenclzero) + +This error occurs when VolumeEnclosed detects an element of zero size. Check that a mesh point is not coincident with the origin: + + var func = VolumeEnclosed() + func.total(mesh) // Causes 'VolEnclZero' if element has zero size + +## LnElstctyRef +[taglnelstctyref]: # (lnelstctyref) + +This error occurs when LinearElasticity requires a mesh as an argument but doesn't receive one: + + var func = LinearElasticity() + func.total() // Causes 'LnElstctyRef' + +## LnElstctyPrp +[taglnelstctyprp]: # (lnelstctyprp) + +This error occurs when LinearElasticity is missing required properties. It requires 'reference' to be a mesh, 'grade' to be an integer, and 'poissonratio' to be a number: + + var func = LinearElasticity() + func.reference = "invalid" // Causes 'LnElstctyPrp' + +## HydrglArgs +[taghydrglargs]: # (hydrglargs) + +This error occurs when Hydrogel receives invalid arguments. It requires a reference mesh and allows 'grade', 'a', 'b', 'c', 'd', 'phi0', and 'phiref' as optional arguments. + +## HydrglPrp +[taghydrglprp]: # (hydrglprp) + +This error occurs when Hydrogel is missing required properties. It requires the first argument to be a mesh, 'grade' to be an integer, 'a', 'b', 'c', 'd', 'phiref' to be numbers, and 'phi0' to be a number or Field. + +## HydrglFldGrd +[taghydrglfldgrd]: # (hydrglfldgrd) + +This error occurs when Hydrogel is given phi0 as a Field that lacks scalar elements in the required grade. + +## HydrglZrRfVl +[taghydrglzrrfvl]: # (hydrglzrrfvl) + +This error occurs when a Hydrogel reference element has a tiny volume. This is a warning. + +## HydrglBnds +[taghydrglbnds]: # (hydrglbnds) + +This error occurs when phi is outside bounds in a Hydrogel calculation. This is a warning. + +## EquiElArgs +[tagequielargs]: # (equielargs) + +This error occurs when EquiElement receives invalid arguments. It allows 'grade' and 'weight' as optional arguments. + +## GradSqArgs +[taggradsqargs]: # (gradsqargs) + +This error occurs when GradSq receives invalid arguments. It requires a field as the argument: + + var func = GradSq() + func.total("invalid") // Causes 'GradSqArgs' + +## NmtcArgs +[tagnmtcargs]: # (nmtcargs) + +This error occurs when Nematic receives invalid arguments. It requires a field as the argument: + + var func = Nematic() + func.total("invalid") // Causes 'NmtcArgs' + +## NmtcElArgs +[tagnmtcelargs]: # (nmtcelargs) + +This error occurs when NematicElectric receives invalid arguments. It requires the director and electric field or potential as arguments (in that order). + +## SclrPtFnCllbl +[tagsclrptfncllbl]: # (sclrptfncllbl) + +This error occurs when a ScalarPotential function is not callable: + + var func = ScalarPotential() + func.function = "invalid" // Causes 'SclrPtFnCllbl' + +## IntgrlArgs +[tagintgrlargs]: # (intgrlargs) + +This error occurs when an Integral functional receives invalid arguments. It requires a callable argument followed by zero or more Fields: + + var func = LineIntegral() + func.total("invalid") // Causes 'IntgrlArgs' + +## IntgrlMthdDct +[tagintgrlmthddct]: # (intgrlmthddct) + +This error occurs when an Integral's method argument is not a Dictionary containing configuration settings: + + var func = LineIntegral() + func.method = "invalid" // Causes 'IntgrlMthdDct' + +## IntgrlFld +[tagintgrlfld]: # (intgrlfld) + +This error occurs when an Integral cannot identify a field: + + var func = LineIntegral() + func.total(fn(x) { return x }, "invalid") // Causes 'IntgrlFld' + +## IntgrlGrdEvl +[tagintgrlgrdevl]: # (intgrlgrdevl) + +This error occurs when gradient evaluation fails in an Integral: + + var func = LineIntegral() + func.gradient(mesh) // Causes 'IntgrlGrdEvl' if evaluation fails + +## IntgrlAmbgsFld +[tagintgrlambgsfld]: # (intgrlambgsfld) + +This error occurs when a field reference is ambiguous in an Integral. Call with a Field object: + + var func = LineIntegral() + func.total(fn(x) { return x }) // Causes 'IntgrlAmbgsFld' if ambiguous + +## IntgrlNFlds +[tagintgrlnflds]: # (intgrlnflds) + +This error occurs when an incorrect number of Fields is provided for an integrand function: + + var func = LineIntegral() + func.total(fn(x, y) { return x + y }, field1) // Causes 'IntgrlNFlds' if wrong number + +## IntgrlSpclFn +[tagintgrlspclfn]: # (intgrlspclfn) + +This error occurs when a special function is called outside of an Integral: + + tangent() // Causes 'IntgrlSpclFn' (must be called within integrand) + +## IntgrtrSbdvns +[tagintgrtrsbdvns]: # (intgrtrsbdvns) + +This error occurs when too many subdivisions are needed in evaluating an integral, possibly indicating a singularity: + + // Occurs during numerical integration when subdivision limit is exceeded + +## IntgrtrRlNtFnd +[tagintgrtrrlntfnd]: # (intgrtrrlntfnd) + +This error occurs when an integrator quadrature rule cannot be found: + + var method = {"rule": "nonexistent"} + // Causes 'IntgrtrRlNtFnd' when rule doesn't exist + +## IntgrtrRlUnavlb +[tagintgrtrrlunavlb]: # (intgrtrrlunavlb) + +This error occurs when no quadrature rule is available that matches the provided integrator method dictionary: + + var method = {"rule": "invalid", "degree": 100} + // Causes 'IntgrtrRlUnavlb' if no matching rule + +## IntgrtrMthdTyp +[tagintgrtrmthdtyp]: # (intgrtrmthdtyp) + +This error occurs when an integrator method dictionary option has the wrong type: + + var method = {"rule": 123} // Causes 'IntgrtrMthdTyp' if rule must be string + +## DbgSymbl +[tagdbgsymbl]: # (dbgsymbl) + +This error occurs in the debugger when a symbol cannot be found in the current context: + + // Occurs when debugging and accessing a symbol that doesn't exist + +## DbgSymblPrpty +[tagdbgsymblprpty]: # (dbgsymblprpty) + +This error occurs in the debugger when a symbol lacks a requested property: + + // Occurs when debugging and accessing a property that doesn't exist + +## DbgInvldRg +[tagdbginvldrg]: # (dbginvldrg) + +This error occurs in the debugger when an invalid register is accessed: + + // Occurs when debugging and accessing an invalid register + +## DbgInvldGlbl +[tagdbginvldglbl]: # (dbginvldglbl) + +This error occurs in the debugger when an invalid global is accessed: + + // Occurs when debugging and accessing an invalid global + +## DbgInvldInstr +[tagdbginvldinstr]: # (dbginvldinstr) + +This error occurs in the debugger when an invalid instruction is encountered: + + // Occurs when debugging and encountering an invalid instruction + +## DbgRgObj +[tagdbgrgobj]: # (dbgrgobj) + +This error occurs in the debugger when a register doesn't contain an object: + + // Occurs when debugging and expecting an object in a register + +## DbgStPrp +[tagdbgstprp]: # (dbgstprp) + +This error occurs in the debugger when attempting to set a property on an object that doesn't support it: + + // Occurs when debugging and trying to set a property diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index d880cfe3..f1f85d74 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -162,6 +162,39 @@ objecttypedefn objectbuiltinfunctiondefn = { .cmpfn=NULL }; +/* ********************************************************************** + * Signature parsing + * ********************************************************************** */ + +/** This mechanism allows builtin classes to cross-reference one another in method signature declarations */ + +typedef struct _sigparses { + const char *sig; + signature *dest; +} _sigparse; + +DECLARE_VARRAY(_sigparse, _sigparse) +DEFINE_VARRAY(_sigparse, _sigparse) + +varray__sigparse sigparseworklist; + +/** Add a signature to be parsed on the next call to builtin_parsesignatures */ +void builtin_addparsesignature(const char *sig, signature *dest) { + _sigparse s = { .sig = sig, .dest = dest }; + varray__sigparsewrite(&sigparseworklist, s); +} + +/** Parses all signatures on the worklist */ +bool builtin_parsesignatures(void) { + _sigparse s; + while (varray__sigparsepop(&sigparseworklist, &s)) { + if (!signature_parse(s.sig, s.dest)) { + return false; + } + } + return true; +} + /* ********************************************************************** * Create and find builtin functions * ********************************************************************** */ @@ -264,10 +297,7 @@ bool morpho_addfunction(char *name, char *signature, builtinfunction func, built if (!name) goto morpho_addfunction_cleanup; // Parse function signature if provided - if (signature && - !signature_parse(signature, &new->sig)) { - UNREACHABLE("Syntax error in signature definition."); - } + if (signature) builtin_addparsesignature(signature, &new->sig); value newfn = MORPHO_OBJECT(new); @@ -340,9 +370,7 @@ bool morpho_addclass(char *name, builtinclassentry desc[], int nparents, value * newmethod->klass=new; newmethod->name=object_stringfromcstring(desc[i].name, strlen(desc[i].name)); newmethod->flags=desc[i].flags; - if (desc[i].signature) { - success &= signature_parse(desc[i].signature, &newmethod->sig); - } + if (desc[i].signature) builtin_addparsesignature(desc[i].signature, &newmethod->sig); dictionary_intern(&builtin_symboltable, newmethod->name); value method = MORPHO_OBJECT(newmethod); @@ -425,10 +453,12 @@ void builtin_initialize(void) { builtin_setclasstable(&builtin_classtable); // Initialize core object types - objectstringtype=object_addtype(&objectstringdefn); objectclasstype=object_addtype(&objectclassdefn); + objectstringtype=object_addtype(&objectstringdefn); objectbuiltinfunctiontype=object_addtype(&objectbuiltinfunctiondefn); + varray__sigparseinit(&sigparseworklist); + /* Initialize builtin classes and functions */ instance_initialize(); // Must initialize first so that Object exists @@ -462,7 +492,7 @@ void builtin_initialize(void) { // Initialize linear algebra #ifdef MORPHO_INCLUDE_LINALG - matrix_initialize(); + linalg_initialize(); #endif #ifdef MORPHO_INCLUDE_SPARSE @@ -473,6 +503,10 @@ void builtin_initialize(void) { // Initialize geometry geometry_initialize(); #endif + + if (!builtin_parsesignatures()) { + UNREACHABLE("Syntax error in signature."); + } morpho_addfinalizefn(builtin_finalize); } @@ -484,6 +518,8 @@ void builtin_finalize(void) { builtin_objects=next; } + varray__sigparseclear(&sigparseworklist); + dictionary_clear(&builtin_functiontable); dictionary_clear(&builtin_classtable); dictionary_clear(&builtin_symboltable); diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h index c5d0dd49..e9c106fd 100644 --- a/src/builtin/builtin.h +++ b/src/builtin/builtin.h @@ -16,6 +16,9 @@ #include "signature.h" +/** Call to pase method and function signatures */ +bool builtin_parsesignatures(void); + /* ------------------------------------------------------- * Built in function objects * ------------------------------------------------------- */ diff --git a/src/builtin/functiondefs.c b/src/builtin/functiondefs.c index 4211d84c..4285b4c9 100644 --- a/src/builtin/functiondefs.c +++ b/src/builtin/functiondefs.c @@ -14,7 +14,7 @@ #include "common.h" #include "cmplx.h" -#include "matrix.h" +#include "linalg.h" #include "sparse.h" #include "mesh.h" @@ -324,7 +324,7 @@ value builtin_arctan(vm *v, int nargs, value *args) { morpho_runtimeerror(v, MATH_NUMARGS, "arctan"); return MORPHO_NIL; - } + } } /** Remainder */ @@ -747,7 +747,6 @@ void functiondefs_initialize(void) { /* Define errors */ morpho_defineerror(MATH_ARGS, ERROR_HALT, MATH_ARGS_MSG); morpho_defineerror(MATH_NUMARGS, ERROR_HALT, MATH_NUMARGS_MSG); - morpho_defineerror(MATH_ATANARGS, ERROR_HALT, MATH_ATANARGS_MSG); morpho_defineerror(TYPE_NUMARGS, ERROR_HALT, TYPE_NUMARGS_MSG); morpho_defineerror(MAX_ARGS, ERROR_HALT, MAX_ARGS_MSG); morpho_defineerror(APPLY_ARGS, ERROR_HALT, APPLY_ARGS_MSG); diff --git a/src/builtin/functiondefs.h b/src/builtin/functiondefs.h index df4c660e..5946b8ae 100644 --- a/src/builtin/functiondefs.h +++ b/src/builtin/functiondefs.h @@ -52,9 +52,6 @@ #define MATH_NUMARGS "ExpctArgNm" #define MATH_NUMARGS_MSG "Function '%s' expects a single numerical argument." -#define MATH_ATANARGS "AtanArgNm" -#define MATH_ATANARGS_MSG "Function 'arctan' expects either 1 or 2 numerical arguments." - #define TYPE_NUMARGS "TypArgNm" #define TYPE_NUMARGS_MSG "Function '%s' expects one argument." diff --git a/src/classes/array.c b/src/classes/array.c index a1ef3c96..832b7ad8 100644 --- a/src/classes/array.c +++ b/src/classes/array.c @@ -180,7 +180,7 @@ void array_print(vm *v, objectarray *a) { errorid array_error(objectarrayerror err) { switch (err) { case ARRAY_OUTOFBOUNDS: return VM_OUTOFBOUNDS; - case ARRAY_WRONGDIM: return VM_ARRAYWRONGDIM; + case ARRAY_WRONGDIM: return ARRAY_DIMENSION; case ARRAY_NONINTINDX: return VM_NONNUMINDX; case ARRAY_ALLOC_FAILED: return ERROR_ALLOCATIONFAILED; case ARRAY_OK: UNREACHABLE("array_error called incorrectly."); @@ -193,9 +193,9 @@ errorid array_error(objectarrayerror err) { errorid array_to_matrix_error(objectarrayerror err) { #ifdef MORPHO_INCLUDE_LINALG switch (err) { - case ARRAY_OUTOFBOUNDS: return MATRIX_INDICESOUTSIDEBOUNDS; - case ARRAY_WRONGDIM: return MATRIX_INVLDNUMINDICES; - case ARRAY_NONINTINDX: return MATRIX_INVLDINDICES; + case ARRAY_OUTOFBOUNDS: return LINALG_INDICESOUTSIDEBOUNDS; + case ARRAY_WRONGDIM: return ARRAY_DIMENSION; + case ARRAY_NONINTINDX: return ARRAY_INVLDINDICES; case ARRAY_ALLOC_FAILED: return ERROR_ALLOCATIONFAILED; case ARRAY_OK: UNREACHABLE("array_to_matrix_error called incorrectly."); } @@ -457,7 +457,7 @@ value array_constructor(vm *v, int nargs, value *args) { new = array_constructfromlist(ndim, dim, MORPHO_GETLIST(initializer)); if (!new) morpho_runtimeerror(v, ARRAY_CMPT); } else { - morpho_runtimeerror(v, ARRAY_ARGS); + morpho_runtimeerror(v, ARRAY_INIT); } // Bind the new array to the VM @@ -630,4 +630,6 @@ void array_initialize(void) { morpho_defineerror(ARRAY_ARGS, ERROR_HALT, ARRAY_ARGS_MSG); morpho_defineerror(ARRAY_INIT, ERROR_HALT, ARRAY_INIT_MSG); morpho_defineerror(ARRAY_CMPT, ERROR_HALT, ARRAY_CMPT_MSG); + morpho_defineerror(ARRAY_DIMENSION, ERROR_HALT, ARRAY_DIMENSION_MSG); + morpho_defineerror(ARRAY_INVLDINDICES, ERROR_HALT, ARRAY_INVLDINDICES_MSG); } diff --git a/src/classes/array.h b/src/classes/array.h index e0b7f97b..2f5fa950 100644 --- a/src/classes/array.h +++ b/src/classes/array.h @@ -65,6 +65,12 @@ objectarray *object_arrayfromvalueindices(unsigned int ndim, value *dim); #define ARRAY_CMPT "ArrayCmpt" #define ARRAY_CMPT_MSG "Array initializer is not compatible with the requested dimensions." +#define ARRAY_DIMENSION "ArrayDim" +#define ARRAY_DIMENSION_MSG "Incorrect number of dimensions for Array." + +#define ARRAY_INVLDINDICES "ArrayIndx" +#define ARRAY_INVLDINDICES_MSG "Array requires integers as indices." + /* ------------------------------------------------------- * Array interface * ------------------------------------------------------- */ diff --git a/src/classes/classes.h b/src/classes/classes.h index 91b2994a..75d48dec 100644 --- a/src/classes/classes.h +++ b/src/classes/classes.h @@ -35,6 +35,6 @@ //#include "system.h" #include "json.h" -#include "matrix.h" +#include "linalg.h" #endif /* classes_h */ diff --git a/src/classes/clss.c b/src/classes/clss.c index 5665617c..1eeea843 100644 --- a/src/classes/clss.c +++ b/src/classes/clss.c @@ -196,5 +196,4 @@ void class_initialize(void) { // No constructor function; classes are generated by the compiler // Class error messages - morpho_defineerror(CLASS_INVK, ERROR_HALT, CLASS_INVK_MSG); } diff --git a/src/classes/clss.h b/src/classes/clss.h index 6b7b1a26..2d61f271 100644 --- a/src/classes/clss.h +++ b/src/classes/clss.h @@ -46,9 +46,6 @@ typedef struct sobjectclass { * Class error messages * ------------------------------------------------------- */ -#define CLASS_INVK "ClssInvk" -#define CLASS_INVK_MSG "Cannot invoke method '%s' on a class." - /* ------------------------------------------------------- * Class interface * ------------------------------------------------------- */ diff --git a/src/classes/invocation.c b/src/classes/invocation.c index a39ad6ed..204adc56 100644 --- a/src/classes/invocation.c +++ b/src/classes/invocation.c @@ -156,5 +156,4 @@ void invocation_initialize(void) { // Invocation error messages morpho_defineerror(INVOCATION_ARGS, ERROR_HALT, INVOCATION_ARGS_MSG); - morpho_defineerror(INVOCATION_METHOD, ERROR_HALT, INVOCATION_METHOD_MSG); } diff --git a/src/classes/invocation.h b/src/classes/invocation.h index 6f2b6aa8..9f11184a 100644 --- a/src/classes/invocation.h +++ b/src/classes/invocation.h @@ -52,9 +52,6 @@ objectinvocation *object_newinvocation(value receiver, value method); #define INVOCATION_ARGS "InvocationArgs" #define INVOCATION_ARGS_MSG "Invocation must be called with an object and a method name as arguments." -#define INVOCATION_METHOD "InvocationMethod" -#define INVOCATION_METHOD_MSG "Method not found." - /* ------------------------------------------------------- * Invocation interface * ------------------------------------------------------- */ diff --git a/src/classes/list.c b/src/classes/list.c index e5558eaf..5569e0a5 100644 --- a/src/classes/list.c +++ b/src/classes/list.c @@ -130,35 +130,45 @@ int list_sortfunction(const void *a, const void *b) { } /** Sort the contents of a list */ -void list_sort(objectlist *list) { - qsort(list->val.data, list->val.count, sizeof(value), list_sortfunction); +void list_sortcontents(value *values, size_t count) { + qsort(values, count, sizeof(value), list_sortfunction); } -static vm *list_sortwithfn_vm; -static value list_sortwithfn_fn; -static bool list_sortwithfn_err; - -/** Sort function for list_sort */ -int list_sortfunctionwfn(const void *a, const void *b) { - value args[2] = {*(value *) a, *(value *) b}; - value ret; +/** Sort the contents of a list */ +void list_sort(objectlist *list) { + list_sortcontents(list->val.data, list->val.count); +} - if (morpho_call(list_sortwithfn_vm, list_sortwithfn_fn, 2, args, &ret)) { +/** Sort the contents of a list using a re-entrant comparison function */ +typedef struct { + vm *v; + value cmpfn; + bool errq; +} _sortfninfo; + +static int _sortfn(const void *a, const void *b, void *context) { + _sortfninfo *info = (_sortfninfo *) context; + value ret, args[2] = {*(value *) a, *(value *) b}; + + if (morpho_call(info->v, info->cmpfn, 2, args, &ret)) { if (MORPHO_ISINTEGER(ret)) return MORPHO_GETINTEGERVALUE(ret); if (MORPHO_ISFLOAT(ret)) return morpho_comparevalue(MORPHO_FLOAT(0), ret); } - - list_sortwithfn_err=true; + + info->errq=true; return 0; } +/** Sort a list of values */ +bool list_sortcontentswithfn(vm *v, value cmpfn, value *values, size_t count) { + _sortfninfo info = { .v = v, .cmpfn = cmpfn, .errq=false }; + platform_qsort_r(values, count, sizeof(value), &info, _sortfn); + return !info.errq; +} + /** Sort the contents of a list */ -bool list_sortwithfn(vm *v, value fn, objectlist *list) { - list_sortwithfn_vm=v; - list_sortwithfn_fn=fn; - list_sortwithfn_err=false; - qsort(list->val.data, list->val.count, sizeof(value), list_sortfunctionwfn); - return !list_sortwithfn_err; +bool list_sortwithfn(vm *v, value cmpfn, objectlist *list) { + return list_sortcontentswithfn(v, cmpfn, list->val.data, list->val.count); } /** Sort function for list_order */ @@ -612,21 +622,6 @@ value List_roll(vm *v, int nargs, value *args) { return out; } -/** Sorts a list */ -value XList_sort(vm *v, int nargs, value *args) { - objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args)); - - if (nargs==0) { - list_sort(slf); - } else if (nargs==1 && MORPHO_ISCALLABLE(MORPHO_GETARG(args, 0))) { - if (!list_sortwithfn(v, MORPHO_GETARG(args, 0), slf)) { - morpho_runtimeerror(v, LIST_SRTFN); - } - } - - return MORPHO_NIL; -} - /** Sorts a list */ value List_sort(vm *v, int nargs, value *args) { list_sort(MORPHO_GETLIST(MORPHO_SELF(args))); diff --git a/src/classes/list.h b/src/classes/list.h index ba7feb88..f0b0a521 100644 --- a/src/classes/list.h +++ b/src/classes/list.h @@ -77,7 +77,11 @@ bool list_resize(objectlist *list, int size); void list_append(objectlist *list, value v); unsigned int list_length(objectlist *list); bool list_getelement(objectlist *list, int i, value *out); +void list_sortcontents(value *values, size_t count); void list_sort(objectlist *list); +bool list_sortcontentswithfn(vm *v, value cmpfn, value *values, size_t count); +bool list_sortwithfn(vm *v, value cmpfn, objectlist *list); + objectlist *list_clone(objectlist *list); void list_initialize(void); diff --git a/src/classes/tuple.c b/src/classes/tuple.c index a73ec2d2..dd256f56 100644 --- a/src/classes/tuple.c +++ b/src/classes/tuple.c @@ -257,6 +257,33 @@ value Tuple_join(vm *v, int nargs, value *args) { return out; } +/** Sorts the contents of a tuple, returning a new tuple */ +value Tuple_sort(vm *v, int nargs, value *args) { + objecttuple *src = MORPHO_GETTUPLE(MORPHO_SELF(args)); + + objecttuple *new = object_newtuple(src->length, src->tuple); + if (new) list_sortcontents(new->tuple, new->length); + + return morpho_wrapandbind(v, (object *) new); +} + +/** Sorts the contents of a tuple using a comparison function, returning a new tuple */ +value Tuple_sort_fn(vm *v, int nargs, value *args) { + objecttuple *src = MORPHO_GETTUPLE(MORPHO_SELF(args)); + + objecttuple *new=object_newtuple(src->length, src->tuple); + if (new) { + bool success=list_sortcontentswithfn(v, MORPHO_GETARG(args, 0), new->tuple, new->length); + if (!success) { + morpho_runtimeerror(v, LIST_SRTFN); + object_free((object *) new); + return MORPHO_NIL; + } + } + + return morpho_wrapandbind(v, (object *) new); +} + /** Tests if a tuple has a value as a member */ value Tuple_ismember(vm *v, int nargs, value *args) { objecttuple *slf = MORPHO_GETTUPLE(MORPHO_SELF(args)); @@ -276,6 +303,8 @@ MORPHO_METHOD(MORPHO_GETINDEX_METHOD, Tuple_getindex, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(MORPHO_SETINDEX_METHOD, Tuple_setindex, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(MORPHO_ENUMERATE_METHOD, Tuple_enumerate, BUILTIN_FLAGSEMPTY), MORPHO_METHOD_SIGNATURE(MORPHO_JOIN_METHOD, "Tuple (_)", Tuple_join, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(LIST_SORT_METHOD, "Tuple ()", Tuple_sort, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(LIST_SORT_METHOD, "Tuple (_)", Tuple_sort_fn, BUILTIN_FLAGSEMPTY), MORPHO_METHOD_SIGNATURE(LIST_ISMEMBER_METHOD, "Bool (_)", Tuple_ismember, BUILTIN_FLAGSEMPTY), MORPHO_METHOD_SIGNATURE(MORPHO_CONTAINS_METHOD, "Bool (_)", Tuple_ismember, BUILTIN_FLAGSEMPTY) MORPHO_ENDCLASS diff --git a/src/core/compile.c b/src/core/compile.c index 3c5ef456..55aa5649 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -3380,7 +3380,7 @@ bool _extracttype(compiler *c, syntaxtreenode *node, value *out) { } if (!compiler_findclasswithnamespace(c, typenode, nsnode->content, labelnode->content, &type)) { - compiler_error(c, typenode, COMPILE_SYMBOLNOTDEFINEDNMSPC, MORPHO_GETCSTRING(nsnode->content), MORPHO_GETCSTRING(labelnode->content)); + compiler_error(c, typenode, COMPILE_UNKNWNTYPENMSPC, MORPHO_GETCSTRING(labelnode->content), MORPHO_GETCSTRING(nsnode->content)); return false; } @@ -4990,7 +4990,6 @@ void compile_initialize(void) { morpho_defineerror(COMPILE_CLASSINHERITSELF, ERROR_COMPILE, COMPILE_CLASSINHERITSELF_MSG); morpho_defineerror(COMPILE_TOOMANYARGS, ERROR_COMPILE, COMPILE_TOOMANYARGS_MSG); morpho_defineerror(COMPILE_TOOMANYPARAMS, ERROR_COMPILE, COMPILE_TOOMANYPARAMS_MSG); - morpho_defineerror(COMPILE_ISOLATEDSUPER, ERROR_COMPILE, COMPILE_ISOLATEDSUPER_MSG); morpho_defineerror(COMPILE_VARALREADYDECLARED, ERROR_COMPILE, COMPILE_VARALREADYDECLARED_MSG); morpho_defineerror(COMPILE_FILENOTFOUND, ERROR_COMPILE, COMPILE_FILENOTFOUND_MSG); morpho_defineerror(COMPILE_MODULENOTFOUND, ERROR_COMPILE, COMPILE_MODULENOTFOUND_MSG); diff --git a/src/core/compile.h b/src/core/compile.h index a42781b8..ad945ddb 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -60,9 +60,6 @@ #define COMPILE_TOOMANYPARAMS "TooMnyPrm" #define COMPILE_TOOMANYPARAMS_MSG "Too many parameters." -#define COMPILE_ISOLATEDSUPER "IsoSpr" -#define COMPILE_ISOLATEDSUPER_MSG "Expect '.' after 'super'." - #define COMPILE_VARALREADYDECLARED "VblDcl" #define COMPILE_VARALREADYDECLARED_MSG "Variable with this name already declared in this scope." diff --git a/src/core/core.h b/src/core/core.h index 54e0726d..0a542ad2 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -10,7 +10,13 @@ #include #include +/** Forward declarations of key types */ +struct sprogram; +struct scompiler; + typedef struct svm vm; +typedef struct sprogram program; +typedef struct scompiler compiler; #include "error.h" #include "random.h" diff --git a/src/core/vm.c b/src/core/vm.c index d15d89c6..22ae04e5 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1770,7 +1770,7 @@ value morpho_wrapandbind(vm *v, object *obj) { if (obj) { out=MORPHO_OBJECT(obj); morpho_bindobjects(v, 1, &out); - } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); + } else if (!morpho_checkerror(&v->err)) morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return out; } @@ -2183,7 +2183,6 @@ void morpho_initialize(void) { #endif morpho_defineerror(ERROR_ALLOCATIONFAILED, ERROR_EXIT, ERROR_ALLOCATIONFAILED_MSG); - morpho_defineerror(ERROR_INTERNALERROR, ERROR_EXIT, ERROR_INTERNALERROR_MSG); morpho_defineerror(ERROR_ERROR, ERROR_HALT, ERROR_ERROR_MSG); morpho_defineerror(VM_STCKOVFLW, ERROR_HALT, VM_STCKOVFLW_MSG); @@ -2205,9 +2204,7 @@ void morpho_initialize(void) { morpho_defineerror(VM_NOTINDEXABLE, ERROR_HALT, VM_NOTINDEXABLE_MSG); morpho_defineerror(VM_OUTOFBOUNDS, ERROR_HALT, VM_OUTOFBOUNDS_MSG); morpho_defineerror(VM_NONNUMINDX, ERROR_HALT, VM_NONNUMINDX_MSG); - morpho_defineerror(VM_ARRAYWRONGDIM, ERROR_HALT, VM_ARRAYWRONGDIM_MSG); morpho_defineerror(VM_DVZR, ERROR_HALT, VM_DVZR_MSG); - morpho_defineerror(VM_GETINDEXARGS, ERROR_HALT, VM_GETINDEXARGS_MSG); morpho_defineerror(VM_MLTPLDSPTCHFLD, ERROR_HALT, VM_MLTPLDSPTCHFLD_MSG); morpho_defineerror(VM_TYPECHK, ERROR_HALT, VM_TYPECHK_MSG); diff --git a/src/datastructures/error.h b/src/datastructures/error.h index 97f0e655..085fdb86 100644 --- a/src/datastructures/error.h +++ b/src/datastructures/error.h @@ -112,9 +112,6 @@ void morpho_unreachable(const char *explanation); #define ERROR_ALLOCATIONFAILED "Alloc" #define ERROR_ALLOCATIONFAILED_MSG "Memory allocation failed." -#define ERROR_INTERNALERROR "Intrnl" -#define ERROR_INTERNALERROR_MSG "Internal error (contact developer)." - #define ERROR_ERROR "Err" #define ERROR_ERROR_MSG "Error." @@ -182,18 +179,12 @@ void morpho_unreachable(const char *explanation); #define VM_NONNUMINDX "NonNmIndx" #define VM_NONNUMINDX_MSG "Non-numerical array index." -#define VM_ARRAYWRONGDIM "ArrayDim" -#define VM_ARRAYWRONGDIM_MSG "Incorrect number of dimensions for array." - #define VM_DBGQUIT "DbgQuit" #define VM_DBGQUIT_MSG "Program terminated by user in debugger." #define VM_DVZR "DvZr" #define VM_DVZR_MSG "Division by zero." -#define VM_GETINDEXARGS "NonintIndex" -#define VM_GETINDEXARGS_MSG "Noninteger array index." - #define VM_MLTPLDSPTCHFLD "MltplDsptchFld" #define VM_MLTPLDSPTCHFLD_MSG "Multiple dispatch could not find an implementation that matches these arguments." diff --git a/src/datastructures/program.h b/src/datastructures/program.h index 00a16644..0ee32b04 100644 --- a/src/datastructures/program.h +++ b/src/datastructures/program.h @@ -44,7 +44,7 @@ DECLARE_VARRAY(globalinfo, globalinfo) * ------------------------------------------------------- */ /** @brief Morpho code program and associated data */ -typedef struct { +typedef struct sprogram { varray_instruction code; /** Compiled instructions */ varray_debugannotation annotations; /** Information about how the code connects to the source */ objectfunction *global; /** Pseudofunction containing global data */ diff --git a/src/datastructures/signature.c b/src/datastructures/signature.c index c83248c0..ca12b020 100644 --- a/src/datastructures/signature.c +++ b/src/datastructures/signature.c @@ -106,7 +106,7 @@ tokendefn sigtokens[] = { }; /** @brief Initializes a lexer for parsing signatures */ -void signature_initializelexer(lexer *l, char *signature) { +void signature_initializelexer(lexer *l, const char *signature) { lex_init(l, signature, 0); lex_settokendefns(l, sigtokens); lex_seteof(l, SIGNATURE_EOF); @@ -178,7 +178,7 @@ bool signature_parsesignature(parser *p, void *out) { } /** Parses a signature */ -bool signature_parse(char *sig, signature *out) { +bool signature_parse(const char *sig, signature *out) { error err; error_init(&err); diff --git a/src/datastructures/signature.h b/src/datastructures/signature.h index 028ca4d1..18ddc650 100644 --- a/src/datastructures/signature.h +++ b/src/datastructures/signature.h @@ -31,7 +31,7 @@ value signature_getreturntype(signature *s); int signature_countparams(signature *s); void signature_set(signature *s, int nparam, value *types); -bool signature_parse(char *sig, signature *out); +bool signature_parse(const char *sig, signature *out); void signature_print(signature *s); diff --git a/src/datastructures/value.h b/src/datastructures/value.h index 29dc1abd..b466fa31 100644 --- a/src/datastructures/value.h +++ b/src/datastructures/value.h @@ -9,6 +9,7 @@ #include #include +#include #include #include "build.h" @@ -23,10 +24,10 @@ typedef struct sobject object; /** Values are the basic data type in morpho: each variable declared with 'var' corresponds to one value. Values can contain the following types: - VALUE_NIL - nil - VALUE_INTEGER - 32 bit integer - VALUE_DOUBLE - - VALUE_BOOL - boolean type + VALUE_NIL - nil + VALUE_INTEGER - 32 bit integer + VALUE_DOUBLE - + VALUE_BOOL - boolean type VALUE_OBJECT - pointer to an object The implementation of a value is intentionally opaque and can be NAN boxed into a 64-bit double or left as a struct. This file therefore defines several kinds of macro to: @@ -41,92 +42,94 @@ typedef struct sobject object; typedef uint64_t value; /** Define macros that enable us to refer to various bits */ -#define SIGN_BIT ((uint64_t) 0x8000000000000000) -#define QNAN ((uint64_t) 0x7ffc000000000000) -#define LOWER_WORD ((uint64_t) 0x00000000ffffffff) +#define QNAN ((uint64_t) 0x7ff8000000000000ull) +#define LOWER_WORD ((uint64_t) 0x00000000ffffffffull) -/** Store the type in bits 47-49 */ -#define TAG_NIL (1ull<<47) // 001 -#define TAG_BOOL (2ull<<47) // 010 -#define TAG_INT (3ull<<47) // 011 -#define TAG_OBJ SIGN_BIT +/** Store the type in bits 48-50 */ +#define TAG_SHIFT 48 +#define TAG_MASK ((uint64_t) (0x7ull << TAG_SHIFT)) // bits 48..50 +#define PAYLOAD_MASK ((uint64_t) 0x0000ffffffffffffull) // bits 0..47 +#define EXP_MASK ((uint64_t) 0x7ff0000000000000ull) // Exponent bits + +#define TAG_NIL ((uint64_t) 1ull << TAG_SHIFT) +#define TAG_BOOL ((uint64_t) 2ull << TAG_SHIFT) +#define TAG_INT ((uint64_t) 3ull << TAG_SHIFT) +#define TAG_OBJ ((uint64_t) 4ull << TAG_SHIFT) + +/** Manipulations */ +#define MORPHO_EXPALLONES(v) ((((uint64_t)(v)) & EXP_MASK) == EXP_MASK) +#define MORPHO_TAGBITS(v) (((uint64_t)(v)) & TAG_MASK) /** Bool values are stored in the lowest bit */ #define TAG_TRUE 1 #define TAG_FALSE 0 -/** Bit mask used to select type bits */ -#define TYPE_BITS (TAG_OBJ | TAG_NIL | TAG_BOOL | TAG_INT) - /** Map VALUE_XXX macros to type bits */ #define VALUE_NIL (TAG_NIL) #define VALUE_INTEGER (TAG_INT) -#define VALUE_DOUBLE () +#define VALUE_DOUBLE ((uint64_t) 0ull) #define VALUE_BOOL (TAG_BOOL) #define VALUE_OBJECT (TAG_OBJ) /** Get the type from a value */ -#define MORPHO_GETTYPE(x) ((x) & TYPE_BITS) - -/** Union to enable conversion of a double to a 64 bit integer */ -typedef union { - uint64_t bits; - double num; -} doubleunion; +#define MORPHO_GETTYPE(x) (MORPHO_ISBOXED(x) ? MORPHO_TAGBITS(x) : VALUE_DOUBLE) -/** Converts a double to a value by type punning */ +/** Converts a double to a value */ static inline value doubletovalue(double num) { - doubleunion data; - data.num = num; - return data.bits; + value bits; + memcpy(&bits, &num, sizeof(bits)); + // If this is NaN or Inf (exp all ones), force tag bits to 0 so it is a genuine float NaN/Inf + if ((bits & EXP_MASK) == EXP_MASK) { + bits &= ~TAG_MASK; + } + return bits; } -/** Converts a value to a double by type punning */ +/** Converts a value to a double */ static inline double valuetodouble(value v) { - doubleunion data; - data.bits = v; - return data.num; + double num; + memcpy(&num, &v, sizeof(num)); + return num; } /** Create a literal */ -#define MORPHO_NIL ((value) (uint64_t) (QNAN | TAG_NIL)) -#define MORPHO_TRUE ((value) (uint64_t) (QNAN | TAG_BOOL | TAG_TRUE)) -#define MORPHO_FALSE ((value) (uint64_t) (QNAN | TAG_BOOL | TAG_FALSE)) +#define MORPHO_NIL ((value) (QNAN | TAG_NIL)) +#define MORPHO_TRUE ((value) (QNAN | TAG_BOOL | TAG_TRUE)) +#define MORPHO_FALSE ((value) (QNAN | TAG_BOOL | TAG_FALSE)) -#define MORPHO_INTEGER(x) ((((uint64_t) (x)) & LOWER_WORD) | QNAN | TAG_INT) +#define MORPHO_BOOL(x) ((x) ? MORPHO_TRUE : MORPHO_FALSE) +#define MORPHO_INTEGER(x) ((value) (QNAN | TAG_INT | (((uint64_t)(x)) & LOWER_WORD))) #define MORPHO_FLOAT(x) doubletovalue(x) -#define MORPHO_BOOL(x) ((x) ? MORPHO_TRUE : MORPHO_FALSE) -#define MORPHO_OBJECT(x) ((value) (TAG_OBJ | QNAN | (uint64_t)(uintptr_t)(x))) +#define MORPHO_OBJECT(x) ((value) (QNAN | TAG_OBJ | (((uint64_t)(uintptr_t)(x)) & PAYLOAD_MASK))) /** Test for the type of a value */ -#define MORPHO_ISNIL(v) ((v) == MORPHO_NIL) -#define MORPHO_ISINTEGER(v) (((v) & (QNAN | TYPE_BITS)) == (QNAN | TAG_INT)) -#define MORPHO_ISFLOAT(v) (((v) & QNAN) != QNAN) -#define MORPHO_ISBOOL(v) (((v) & (QNAN | TYPE_BITS)) == (QNAN | TAG_BOOL)) -#define MORPHO_ISOBJECT(v) \ - (((v) & (QNAN | TYPE_BITS))== (QNAN | TAG_OBJ)) +#define MORPHO_ISNIL(v) ((v) == MORPHO_NIL) +#define MORPHO_ISBOXED(v) (MORPHO_EXPALLONES(v) && (MORPHO_TAGBITS(v) != 0)) +#define MORPHO_ISINTEGER(v) (MORPHO_ISBOXED(v) && (MORPHO_TAGBITS(v) == TAG_INT)) +#define MORPHO_ISBOOL(v) (MORPHO_ISBOXED(v) && (MORPHO_TAGBITS(v) == TAG_BOOL)) +#define MORPHO_ISOBJECT(v) (MORPHO_ISBOXED(v) && (MORPHO_TAGBITS(v) == TAG_OBJ)) +#define MORPHO_ISFLOAT(v) (!MORPHO_ISBOXED(v)) /** Get a value */ -#define MORPHO_GETINTEGERVALUE(v) ((int) ((uint32_t) (v & LOWER_WORD))) +#define MORPHO_GETPAYLOAD(v) (((uint64_t)(v)) & PAYLOAD_MASK) +#define MORPHO_GETINTEGERVALUE(v) ((int32_t) ((uint32_t)((uint64_t)(v) & LOWER_WORD))) #define MORPHO_GETFLOATVALUE(v) valuetodouble(v) #define MORPHO_GETBOOLVALUE(v) ((v) == MORPHO_TRUE) -#define MORPHO_GETOBJECT(v) ((object *) (uintptr_t) ((v) & ~(TAG_OBJ | QNAN))) +#define MORPHO_GETOBJECT(v) ((object *)(uintptr_t)(((uint64_t)(v)) & PAYLOAD_MASK)) static inline bool morpho_ofsametype(value a, value b) { - if (MORPHO_ISFLOAT(a) || MORPHO_ISFLOAT(b)) { - return MORPHO_ISFLOAT(a) && MORPHO_ISFLOAT(b); - } else { - if ((a & TYPE_BITS)==(b & TYPE_BITS)) { - return true; - } - } + bool af = MORPHO_ISFLOAT(a); + bool bf = MORPHO_ISFLOAT(b); + + if (af || bf) return (af && bf); - return false; + /* both are boxed: compare tag field only */ + return ((a & TAG_MASK) == (b & TAG_MASK)); } /** Get a non-object's type field as an integer */ static inline int _getorderedtype(value x) { - return (MORPHO_ISFLOAT(x) ? 0 : (((x) & TYPE_BITS)>>47) & 0x7); + return MORPHO_ISFLOAT(x) ? 0 : (int)(((uint64_t)x & TAG_MASK) >> TAG_SHIFT); } #define MORPHO_GETORDEREDTYPE(x) _getorderedtype(x) diff --git a/src/geometry/field.c b/src/geometry/field.c index e5931648..6f6927fd 100644 --- a/src/geometry/field.c +++ b/src/geometry/field.c @@ -11,7 +11,7 @@ #include "morpho.h" #include "classes.h" #include "common.h" -#include "matrix.h" +#include "linalg.h" #include "sparse.h" #include "geometry.h" @@ -153,6 +153,8 @@ objectfield *object_newfield(objectmesh *mesh, value prototype, value fnspc, uns object_init(&new->data.obj, OBJECT_MATRIX); new->data.ncols=1; new->data.nrows=size; + new->data.nvals=1; + new->data.nels=new->data.ncols*new->data.nrows*new->data.nvals; new->data.elements=new->data.matrixdata; if (MORPHO_ISMATRIX(prototype)) { @@ -310,6 +312,8 @@ bool field_addpool(objectfield *f) { m[i].elements=f->data.elements+i*f->psize; m[i].ncols=prototype->ncols; m[i].nrows=prototype->nrows; + m[i].nvals=prototype->nvals; + m[i].nels=m[i].ncols*m[i].nrows*m[i].nvals; } } return true; @@ -485,21 +489,23 @@ unsigned int field_dofforgrade(objectfield *f, grade g) { /** Adds two fields together */ bool field_add(objectfield *left, objectfield *right, objectfield *out) { - return (matrix_add(&left->data, &right->data, &out->data)==MATRIX_OK); + return (matrix_copy(&left->data, &out->data)==LINALGERR_OK && + matrix_axpy(1.0, &right->data, &out->data)==LINALGERR_OK); } /** Subtracts one field from another */ bool field_sub(objectfield *left, objectfield *right, objectfield *out) { - return (matrix_sub(&left->data, &right->data, &out->data)==MATRIX_OK); + return (matrix_copy(&left->data, &out->data)==LINALGERR_OK && + matrix_axpy(-1.0, &right->data, &out->data)==LINALGERR_OK); } /** Accumulate, i.e. a <- a + lambda*b */ bool field_accumulate(objectfield *left, double lambda, objectfield *right) { - return (matrix_accumulate(&left->data, lambda, &right->data)==MATRIX_OK); + return (matrix_axpy(lambda, &right->data, &left->data)==LINALGERR_OK); } bool field_inner(objectfield *left, objectfield *right, double *out) { - return (matrix_inner(&left->data, &right->data, out)==MATRIX_OK); + return (matrix_inner(&left->data, &right->data, out)==LINALGERR_OK); } /** Calls a function fn on every element of a field, optionally with other fields as arguments */ @@ -685,7 +691,7 @@ value Field_assign(vm *v, int nargs, value *args) { } else if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); - if (matrix_copy(b, &a->data)!=MATRIX_OK) morpho_runtimeerror(v, FIELD_INCOMPATIBLEMATRICES); + if (matrix_copy(b, &a->data)!=LINALGERR_OK) morpho_runtimeerror(v, FIELD_INCOMPATIBLEMATRICES); } else morpho_runtimeerror(v, FIELD_ARITHARGS); return MORPHO_NIL; @@ -726,7 +732,7 @@ value Field_addr(vm *v, int nargs, value *args) { if (i==0) { out=MORPHO_SELF(args); } else UNREACHABLE("Right addition to non-zero value."); - } else morpho_runtimeerror(v, MATRIX_ARITHARGS); + } else morpho_runtimeerror(v, LINALG_INVLDARGS); return out; } @@ -808,7 +814,7 @@ value Field_mul(vm *v, int nargs, value *args) { morpho_bindobjects(v, 1, &out); } } - } else morpho_runtimeerror(v, MATRIX_ARITHARGS); + } else morpho_runtimeerror(v, LINALG_INVLDARGS); return out; } @@ -943,7 +949,7 @@ value Field_linearize(vm *v, int nargs, value *args) { objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args)); value out = MORPHO_NIL; - objectmatrix *m=object_clonematrix(&f->data); + objectmatrix *m=matrix_clone(&f->data); if (m) { out = MORPHO_OBJECT(m); morpho_bindobjects(v, 1, &out); diff --git a/src/geometry/field.h b/src/geometry/field.h index 93ee56f0..47a868e6 100644 --- a/src/geometry/field.h +++ b/src/geometry/field.h @@ -12,7 +12,7 @@ #include "object.h" #include "mesh.h" -#include "matrix.h" +#include "linalg.h" #include /* ------------------------------------------------------- diff --git a/src/geometry/functional.c b/src/geometry/functional.c index ab571011..c9e6888f 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -17,7 +17,7 @@ #include "threadpool.h" -#include "matrix.h" +#include "linalg.h" #include "sparse.h" #include "geometry.h" @@ -154,12 +154,12 @@ bool functional_symmetrysumforces(objectmesh *mesh, objectmatrix *frc) { double *fi, *fj, fsum[mesh->dim]; while (sparsedok_loop(&s->dok, &ctr, &i, &j)) { - if (matrix_getcolumn(frc, i, &fi) && - matrix_getcolumn(frc, j, &fj)) { + if (matrix_getcolumnptr(frc, i, &fi)==LINALGERR_OK && + matrix_getcolumnptr(frc, j, &fj)==LINALGERR_OK) { for (unsigned int k=0; kdim; k++) fsum[k]=fi[k]+fj[k]; - matrix_setcolumn(frc, i, fsum); - matrix_setcolumn(frc, j, fsum); + if (matrix_setcolumnptr(frc, i, fsum)!=LINALGERR_OK) return false; + if (matrix_setcolumnptr(frc, j, fsum)!=LINALGERR_OK) return false; } } } @@ -279,7 +279,7 @@ bool functional_mapintegrandX(vm *v, functional_mapinfo *info, value *out) { /* Create the output matrix */ if (n>0) { - new=object_newmatrix(1, n, true); + new=matrix_new(1, n, true); if (!new) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; } } @@ -357,7 +357,7 @@ bool functional_mapgradientX(vm *v, functional_mapinfo *info, value *out) { /* Create the output matrix */ if (n>0) { - frc=object_newmatrix(mesh->vert->nrows, mesh->vert->ncols, true); + frc=matrix_new(mesh->vert->nrows, mesh->vert->ncols, true); if (!frc) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; } } @@ -566,7 +566,7 @@ bool functional_mapnumericalgradientX(vm *v, functional_mapinfo *info, value *ou /* Create the output matrix */ if (n>0) { - frc=object_newmatrix(mesh->vert->nrows, mesh->vert->ncols, true); + frc=matrix_new(mesh->vert->nrows, mesh->vert->ncols, true); if (!frc) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; } } @@ -1047,7 +1047,7 @@ bool functional_mapintegrand(vm *v, functional_mapinfo *info, value *out) { /* Create output matrix */ if (task[0].nel>0) { - new=object_newmatrix(1, task[0].nel, true); + new=matrix_new(1, task[0].nel, true); if (!new) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; } } @@ -1093,7 +1093,7 @@ bool functional_mapgradient(vm *v, functional_mapinfo *info, value *out) { /* Create output matrix */ for (int i=0; imesh->vert->nrows, info->mesh->vert->ncols, true); + new[i]=matrix_new(info->mesh->vert->nrows, info->mesh->vert->ncols, true); if (!new[i]) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_mapgradient_cleanup; } task[i].mapfn=(functional_mapfn *) info->grad; @@ -1103,7 +1103,7 @@ bool functional_mapgradient(vm *v, functional_mapinfo *info, value *out) { functional_parallelmap(ntask, task); /* Then add up all the matrices */ - for (int i=1; isym==SYMMETRY_ADD) functional_symmetrysumforces(info->mesh, new[0]); @@ -1195,12 +1195,12 @@ bool functional_mapnumericalgradient(vm *v, functional_mapinfo *info, value *out for (int i=0; imesh->vert->nrows, info->mesh->vert->ncols, true); + new[i]=matrix_new(info->mesh->vert->nrows, info->mesh->vert->ncols, true); if (!new[i]) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_mapgradient_cleanup; } // Clone the vertex matrix for each thread meshclones[i]=*info->mesh; - meshclones[i].vert=object_clonematrix(info->mesh->vert); + meshclones[i].vert=matrix_clone(info->mesh->vert); task[i].mesh=&meshclones[i]; task[i].ref=(void *) info; // Use this to pass the info structure @@ -1211,7 +1211,7 @@ bool functional_mapnumericalgradient(vm *v, functional_mapinfo *info, value *out functional_parallelmap(ntask, task); /* Then add up all the matrices */ - for (int i=1; idata, &new[1]->data, &new[0]->data); + for (int i=1; idata, &new[0]->data); // TODO: Use symmetry actions //if (info->sym==SYMMETRY_ADD) functional_symmetrysumforces(info->mesh, new[0]); @@ -1394,7 +1394,7 @@ bool functional_mapnumericalfieldgradient(vm *v, functional_mapinfo *info, value functional_parallelmap(ntask, task); /* Then add up all the fields */ - for (int i=1; idata, &new[i]->data, &new[0]->data); + for (int i=1; idata, &new[0]->data); success=true; @@ -1561,7 +1561,7 @@ bool functional_mapnumericalhessian(vm *v, functional_mapinfo *info, value *out) // Clone the vertex matrix for each thread meshclones[i]=*info->mesh; - meshclones[i].vert=object_clonematrix(info->mesh->vert); + meshclones[i].vert=matrix_clone(info->mesh->vert); if (!meshclones[i].vert) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_maphessian_cleanup; } task[i].mesh=&meshclones[i]; @@ -1713,7 +1713,7 @@ bool functional_elementgradient(vm *v, objectmesh *mesh, grade g, elementid id, bool length_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) { if (nv!=2) return false; double *x[nv], s0[mesh->dim]; - for (int j=0; jvert, vid[j], &x[j]); + for (int j=0; jvert, vid[j], &x[j]); functional_vecsub(mesh->dim, x[1], x[0], s0); @@ -1724,14 +1724,14 @@ bool length_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, v /** Calculate scaled gradient */ bool length_gradient_scale(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc, double scale) { double *x[nv], s0[mesh->dim], norm; - for (int j=0; jvert, vid[j], &x[j]); + for (int j=0; jvert, vid[j], &x[j]); functional_vecsub(mesh->dim, x[1], x[0], s0); norm=functional_vecnorm(mesh->dim, s0); if (normdim], normcx; - for (int j=0; jvert, vid[j], &x[j]); + for (int j=0; jvert, vid[j], &x[j]); if (mesh->dim==2) { functional_veccross2d(x[0], x[1], cx); @@ -1783,7 +1783,7 @@ bool areaenclosed_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int * bool areaenclosed_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc) { double *x[nv], cx[3], s[3]; double norm; - for (int j=0; jvert, vid[j], &x[j]); + for (int j=0; jvert, vid[j], &x[j]); if (mesh->dim==3) { functional_veccross(x[0], x[1], cx); @@ -1791,10 +1791,10 @@ bool areaenclosed_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int *v if (normdim==2) { functional_veccross2d(x[0], x[1], cx); @@ -1829,7 +1829,7 @@ bool area_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, voi if (nv!=3) return false; double *x[nv], s0[3], s1[3], cx[3]; for (int j=0; j<3; j++) { s0[j]=0; s1[j]=0; cx[j]=0; } - for (int j=0; jvert, vid[j], &x[j]); + for (int j=0; jvert, vid[j], &x[j]); functional_vecsub(mesh->dim, x[1], x[0], s0); functional_vecsub(mesh->dim, x[2], x[1], s1); @@ -1845,7 +1845,7 @@ bool area_gradient_scale(vm *v, objectmesh *mesh, elementid id, int nv, int *vid double *x[nv], s0[3], s1[3], s01[3], s010[3], s011[3]; double norm; for (int j=0; j<3; j++) { s0[j]=0; s1[j]=0; s01[j]=0; s010[j]=0; s011[j]=0; } - for (int j=0; jvert, vid[j], &x[j])) return false; + for (int j=0; jvert, vid[j], &x[j])!=LINALGERR_OK) return false; functional_vecsub(mesh->dim, x[1], x[0], s0); functional_vecsub(mesh->dim, x[2], x[1], s1); @@ -1857,12 +1857,12 @@ bool area_gradient_scale(vm *v, objectmesh *mesh, elementid id, int nv, int *vid functional_veccross(s01, s0, s010); functional_veccross(s01, s1, s011); - matrix_addtocolumn(frc, vid[0], 0.5/norm*scale, s011); - matrix_addtocolumn(frc, vid[2], 0.5/norm*scale, s010); + if (matrix_addtocolumnptr(frc, vid[0], 0.5/norm*scale, s011)!=LINALGERR_OK) return false; + if (matrix_addtocolumnptr(frc, vid[2], 0.5/norm*scale, s010)!=LINALGERR_OK) return false; functional_vecadd(mesh->dim, s010, s011, s0); - matrix_addtocolumn(frc, vid[1], -0.5/norm*scale, s0); + if (matrix_addtocolumnptr(frc, vid[1], -0.5/norm*scale, s0)!=LINALGERR_OK) return false; return true; } @@ -1895,7 +1895,7 @@ MORPHO_ENDCLASS /** Calculate enclosed volume */ bool volumeenclosed_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) { double *x[nv], cx[mesh->dim]; - for (int j=0; jvert, vid[j], &x[j])) return false; + for (int j=0; jvert, vid[j], &x[j])!=LINALGERR_OK) return false; functional_veccross(x[0], x[1], cx); @@ -1906,7 +1906,7 @@ bool volumeenclosed_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int /** Calculate gradient */ bool volumeenclosed_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc) { double *x[nv], cx[mesh->dim], dot; - for (int j=0; jvert, vid[j], &x[j]); + for (int j=0; jvert, vid[j], &x[j])!=LINALGERR_OK) return false; functional_veccross(x[0], x[1], cx); dot=functional_vecdot(mesh->dim, cx, x[2]); @@ -1917,13 +1917,13 @@ bool volumeenclosed_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int dot/=fabs(dot); - matrix_addtocolumn(frc, vid[2], dot/6.0, cx); + if (matrix_addtocolumnptr(frc, vid[2], dot/6.0, cx)!=LINALGERR_OK) return false; functional_veccross(x[1], x[2], cx); - matrix_addtocolumn(frc, vid[0], dot/6.0, cx); + if (matrix_addtocolumnptr(frc, vid[0], dot/6.0, cx)!=LINALGERR_OK) return false; functional_veccross(x[2], x[0], cx); - matrix_addtocolumn(frc, vid[1], dot/6.0, cx); + if (matrix_addtocolumnptr(frc, vid[1], dot/6.0, cx)!=LINALGERR_OK) return false; return true; } @@ -1949,7 +1949,7 @@ MORPHO_ENDCLASS /** Calculate enclosed volume */ bool volume_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) { double *x[nv], s10[mesh->dim], s20[mesh->dim], s30[mesh->dim], cx[mesh->dim]; - for (int j=0; jvert, vid[j], &x[j]); + for (int j=0; jvert, vid[j], &x[j]); functional_vecsub(mesh->dim, x[1], x[0], s10); functional_vecsub(mesh->dim, x[2], x[0], s20); @@ -1965,7 +1965,7 @@ bool volume_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, v bool volume_gradient_scale(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, objectmatrix *frc, double scale) { double *x[nv], s10[mesh->dim], s20[mesh->dim], s30[mesh->dim]; double s31[mesh->dim], s21[mesh->dim], cx[mesh->dim], uu; - for (int j=0; jvert, vid[j], &x[j]); + for (int j=0; jvert, vid[j], &x[j]); functional_vecsub(mesh->dim, x[1], x[0], s10); functional_vecsub(mesh->dim, x[2], x[0], s20); @@ -1977,16 +1977,16 @@ bool volume_gradient_scale(vm *v, objectmesh *mesh, elementid id, int nv, int *v uu=functional_vecdot(mesh->dim, s10, cx); uu=(uu>0 ? 1.0 : -1.0); - matrix_addtocolumn(frc, vid[1], uu/6.0*scale, cx); + if (matrix_addtocolumnptr(frc, vid[1], uu/6.0*scale, cx)!=LINALGERR_OK) return false; functional_veccross(s31, s21, cx); - matrix_addtocolumn(frc, vid[0], uu/6.0*scale, cx); + if (matrix_addtocolumnptr(frc, vid[0], uu/6.0*scale, cx)!=LINALGERR_OK) return false; functional_veccross(s30, s10, cx); - matrix_addtocolumn(frc, vid[2], uu/6.0*scale, cx); + if (matrix_addtocolumnptr(frc, vid[2], uu/6.0*scale, cx)!=LINALGERR_OK) return false; functional_veccross(s10, s20, cx); - matrix_addtocolumn(frc, vid[3], uu/6.0*scale, cx); + if (matrix_addtocolumnptr(frc, vid[3], uu/6.0*scale, cx)!=LINALGERR_OK) return false; return true; } @@ -2034,7 +2034,7 @@ bool scalarpotential_integrand(vm *v, objectmesh *mesh, elementid id, int nv, in value args[mesh->dim]; value ret; - matrix_getcolumn(mesh->vert, id, &x); + if (matrix_getcolumnptr(mesh->vert, id, &x)!=LINALGERR_OK) return false; for (int i=0; idim; i++) args[i]=MORPHO_FLOAT(x[i]); if (morpho_call(v, fn, mesh->dim, args, &ret)) { @@ -2051,7 +2051,7 @@ bool scalarpotential_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int value args[mesh->dim]; value ret; - matrix_getcolumn(mesh->vert, id, &x); + if (matrix_getcolumnptr(mesh->vert, id, &x)!=LINALGERR_OK) return false; for (int i=0; idim; i++) args[i]=MORPHO_FLOAT(x[i]); if (morpho_call(v, fn, mesh->dim, args, &ret)) { @@ -2059,7 +2059,7 @@ bool scalarpotential_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int objectmatrix *vf=MORPHO_GETMATRIX(ret); if (vf->nrows*vf->ncols==frc->nrows) { - return matrix_addtocolumn(frc, id, 1.0, vf->elements); + return (matrix_addtocolumnptr(frc, id, 1.0, vf->elements)==LINALGERR_OK); } } } @@ -2157,7 +2157,7 @@ void linearelasticity_calculategram(objectmatrix *vert, int dim, int nv, int *vi double *x[nv], // Positions of vertices s[gdim][nv]; // Side vectors - for (int j=0; j for (int i=0; ielements[i+j*gdim]=functional_vecdot(dim, s[i], s[j]); @@ -2180,16 +2180,17 @@ bool linearelasticity_integrand(vm *v, objectmesh *mesh, elementid id, int nv, i linearelasticity_calculategram(info->refmesh->vert, mesh->dim, nv, vid, &gramref); linearelasticity_calculategram(mesh->vert, mesh->dim, nv, vid, &gramdef); - if (matrix_inverse(&gramref, &q)!=MATRIX_OK) return false; - if (matrix_mul(&gramdef, &q, &r)!=MATRIX_OK) return false; + if (matrix_copy(&gramref, &q)!=LINALGERR_OK) return false; + if (matrix_inverse(&q)!=LINALGERR_OK) return false; + if (matrix_mul(&gramdef, &q, &r)!=LINALGERR_OK) return false; - matrix_identity(&cg); + if (matrix_identity(&cg)!=LINALGERR_OK) return false; matrix_scale(&cg, -0.5); - matrix_accumulate(&cg, 0.5, &r); + matrix_axpy(0.5, &r, &cg); // y <- alpha*x + y double trcg=0.0, trcgcg=0.0; matrix_trace(&cg, &trcg); - + matrix_mul(&cg, &cg, &r); matrix_trace(&r, &trcgcg); @@ -2559,7 +2560,9 @@ bool equielement_prepareref(objectinstance *self, objectmesh *mesh, grade g, obj MORPHO_ISMATRIX(weight) ) { ref->weight=MORPHO_GETMATRIX(weight); if (ref->weight) { - ref->mean=matrix_sum(ref->weight); + double sum[ref->weight->nvals]; + matrix_sum(ref->weight, sum); + ref->mean = sum[0]; ref->mean/=ref->weight->ncols; } } @@ -2917,7 +2920,7 @@ bool linetorsionsq_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int /* We now have an ordered list of vertices. Get the vertex positions */ double *x[6]; - for (int i=0; i<6; i++) matrix_getcolumn(mesh->vert, vlist[i], &x[i]); + for (int i=0; i<6; i++) matrix_getcolumnptr(mesh->vert, vlist[i], &x[i]); double A[3], B[3], C[3], crossAB[3], crossBC[3]; functional_vecsub(3, x[1], x[0], A); @@ -3081,7 +3084,7 @@ bool meancurvaturesq_integrand(vm *v, objectmesh *mesh, elementid id, int nv, in double *x[3], s0[3], s1[3], s01[3], s101[3]; double norm; - for (int j=0; j<3; j++) matrix_getcolumn(mesh->vert, vids[j], &x[j]); + for (int j=0; j<3; j++) matrix_getcolumnptr(mesh->vert, vids[j], &x[j]); /* s0 = x1-x0; s1 = x2-x1 */ functional_vecsub(mesh->dim, x[1], x[0], s0); @@ -3155,7 +3158,7 @@ bool gausscurvature_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int if (!curvature_ordervertices(&synid, nvert, vids)) goto gausscurv_cleanup; double *x[3], s0[3], s1[3], s01[3]; - for (int j=0; j<3; j++) matrix_getcolumn(mesh->vert, vids[j], &x[j]); + for (int j=0; j<3; j++) matrix_getcolumnptr(mesh->vert, vids[j], &x[j]); /* s0 = x1-x0; s1 = x2-x0 */ functional_vecsub(mesh->dim, x[1], x[0], s0); @@ -3328,18 +3331,16 @@ bool gradsq_evaluategradient3d(objectmesh *mesh, objectfield *field, int nv, int objectmatrix Mt = MORPHO_STATICMATRIX(xtarray, mesh->dim, mesh->dim); matrix_transpose(&M, &Mt); - double farray[nentries*mesh->dim]; // Field elements - objectmatrix frhs = MORPHO_STATICMATRIX(farray, mesh->dim, nentries); objectmatrix grad = MORPHO_STATICMATRIX(out, mesh->dim, nentries); // Loop over elements of the field for (unsigned int i=0; idim; j++) farray[i*mesh->dim+j] = f[j+1][i]-f[0][i]; + for (unsigned int j=0; jdim; j++) out[i*mesh->dim+j] = f[j+1][i]-f[0][i]; } // Solve to obtain the gradient of each element - matrix_divs(&Mt, &frhs, &grad); + matrix_solvesmall(&Mt, &grad); return true; } @@ -4003,7 +4004,7 @@ void integral_evaluatetangent(vm *v, value *out) { int dim = elref->mesh->dim; - objectmatrix *mtangent = object_newmatrix(dim, 1, false); + objectmatrix *mtangent = matrix_new(dim, 1, false); if (!mtangent) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return; @@ -4040,7 +4041,7 @@ void integral_evaluatenormal(vm *v, value *out) { int dim = elref->mesh->dim; double s0[dim], s1[dim]; - objectmatrix *mnormal = object_newmatrix(dim, 1, false); + objectmatrix *mnormal = matrix_new(dim, 1, false); if (!mnormal) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return; @@ -4086,7 +4087,8 @@ bool integral_prepareinvjacobian(unsigned int dim, grade g, double **x, objectma if (g==dim) { objectmatrix smat = MORPHO_STATICMATRIX(s, dim, dim); - success=(matrix_inverse(&smat, invj)==MATRIX_OK); + success=(matrix_copy(&smat, invj)==LINALGERR_OK && + matrix_inverse(invj)==LINALGERR_OK); } else if (g==1) { double s01norm = functional_vecdot(dim, s, s); if (s01norm>0) { @@ -4117,7 +4119,7 @@ bool integral_prepareinvjacobian(unsigned int dim, grade g, double **x, objectma /** Allocate suitable storage for the gradient */ bool integral_gradalloc(int dim, value prototype, value *out) { if (MORPHO_ISNIL(prototype)) { // Scalar - objectmatrix *mgrad=object_newmatrix(dim, 1, false); + objectmatrix *mgrad=matrix_new(dim, 1, false); if (mgrad) *out = MORPHO_OBJECT(mgrad); return mgrad; } else if (MORPHO_ISMATRIX(prototype)) { @@ -4135,7 +4137,7 @@ bool integral_gradsuminit(int i, value prototype, value dest, value *sum) { if (i>=list_length(lst)) { objectmatrix *prmat = MORPHO_GETMATRIX(prototype); - objectmatrix *new = object_newmatrix(prmat->nrows, prmat->ncols, true); + objectmatrix *new = matrix_new(prmat->nrows, prmat->ncols, true); if (!new) return false; *sum = MORPHO_OBJECT(new); list_append(lst, *sum); @@ -4169,7 +4171,7 @@ bool integral_oldgradcopy(int dim, int ndof, double *grad, value prototype, valu value el; if (i>=list_length(lst)) { - mgrad=object_newmatrix(proto->nrows, proto->ncols, false); // Should copy prototype dimensions! + mgrad=matrix_new(proto->nrows, proto->ncols, false); // Should copy prototype dimensions! if (mgrad) { for (int k=0; kelements[k]=grad[k*dim+i]; list_append(lst, MORPHO_OBJECT(mgrad)); @@ -4219,7 +4221,7 @@ bool integral_evaluategradient(vm *v, value q, value *out) { // Evaluate gradient if (MORPHO_ISFESPACE(fld->fnspc)) { if (!elref->invj) { - elref->invj=object_newmatrix(elref->g, elref->mesh->dim, false); + elref->invj=matrix_new(elref->g, elref->mesh->dim, false); if (elref->invj) { integral_prepareinvjacobian(elref->mesh->dim, elref->g, elref->vertexposn, elref->invj); @@ -4241,7 +4243,7 @@ bool integral_evaluategradient(vm *v, value q, value *out) { double fmatdata[nnodes * dim]; objectmatrix fmat = MORPHO_STATICMATRIX(fmatdata, nnodes, dim); - if (matrix_mul(&gmat, elref->invj, &fmat)!=MATRIX_OK) { + if (matrix_mul(&gmat, elref->invj, &fmat)!=LINALGERR_OK) { morpho_runtimeerror(v, INTEGRAL_GRDEVL); return false; } @@ -4302,7 +4304,7 @@ void integral_evaluatecg(vm *v, value *out) { int gdim=elref->nv-1; // Dimension of Gram matrix - objectmatrix *cg=object_newmatrix(gdim, gdim, true); + objectmatrix *cg=matrix_new(gdim, gdim, true); if (!cg) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return; } double gramrefel[gdim*gdim], gramdefel[gdim*gdim], qel[gdim*gdim], rel[gdim*gdim]; @@ -4314,12 +4316,13 @@ void integral_evaluatecg(vm *v, value *out) { linearelasticity_calculategram(elref->iref->mref->vert, elref->mesh->dim, elref->nv, elref->vid, &gramref); linearelasticity_calculategram(elref->mesh->vert, elref->mesh->dim, elref->nv, elref->vid, &gramdef); - if (matrix_inverse(&gramref, &q)!=MATRIX_OK) return; - if (matrix_mul(&gramdef, &q, &r)!=MATRIX_OK) return; + if (matrix_copy(&gramref, &q)!=LINALGERR_OK) return; + if (matrix_inverse(&q)!=LINALGERR_OK) return; + if (matrix_mul(&gramdef, &q, &r)!=LINALGERR_OK) return; - matrix_identity(cg); + if (matrix_identity(cg)!=LINALGERR_OK) return; matrix_scale(cg, -0.5); - matrix_accumulate(cg, 0.5, &r); + matrix_axpy(0.5, &r, cg); vm_settlvar(v, cauchygreenhandle, MORPHO_OBJECT(cg)); *out = MORPHO_OBJECT(cg); diff --git a/src/geometry/integrate.c b/src/geometry/integrate.c index 34e379bf..b19fcb87 100644 --- a/src/geometry/integrate.c +++ b/src/geometry/integrate.c @@ -14,7 +14,7 @@ #include "morpho.h" #include "classes.h" -#include "matrix.h" +#include "linalg.h" #include "sparse.h" #include "geometry.h" @@ -24,7 +24,7 @@ bool integrate_recognizequantities(unsigned int nquantity, value *quantity, valu if (MORPHO_ISFLOAT(quantity[i])) { out[i]=MORPHO_FLOAT(0); } else if (MORPHO_ISMATRIX(quantity[i])) { - out[i]=MORPHO_OBJECT(object_clonematrix(MORPHO_GETMATRIX(quantity[i]))); + out[i]=MORPHO_OBJECT(matrix_clone(MORPHO_GETMATRIX(quantity[i]))); } else return false; } } @@ -84,7 +84,7 @@ void integrate_interpolatequantitiesline(unsigned int dim, double t, unsigned in *out=(MORPHO_ISMATRIX(qout[i]) ? MORPHO_GETMATRIX(qout[i]): NULL); if (!out) { - out = object_clonematrix(m0); + out = matrix_clone(m0); qout[i]=MORPHO_OBJECT(out); } @@ -265,7 +265,7 @@ void integrate_interpolatequantitiestri(unsigned int dim, double *lambda, unsign *out=(MORPHO_ISMATRIX(qout[i]) ? MORPHO_GETMATRIX(qout[i]): NULL); if (!out) { - out = object_clonematrix(m0); + out = matrix_clone(m0); qout[i]=MORPHO_OBJECT(out); } @@ -603,7 +603,7 @@ void integrate_interpolatequantitiesvol(unsigned int dim, double *lambda, unsign *out=(MORPHO_ISMATRIX(qout[i]) ? MORPHO_GETMATRIX(qout[i]): NULL); if (!out) { - out = object_clonematrix(m0); + out = matrix_clone(m0); qout[i]=MORPHO_OBJECT(out); } @@ -2226,9 +2226,9 @@ void integrator_initializequantities(integrator *integrate, int nq, quantity *qu integrate->qval[i]=q; } else if (MORPHO_ISMATRIX(q)) { objectmatrix *m = MORPHO_GETMATRIX(q); - quantity[i].ndof=matrix_countdof(m); + quantity[i].ndof=(int) matrix_countdof(m); - objectmatrix *new = object_clonematrix(m); // Use a copy of the matrix + objectmatrix *new = matrix_clone(m); // Use a copy of the matrix integrate->qval[i]=MORPHO_OBJECT(new); } else return; } @@ -2396,7 +2396,7 @@ bool integrator_sumquantityweighted(int n, double *wts, value *q, value *out) { } else if (MORPHO_ISMATRIX(q[0])) { objectmatrix *sum = MORPHO_GETMATRIX(*out); matrix_zero(sum); - for (int j=0; jdim=dim; new->conn=NULL; - new->vert=object_newmatrix(dim, nv, false); + new->vert=matrix_new(dim, nv, false); new->link=NULL; if (new->vert) { mesh_link(new, (object *) new->vert); @@ -129,7 +129,7 @@ void mesh_delink(objectmesh *mesh, object *obj) { /** Gets vertex coordinates */ bool mesh_getvertexcoordinates(objectmesh *mesh, elementid id, double *out) { double *coords; - if (matrix_getcolumn(mesh->vert, id, &coords)) { + if (matrix_getcolumnptr(mesh->vert, id, &coords)==LINALGERR_OK) { for (unsigned int i=0; idim; i++) out[i]=coords[i]; return true; } @@ -139,7 +139,7 @@ bool mesh_getvertexcoordinates(objectmesh *mesh, elementid id, double *out) { /** Gets vertex coordinates as a list */ bool mesh_getvertexcoordinatesaslist(objectmesh *mesh, elementid id, double **out) { double *coords=NULL; - if (matrix_getcolumn(mesh->vert, id, &coords)) { + if (matrix_getcolumnptr(mesh->vert, id, &coords)==LINALGERR_OK) { *out=coords; } return coords; @@ -147,14 +147,14 @@ bool mesh_getvertexcoordinatesaslist(objectmesh *mesh, elementid id, double **ou /** Gets vertex coordinates */ bool mesh_setvertexcoordinates(objectmesh *mesh, elementid id, double *x) {; - return matrix_setcolumn(mesh->vert, id, x); + return matrix_setcolumnptr(mesh->vert, id, x); } /** Gets vertex coordinates as a value list */ bool mesh_getvertexcoordinatesasvalues(objectmesh *mesh, elementid id, value *val) { double *x=NULL; // The vertex positions - bool success=matrix_getcolumn(mesh->vert, id, &x); + bool success=(matrix_getcolumnptr(mesh->vert, id, &x)==LINALGERR_OK); if (success) { for (unsigned int i=0; idim; i++) val[i]=MORPHO_FLOAT(x[i]); @@ -175,7 +175,7 @@ bool mesh_nearestvertex(objectmesh *mesh, double *x, elementid *id, double *sepa elementid bestid=0; for (elementid i=0; ivert, i, &vx)) return false; + if (matrix_getcolumnptr(mesh->vert, i, &vx)!=LINALGERR_OK) return false; sep=0; for (int k=0; kdim; k++) sep+=(vx[k]-x[k])*(vx[k]-x[k]); if (i==0 || sepvert->nrows; j++) { double x; - if (matrix_getelement(m->vert, j, i, &x)) { + if (matrix_getelement(m->vert, j, i, &x) == LINALGERR_OK) { fprintf(f, "%g ", x); } } @@ -1123,10 +1123,10 @@ value Mesh_vertexposition(vm *v, int nargs, value *args) { unsigned int id=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); double *vals; - if (matrix_getcolumn(m->vert, id, &vals)) { - objectmatrix *new=object_newmatrix(m->dim, 1, true); + if (matrix_getcolumnptr(m->vert, id, &vals)==LINALGERR_OK) { + objectmatrix *new=matrix_new(m->dim, 1, true); if (new) { - matrix_setcolumn(new, 0, vals); + matrix_setcolumnptr(new, 0, vals); out=MORPHO_OBJECT(new); morpho_bindobjects(v, 1, &out); } @@ -1145,7 +1145,7 @@ value Mesh_setvertexposition(vm *v, int nargs, value *args) { unsigned int id=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); objectmatrix *mat = MORPHO_GETMATRIX(MORPHO_GETARG(args, 1)); - if (!matrix_setcolumn(m->vert, id, mat->elements)) morpho_runtimeerror(v, MESH_INVLDID); + if (matrix_setcolumnptr(m->vert, id, mat->elements)!=LINALGERR_OK) morpho_runtimeerror(v, MESH_INVLDID); } else morpho_runtimeerror(v, MESH_STVRTPSNARGS); return MORPHO_NIL; diff --git a/src/geometry/mesh.h b/src/geometry/mesh.h index 0a530fa7..883b928e 100644 --- a/src/geometry/mesh.h +++ b/src/geometry/mesh.h @@ -12,7 +12,7 @@ #ifdef MORPHO_INCLUDE_GEOMETRY #include "varray.h" -#include "matrix.h" +#include "linalg.h" #include "sparse.h" /* ------------------------------------------------------- diff --git a/src/geometry/selection.c b/src/geometry/selection.c index 38b1d66a..7af5ab7c 100644 --- a/src/geometry/selection.c +++ b/src/geometry/selection.c @@ -11,7 +11,7 @@ #include "object.h" #include "builtin.h" #include "classes.h" -#include "matrix.h" +#include "linalg.h" #include "sparse.h" #include "mesh.h" #include "selection.h" @@ -186,7 +186,7 @@ void selection_selectwithmatrix(vm *v, objectselection *sel, value fn, objectmat int nv = vert->ncols; if (matrix->ncols!=nv) { - morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); + morpho_runtimeerror(v, LINALG_INCOMPATIBLEMATRICES); return; } @@ -196,9 +196,7 @@ void selection_selectwithmatrix(vm *v, objectselection *sel, value fn, objectmat value ret=MORPHO_NIL; // Return value for (elementid i=0; i + +#include "linalg.h" +#include "format.h" +#include "cmplx.h" + +objecttype objectcomplexmatrixtype; +#define OBJECT_COMPLEXMATRIX objectcomplexmatrixtype + +typedef objectmatrix objectcomplexmatrix; + +/* ********************************************************************** + * ComplexMatrix utility functions + * ********************************************************************** */ + +/* ---------------------- + * Callbacks + * ---------------------- */ + +static void _printelfn(vm *v, double *el) { + objectcomplex cmplx = MORPHO_STATICCOMPLEX(el[0], el[1]); + complex_print(v, &cmplx); +} + +static bool _printeltobufffn(varray_char *out, char *format, double *el) { + if (!format_printtobuffer(MORPHO_FLOAT(el[0]), format, out)) return false; + varray_charadd(out, " ", 1); + varray_charadd(out, (el[1]<0 ? "-" : "+"), 1); + if (!format_printtobuffer(MORPHO_FLOAT(fabs(el[1])), format, out)) return false; + varray_charadd(out, "im", 2); + return true; +} + +static value _getelfn(vm *v, double *el) { + objectcomplex *new = object_newcomplex(el[0], el[1]); + return morpho_wrapandbind(v, (object *) new); +} + +static linalgError_t _setelfn(vm *v, value in, double *el) { + if (MORPHO_ISCOMPLEX(in)) { + *((MorphoComplex *) el) = MORPHO_GETCOMPLEX(in)->Z; + } else if (morpho_valuetofloat(in, el)) { + el[1] = 0.0; // Set imaginary part to zero + } else return LINALGERR_NON_NUMERICAL; + return LINALGERR_OK; +} + +/** Evaluate norms */ +static double _normfn(objectmatrix *a, matrix_norm_t nrm) { + char cnrm = matrix_normtolapack(nrm); + int nrows=a->nrows, ncols=a->ncols; + +#ifdef MORPHO_LINALG_USE_LAPACKE + return LAPACKE_zlange(LAPACK_COL_MAJOR, cnrm, a->nrows, a->ncols, (linalg_complexdouble_t *) a->elements, a->nrows); +#else + double work[a->nrows]; + return zlange_(&cnrm, &nrows, &ncols, (linalg_complexdouble_t *) a->elements, &nrows, work); +#endif +} + +/** Low level linear solve */ +static linalgError_t _solve(objectmatrix *a, objectmatrix *b, int *pivot) { + int n=a->nrows, nrhs = b->ncols, info; + +#ifdef MORPHO_LINALG_USE_LAPACKE + info=LAPACKE_zgesv(LAPACK_COL_MAJOR, n, nrhs, (linalg_complexdouble_t *) a->elements, n, pivot, (linalg_complexdouble_t *) b->elements, n); +#else + zgesv_(&n, &nrhs, (linalg_complexdouble_t *) a->elements, + &n, pivot, (linalg_complexdouble_t *) b->elements, &n, &info); +#endif + + return (info==0 ? LINALGERR_OK : (info>0 ? LINALGERR_MATRIX_SINGULAR : LINALGERR_LAPACK_INVLD_ARGS)); +} + +/** Low level eigensolver */ +static linalgError_t _eigen(objectmatrix *a, MorphoComplex *w, objectmatrix *vec) { + int info, n=a->nrows; + +#ifdef MORPHO_LINALG_USE_LAPACKE + info=LAPACKE_zgeev(LAPACK_COL_MAJOR, 'N', (vec ? 'V' : 'N'), n, (linalg_complexdouble_t *) a->elements, n, (linalg_complexdouble_t *) w, NULL, n, (linalg_complexdouble_t *) (vec ? vec->elements : NULL), n); +#else + int lwork=4*n; MorphoComplex work[4*n]; double rwork[2*n]; + zgeev_("N", (vec ? "V" : "N"), &n, (linalg_complexdouble_t *) a->elements, &n, (linalg_complexdouble_t *) w, NULL, &n, (linalg_complexdouble_t *) (vec ? vec->elements : NULL), &n, work, &lwork, rwork, &info); +#endif + + return (info==0 ? LINALGERR_OK : (info>0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS)); +} + +/** Low level SVD */ +static linalgError_t _svd(objectmatrix *a, double *s, objectmatrix *u, objectmatrix *vt) { + int info, m=a->nrows, n=a->ncols; + int minmn = (m < n) ? m : n; + +#ifdef MORPHO_LINALG_USE_LAPACKE + double* superb = malloc(minmn * sizeof(double)); + info = LAPACKE_zgesvd(LAPACK_COL_MAJOR, + (u ? 'A' : 'N'), // jobu: 'A' = all U columns, 'N' = no U + (vt ? 'A' : 'N'), // jobvt: 'A' = all VT rows, 'N' = no VT + m, n, + (linalg_complexdouble_t *) a->elements, m, // input matrix A (overwritten) + s, // singular values (min(m,n)) + (linalg_complexdouble_t *) (u ? u->elements : NULL), m, // U matrix (m×m) + (linalg_complexdouble_t *) (vt ? vt->elements : NULL), n, // VT matrix (n×n) + superb + ); +#else + int lwork = -1; + linalg_complexdouble_t work_query; + double rwork[5 * minmn]; // rwork needs at least 5*min(m,n) for zgesvd + + // Query optimal work size + zgesvd_((u ? "A" : "N"), (vt ? "A" : "N"), &m, &n, + (linalg_complexdouble_t *) a->elements, &m, s, + (linalg_complexdouble_t *) (u ? u->elements : NULL), &m, + (linalg_complexdouble_t *) (vt ? vt->elements : NULL), &n, + &work_query, &lwork, rwork, &info); + + if (info != 0) return (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS); + + lwork = (int)creal(work_query); + linalg_complexdouble_t work[lwork]; + zgesvd_((u ? "A" : "N"), (vt ? "A" : "N"), &m, &n, + (linalg_complexdouble_t *) a->elements, &m, s, + (linalg_complexdouble_t *) (u ? u->elements : NULL), &m, + (linalg_complexdouble_t *) (vt ? vt->elements : NULL), &n, + work, &lwork, rwork, &info); +#endif + + return (info == 0 ? LINALGERR_OK : (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS)); +} + +/** Low level QR decomposition */ +static linalgError_t _qr(objectmatrix *a, objectmatrix *q, objectmatrix *r) { + int info, m=a->nrows, n=a->ncols; + int minmn = (m < n) ? m : n; + + // Compute QR factorization without pivoting: A = Q*R +#ifdef MORPHO_LINALG_USE_LAPACKE + linalg_complexdouble_t tau[minmn]; + info = LAPACKE_zgeqrf(LAPACK_COL_MAJOR, m, n, (linalg_complexdouble_t *) a->elements, m, tau); + if (info != 0) return (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS); +#else + linalg_complexdouble_t tau[minmn]; + int lwork = -1; + linalg_complexdouble_t work_query; + + // Query optimal work size for ZGEQRF, which is reused for ZUNGQR + zgeqrf_(&m, &n, (linalg_complexdouble_t *) a->elements, &m, tau, &work_query, &lwork, &info); + if (info != 0) return (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS); + + int lwork_geqrf = (int) creal(work_query); + linalg_complexdouble_t work[lwork_geqrf]; + lwork = lwork_geqrf; + + // Compute QR factorization without pivoting + zgeqrf_(&m, &n, (linalg_complexdouble_t *) a->elements, &m, tau, work, &lwork, &info); + if (info != 0) return (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS); +#endif + + // Extract R (upper triangle of a) into r + // Copy entire matrix first then zero out below the diagonal + matrix_copy(a, r); + linalg_complexdouble_t *relems = (linalg_complexdouble_t *) r->elements; + for (int j = 0; j < n && j < m - 1; j++) { + memset(&relems[j * m + (j + 1)], 0, (m - j - 1) * sizeof(linalg_complexdouble_t)); + } + + // Generate Q from reflectors + if (q) { + // Copy reflectors from a to q (only first n columns, since a is m×n and q is m×m) + // ZGEQRF stores reflectors in lower triangle and R in upper triangle of first n columns + linalg_complexdouble_t *aelems = (linalg_complexdouble_t *) a->elements; + linalg_complexdouble_t *qelems = (linalg_complexdouble_t *) q->elements; + for (int j = 0; j < minmn; j++) { + cblas_zcopy(m, &aelems[j * m], 1, &qelems[j * m], 1); + } + +#ifdef MORPHO_LINALG_USE_LAPACKE + info = LAPACKE_zungqr(LAPACK_COL_MAJOR, m, minmn, minmn, (linalg_complexdouble_t *) q->elements, m, tau); + if (info != 0) return (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS); +#else + lwork = lwork_geqrf; + zungqr_(&m, &minmn, &minmn, (linalg_complexdouble_t *) q->elements, &m, tau, work, &lwork, &info); + if (info != 0) return (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS); +#endif + + // If Q should be m×m, zero out remaining columns if m > minmn + if (m > minmn) memset(&q->elements[minmn * m * q->nvals], 0, (m - minmn) * m * sizeof(linalg_complexdouble_t)); + } + + return LINALGERR_OK; +} + +/* ---------------------- + * Interface definition + * ---------------------- */ + +matrixinterfacedefn complexmatrixdefn = { + .printelfn = _printelfn, + .printeltobufffn = _printeltobufffn, + .getelfn = _getelfn, + .setelfn = _setelfn, + .normfn = _normfn, + .solvefn = _solve, + .eigenfn = _eigen, + .svdfn = _svd, + .qrfn = _qr +}; + +/* ---------------------- + * Constructor + * ---------------------- */ + +/** Create a new complex matrix */ +objectcomplexmatrix *complexmatrix_new(MatrixIdx_t nrows, MatrixIdx_t ncols, bool zero) { + return (objectcomplexmatrix *) matrix_newwithtype(OBJECT_COMPLEXMATRIX, nrows, ncols, 2, zero); +} + +/* ---------------------- + * Element access + * ---------------------- */ + +/** Sets a matrix element. */ +linalgError_t complexmatrix_setelement(objectcomplexmatrix *matrix, MatrixIdx_t row, MatrixIdx_t col, MorphoComplex value) { + MatrixIdx_t row_idx = row, col_idx = col; + LINALG_ERRCHECKRETURN(matrix_validateindex(&row_idx, matrix->nrows)); + LINALG_ERRCHECKRETURN(matrix_validateindex(&col_idx, matrix->ncols)); + MatrixCount_t ix = matrix->nvals*(col_idx*matrix->nrows+row_idx); + matrix->elements[ix]=creal(value); + matrix->elements[ix+1]=cimag(value); + return LINALGERR_OK; +} + +/** Gets a matrix element */ +linalgError_t complexmatrix_getelement(objectcomplexmatrix *matrix, MatrixIdx_t row, MatrixIdx_t col, MorphoComplex *value) { + MatrixIdx_t row_idx = row, col_idx = col; + LINALG_ERRCHECKRETURN(matrix_validateindex(&row_idx, matrix->nrows)); + LINALG_ERRCHECKRETURN(matrix_validateindex(&col_idx, matrix->ncols)); + MatrixCount_t ix = matrix->nvals*(col_idx*matrix->nrows+row_idx); + if (value) *value=MCBuild(matrix->elements[ix],matrix->elements[ix+1]); + return LINALGERR_OK; +} + +/** Copies a real matrix x into a complex matrix y */ +static linalgError_t _stridedcopy(objectmatrix *x, objectmatrix *y, int offset) { + if (!(x->ncols==y->ncols && x->nrows==y->nrows)) return LINALGERR_INCOMPATIBLE_DIM; + + cblas_dcopy((linalg_int_t) x->ncols*x->nrows, x->elements+offset, x->nvals, y->elements, y->nvals); + return LINALGERR_OK; +} + +linalgError_t complexmatrix_promote(objectmatrix *x, objectcomplexmatrix *y) { + return _stridedcopy(x, y, 0); +} + +/** Copies the real part of a complex matrix y into */ +linalgError_t complexmatrix_demote(objectcomplexmatrix *x, objectmatrix *y, bool imag) { + return _stridedcopy(x, y, (imag?1:0)); +} + +/* ---------------------- + * Complex arithmetic + * ---------------------- */ + +/** Performs c <- alpha*(a*b) + beta*c with complex matrices */ +linalgError_t complexmatrix_mmul(MorphoComplex alpha, objectmatrix *a, objectmatrix *b, MorphoComplex beta, objectmatrix *c) { + if (!(a->ncols==b->nrows && a->nrows==c->nrows && b->ncols==c->ncols)) return LINALGERR_INCOMPATIBLE_DIM; + + cblas_zgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, + a->nrows, b->ncols, a->ncols, + &alpha, (linalg_complexdouble_t *) a->elements, + a->nrows, (linalg_complexdouble_t *) b->elements, b->nrows, + &beta, (linalg_complexdouble_t *) c->elements, c->nrows); + return LINALGERR_OK; +} + +/** Scales a matrix x <- scale * x >*/ +void complematrix_scale(objectmatrix *a, MorphoComplex scale) { + cblas_zscal(a->nrows * a->ncols, (linalg_complexdouble_t *) &scale, (linalg_complexdouble_t *) a->elements, 1); +} + +/** Finds the Frobenius inner product of two complex matrices (a, b) = \sum_{i,j} conj(a)_ij * b_ij */ +linalgError_t complexmatrix_inner(objectcomplexmatrix *a, objectcomplexmatrix *b, MorphoComplex *out) { + if (!(a->ncols==b->ncols && a->nrows==b->nrows)) return LINALGERR_INCOMPATIBLE_DIM; + + cblas_zdotc_sub(a->nrows * a->ncols, (linalg_complexdouble_t *) a->elements, 1, + (linalg_complexdouble_t *) b->elements, 1, + (linalg_complexdouble_t *) out); + return LINALGERR_OK; +} + +/** Rank 1 update: Performs c <- alpha*a \outer b + c; a and b are treated as column vectors */ +linalgError_t complexmatrix_r1update(MorphoComplex alpha, objectcomplexmatrix *a, objectcomplexmatrix *b, objectcomplexmatrix *c) { + MatrixIdx_t m=a->nrows*a->ncols, n=b->nrows*b->ncols; + if (!(m==c->nrows && n==c->ncols)) return LINALGERR_INCOMPATIBLE_DIM; + + cblas_zgeru(CblasColMajor, m, n, (linalg_complexdouble_t *) &alpha, (linalg_complexdouble_t *) a->elements, 1, + (linalg_complexdouble_t *) b->elements, 1, + (linalg_complexdouble_t *) c->elements, c->nrows); + return LINALGERR_OK; +} + +/** Calculate the trace of a matrix */ +linalgError_t complexmatrix_trace(objectcomplexmatrix *a, MorphoComplex *out) { + if (a->nrows!=a->ncols) return LINALGERR_NOT_SQUARE; + MorphoComplex one = MCBuild(1.0, 0.0); + cblas_zdotu_sub(a->nrows, (linalg_complexdouble_t *) a->elements, a->ncols+1, (linalg_complexdouble_t *) &one, 0, (linalg_complexdouble_t *) out); + return LINALGERR_OK; +} + +/** Inverts the matrix a + * @param[in] a matrix to be inverted + * @returns linalgError_t indicating the status; MATRIX_OK indicates success. */ +linalgError_t complexmatrix_inverse(objectcomplexmatrix *a) { + int nrows=a->nrows, ncols=a->ncols, info; + int pivot[nrows]; + +#ifdef MORPHO_LINALG_USE_LAPACKE + info=LAPACKE_zgetrf(LAPACK_COL_MAJOR, nrows, ncols, (linalg_complexdouble_t *) a->elements, nrows, pivot); +#else + zgetrf_(&nrows, &ncols, (linalg_complexdouble_t *) a->elements, &nrows, pivot, &info); +#endif + if (info!=0) return (info>0 ? LINALGERR_MATRIX_SINGULAR : LINALGERR_LAPACK_INVLD_ARGS); + +#ifdef MORPHO_LINALG_USE_LAPACKE + info=LAPACKE_zgetri(LAPACK_COL_MAJOR, nrows, (linalg_complexdouble_t *) a->elements, nrows, pivot); +#else + int lwork=nrows*ncols; linalg_complexdouble_t work[nrows*ncols]; + zgetri_(&nrows, (linalg_complexdouble_t *) a->elements, &nrows, pivot, work, &lwork, &info); +#endif + + return (info==0 ? LINALGERR_OK : (info>0 ? LINALGERR_MATRIX_SINGULAR : LINALGERR_LAPACK_INVLD_ARGS)); +} + +/* ********************************************************************** + * ComplexMatrix constructors + * ********************************************************************** */ + +value complexmatrix_constructor__int_int(vm *v, int nargs, value *args) { + MatrixIdx_t nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + MatrixIdx_t ncols = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1)); + + objectcomplexmatrix *new=complexmatrix_new(nrows, ncols, true); + return morpho_wrapandbind(v, (object *) new); +} + +value complexmatrix_constructor__int(vm *v, int nargs, value *args) { + MatrixIdx_t nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + + objectcomplexmatrix *new=complexmatrix_new(nrows, 1, true); + return morpho_wrapandbind(v, (object *) new); +} + +value complematrix_constructor__matrix(vm *v, int nargs, value *args) { + objectmatrix *a = MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + + objectcomplexmatrix *new=complexmatrix_new(a->nrows, a->ncols, true); + if (new) complexmatrix_promote(a, new); + return morpho_wrapandbind(v, (object *) new); +} + +/** Constructs a complexmatrix from a list of lists or tuples */ +value complexmatrix_constructor__list(vm *v, int nargs, value *args) { + objectmatrix *new = matrix_listconstructor(v, MORPHO_GETARG(args, 0), OBJECT_COMPLEXMATRIX, 2); + return morpho_wrapandbind(v, (object *) new); +} + +/** Constructs a matrix from an array */ +value complexmatrix_constructor__array(vm *v, int nargs, value *args) { + objectarray *a = MORPHO_GETARRAY(MORPHO_GETARG(args, 0)); + if (a->ndim!=2) { morpho_runtimeerror(v, LINALG_INVLDARGS); return MORPHO_NIL; } + + objectmatrix *new = matrix_arrayconstructor(v, a, OBJECT_COMPLEXMATRIX, 2); + return morpho_wrapandbind(v, (object *) new); +} + +/* ********************************************************************** + * ComplexMatrix veneer class + * ********************************************************************** */ + +/* ---------------------- + * Arithmetic + * ---------------------- */ + +/** Add a vector */ +static value _axpy(vm *v, int nargs, value *args, double alpha) { + objectcomplexmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + value out=MORPHO_NIL; + + if (a->ncols==b->ncols && a->nrows==b->nrows) { + objectcomplexmatrix *new=complexmatrix_new(a->nrows, a->ncols, true); + if (new) { + complexmatrix_promote(b, new); + matrix_axpy(alpha, a, new); + } + out = morpho_wrapandbind(v, (object *) new); + } else morpho_runtimeerror(v, LINALG_INCOMPATIBLEMATRICES); + return out; +} + +value ComplexMatrix_add__matrix(vm *v, int nargs, value *args) { + return _axpy(v, nargs, args, 1.0); +} + +value ComplexMatrix_sub__matrix(vm *v, int nargs, value *args) { + value out = _axpy(v, nargs, args, -1.0); + if (matrix_isamatrix(out)) matrix_scale(MORPHO_GETMATRIX(out), -1.0); // -(-A + B) + return out; +} + +value ComplexMatrix_subr__matrix(vm *v, int nargs, value *args) { + return _axpy(v, nargs, args, -1.0); +} + +value ComplexMatrix_mul__complex(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + + objectmatrix *new = matrix_clone(a); + if (new) complematrix_scale(new, MORPHO_GETCOMPLEX(MORPHO_GETARG(args, 0))->Z); + return morpho_wrapandbind(v, (object *) new); +} + +/** Multiplication by a complexmatrix or a regular matrix */ +static bool _promote(vm *v, objectmatrix *b, objectmatrix **bp) { // Promotes b to a complexmatrix + *bp=complexmatrix_new(b->nrows, b->ncols, true); + if (!*bp) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; } + return complexmatrix_promote(b, *bp)==LINALGERR_OK; +} + +static value _axb(vm *v, objectmatrix *a, objectmatrix *b) { // Performs a*b returning a wrapped value + if (a->ncols!=b->nrows) { morpho_runtimeerror(v, LINALG_INCOMPATIBLEMATRICES); return MORPHO_NIL; } + objectcomplexmatrix *new=complexmatrix_new(a->nrows, b->ncols, false); + if (!new) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return MORPHO_NIL; } + complexmatrix_mmul(MCBuild(1.0, 0.0), a, b, MCBuild(0.0, 0.0), new); + return morpho_wrapandbind(v, (object *) new); +} + +static value _mul(vm *v, value a, value b, bool promoteb, bool swap) { // Driver routine for a*b + objectmatrix *A=MORPHO_GETMATRIX(a), *B=MORPHO_GETMATRIX(b), *bp=NULL; + if (promoteb) { if (_promote(v, B, &bp)) { B=bp; } else { return MORPHO_NIL; } } // Promote b if requested + value out = (swap ? _axb(v, B, A) : _axb(v, A, B)); // Multiply, swapping arguments if requested + if (bp) object_free((object *) bp); + return out; +} + +value ComplexMatrix_mul__complexmatrix(vm *v, int nargs, value *args) { + return _mul(v, MORPHO_SELF(args), MORPHO_GETARG(args, 0), false, false); +} + +value ComplexMatrix_mul__matrix(vm *v, int nargs, value *args) { + return _mul(v, MORPHO_SELF(args), MORPHO_GETARG(args, 0), true, false); +} + +value ComplexMatrix_mulr__matrix(vm *v, int nargs, value *args) { + return _mul(v, MORPHO_SELF(args), MORPHO_GETARG(args, 0), true, true); +} + +value ComplexMatrix_div__matrix(vm *v, int nargs, value *args) { + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_SELF(args)), *A=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)), *ap=NULL; + objectmatrix *new=matrix_clone(b); + if (new && _promote(v, A, &ap)) matrix_solve(ap, new); + return morpho_wrapandbind(v, (object *) new); +} + +value ComplexMatrix_divr__matrix(vm *v, int nargs, value *args) { + objectmatrix *A=MORPHO_GETMATRIX(MORPHO_SELF(args)), *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)), *bp=NULL; + if (_promote(v, b, &bp)) matrix_solve(A, bp); // Promote the matrix that will contain the solution anyway + return morpho_wrapandbind(v, (object *) bp); +} + +/** Computes the trace */ +value ComplexMatrix_trace(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + MorphoComplex tr=MCBuild(0,0); + LINALG_ERRCHECKVM(complexmatrix_trace(a, &tr)); + objectcomplex *new = object_newcomplex(creal(tr), cimag(tr)); + return morpho_wrapandbind(v, (object *) new); +} + +/** Inverts a matrix */ +value ComplexMatrix_inverse(vm *v, int nargs, value *args) { + objectcomplexmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + objectcomplexmatrix *new = matrix_clone(a); + out = morpho_wrapandbind(v, (object *) new); + if (new) LINALG_ERRCHECKVM(complexmatrix_inverse(new)); + + return out; +} + +static value _realimag(vm *v, int nargs, value *args, bool imag) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectmatrix *new=matrix_new(a->nrows, a->ncols, false); + if (new) complexmatrix_demote(a, new, imag); + return morpho_wrapandbind(v, (object *) new); +} + +/** Extract real part */ +value ComplexMatrix_real(vm *v, int nargs, value *args) { + return _realimag(v, nargs, args, false); +} + +/** Extract imaginary part */ +value ComplexMatrix_imag(vm *v, int nargs, value *args) { + return _realimag(v, nargs, args, true); +} + +static value _conj(vm *v, int nargs, value *args, bool trans) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectmatrix *new=matrix_clone(a); + if (new) { + if (trans) matrix_transpose(a, new); + cblas_dscal(a->nrows*a->ncols, -1.0, new->elements+1, new->nvals); + } + return morpho_wrapandbind(v, (object *) new); +} + +/** Extract imaginary part */ +value ComplexMatrix_conj(vm *v, int nargs, value *args) { + return _conj(v, nargs, args, false); +} + +/** Return conjugate transpose */ +value ComplexMatrix_conjTranspose(vm *v, int nargs, value *args) { + return _conj(v, nargs, args, true); +} + +/* --------- + * Products + * --------- */ + +/** Frobenius inner product */ +value ComplexMatrix_inner(vm *v, int nargs, value *args) { + objectcomplexmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectcomplexmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + MorphoComplex prod=MCBuild(0.0, 0.0); + value out = MORPHO_NIL; + + if (complexmatrix_inner(a, b, &prod)==LINALGERR_OK) { + objectcomplex *new = object_newcomplex(creal(prod), cimag(prod)); + out = morpho_wrapandbind(v, (object *) new); + } else morpho_runtimeerror(v, LINALG_INCOMPATIBLEMATRICES); + + return out; +} + +/** Outer product */ +value ComplexMatrix_outer(vm *v, int nargs, value *args) { + objectcomplexmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectcomplexmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + + objectcomplexmatrix *new=complexmatrix_new(a->nrows*a->ncols, b->nrows*b->ncols, true); + if (new) LINALG_ERRCHECKVM(complexmatrix_r1update(MCBuild(1.0,0.0), a, b, new)); + + return morpho_wrapandbind(v, (object *) new); +} + +MORPHO_BEGINCLASS(ComplexMatrix) +MORPHO_METHOD_SIGNATURE(MORPHO_PRINT_METHOD, "()", Matrix_print, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_FORMAT_METHOD, "(String)", Matrix_format, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ASSIGN_METHOD, "(ComplexMatrix)", Matrix_assign, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_CLONE_METHOD, "ComplexMatrix ()", Matrix_clone, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_GETINDEX_METHOD, "Complex (Int)", Matrix_enumerate, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_GETINDEX_METHOD, "Complex (Int, Int)", Matrix_index__int_int, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_GETINDEX_METHOD, "ComplexMatrix (_,_)", Matrix_index__x_x, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_GETINDEX_METHOD, "Matrix (...)", Matrix_index__err, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MORPHO_SETINDEX_METHOD, "(Int,_)", Matrix_setindex__int_x, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SETINDEX_METHOD, "(Int,Int,_)", Matrix_setindex__int_int_x, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SETINDEX_METHOD, "(_,_,ComplexMatrix)", Matrix_setindex__x_x_matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_GETCOLUMN_METHOD, "ComplexMatrix (Int)", Matrix_getcolumn__int, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_SETCOLUMN_METHOD, "(Int, ComplexMatrix)", Matrix_setcolumn__int_matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADD_METHOD, "ComplexMatrix (ComplexMatrix)", Matrix_add__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADD_METHOD, "ComplexMatrix (Matrix)", ComplexMatrix_add__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADD_METHOD, "ComplexMatrix (_)", Matrix_add__x, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADD_METHOD, "ComplexMatrix (Nil)", Matrix_add__nil, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADDR_METHOD, "ComplexMatrix (Matrix)", ComplexMatrix_add__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADDR_METHOD, "ComplexMatrix (_)", Matrix_add__x, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADDR_METHOD, "ComplexMatrix (Nil)", Matrix_add__nil, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SUB_METHOD, "ComplexMatrix (ComplexMatrix)", Matrix_sub__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SUB_METHOD, "ComplexMatrix (Matrix)", ComplexMatrix_sub__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SUB_METHOD, "ComplexMatrix (Nil)", Matrix_add__nil, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SUB_METHOD, "ComplexMatrix (_)", Matrix_sub__x, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SUBR_METHOD, "ComplexMatrix (_)", Matrix_subr__x, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SUBR_METHOD, "ComplexMatrix (Matrix)", ComplexMatrix_subr__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_MUL_METHOD, "ComplexMatrix (_)", Matrix_mul__float, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_MUL_METHOD, "ComplexMatrix (Complex)", ComplexMatrix_mul__complex, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_MUL_METHOD, "ComplexMatrix (ComplexMatrix)", ComplexMatrix_mul__complexmatrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_MUL_METHOD, "ComplexMatrix (Matrix)", ComplexMatrix_mul__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_MULR_METHOD, "ComplexMatrix (Matrix)", ComplexMatrix_mulr__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_MULR_METHOD, "ComplexMatrix (Complex)", ComplexMatrix_mul__complex, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_MULR_METHOD, "ComplexMatrix (_)", Matrix_mul__float, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_DIV_METHOD, "ComplexMatrix (_)", Matrix_div__float, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_DIV_METHOD, "ComplexMatrix (Matrix)", ComplexMatrix_div__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_DIV_METHOD, "ComplexMatrix (ComplexMatrix)", Matrix_div__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_DIVR_METHOD, "ComplexMatrix (Matrix)", ComplexMatrix_divr__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ACC_METHOD, "(_, ComplexMatrix)", Matrix_acc__x_x_matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_INVERSE_METHOD, "ComplexMatrix ()", ComplexMatrix_inverse, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_NORM_METHOD, "Float (_)", Matrix_norm__x, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MATRIX_NORM_METHOD, "Float ()", Matrix_norm, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MORPHO_SUM_METHOD, "Complex ()", Matrix_sum, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_TRACE_METHOD, "Complex ()", ComplexMatrix_trace, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_TRANSPOSE_METHOD, "ComplexMatrix ()", Matrix_transpose, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(COMPLEX_REAL_METHOD, "Matrix ()", ComplexMatrix_real, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(COMPLEX_IMAG_METHOD, "Matrix ()", ComplexMatrix_imag, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(COMPLEX_CONJUGATE_METHOD, "ComplexMatrix ()", ComplexMatrix_conj, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(COMPLEXMATRIX_CONJTRANSPOSE_METHOD, "ComplexMatrix ()", ComplexMatrix_conjTranspose, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_INNER_METHOD, "Complex (Matrix)", ComplexMatrix_inner, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_OUTER_METHOD, "ComplexMatrix (ComplexMatrix)", ComplexMatrix_outer, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_EIGENVALUES_METHOD, "Tuple ()", Matrix_eigenvalues, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_EIGENSYSTEM_METHOD, "Tuple ()", Matrix_eigensystem, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_SVD_METHOD, "Tuple ()", Matrix_svd, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_QR_METHOD, "Tuple ()", Matrix_qr, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_RESHAPE_METHOD, "(Int,Int)", Matrix_reshape, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_ROLL_METHOD, "ComplexMatrix (Int)", Matrix_roll__int, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_ROLL_METHOD, "ComplexMatrix (Int,Int)", Matrix_roll__int_int, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ENUMERATE_METHOD, "(Int)", Matrix_enumerate, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_COUNT_METHOD, "Int ()", Matrix_count, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MATRIX_DIMENSIONS_METHOD, "Tuple ()", Matrix_dimensions, BUILTIN_FLAGSEMPTY) +MORPHO_ENDCLASS + +/* ********************************************************************** + * Initialization + * ********************************************************************** */ + +void complexmatrix_initialize(void) { + objectcomplexmatrixtype=object_addtype(&objectmatrixdefn); + matrix_addinterface(&complexmatrixdefn); + + objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); + value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); + + value complexmatrixclass=builtin_addclass(COMPLEXMATRIX_CLASSNAME, MORPHO_GETCLASSDEFINITION(ComplexMatrix), objclass); + object_setveneerclass(OBJECT_COMPLEXMATRIX, complexmatrixclass); + + morpho_addfunction(COMPLEXMATRIX_CLASSNAME, "ComplexMatrix (Int, Int)", complexmatrix_constructor__int_int, MORPHO_FN_CONSTRUCTOR, NULL); + morpho_addfunction(COMPLEXMATRIX_CLASSNAME, "ComplexMatrix (Int)", complexmatrix_constructor__int, MORPHO_FN_CONSTRUCTOR, NULL); + morpho_addfunction(COMPLEXMATRIX_CLASSNAME, "ComplexMatrix (ComplexMatrix)", matrix_constructor__matrix, MORPHO_FN_CONSTRUCTOR, NULL); + morpho_addfunction(COMPLEXMATRIX_CLASSNAME, "ComplexMatrix (Matrix)", complematrix_constructor__matrix, MORPHO_FN_CONSTRUCTOR, NULL); + morpho_addfunction(COMPLEXMATRIX_CLASSNAME, "ComplexMatrix (List)", complexmatrix_constructor__list, MORPHO_FN_CONSTRUCTOR, NULL); + morpho_addfunction(COMPLEXMATRIX_CLASSNAME, "ComplexMatrix (Tuple)", complexmatrix_constructor__list, MORPHO_FN_CONSTRUCTOR, NULL); + morpho_addfunction(COMPLEXMATRIX_CLASSNAME, "ComplexMatrix (Array)", complexmatrix_constructor__array, MORPHO_FN_CONSTRUCTOR, NULL); +} diff --git a/src/linalg/complexmatrix.h b/src/linalg/complexmatrix.h new file mode 100644 index 00000000..cdb7bdd8 --- /dev/null +++ b/src/linalg/complexmatrix.h @@ -0,0 +1,20 @@ +/** @file complexmatrix.h + * @author T J Atherton + * + * @brief New linear algebra library +*/ + +#ifndef complexmatrix_h +#define complexmatrix_h + +/* ------------------------------------------------------- + * ComplexMatrix veneer class + * ------------------------------------------------------- */ + +#define COMPLEXMATRIX_CLASSNAME "ComplexMatrix" + +#define COMPLEXMATRIX_CONJTRANSPOSE_METHOD "conjTranspose" + +void complexmatrix_initialize(void); + +#endif diff --git a/src/linalg/linalg.c b/src/linalg/linalg.c new file mode 100644 index 00000000..ac8893a1 --- /dev/null +++ b/src/linalg/linalg.c @@ -0,0 +1,49 @@ +/** @file linalg.c + * @author T J Atherton + * + * @brief Improved linear algebra library +*/ + +#include "linalg.h" + +/* ------------------------------------------------------- + * Errors + * ------------------------------------------------------- */ + +void linalg_raiseerror(vm *v, linalgError_t err) { + switch (err) { + case LINALGERR_OK: break; + case LINALGERR_INCOMPATIBLE_DIM: morpho_runtimeerror(v, LINALG_INCOMPATIBLEMATRICES); break; + case LINALGERR_INDX_OUT_OF_BNDS: morpho_runtimeerror(v, LINALG_INDICESOUTSIDEBOUNDS); break; + case LINALGERR_MATRIX_SINGULAR: morpho_runtimeerror(v, LINALG_SINGULAR); break; + case LINALGERR_NOT_SQUARE: morpho_runtimeerror(v, LINALG_NOTSQ); break; + case LINALGERR_LAPACK_INVLD_ARGS: morpho_runtimeerror(v, LINALG_LAPACK_ARGS); break; + case LINALGERR_OP_FAILED: morpho_runtimeerror(v, LINALG_OPFAILED); break; + case LINALGERR_NOT_SUPPORTED: morpho_runtimeerror(v, LINALG_NOTSUPPORTED); break; + case LINALGERR_NON_NUMERICAL: morpho_runtimeerror(v, LINALG_NNNMRCL_ARG); break; + case LINALGERR_INVLD_ARG: morpho_runtimeerror(v, LINALG_INVLDARGS); break; + case LINALGERR_ALLOC: morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); break; + } +} + +/* ------------------------------------------------------- + * Initialization and finalization + * ------------------------------------------------------- */ + +void linalg_initialize(void) { + matrix_initialize(); + + morpho_defineerror(LINALG_INCOMPATIBLEMATRICES, ERROR_HALT, LINALG_INCOMPATIBLEMATRICES_MSG); + morpho_defineerror(LINALG_INDICESOUTSIDEBOUNDS, ERROR_HALT, LINALG_INDICESOUTSIDEBOUNDS_MSG); + morpho_defineerror(LINALG_SINGULAR, ERROR_HALT, LINALG_SINGULAR_MSG); + morpho_defineerror(LINALG_NOTSQ, ERROR_HALT, LINALG_NOTSQ_MSG); + morpho_defineerror(LINALG_LAPACK_ARGS, ERROR_HALT, LINALG_LAPACK_ARGS_MSG); + morpho_defineerror(LINALG_OPFAILED, ERROR_HALT, LINALG_OPFAILED_MSG); + morpho_defineerror(LINALG_NOTSUPPORTED, ERROR_HALT, LINALG_NOTSUPPORTED_MSG); + morpho_defineerror(LINALG_INVLDARGS, ERROR_HALT, LINALG_INVLDARGS_MSG); + morpho_defineerror(LINALG_NNNMRCL_ARG, ERROR_HALT, LINALG_NNNMRCL_ARG_MSG); + morpho_defineerror(LINALG_NORMARGS, ERROR_HALT, LINALG_NORMARGS_MSG); + morpho_defineerror(LINALG_ARITHARGS, ERROR_HALT, LINALG_ARITHARGS_MSG); + morpho_defineerror(LINALG_INVLDINDICES, ERROR_HALT, LINALG_INVLDINDICES_MSG); +} + diff --git a/src/linalg/linalg.h b/src/linalg/linalg.h new file mode 100644 index 00000000..835247f4 --- /dev/null +++ b/src/linalg/linalg.h @@ -0,0 +1,104 @@ + +/** @file linalg.h + * @author T J Atherton + * + * @brief Improved linear algebra library +*/ + +#ifndef linalg_h +#define linalg_h + +#include "morpho.h" + +/* ------------------------------------------------------- + * objectmatrixerror type + * ------------------------------------------------------- */ + +typedef enum { + LINALGERR_OK, // Operation performed correctly + LINALGERR_INCOMPATIBLE_DIM, // Matrices have incompatible dimensions, e.g. for multiplication + LINALGERR_INDX_OUT_OF_BNDS, // Index out of bounds, e.g. for access. + LINALGERR_MATRIX_SINGULAR, // Matrix is singular + LINALGERR_NOT_SQUARE, // Matrix is required to be square for this algorithm + LINALGERR_LAPACK_INVLD_ARGS, // Invalid arguments to LAPACK routine + LINALGERR_OP_FAILED, // Matrix operation failed + LINALGERR_NOT_SUPPORTED, // Operation not supported for this matrix type + LINALGERR_NON_NUMERICAL, // Non numerical args supplied + LINALGERR_INVLD_ARG, // Invalid argument supplied + LINALGERR_ALLOC // Memory allocation failed +} linalgError_t; + +/* ------------------------------------------------------- + * Errors + * ------------------------------------------------------- */ + +#define LINALG_INCOMPATIBLEMATRICES "LnAlgMtrxIncmptbl" +#define LINALG_INCOMPATIBLEMATRICES_MSG "Matrices have incompatible shape." + +#define LINALG_INDICESOUTSIDEBOUNDS "LnAlgMtrxIndxBnds" +#define LINALG_INDICESOUTSIDEBOUNDS_MSG "Matrix index out of bounds." + +#define LINALG_SINGULAR "LnAlgMtrxSnglr" +#define LINALG_SINGULAR_MSG "Matrix is singular." + +#define LINALG_NOTSQ "LnAlgMtrxNtSq" +#define LINALG_NOTSQ_MSG "Matrix is not square." + +#define LINALG_LAPACK_ARGS "LnAlgLapackArgs" +#define LINALG_LAPACK_ARGS_MSG "Lapack function called with invalid arguments." + +#define LINALG_OPFAILED "LnAlgMtrxOpFld" +#define LINALG_OPFAILED_MSG "Matrix operation failed." + +#define LINALG_NOTSUPPORTED "LnAlgMtrxNtSpprtd" +#define LINALG_NOTSUPPORTED_MSG "Operation not supported for this matrix type." + +#define LINALG_INVLDARGS "LnAlgMtrxInvldArg" +#define LINALG_INVLDARGS_MSG "Invalid arguments to matrix method." + +#define LINALG_NNNMRCL_ARG "LnAlgMtrxNnNmrclArg" +#define LINALG_NNNMRCL_ARG_MSG "Matrix method requires numerical arguments." + +#define LINALG_NORMARGS "LnAlgMtrxNrmArgs" +#define LINALG_NORMARGS_MSG "Method 'norm' requires a supported argument: 1 or inf." + +#define LINALG_ARITHARGS "LnAlgInvldArg" +#define LINALG_ARITHARGS_MSG "Matrix arithmetic methods expect a matrix or number as their argument." + +#define LINALG_INVLDINDICES "LnAlgInvldIndx" +#define LINALG_INVLDINDICES_MSG "Matrices require two arguments for indexing." + +/* ------------------------------------------------------- + * Interface + * ------------------------------------------------------- */ + +void linalg_raiseerror(vm *v, linalgError_t err); + +/** Macros to simplify error checking: + - evaluates expression f that returns linalgError_t; + - if an error occurred, raises the corresponding error in a vm called v */ +#define LINALG_ERRCHECKVM(f) { linalgError_t err = f; if (err!=LINALGERR_OK) linalg_raiseerror(v, err); } + +/** As for LINALG_ERRCHECKVM but additionally jumps to a given label */ +#define LINALG_ERRCHECKVMGOTO(f, label) { linalgError_t err = f; if (err!=LINALGERR_OK) { linalg_raiseerror(v, err); goto label; }} + +/** As for LINALG_ERRCHECKVM but additionally returnsl */ +#define LINALG_ERRCHECKVMRETURN(f, ret) { linalgError_t err = f; if (err!=LINALGERR_OK) { linalg_raiseerror(v, err); return ret; }} + +/** Similar to the above, except returns the error rather than raising it */ +#define LINALG_ERRCHECKRETURN(f) { linalgError_t err = f; if (err!=LINALGERR_OK) return err; } + +/* ------------------------------------------------------- + * Include the rest of the library + * ------------------------------------------------------- */ + +#include "matrix.h" +#include "complexmatrix.h" + +/* ------------------------------------------------------- + * Initialization and finalization + * ------------------------------------------------------- */ + +void linalg_initialize(void); + +#endif /* linalg_h */ diff --git a/src/linalg/matrix.c b/src/linalg/matrix.c index b71e1aed..557e6584 100644 --- a/src/linalg/matrix.c +++ b/src/linalg/matrix.c @@ -1,8 +1,8 @@ /** @file matrix.c * @author T J Atherton * - * @brief Veneer class over the objectmatrix type that interfaces with blas and lapack - */ + * @brief New matrices +*/ #include "build.h" #ifdef MORPHO_INCLUDE_LINALG @@ -15,21 +15,50 @@ #include "sparse.h" #include "format.h" +/* ********************************************************************** + * Matrix interface definitions + * ********************************************************************** */ + +/** Hold the matrix interface definitions as they're created */ +static matrixinterfacedefn _matrixdefn[LINALG_MAXMATRIXDEFNS]; +objecttype matrixinterfacedefnnext=0; /** Type of the next object definition */ + +void matrix_addinterface(matrixinterfacedefn *defn) { + if (matrixinterfacedefnnextobj.type-OBJECT_MATRIX; + if (iindxtype-OBJECT_MATRIX; + return iindx>=0 && iindxncols * - ((objectmatrix *) obj)->nrows; + return sizeof(objectmatrix)+sizeof(double) * ((objectmatrix *) obj)->nels; } void objectmatrix_printfn(object *obj, void *v) { - morpho_printf(v, ""); + objectclass *klass=object_getveneerclass(obj->type); + morpho_printf(v, "<"); + morpho_printvalue(v, klass->name); + morpho_printf(v, ">"); } objecttypedefn objectmatrixdefn = { @@ -41,253 +70,377 @@ objecttypedefn objectmatrixdefn = { .cmpfn=NULL }; -/** Creates a matrix object */ -objectmatrix *object_newmatrix(unsigned int nrows, unsigned int ncols, bool zero) { - unsigned int nel = nrows*ncols; - objectmatrix *new = (objectmatrix *) object_new(sizeof(objectmatrix)+nel*sizeof(double), OBJECT_MATRIX); - - if (new) { - new->ncols=ncols; - new->nrows=nrows; - new->elements=new->matrixdata; - if (zero) { - memset(new->elements, 0, sizeof(double)*nel); - } - } - - return new; -} - /* ********************************************************************** - * Other constructors + * Matrix utility functions * ********************************************************************** */ -/* - * Create matrices from array objects - */ - -void matrix_raiseerror(vm *v, objectmatrixerror err) { - switch(err) { - case MATRIX_OK: break; - case MATRIX_INCMPTBLDIM: morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); break; - case MATRIX_SING: morpho_runtimeerror(v, MATRIX_SINGULAR); break; - case MATRIX_INVLD: morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT); break; - case MATRIX_BNDS: morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); break; - case MATRIX_NSQ: morpho_runtimeerror(v, MATRIX_NOTSQ); break; - case MATRIX_FAILED: morpho_runtimeerror(v, MATRIX_OPFAILED); break; - case MATRIX_ALLOC: morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); break; - } +/* ---------------------- + * Matrix interface + * ---------------------- */ + +static void _printelfn(vm *v, double *el) { + double val=*el; + morpho_printf(v, "%g", (fabs(val)ndim; n++) { - int k=MORPHO_GETINTEGERVALUE(array->data[n]); - if (k>dim[n]) dim[n]=k; +/** Convert matrix_norm_t to a character for use with lapack routines */ +char matrix_normtolapack(matrix_norm_t norm) { + switch (norm) { + case MATRIX_NORM_MAX: return 'M'; + case MATRIX_NORM_L1: return '1'; + case MATRIX_NORM_INF: return 'I'; + case MATRIX_NORM_FROBENIUS: return 'F'; } + return '\0'; +} + +/** Evaluate norms */ +static double _normfn(objectmatrix *a, matrix_norm_t nrm) { + char cnrm = matrix_normtolapack(nrm); + int nrows=a->nrows, ncols=a->ncols; + +#ifdef MORPHO_LINALG_USE_LAPACKE + return LAPACKE_dlange(LAPACK_COL_MAJOR, cnrm, a->nrows, a->ncols, a->elements, a->nrows); +#else + double work[a->nrows]; + return dlange_(&cnrm, &nrows, &ncols, a->elements, &nrows, work); +#endif +} + +/** Low level linear solve */ +static linalgError_t _solve(objectmatrix *a, objectmatrix *b, int *pivot) { + int n=a->nrows, nrhs = b->ncols, info; - if (maxdimndim) return false; +#ifdef MORPHO_LINALG_USE_LAPACKE + info=LAPACKE_dgesv(LAPACK_COL_MAJOR, n, nrhs, a->elements, n, pivot, b->elements, n); +#else + dgesv_(&n, &nrhs, a->elements, &n, pivot, b->elements, &n, &info); +#endif - for (unsigned int i=array->ndim; indim+array->nelements; i++) { - if (MORPHO_ISARRAY(array->data[i])) { - if (!matrix_getarraydimensions(MORPHO_GETARRAY(array->data[i]), dim+n, maxdim-n, &m)) return false; - } - } - *ndim=n+m; + return (info==0 ? LINALGERR_OK : (info>0 ? LINALGERR_MATRIX_SINGULAR : LINALGERR_LAPACK_INVLD_ARGS)); +} + +/** Low level eigensolver */ +static linalgError_t _eigen(objectmatrix *a, MorphoComplex *w, objectmatrix *vec) { + int info, n=a->nrows; + double wr[n], wi[n]; - return true; +#ifdef MORPHO_LINALG_USE_LAPACKE + info=LAPACKE_dgeev(LAPACK_COL_MAJOR, 'N', (vec ? 'V' : 'N'), n, a->elements, n, wr, wi, NULL, n, (vec ? vec->elements : NULL), n); +#else + int lwork=4*n; double work[4*n]; + dgeev_("N", (vec ? "V" : "N"), &n, a->elements, &n, wr, wi, NULL, &n, (vec ? vec->elements : NULL), &n, work, &lwork, &info); +#endif + for (int i=0; i0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS)); } -/** Looks up an array element recursively if necessary */ -value matrix_getarrayelement(objectarray *array, unsigned int ndim, unsigned int *indx) { - unsigned int na=array->ndim; - value out; +/** Low level SVD */ +static linalgError_t _svd(objectmatrix *a, double *s, objectmatrix *u, objectmatrix *vt) { + int info, m=a->nrows, n=a->ncols; + int minmn = (m < n) ? m : n; - if (array_getelement(array, na, indx, &out)==ARRAY_OK) { - if (ndim==na) return out; - if (MORPHO_ISARRAY(out)) { - return matrix_getarrayelement(MORPHO_GETARRAY(out), ndim-na, indx+na); - } - } +#ifdef MORPHO_LINALG_USE_LAPACKE + double* superb = malloc(minmn * sizeof(double)); + info = LAPACKE_dgesvd(LAPACK_COL_MAJOR, + (u ? 'A' : 'N'), // jobu: 'A' = all U columns, 'N' = no U + (vt ? 'A' : 'N'), // jobvt: 'A' = all VT rows, 'N' = no VT + m, n, + a->elements, m, // input matrix A (overwritten) + s, // singular values (min(m,n)) + (u ? u->elements : NULL), m, // U matrix (m×m) + (vt ? vt->elements : NULL), n, // VT matrix (n×n) + superb + ); +#else + int lwork = -1; + double work_query; + // Query optimal work size + dgesvd_((u ? "A" : "N"), (vt ? "A" : "N"), &m, &n, a->elements, &m, s, + (u ? u->elements : NULL), &m, (vt ? vt->elements : NULL), &n, + &work_query, &lwork, &info); + if (info != 0) return (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS); + + lwork = (int)work_query; + double work[lwork]; + dgesvd_((u ? "A" : "N"), (vt ? "A" : "N"), &m, &n, a->elements, &m, s, + (u ? u->elements : NULL), &m, (vt ? vt->elements : NULL), &n, + work, &lwork, &info); +#endif - return MORPHO_NIL; + return (info == 0 ? LINALGERR_OK : (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS)); } -/** Creates a new array from a list of values */ -objectmatrix *object_matrixfromarray(objectarray *array) { - unsigned int dim[2]={0,1}; // The 1 is to allow for vector arrays. - unsigned int ndim=0; - objectmatrix *ret=NULL; +/** Low level QR decomposition without pivoting */ +static linalgError_t _qr(objectmatrix *a, objectmatrix *q, objectmatrix *r) { + int info, m=a->nrows, n=a->ncols; + int minmn = (m < n) ? m : n; + double tau[minmn]; + +#ifdef MORPHO_LINALG_USE_LAPACKE + info = LAPACKE_dgeqrf(LAPACK_COL_MAJOR, m, n, a->elements, m, tau); + if (info != 0) return (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS); +#else + int lwork = -1; + double work_query; + + // Query optimal work size for DGEQRF, which is reused for DORGQR + dgeqrf_(&m, &n, a->elements, &m, tau, &work_query, &lwork, &info); + if (info != 0) return (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS); + + int lwork_geqrf = (int) work_query; + double work[lwork_geqrf]; + lwork = lwork_geqrf; + + // Compute QR factorization without pivoting + dgeqrf_(&m, &n, a->elements, &m, tau, work, &lwork, &info); + if (info != 0) return (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS); +#endif - if (matrix_getarraydimensions(array, dim, 2, &ndim)) { - ret=object_newmatrix(dim[0], dim[1], true); + // Extract R (upper triangle of a) into r + // Copy entire matrix first, then zero out below the diagonal + matrix_copy(a, r); + // Only process columns where there are rows below the diagonal (j < m - 1) + for (int j = 0; j < n && j < m - 1; j++) { + memset(&r->elements[j * m + (j + 1)], 0, (m - j - 1) * sizeof(double)); } - unsigned int indx[2]; - if (ret) for (unsigned int i=0; ielements[j*dim[0]+i]); - } else if (!MORPHO_ISNIL(f)) { - object_free((object *) ret); return NULL; - } - } + // Generate Q from reflectors + if (q) { + // Copy reflectors from a to q (only first n columns, since a is m×n and q is m×m) + // DGEQRF stores reflectors in lower triangle and R in upper triangle of first n columns + for (int j = 0; j < minmn; j++) cblas_dcopy(m, &a->elements[j * m], 1, &q->elements[j * m], 1); + +#ifdef MORPHO_LINALG_USE_LAPACKE + info = LAPACKE_dorgqr(LAPACK_COL_MAJOR, m, minmn, minmn, q->elements, m, tau); + if (info != 0) return (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS); +#else + lwork = lwork_geqrf; + dorgqr_(&m, &minmn, &minmn, q->elements, &m, tau, work, &lwork, &info); + if (info != 0) return (info > 0 ? LINALGERR_OP_FAILED : LINALGERR_LAPACK_INVLD_ARGS); +#endif + + // If Q should be m×m, zero out remaining columns if m > minmn + // DORGQR only generates the first minmn columns, so we zero the rest + if (m > minmn) memset(&q->elements[minmn * m], 0, (m - minmn) * m * sizeof(double)); } - return ret; + return LINALGERR_OK; } -/* - * Create matrices from lists - */ +/* ---------------------- + * Interface definition + * ---------------------- */ -/** Recurses into an objectlist to find the dimensions of the array and all child arrays - * @param[in] list - to search - * @param[out] dim - array of dimensions to be filled out (must be zero'd before initial call) - * @param[in] maxdim - maximum number of dimensions - * @param[out] ndim - number of dimensions of the array */ -bool matrix_getlistdimensions(objectlist *list, unsigned int dim[], unsigned int maxdim, unsigned int *ndim) { - unsigned int m=0; +matrixinterfacedefn matrixdefn = { + .printelfn = _printelfn, + .printeltobufffn = _printeltobufffn, + .getelfn = _getelfn, + .setelfn = _setelfn, + .normfn = _normfn, + .solvefn = _solve, + .eigenfn = _eigen, + .svdfn = _svd, + .qrfn = _qr +}; + +/* ---------------------- + * Constructors + * ---------------------- */ + +/** Create a generic matrix with given type and layout */ +objectmatrix *matrix_newwithtype(objecttype type, MatrixIdx_t nrows, MatrixIdx_t ncols, MatrixIdx_t nvals, bool zero) { + MatrixCount_t nels = nrows*ncols*nvals; + objectmatrix *new = (objectmatrix *) object_new(sizeof(objectmatrix) + nels*sizeof(double), type); - if (maxdim==0) return false; + if (new) { + new->nrows=nrows; + new->ncols=ncols; + new->nvals=nvals; + new->nels=nels; + new->elements=new->matrixdata; + if (zero) memset(new->elements, 0, nels*sizeof(double)); + } - /* Store the length */ - if (list->val.count>dim[0]) dim[0]=list->val.count; + return new; +} + +/** Create a new real matrix */ +objectmatrix *matrix_new(MatrixIdx_t nrows, MatrixIdx_t ncols, bool zero) { + return matrix_newwithtype(OBJECT_MATRIX, nrows, ncols, 1, zero); +} + +/** Clone a matrix */ +objectmatrix *matrix_clone(objectmatrix *in) { + objectmatrix *new = matrix_newwithtype(in->obj.type, in->nrows, in->ncols, in->nvals, false); - for (unsigned int i=0; ival.count; i++) { - if (MORPHO_ISLIST(list->val.data[i]) && maxdim>0) { - matrix_getlistdimensions(MORPHO_GETLIST(list->val.data[i]), dim+1, maxdim-1, &m); - } + if (new) cblas_dcopy((linalg_int_t) in->nels, in->elements, 1, new->elements, 1); + return new; +} + +static bool _getelement(value v, int i, value *out) { + if (MORPHO_ISLIST(v)) { + return list_getelement(MORPHO_GETLIST(v), i, out); + } else if (MORPHO_ISTUPLE(v)) { + return tuple_getelement(MORPHO_GETTUPLE(v), i, out); + } else if (MORPHO_ISNUMBER(v) || MORPHO_ISCOMPLEX(v)) { + if (i==0) { *out = v; return true; } } - *ndim=m+1; - - return true; + return false; } -/** Gets a matrix element from a (potentially nested) list. */ -bool matrix_getlistelement(objectlist *list, unsigned int ndim, unsigned int *indx, value *val) { - value out=MORPHO_NIL; - objectlist *l=list; - for (unsigned int i=0; ival.count) { - out=l->val.data[indx[i]]; - if (incols) ncols=rlen; + } else return NULL; } - if (ndim>2) return false; - - unsigned int indx[2]; - if (ret) for (unsigned int i=0; ielements[j*dim[0]+i]); - } else { - object_free((object *) ret); - return NULL; + objectmatrix *new=matrix_newwithtype(type, nrows, ncols, nvals, true); + if (!new) return NULL; + + for (int i=0; isetelfn(v, jel, new->elements+(j*nrows + i)*new->nvals)!=LINALGERR_OK) goto matrix_listconstructor_cleanup; } } } - return ret; + return new; +matrix_listconstructor_cleanup: + object_free((object *) new); + return NULL; } -/** Creates a matrix from a list of floats */ -objectmatrix *object_matrixfromfloats(unsigned int nrows, unsigned int ncols, double *list) { - objectmatrix *ret=NULL; +/** Construct a matrix from an array */ +objectmatrix *matrix_arrayconstructor(vm *v, objectarray *a, objecttype type, MatrixIdx_t nvals) { + int nrows = MORPHO_GETINTEGERVALUE(a->dimensions[0]); + int ncols = MORPHO_GETINTEGERVALUE(a->dimensions[1]); - ret=object_newmatrix(nrows, ncols, true); - if (ret) cblas_dcopy(ncols*nrows, list, 1, ret->elements, 1); - - return ret; -} - -/* - * Clone matrices - */ - -/** Clone a matrix */ -objectmatrix *object_clonematrix(objectmatrix *in) { - objectmatrix *new = object_newmatrix(in->nrows, in->ncols, false); + objectmatrix *new=matrix_newwithtype(type, nrows, ncols, nvals, true); + if (!new) return NULL; - if (new) { - cblas_dcopy(in->ncols * in->nrows, in->elements, 1, new->elements, 1); + for (int i=0; isetelfn(v, el, new->elements+(j*nrows + i)*new->nvals); + } + } } - return new; } -/* ********************************************************************** - * Matrix operations - * ********************************************************************* */ +/* ---------------------- + * Accessing elements + * ---------------------- */ + + /** @brief Validates index bounds, converting negative indices to positive + * @param idx Pointer to the index, updated if valid and negative + * @param size The size of the dimension + * @returns LINALGERR_OK if conversion successful, LINALGERR_INDX_OUT_OF_BNDS if out of bounds */ +linalgError_t matrix_validateindex(MatrixIdx_t *idx, MatrixIdx_t size) { + if (*idx < 0) { + if (*idx < -size) return LINALGERR_INDX_OUT_OF_BNDS; + *idx = size + *idx; + } else if (*idx >= size) return LINALGERR_INDX_OUT_OF_BNDS; + return LINALGERR_OK; +} /** @brief Sets a matrix element. - @returns true if the element is in the range of the matrix, false otherwise */ -bool matrix_setelement(objectmatrix *matrix, unsigned int row, unsigned int col, double value) { - if (colncols && rownrows) { - matrix->elements[col*matrix->nrows+row]=value; - return true; - } - return false; + @returns LINALGERR_OK if successful, LINALGERR_INDX_OUT_OF_BNDS if index out of bounds */ +linalgError_t matrix_setelement(objectmatrix *matrix, MatrixIdx_t row, MatrixIdx_t col, double value) { + MatrixIdx_t row_idx = row, col_idx = col; + LINALG_ERRCHECKRETURN(matrix_validateindex(&row_idx, matrix->nrows)); + LINALG_ERRCHECKRETURN(matrix_validateindex(&col_idx, matrix->ncols)); + matrix->elements[matrix->nvals*(col_idx*matrix->nrows+row_idx)]=value; + return LINALGERR_OK; } /** @brief Gets a matrix element - * @returns true if the element is in the range of the matrix, false otherwise */ -bool matrix_getelement(objectmatrix *matrix, unsigned int row, unsigned int col, double *value) { - if (colncols && rownrows) { - if (value) *value=matrix->elements[col*matrix->nrows+row]; - return true; - } - return false; + * @returns LINALGERR_OK if successful, LINALGERR_INDX_OUT_OF_BNDS if index out of bounds */ +linalgError_t matrix_getelement(objectmatrix *matrix, MatrixIdx_t row, MatrixIdx_t col, double *value) { + MatrixIdx_t row_idx = row, col_idx = col; + LINALG_ERRCHECKRETURN(matrix_validateindex(&row_idx, matrix->nrows)); + LINALG_ERRCHECKRETURN(matrix_validateindex(&col_idx, matrix->ncols)); + if (value) *value=matrix->elements[matrix->nvals*(col_idx*matrix->nrows+row_idx)]; + return LINALGERR_OK; } -/** @brief Gets a column's entries - * @param[in] matrix - the matrix - * @param[in] col - column number - * @param[out] v - column entries (matrix->nrows in number) - * @returns true if the element is in the range of the matrix, false otherwise */ -bool matrix_getcolumn(objectmatrix *matrix, unsigned int col, double **v) { - if (colncols) { - *v=&matrix->elements[col*matrix->nrows]; - return true; - } - return false; +/** @brief Gets a pointer to a matrix element + * @returns LINALGERR_OK if successful, LINALGERR_INDX_OUT_OF_BNDS if index out of bounds */ +linalgError_t matrix_getelementptr(objectmatrix *matrix, MatrixIdx_t row, MatrixIdx_t col, double **value) { + MatrixIdx_t row_idx = row, col_idx = col; + LINALG_ERRCHECKRETURN(matrix_validateindex(&row_idx, matrix->nrows)); + LINALG_ERRCHECKRETURN(matrix_validateindex(&col_idx, matrix->ncols)); + if (value) *value=matrix->elements+matrix->nvals*(col_idx*matrix->nrows+row_idx); + return LINALGERR_OK; } -/** @brief Sets a column's entries - * @param[in] matrix - the matrix - * @param[in] col - column number - * @param[in] v - column entries (matrix->nrows in number) - * @returns true if the element is in the range of the matrix, false otherwise */ -bool matrix_setcolumn(objectmatrix *matrix, unsigned int col, double *v) { - if (colncols) { - cblas_dcopy(matrix->nrows, v, 1, &matrix->elements[col*matrix->nrows], 1); - return true; - } - return false; +/** @brief Gets a pointer to a matrix column + * @returns true if the column is in the range of the matrix, false otherwise */ +linalgError_t matrix_getcolumnptr(objectmatrix *matrix, MatrixIdx_t col, double **value) { + return matrix_getelementptr(matrix, 0, col, value); +} + +/** Copies the column col of matrix a into the column vector b */ +linalgError_t matrix_getcolumn(objectmatrix *a, MatrixIdx_t col, objectmatrix *b) { + MatrixIdx_t col_idx = col; + LINALG_ERRCHECKRETURN(matrix_validateindex(&col_idx, a->ncols)); + if (b->nels!=a->nrows*a->nvals) return LINALGERR_INCOMPATIBLE_DIM; + cblas_dcopy((linalg_int_t) b->nels, a->elements+a->nvals*col_idx*a->nrows, 1, b->elements, 1); + return LINALGERR_OK; +} + +/** Copies the column vector b into column col of matrix a */ +linalgError_t matrix_setcolumn(objectmatrix *a, MatrixIdx_t col, objectmatrix *b) { + MatrixIdx_t col_idx = col; + LINALG_ERRCHECKRETURN(matrix_validateindex(&col_idx, a->ncols)); + if (b->nels!=a->nrows*a->nvals) return LINALGERR_INCOMPATIBLE_DIM; + cblas_dcopy((linalg_int_t) b->nels, b->elements, 1, a->elements+a->nvals*col_idx*a->nrows, 1); + return LINALGERR_OK; +} + +/** Copies the column vector b as a raw list of doubles into column col of matrix a */ +linalgError_t matrix_setcolumnptr(objectmatrix *a, MatrixIdx_t col, double *b) { + MatrixIdx_t col_idx = col; + LINALG_ERRCHECKRETURN(matrix_validateindex(&col_idx, a->ncols)); + cblas_dcopy((linalg_int_t) a->nrows*a->nvals, b, 1, a->elements+a->nvals*col_idx*a->nrows, 1); + return LINALGERR_OK; } /** @brief Add a vector to a column in a matrix @@ -296,1269 +449,1145 @@ bool matrix_setcolumn(objectmatrix *matrix, unsigned int col, double *v) { * @param[in] alpha - scale * @param[out] v - column entries (matrix->nrows in number) [should have m->nrows entries] * @returns true on success */ -bool matrix_addtocolumn(objectmatrix *m, unsigned int col, double alpha, double *v) { - if (colncols) { - cblas_daxpy(m->nrows, alpha, v, 1, &m->elements[col*m->nrows], 1); - return true; - } - return false; +linalgError_t matrix_addtocolumnptr(objectmatrix *a, MatrixIdx_t col, double alpha, double *b) { + if (col<0 || col>=a->ncols) return LINALGERR_INDX_OUT_OF_BNDS; + + cblas_daxpy(a->nrows*a->nvals, alpha, b, 1, a->elements+a->nvals*col*a->nrows, 1); + return LINALGERR_OK; } -/* ********************************************************************** - * Matrix arithmetic - * ********************************************************************* */ +/** Counts the number of dofs in a matrix */ +MatrixCount_t matrix_countdof(objectmatrix *a) { + return a->ncols*a->nrows*a->nvals; +} + +/* ---------------------- + * Arithmetic operations + * ---------------------- */ -/** Copies one matrix to another */ -unsigned int matrix_countdof(objectmatrix *a) { - return a->ncols*a->nrows; +/** Vector addition: Performs y <- alpha*x + y */ +linalgError_t matrix_axpy(double alpha, objectmatrix *x, objectmatrix *y) { + if (!(x->ncols==y->ncols && x->nrows==y->nrows)) return LINALGERR_INCOMPATIBLE_DIM; + + cblas_daxpy((linalg_int_t) x->nels, alpha, x->elements, 1, y->elements, 1); + return LINALGERR_OK; } -/** Copies one matrix to another */ -objectmatrixerror matrix_copy(objectmatrix *a, objectmatrix *out) { - if (a->ncols==out->ncols && a->nrows==out->nrows) { - cblas_dcopy(a->ncols * a->nrows, a->elements, 1, out->elements, 1); - return MATRIX_OK; - } - return MATRIX_INCMPTBLDIM; +/** Copies a matrix y <- x */ +linalgError_t matrix_copy(objectmatrix *x, objectmatrix *y) { + if (!(x->ncols==y->ncols && x->nrows==y->nrows)) return LINALGERR_INCOMPATIBLE_DIM; + + cblas_dcopy((linalg_int_t) x->nels, x->elements, 1, y->elements, 1); + return LINALGERR_OK; } -/** Copies a matrix to another at an arbitrary point */ -objectmatrixerror matrix_copyat(objectmatrix *a, objectmatrix *out, int row0, int col0) { - if (col0+a->ncols<=out->ncols && row0+a->nrows<=out->nrows) { - for (int j=0; jncols; j++) { - for (int i=0; inrows; i++) { - double value; - if (!matrix_getelement(a, i, j, &value)) return MATRIX_BNDS; - if (!matrix_setelement(out, row0+i, col0+j, value)) return MATRIX_BNDS; - } +/** Copies one matrix into another at an arbitrary point */ +linalgError_t matrix_copyat(objectmatrix *a, objectmatrix *out, int row0, int col0) { + if (!(col0+a->ncols<=out->ncols && row0+a->nrows<=out->nrows)) return LINALGERR_INCOMPATIBLE_DIM; + + for (int j=0; jncols; j++) { + for (int i=0; inrows; i++) { + double *src, *dest; + LINALG_ERRCHECKRETURN(matrix_getelementptr(a, i, j, &src)); + LINALG_ERRCHECKRETURN(matrix_getelementptr(out, row0+i, col0+j, &dest)); + memcpy(dest, src, sizeof(double)*a->nvals); } - - return MATRIX_OK; } - return MATRIX_INCMPTBLDIM; + return LINALGERR_OK; } -/** Performs a + b -> out. */ -objectmatrixerror matrix_add(objectmatrix *a, objectmatrix *b, objectmatrix *out) { - if (a->ncols==b->ncols && a->ncols==out->ncols && - a->nrows==b->nrows && a->nrows==out->nrows) { - if (a!=out) cblas_dcopy(a->ncols * a->nrows, a->elements, 1, out->elements, 1); - cblas_daxpy(a->ncols * a->nrows, 1.0, b->elements, 1, out->elements, 1); - return MATRIX_OK; - } - return MATRIX_INCMPTBLDIM; +/** Scales a matrix x <- scale * x >*/ +void matrix_scale(objectmatrix *x, double scale) { + cblas_dscal((linalg_int_t) x->nels, scale, x->elements, 1); } -/** Performs lambda*a + beta -> out. */ -objectmatrixerror matrix_addscalar(objectmatrix *a, double lambda, double beta, objectmatrix *out) { - if (a->ncols==out->ncols && a->nrows==out->nrows) { - for (unsigned int i=0; inrows*out->ncols; i++) { - out->elements[i]=lambda*a->elements[i]+beta; - } - return MATRIX_OK; - } +/** Loads the zero matrix a <- 0 */ +linalgError_t matrix_zero(objectmatrix *x) { + memset(x->elements, 0, sizeof(double)*x->nrows*x->ncols*x->nvals); + return LINALGERR_OK; +} - return MATRIX_INCMPTBLDIM; +/** Loads the identity matrix a <- I(n) */ +linalgError_t matrix_identity(objectmatrix *x) { + if (x->ncols!=x->nrows) return LINALGERR_NOT_SQUARE; + matrix_zero(x); + for (int i=0; inrows; i++) x->elements[x->nvals*(i+x->nrows*i)]=1.0; + return LINALGERR_OK; } -/** Performs a + lambda*b -> a. */ -objectmatrixerror matrix_accumulate(objectmatrix *a, double lambda, objectmatrix *b) { - if (a->ncols==b->ncols && a->nrows==b->nrows ) { - cblas_daxpy(a->ncols * a->nrows, lambda, b->elements, 1, a->elements, 1); - return MATRIX_OK; - } - return MATRIX_INCMPTBLDIM; +/** Performs z <- alpha*(x*y) + beta*z */ +linalgError_t matrix_mmul(double alpha, objectmatrix *x, objectmatrix *y, double beta, objectmatrix *z) { + if (!(x->ncols==y->nrows && x->nrows==z->nrows && y->ncols==z->ncols)) return LINALGERR_INCOMPATIBLE_DIM; + + cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, x->nrows, y->ncols, x->ncols, alpha, x->elements, x->nrows, y->elements, y->nrows, beta, z->elements, z->nrows); + return LINALGERR_OK; } -/** Performs a - b -> out */ -objectmatrixerror matrix_sub(objectmatrix *a, objectmatrix *b, objectmatrix *out) { - if (a->ncols==b->ncols && a->ncols==out->ncols && - a->nrows==b->nrows && a->nrows==out->nrows) { - if (a!=out) cblas_dcopy(a->ncols * a->nrows, a->elements, 1, out->elements, 1); - cblas_daxpy(a->ncols * a->nrows, -1.0, b->elements, 1, out->elements, 1); - return MATRIX_OK; - } - return MATRIX_INCMPTBLDIM; +linalgError_t matrix_mul(objectmatrix *x, objectmatrix *y, objectmatrix *z) { + return matrix_mmul(1.0, x, y, 0.0, z); } -/** Performs a * b -> out */ -objectmatrixerror matrix_mul(objectmatrix *a, objectmatrix *b, objectmatrix *out) { - if (a->ncols==b->nrows && a->nrows==out->nrows && b->ncols==out->ncols) { - cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, a->nrows, b->ncols, a->ncols, 1.0, a->elements, a->nrows, b->elements, b->nrows, 0.0, out->elements, out->nrows); - return MATRIX_OK; +/** Performs x <- alpha*x + beta */ +linalgError_t matrix_addscalar(objectmatrix *x, double alpha, double beta) { + for (MatrixCount_t i=0; incols*x->nrows; i++) { + for (int k=0; knvals; k++) { + x->elements[i*x->nvals+k]*=alpha; + if (k==0) x->elements[i*x->nvals+k]+=beta; + } } - return MATRIX_INCMPTBLDIM; + return LINALGERR_OK; } -/** Finds the Frobenius inner product of two matrices */ -objectmatrixerror matrix_inner(objectmatrix *a, objectmatrix *b, double *out) { - if (a->ncols==b->ncols && a->nrows==b->nrows) { - *out=cblas_ddot(a->ncols*a->nrows, a->elements, 1, b->elements, 1); - return MATRIX_OK; +/** Performs y <- x^T>*/ +linalgError_t matrix_transpose(objectmatrix *x, objectmatrix *y) { + if (!(x->ncols==y->nrows && x->nrows==y->ncols)) return LINALGERR_INCOMPATIBLE_DIM; + + for (MatrixCount_t i=0; incols; i++) { + for (MatrixCount_t j=0; jnrows; j++) { + for (int k=0; knvals; k++) { + y->elements[j*y->nrows*y->nvals+i*y->nvals+k] = x->elements[i*x->nrows*x->nvals+j*x->nvals+k]; + } + } } - return MATRIX_INCMPTBLDIM; + return LINALGERR_OK; } -/** Computes the outer product of two matrices */ -objectmatrixerror matrix_outer(objectmatrix *a, objectmatrix *b, objectmatrix *out) { - int m=a->nrows*a->ncols, n=b->nrows*b->ncols; - if (m==out->nrows && n==out->ncols) { - cblas_dger(CblasColMajor, m, n, 1, a->elements, 1, b->elements, 1, out->elements, out->nrows); - return MATRIX_OK; - } - return MATRIX_INCMPTBLDIM; -} - -/** Solves the system a.x = b - * @param[in] a lhs - * @param[in] b rhs - * @param[in] out - the solution x - * @param[out] lu - LU decomposition of a; you must provide an array the same size as a. - * @param[out] pivot - you must provide an array with the same number of rows as a. - * @returns objectmatrixerror indicating the status; MATRIX_OK indicates success. - * */ -static objectmatrixerror matrix_div(objectmatrix *a, objectmatrix *b, objectmatrix *out, double *lu, int *pivot) { - int n=a->nrows, nrhs = b->ncols, info; - - cblas_dcopy(a->ncols * a->nrows, a->elements, 1, lu, 1); - if (b!=out) cblas_dcopy(b->ncols * b->nrows, b->elements, 1, out->elements, 1); -#ifdef MORPHO_LINALG_USE_LAPACKE - info=LAPACKE_dgesv(LAPACK_COL_MAJOR, n, nrhs, lu, n, pivot, out->elements, n); -#else - dgesv_(&n, &nrhs, lu, &n, pivot, out->elements, &n, &info); -#endif - - return (info==0 ? MATRIX_OK : (info>0 ? MATRIX_SING : MATRIX_INVLD)); +/* ---------------------- + * Unary operations + * ---------------------- */ + +/** Computes various matrix norms */ +double matrix_norm(objectmatrix *a, matrix_norm_t norm) { + return matrix_getinterface(a)->normfn(a, norm); } -/** Solves the system a.x = b for small matrices (test with MATRIX_ISSMALL) - * @warning Uses the C stack for storage, which avoids malloc but can cause stack overflow */ -objectmatrixerror matrix_divs(objectmatrix *a, objectmatrix *b, objectmatrix *out) { - if (a->ncols==b->nrows && a->ncols == out->nrows) { - int pivot[a->nrows]; - double lu[a->nrows*a->ncols]; - - return matrix_div(a, b, out, lu, pivot); +/** Computes the sum of all elements in a matrix */ +void matrix_sum(objectmatrix *a, double *sum) { + double c[a->nvals], y, t; + for (int i=0; invals; i++) { sum[i]=0; c[i]=0; } + + for (MatrixCount_t i=0; inels; i+=a->nvals) { + for (int k=0; knvals; k++) { + y=a->elements[i+k]-c[k]; + t=sum[k]+y; + c[k]=(t-sum[k])-y; + sum[k]=t; + } } - return MATRIX_INCMPTBLDIM; } -/** Solves the system a.x = b for large matrices (test with MATRIX_ISSMALL) */ -objectmatrixerror matrix_divl(objectmatrix *a, objectmatrix *b, objectmatrix *out) { - objectmatrixerror ret = MATRIX_ALLOC; // Returned if allocation fails - if (!(a->ncols==b->nrows && a->ncols == out->nrows)) return MATRIX_INCMPTBLDIM; +/** Calculate the trace of a matrix */ +linalgError_t matrix_trace(objectmatrix *a, double *out) { + if (a->nrows!=a->ncols) return LINALGERR_NOT_SQUARE; + *out = 0.0; + for (int i = 0; i < a->nrows; i++) { + *out += a->elements[a->nvals * (i * a->nrows + i)]; + } - int *pivot=MORPHO_MALLOC(sizeof(int)*a->nrows); - double *lu=MORPHO_MALLOC(sizeof(double)*a->nrows*a->ncols); + return LINALGERR_OK; +} + +/* ---------------------- + * Binary operations + * ---------------------- */ + +/** Finds the Frobenius inner product of two matrices */ +linalgError_t matrix_inner(objectmatrix *x, objectmatrix *y, double *out) { + if (!(x->ncols==y->ncols && x->nrows==y->nrows)) return LINALGERR_INCOMPATIBLE_DIM; - if (pivot && lu) ret=matrix_div(a, b, out, lu, pivot); + *out=cblas_ddot((linalg_int_t) x->nels, x->elements, 1, y->elements, 1); + return LINALGERR_OK; +} + +/** Rank 1 update: Performs c <- alpha*a \outer b + c; a and b are treated as column vectors */ +linalgError_t matrix_r1update(double alpha, objectmatrix *a, objectmatrix *b, objectmatrix *c) { + MatrixIdx_t m=a->nrows*a->ncols, n=b->nrows*b->ncols; + if (!(m==c->nrows && n==c->ncols)) return LINALGERR_INCOMPATIBLE_DIM; + cblas_dger(CblasColMajor, m, n, alpha, a->elements, 1, b->elements, 1, c->elements, c->nrows); + return LINALGERR_OK; +} + +/** Solve the linear system a.x = b using stack allocated memory for temporary */ +linalgError_t matrix_solvesmall(objectmatrix *a, objectmatrix *b) { + int pivot[a->nrows]; + double els[a->nels]; + objectmatrix A = MORPHO_STATICMATRIX(els, a->nrows, a->ncols); + matrix_copy(a, &A); + return (matrix_getinterface(a)->solvefn) (&A, b, pivot); +} + +/** Solve the linear system a.x = b using heap allocated memory for temporary */ +linalgError_t matrix_solvelarge(objectmatrix *a, objectmatrix *b) { + int *pivot = MORPHO_MALLOC(sizeof(int)*a->nrows); + objectmatrix *A = matrix_clone(a); + linalgError_t out = LINALGERR_ALLOC; + if (pivot && A) { + out = (matrix_getinterface(a)->solvefn) (A, b, pivot); + } + if (A) object_free((object *) A); if (pivot) MORPHO_FREE(pivot); - if (lu) MORPHO_FREE(lu); - - return ret; + return out; +} + +/** Solve the linear system a.x = b; automatrically allocates storage depending on size of the matrix + * @param[in] a lhs + * @param[in|out] b rhs — overwritten by the solution + * @returns linalgError_t indicating the status; MATRIX_OK indicates success. */ +linalgError_t matrix_solve(objectmatrix *a, objectmatrix *b) { + if (MATRIX_ISSMALL(a)) return matrix_solvesmall(a, b); + else return matrix_solvelarge(a, b); } /** Inverts the matrix a - * @param[in] a lhs - * @param[in] out - the solution x - * @returns objectmatrixerror indicating the status; MATRIX_OK indicates success. - * */ -objectmatrixerror matrix_inverse(objectmatrix *a, objectmatrix *out) { + * @param[in] a matrix to be inverted + * @returns linalgError_t indicating the status; MATRIX_OK indicates success. */ +linalgError_t matrix_inverse(objectmatrix *a) { int nrows=a->nrows, ncols=a->ncols, info; - if (!(a->ncols==out->nrows && a->ncols == out->nrows)) return MATRIX_INCMPTBLDIM; - int pivot[nrows]; - cblas_dcopy(a->ncols * a->nrows, a->elements, 1, out->elements, 1); #ifdef MORPHO_LINALG_USE_LAPACKE - info=LAPACKE_dgetrf(LAPACK_COL_MAJOR, nrows, ncols, out->elements, nrows, pivot); + info=LAPACKE_dgetrf(LAPACK_COL_MAJOR, nrows, ncols, a->elements, nrows, pivot); #else - dgetrf_(&nrows, &ncols, out->elements, &nrows, pivot, &info); + dgetrf_(&nrows, &ncols, a->elements, &nrows, pivot, &info); #endif - - if (info!=0) return (info>0 ? MATRIX_SING : MATRIX_INVLD); + if (info!=0) return (info>0 ? LINALGERR_MATRIX_SINGULAR : LINALGERR_LAPACK_INVLD_ARGS); #ifdef MORPHO_LINALG_USE_LAPACKE - info=LAPACKE_dgetri(LAPACK_COL_MAJOR, nrows, out->elements, nrows, pivot); + info=LAPACKE_dgetri(LAPACK_COL_MAJOR, nrows, a->elements, nrows, pivot); #else int lwork=nrows*ncols; double work[nrows*ncols]; - dgetri_(&nrows, out->elements, &nrows, pivot, work, &lwork, &info); + dgetri_(&nrows, a->elements, &nrows, pivot, work, &lwork, &info); #endif - return (info==0 ? MATRIX_OK : (info>0 ? MATRIX_SING : MATRIX_INVLD)); + return (info==0 ? LINALGERR_OK : (info>0 ? LINALGERR_MATRIX_SINGULAR : LINALGERR_LAPACK_INVLD_ARGS)); } -/** Compute eigenvalues and eigenvectors of a matrix - * @param[in] a - an objectmatrix to diagonalize of size n - * @param[out] wr - a buffer of size n will hold the real part of the eigenvalues on exit - * @param[out] wi - a buffer of size n will hold the imag part of the eigenvalues on exit - * @param[out] vec - (optional) will be filled out with eigenvectors as columns (should be of size n) - * @returns an error code or MATRIX_OK on success */ -objectmatrixerror matrix_eigensystem(objectmatrix *a, double *wr, double *wi, objectmatrix *vec) { - int info, n=a->nrows; - if (a->nrows!=a->ncols) return MATRIX_NSQ; - if (vec && ((a->nrows!=vec->nrows) || (a->nrows!=vec->ncols))) return MATRIX_INCMPTBLDIM; - - // Copy a to prevent destruction - size_t size = ((size_t) n) * ((size_t) n) * sizeof(double); - double *acopy=MORPHO_MALLOC(size); - if (!acopy) return MATRIX_ALLOC; - cblas_dcopy(n*n, a->elements, 1, acopy, 1); +/** Interface to eigensystem */ +linalgError_t matrix_eigen(objectmatrix *a, MorphoComplex *w, objectmatrix *vec) { + if (a->nrows!=a->ncols) return LINALGERR_NOT_SQUARE; + if (vec && ((a->nrows!=vec->nrows) || (a->nrows!=vec->ncols))) return LINALGERR_INCOMPATIBLE_DIM; -#ifdef MORPHO_LINALG_USE_LAPACKE - info=LAPACKE_dgeev(LAPACK_COL_MAJOR, 'N', (vec ? 'V' : 'N'), n, acopy, n, wr, wi, NULL, n, (vec ? vec->elements : NULL), n); -#else - int lwork=4*n; double work[4*n]; - dgeev_("N", (vec ? "V" : "N"), &n, acopy, &n, wr, wi, NULL, &n, (vec ? vec->elements : NULL), &n, work, &lwork, &info); -#endif + matrix_eigenfn_t efn = matrix_getinterface(a)->eigenfn; + if (!efn) return LINALGERR_NOT_SUPPORTED; - if (acopy) MORPHO_FREE(acopy); // Free up buffer + objectmatrix *temp = matrix_clone(a); + if (!temp) return LINALGERR_ALLOC; - if (info!=0) return (info>0 ? MATRIX_FAILED : MATRIX_INVLD); - - return MATRIX_OK; + return efn(temp, w, vec); } +/* ---------------------- + * Display + * ---------------------- */ -/** Sums all elements of a matrix using Kahan summation */ -double matrix_sum(objectmatrix *a) { - unsigned int nel=a->ncols*a->nrows; - double sum=0.0, c=0.0, y,t; - - for (unsigned int i=0; ielements[i]-c; - t=sum+y; - c=(t-sum)-y; - sum=t; +/** Prints a matrix */ +void matrix_print(vm *v, objectmatrix *m) { + matrixinterfacedefn *interface=matrix_getinterface(m); + double *elptr; + for (MatrixIdx_t i=0; inrows; i++) { // Rows run from 0...m + morpho_printf(v, "[ "); + for (MatrixIdx_t j=0; jncols; j++) { // Columns run from 0...k + matrix_getelementptr(m, i, j, &elptr); + (*interface->printelfn) (v, elptr); + morpho_printf(v, " "); + } + morpho_printf(v, "]%s", (inrows-1 ? "\n" : "")); } - return sum; } -/** Norms */ - -/** Computes the Frobenius norm of a matrix */ -double matrix_norm(objectmatrix *a) { - double nrm2=cblas_dnrm2(a->ncols*a->nrows, a->elements, 1); - return nrm2; +/** Prints a matrix to a buffer */ +bool matrix_printtobuffer(objectmatrix *m, char *format, varray_char *out) { + matrixinterfacedefn *interface=matrix_getinterface(m); + double *elptr; + for (MatrixIdx_t i=0; inrows; i++) { // Rows run from 0...m + varray_charadd(out, "[ ", 2); + + for (MatrixIdx_t j=0; jncols; j++) { // Columns run from 0...k + matrix_getelementptr(m, i, j, &elptr); + if (!(*interface->printeltobufffn) (out, format, elptr)) return false; + varray_charadd(out, " ", 1); + } + varray_charadd(out, "]", 1); + if (inrows-1) varray_charadd(out, "\n", 1); + } + return true; } -/** Computes the L1 norm of a matrix */ -double matrix_L1norm(objectmatrix *a) { - unsigned int nel=a->ncols*a->nrows; - double sum=0.0, c=0.0, y,t; +/* ---------------------- + * Roll + * ---------------------- */ + +/** Rolls the matrix list */ +static void _rollflat(objectmatrix *a, objectmatrix *b, int nplaces) { + MatrixCount_t N=a->nrows*a->ncols*a->nvals; + MatrixCount_t n = abs(nplaces)*a->nvals; + if (n>N) n = n % N; + MatrixCount_t Np = N - n; // Number of elements to roll - for (unsigned int i=0; ielements[i])-c; - t=sum+y; - c=(t-sum)-y; - sum=t; + if (nplaces<0) { + memcpy(b->matrixdata, a->matrixdata+n, sizeof(double)*Np); + memcpy(b->matrixdata+Np, a->matrixdata, sizeof(double)*n); + } else { + memcpy(b->matrixdata+n, a->matrixdata, sizeof(double)*Np); + if (n>0) memcpy(b->matrixdata, a->matrixdata+Np, sizeof(double)*n); } - return sum; } -/** Computes the Ln norm of a matrix */ -double matrix_Lnnorm(objectmatrix *a, double n) { - unsigned int nel=a->ncols*a->nrows; - double sum=0.0, c=0.0, y,t; - - for (unsigned int i=0; ielements[i],n)-c; - t=sum+y; - c=(t-sum)-y; - sum=t; - } - return pow(sum,1.0/n); +/** Copies a arow from matrix a into brow for matrix b */ +static void _copyrow(objectmatrix *a, MatrixIdx_t arow, objectmatrix *b, MatrixIdx_t brow) { + for (MatrixIdx_t i=0; incols; i++) + memcpy(b->elements+b->nvals*(i*b->nrows+brow), a->elements+a->nvals*(i*a->nrows+arow), sizeof(double)*a->nvals); } -/** Computes the Linf norm of a matrix */ -double matrix_Linfnorm(objectmatrix *a) { - unsigned int nel=a->ncols*a->nrows; - double max=0.0; +/** Rolls a list by a number of elements along a given axis; stores the result in b */ +linalgError_t matrix_roll(objectmatrix *a, int nplaces, int axis, objectmatrix *b) { + if (!(a->nrows==b->nrows && a->ncols==b->ncols && a->nvals==b->nvals)) return LINALGERR_INCOMPATIBLE_DIM; - for (unsigned int i=0; ielements[i]); - if (y>max) max=y; + switch(axis) { + case 0: + for (int i=0; inrows; i++) { + int j = (i+nplaces); + while (j<0) j+=a->nrows; + _copyrow(a, i, b, j % a->nrows); + } + break; + case 1: _rollflat(a, b, nplaces*a->nrows); break; + default: return LINALGERR_NOT_SUPPORTED; } - return max; + + return LINALGERR_OK; } -/** Transpose a matrix */ -objectmatrixerror matrix_transpose(objectmatrix *a, objectmatrix *out) { - if (!(a->ncols==out->nrows && a->nrows == out->ncols)) return MATRIX_INCMPTBLDIM; +/* ********************************************************************** + * Matrix constructors + * ********************************************************************** */ - /* Copy elements a column at a time */ - for (unsigned int i=0; incols; i++) { - cblas_dcopy(a->nrows, a->elements+(i*a->nrows), 1, out->elements+i, a->ncols); - } - return MATRIX_OK; +value matrix_constructor__int_int(vm *v, int nargs, value *args) { + MatrixIdx_t nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)), + ncols = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1)); + + objectmatrix *new=matrix_new(nrows, ncols, true); + return morpho_wrapandbind(v, (object *) new); } -/** Calculate the trace of a matrix */ -objectmatrixerror matrix_trace(objectmatrix *a, double *out) { - if (a->nrows!=a->ncols) return MATRIX_NSQ; - *out=1.0; - *out=cblas_ddot(a->nrows, a->elements, a->ncols+1, out, 0); +value matrix_constructor__int(vm *v, int nargs, value *args) { + MatrixIdx_t nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); - return MATRIX_OK; + objectmatrix *new=matrix_new(nrows, 1, true); + return morpho_wrapandbind(v, (object *) new); } -/** Scale a matrix */ -objectmatrixerror matrix_scale(objectmatrix *a, double scale) { - cblas_dscal(a->ncols*a->nrows, scale, a->elements, 1); - - return MATRIX_OK; +/** Clones a matrix */ +value matrix_constructor__matrix(vm *v, int nargs, value *args) { + objectmatrix *a = MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + return morpho_wrapandbind(v, (object *) matrix_clone(a)); } -/** Load the indentity matrix*/ -objectmatrixerror matrix_identity(objectmatrix *a) { - if (a->ncols!=a->nrows) return MATRIX_NSQ; - memset(a->elements, 0, sizeof(double)*a->nrows*a->ncols); - for (int i=0; inrows; i++) a->elements[i+a->nrows*i]=1.0; - return MATRIX_OK; +/** Constructs a matrix from a list of lists or tuples */ +value matrix_constructor__list(vm *v, int nargs, value *args) { + objectmatrix *new = matrix_listconstructor(v, MORPHO_GETARG(args, 0), OBJECT_MATRIX, 1); +#ifdef MORPHO_INCLUDE_SPARSE + if (!new) { + /** Could this be a concatenation operation? */ + objectsparseerror err = sparse_catmatrix(MORPHO_GETLIST(MORPHO_GETARG(args, 0)), &new); + if (err==SPARSE_INVLDINIT) { + morpho_runtimeerror(v, LINALG_INVLDARGS); + } else if (err!=SPARSE_OK) sparse_raiseerror(v, err); + } +#else + if (!new) morpho_runtimeerror(v, LINALG_INVLDARGS); +#endif + return morpho_wrapandbind(v, (object *) new); } -/** Sets a matrix to zero */ -objectmatrixerror matrix_zero(objectmatrix *a) { - memset(a->elements, 0, sizeof(double)*a->nrows*a->ncols); +/** Constructs a matrix from an array */ +value matrix_constructor__array(vm *v, int nargs, value *args) { + objectarray *a = MORPHO_GETARRAY(MORPHO_GETARG(args, 0)); + if (a->ndim!=2) { morpho_runtimeerror(v, LINALG_INVLDARGS); return MORPHO_NIL; } - return MATRIX_OK; + objectmatrix *new = matrix_arrayconstructor(v, a, OBJECT_MATRIX, 1); + return morpho_wrapandbind(v, (object *) new); } -/** Prints a matrix */ -void matrix_print(vm *v, objectmatrix *m) { - for (int i=0; inrows; i++) { // Rows run from 0...m - morpho_printf(v, "[ "); - for (int j=0; jncols; j++) { // Columns run from 0...k - double val; - matrix_getelement(m, i, j, &val); - morpho_printf(v, "%g ", (fabs(val)nrows-1 ? "\n" : "")); - } +/** Constructs a matrix from a sparse matrix */ +value matrix_constructor__sparse(vm *v, int nargs, value *args) { + objectmatrix *new = NULL; + objectsparseerror err=sparse_tomatrix(MORPHO_GETSPARSE(MORPHO_GETARG(args, 0)), &new); + if (err!=SPARSE_OK) morpho_runtimeerror(v, LINALG_INVLDARGS); + return morpho_wrapandbind(v, (object *) new); } -/** Prints a matrix to a buffer */ -bool matrix_printtobuffer(objectmatrix *m, char *format, varray_char *out) { - for (int i=0; inrows; i++) { // Rows run from 0...m - varray_charadd(out, "[ ", 2); - - for (int j=0; jncols; j++) { // Columns run from 0...k - double val; - matrix_getelement(m, i, j, &val); - if (!format_printtobuffer(MORPHO_FLOAT(val), format, out)) return false; - varray_charadd(out, " ", 1); - } - varray_charadd(out, "]", 1); - if (inrows-1) varray_charadd(out, "\n", 1); - } - return true; +value matrix_constructor__err(vm *v, int nargs, value *args) { + morpho_runtimeerror(v, MATRIX_CONSTRUCTOR); + return MORPHO_NIL; } -/** Matrix eigensystem */ -bool matrix_eigen(vm *v, objectmatrix *a, value *evals, value *evecs) { - double *ev = MORPHO_MALLOC(sizeof(double)*a->nrows*2); // Allocate temporary memory for eigenvalues - double *er=ev, *ei=ev+a->nrows; - - objectmatrix *vecs=NULL; // A new matrix for eigenvectors - objectlist *vallist = object_newlist(0, NULL); // List to hold eigenvalues - bool success=false; - - if (evecs) vecs=object_clonematrix(a); // Clones a to hold eigenvectors - - // Check that everything was allocated correctly - if (!(ev && vallist && (!evecs || vecs))) { - morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto matrix_eigen_cleanup; }; +/** Creates an identity matrix */ +value matrix_identityconstructor(vm *v, int nargs, value *args) { + if (nargs!=1) { morpho_runtimeerror(v, MATRIX_IDENTCONSTRUCTOR); return MORPHO_NIL; } - objectmatrixerror err=matrix_eigensystem(a, er, ei, vecs); + MatrixIdx_t n = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + + objectmatrix *new = matrix_new(n,n,false); + if (new) matrix_identity(new); - if (err!=MATRIX_OK) { - matrix_raiseerror(v, err); - goto matrix_eigen_cleanup; - } - - // Now process the eigenvalues - for (int i=0; inrows; i++) { - if (fabs(ei[i])val.count; i++) { - if (MORPHO_ISOBJECT(vallist->val.data[i])) object_free(MORPHO_GETOBJECT(vallist->val.data[i])); - } - object_free((object *) vallist); - } - if (vecs) object_free((object *) vecs); - } - - return success; + return morpho_wrapandbind(v, (object *) new); } /* ********************************************************************** * Matrix veneer class - * ********************************************************************* */ + * ********************************************************************** */ -/** Constructs a Matrix object */ -value matrix_constructor(vm *v, int nargs, value *args) { - unsigned int nrows, ncols; - objectmatrix *new=NULL; - value out=MORPHO_NIL; - - if (nargs==2 && - MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) && - MORPHO_ISINTEGER(MORPHO_GETARG(args, 1)) ) { - nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); - ncols = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1)); - new=object_newmatrix(nrows, ncols, true); - } else if (nargs==1 && - MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) { - nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); - ncols = 1; - new=object_newmatrix(nrows, ncols, true); - } else if (nargs==1 && - MORPHO_ISARRAY(MORPHO_GETARG(args, 0))) { - new=object_matrixfromarray(MORPHO_GETARRAY(MORPHO_GETARG(args, 0))); - if (!new) morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT); -#ifdef MORPHO_INCLUDE_SPARSE - } else if (nargs==1 && - MORPHO_ISLIST(MORPHO_GETARG(args, 0))) { - new=object_matrixfromlist(MORPHO_GETLIST(MORPHO_GETARG(args, 0))); - if (!new) { - /** Could this be a concatenation operation? */ - objectsparseerror err = sparse_catmatrix(MORPHO_GETLIST(MORPHO_GETARG(args, 0)), &new); - if (err==SPARSE_INVLDINIT) { - morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT); - } else if (err!=SPARSE_OK) sparse_raiseerror(v, err); - } -#endif - } else if (nargs==1 && - MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { - new=object_clonematrix(MORPHO_GETMATRIX(MORPHO_GETARG(args, 0))); - if (!new) morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT); -#ifdef MORPHO_INCLUDE_SPARSE - } else if (nargs==1 && - MORPHO_ISSPARSE(MORPHO_GETARG(args, 0))) { - objectsparseerror err=sparse_tomatrix(MORPHO_GETSPARSE(MORPHO_GETARG(args, 0)), &new); - if (err!=SPARSE_OK) morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT); -#endif - } else morpho_runtimeerror(v, MATRIX_CONSTRUCTOR); - - if (new) { - out=MORPHO_OBJECT(new); - morpho_bindobjects(v, 1, &out); - } - - return out; +/* ---------------------- + * Common utility methods + * ---------------------- */ + +/** Prints a matrix */ +value Matrix_print(vm *v, int nargs, value *args) { + if (MORPHO_ISCLASS(MORPHO_SELF(args))) return Object_print(v, nargs, args); // Handle calls on the class + objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args)); + matrix_print(v, m); + return MORPHO_NIL; } -/** Creates an identity matrix */ -value matrix_identityconstructor(vm *v, int nargs, value *args) { - int n; - objectmatrix *new=NULL; - value out = MORPHO_NIL; - - if (nargs==1 && - MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) { - n = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); - new=object_newmatrix(n, n, false); - if (new) { - matrix_identity(new); - } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); - } else morpho_runtimeerror(v, MATRIX_IDENTCONSTRUCTOR); - - if (new) { - out=MORPHO_OBJECT(new); - morpho_bindobjects(v, 1, &out); - } +/** Formatted conversion to a string */ +value Matrix_format(vm *v, int nargs, value *args) { + value out=MORPHO_NIL; + varray_char str; + varray_charinit(&str); + + if (matrix_printtobuffer(MORPHO_GETMATRIX(MORPHO_SELF(args)), + MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)), + &str)) { + out = object_stringfromvarraychar(&str); + if (MORPHO_ISOBJECT(out)) morpho_bindobjects(v, 1, &out); + } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); + varray_charclear(&str); return out; } -/** Checks that a matrix is indexed with 2 indices with a generic interface */ -bool matrix_slicedim(value * a, unsigned int ndim){ - if (ndim>2||ndim<0) return false; - return true; +/** Copies the contents of one matrix into another */ +value Matrix_assign(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + LINALG_ERRCHECKVM(matrix_copy(b, a)); + return MORPHO_NIL; } -/** Constucts a new matrix with a generic interface */ -void matrix_sliceconstructor(unsigned int *slicesize,unsigned int ndim,value* out){ - unsigned int numcol = 1; - if (ndim == 2) { - numcol = slicesize[1]; - } - *out = MORPHO_OBJECT(object_newmatrix(slicesize[0],numcol,false)); +/** Clones a matrix */ +value Matrix_clone(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectmatrix *new=matrix_clone(a); + return morpho_wrapandbind(v, (object *) new); } -/** Copies data from a at indx to out at newindx with a generic interface */ -objectarrayerror matrix_slicecopy(value * a,value * out, unsigned int ndim, unsigned int *indx,unsigned int *newindx){ - double num; // matrices store doubles; - unsigned int colindx = 0; - unsigned int colnewindx = 0; - - if (ndim == 2) { - colindx = indx[1]; - colnewindx = newindx[1]; - } - if (!(matrix_getelement(MORPHO_GETMATRIX(*a),indx[0],colindx,&num)&& - matrix_setelement(MORPHO_GETMATRIX(*out),newindx[0],colnewindx,num))){ - return ARRAY_OUTOFBOUNDS; - } - return ARRAY_OK; -} +/* --------- + * index() + * --------- */ -/** Rolls the matrix list */ -void matrix_rollflat(objectmatrix *a, objectmatrix *b, int nplaces) { - unsigned int N = a->nrows*a->ncols; - int n = abs(nplaces); - if (n>N) n = n % N; - unsigned int Np = N - n; // Number of elements to roll +value Matrix_index__int_int(vm *v, int nargs, value *args) { + objectmatrix *m = MORPHO_GETMATRIX(MORPHO_SELF(args)); + MatrixIdx_t i = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)), + j = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1)); + value out=MORPHO_NIL; - if (nplaces<0) { - memcpy(b->matrixdata, a->matrixdata+n, sizeof(double)*Np); - memcpy(b->matrixdata+Np, a->matrixdata, sizeof(double)*n); - } else { - memcpy(b->matrixdata+n, a->matrixdata, sizeof(double)*Np); - if (n>0) memcpy(b->matrixdata, a->matrixdata+Np, sizeof(double)*n); - } + double *elptr=NULL; + LINALG_ERRCHECKVM(matrix_getelementptr(m, i, j, &elptr)); + + if (elptr) out=matrix_getinterface(m)->getelfn(v, elptr); + return out; } -/** Copies arow from matrix a into brow for matrix b */ -void matrix_copyrow(objectmatrix *a, int arow, objectmatrix *b, int brow) { - cblas_dcopy(a->ncols, a->elements+arow, a->nrows, b->elements+brow, a->nrows); +static linalgError_t _slice_count(value in, MatrixIdx_t *count) { + if (morpho_isnumber(in)) { *count=1; return LINALGERR_OK; } + else if (MORPHO_ISRANGE(in)) { *count = (MatrixIdx_t) range_count(MORPHO_GETRANGE(in)); return LINALGERR_OK; } + else if (MORPHO_ISLIST(in)) { *count = (MatrixIdx_t) list_length(MORPHO_GETLIST(in)); return LINALGERR_OK; } + else if (MORPHO_ISTUPLE(in)) { *count = (MatrixIdx_t) tuple_length(MORPHO_GETTUPLE(in)); return LINALGERR_OK; } + return LINALGERR_NON_NUMERICAL; } -/** Rolls a list by a number of elements */ -objectmatrix *matrix_roll(objectmatrix *a, int nplaces, int axis) { - objectmatrix *new=object_newmatrix(a->nrows, a->ncols, false); - - if (new) { - switch(axis) { - case 0: { // TODO: Could probably be faster - for (int i=0; inrows; i++) { - int j = (i+nplaces); - if (j<0) j+=a->nrows; - matrix_copyrow(a, i, new, j % a->nrows); - } - } - break; - case 1: matrix_rollflat(a, new, nplaces*a->nrows); break; +static linalgError_t _slice_iterate(value in, unsigned int i, MatrixIdx_t *ix) { + value val=in; + if (MORPHO_ISRANGE(in)) { + val=range_iterate(MORPHO_GETRANGE(in), i); + } else if (MORPHO_ISLIST(in)) { + if (!list_getelement(MORPHO_GETLIST(in), i, &val)) return LINALGERR_INVLD_ARG; + } else if (MORPHO_ISTUPLE(in)) { + if (!tuple_getelement(MORPHO_GETTUPLE(in), i, &val)) return LINALGERR_INVLD_ARG; + } + + if (MORPHO_ISINTEGER(val)) { *ix=MORPHO_GETINTEGERVALUE(val); return LINALGERR_OK; } + else if (MORPHO_ISFLOAT(val)) { *ix=(MatrixIdx_t) MORPHO_GETFLOATVALUE(val); return LINALGERR_OK; } + return LINALGERR_INVLD_ARG; +} + +static linalgError_t _slice_validate(value iv, value jv, MatrixIdx_t *icnt, MatrixIdx_t *jcnt) { + LINALG_ERRCHECKRETURN(_slice_count(iv, icnt)); + LINALG_ERRCHECKRETURN(_slice_count(jv, jcnt)); + if (*icnt<1 || *jcnt<1) return LINALGERR_INVLD_ARG; + return LINALGERR_OK; +} + +static linalgError_t _slice_copy(value iv, value jv, MatrixIdx_t icnt, MatrixIdx_t jcnt, objectmatrix *a, objectmatrix *b, bool swap) { + double *ael, *bel; + for (MatrixIdx_t j=0; jncols)); + for (MatrixIdx_t i=0; inrows)); + LINALG_ERRCHECKRETURN(matrix_getelementptr(a, ix, jx, &ael)); + LINALG_ERRCHECKRETURN(matrix_getelementptr(b, i, j, &bel)); + if (swap) memcpy(ael, bel, sizeof(double)*a->nvals); + else memcpy(bel, ael, sizeof(double)*b->nvals); } } - - return new; + return LINALGERR_OK; } -/** Gets the matrix element with given indices */ -value Matrix_getindex(vm *v, int nargs, value *args) { - objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args)); - unsigned int indx[2]={0,0}; - value out = MORPHO_NIL; - if (nargs>2){ - morpho_runtimeerror(v, MATRIX_INVLDNUMINDICES); - return out; - } - - if (array_valuelisttoindices(nargs, args+1, indx)) { - double outval; - if (!matrix_getelement(m, indx[0], indx[1], &outval)) { - morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); - } else { - out = MORPHO_FLOAT(outval); - } - } else { // now try to get a slice - objectarrayerror err = getslice(&MORPHO_SELF(args), &matrix_slicedim, &matrix_sliceconstructor, &matrix_slicecopy, nargs, &MORPHO_GETARG(args,0), &out); - if (err!=ARRAY_OK) MORPHO_RAISE(v, array_to_matrix_error(err) ); - if (MORPHO_ISOBJECT(out)){ - morpho_bindobjects(v,1,&out); - } else morpho_runtimeerror(v, MATRIX_INVLDINDICES); - } +value Matrix_index__x_x(vm *v, int nargs, value *args) { + objectmatrix *m = MORPHO_GETMATRIX(MORPHO_SELF(args)), *new=NULL; + value iv=MORPHO_GETARG(args, 0), jv=MORPHO_GETARG(args, 1); + value out=MORPHO_NIL; + + MatrixIdx_t icnt=0, jcnt=0; // Counts become size of new matrix + LINALG_ERRCHECKVMRETURN(_slice_validate(iv, jv, &icnt, &jcnt), MORPHO_NIL); + + new=matrix_newwithtype(MORPHO_GETOBJECTTYPE(MORPHO_SELF(args)), icnt, jcnt, m->nvals, false); + if (!new) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return MORPHO_NIL; } + + linalgError_t err=_slice_copy(iv, jv, icnt, jcnt, m, new, false); + if (err!=LINALGERR_OK) { linalg_raiseerror(v, err); object_free((object *) new); } + else out = morpho_wrapandbind(v, (object *) new); + return out; } -/** Sets the matrix element with given indices */ -value Matrix_setindex(vm *v, int nargs, value *args) { - objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args)); - unsigned int indx[2]={0,0}; - - if (array_valuelisttoindices(nargs-1, args+1, indx)) { - double value=0.0; - if (MORPHO_ISFLOAT(args[nargs])) value=MORPHO_GETFLOATVALUE(args[nargs]); - if (MORPHO_ISINTEGER(args[nargs])) value=(double) MORPHO_GETINTEGERVALUE(args[nargs]); - - if (!matrix_setelement(m, indx[0], indx[1], value)) { - morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); - } - } else morpho_runtimeerror(v, MATRIX_INVLDINDICES); - +value Matrix_index__err(vm *v, int nargs, value *args) { + morpho_runtimeerror(v, LINALG_INVLDINDICES); return MORPHO_NIL; } -/** Sets the column of a matrix */ -value Matrix_setcolumn(vm *v, int nargs, value *args) { - objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args)); - - if (nargs==2 && - MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) && - MORPHO_ISMATRIX(MORPHO_GETARG(args, 1))) { - unsigned int col = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); - objectmatrix *src = MORPHO_GETMATRIX(MORPHO_GETARG(args, 1)); - - if (colncols) { - if (src && src->ncols*src->nrows==m->nrows) { - matrix_setcolumn(m, col, src->elements); - } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); - } else morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); - } else morpho_runtimeerror(v, MATRIX_SETCOLARGS); - - return MORPHO_NIL; +/* --------- + * setindex() + * --------- */ + +static void _setindex(vm *v, objectmatrix *m, MatrixIdx_t i, MatrixIdx_t j, value in) { + double *elptr=NULL; + LINALG_ERRCHECKVM(matrix_getelementptr(m, i, j, &elptr)); + if (elptr) LINALG_ERRCHECKVM(matrix_getinterface(m)->setelfn(v, in, elptr)); } -/** Gets a column of a matrix */ -value Matrix_getcolumn(vm *v, int nargs, value *args) { - objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args)); - value out=MORPHO_NIL; - - if (nargs==1 && - MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) { - unsigned int col = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); - - if (colncols) { - double *vals; - if (matrix_getcolumn(m, col, &vals)) { - objectmatrix *new=object_matrixfromfloats(m->nrows, 1, vals); - if (new) { - out=MORPHO_OBJECT(new); - morpho_bindobjects(v, 1, &out); - } - } - } else morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); - } else morpho_runtimeerror(v, MATRIX_SETCOLARGS); - - return out; +value Matrix_setindex__int_x(vm *v, int nargs, value *args) { + MatrixIdx_t i = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + _setindex(v, MORPHO_GETMATRIX(MORPHO_SELF(args)), i, 0, MORPHO_GETARG(args, 1)); + return MORPHO_NIL; } -/** Prints a matrix */ -value Matrix_print(vm *v, int nargs, value *args) { - value self = MORPHO_SELF(args); - if (!MORPHO_ISMATRIX(self)) return Object_print(v, nargs, args); - - objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args)); - matrix_print(v, m); +value Matrix_setindex__int_int_x(vm *v, int nargs, value *args) { + MatrixIdx_t i = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + MatrixIdx_t j = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1)); + _setindex(v, MORPHO_GETMATRIX(MORPHO_SELF(args)), i, j, MORPHO_GETARG(args, 2)); return MORPHO_NIL; } -/** Formatted conversion to a string */ -value Matrix_format(vm *v, int nargs, value *args) { - value out = MORPHO_NIL; +value Matrix_setindex__x_x_matrix(vm *v, int nargs, value *args) { + objectmatrix *m = MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectmatrix *msrc = MORPHO_GETMATRIX(MORPHO_GETARG(args, 2)); + value iv=MORPHO_GETARG(args, 0), jv=MORPHO_GETARG(args, 1); - if (nargs==1 && - MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) { - varray_char str; - varray_charinit(&str); - - if (matrix_printtobuffer(MORPHO_GETMATRIX(MORPHO_SELF(args)), - MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)), - &str)) { - out = object_stringfromvarraychar(&str); - if (MORPHO_ISOBJECT(out)) morpho_bindobjects(v, 1, &out); - } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); - - varray_charclear(&str); - } else { - morpho_runtimeerror(v, VALUE_FRMTARG); - } + MatrixIdx_t icnt=0, jcnt=0; + LINALG_ERRCHECKVMRETURN(_slice_validate(iv, jv, &icnt, &jcnt), MORPHO_NIL); - return out; -} - -/** Matrix add */ -value Matrix_assign(vm *v, int nargs, value *args) { - objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); - - if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { - objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); - - if (a->ncols==b->ncols && a->nrows==b->nrows) { - matrix_copy(b, a); - } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); - } + LINALG_ERRCHECKVM(_slice_copy(iv, jv, icnt, jcnt, m, msrc, true)); return MORPHO_NIL; } -/** Matrix add */ -value Matrix_add(vm *v, int nargs, value *args) { +/* --------- + * column + * --------- */ + +value Matrix_getcolumn__int(vm *v, int nargs, value *args) { objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + MatrixIdx_t i = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); value out=MORPHO_NIL; - - if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { - objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); - - if (a->ncols==b->ncols && a->nrows==b->nrows) { - objectmatrix *new = object_newmatrix(a->nrows, a->ncols, false); - if (new) { - out=MORPHO_OBJECT(new); - matrix_add(a, b, new); - } - } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); - } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) { - double val; - if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) { - objectmatrix *new = object_newmatrix(a->nrows, a->ncols, false); - if (new) { - out=MORPHO_OBJECT(new); - matrix_addscalar(a, 1.0, val, new); - } - } - } else morpho_runtimeerror(v, MATRIX_ARITHARGS); - if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out); + if (i>=0 && incols) { + objectmatrix *new=matrix_newwithtype(a->obj.type, a->nrows, 1, a->nvals, false); + if (new) matrix_getcolumn(a, i, new); + out=morpho_wrapandbind(v, (object *)new); + } else linalg_raiseerror(v, LINALGERR_INDX_OUT_OF_BNDS); return out; } -/** Right add */ -value Matrix_addr(vm *v, int nargs, value *args) { - objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); +value Matrix_setcolumn__int_matrix(vm *v, int nargs, value *args) { + LINALG_ERRCHECKVM(matrix_setcolumn(MORPHO_GETMATRIX(MORPHO_SELF(args)), + MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)), + MORPHO_GETMATRIX(MORPHO_GETARG(args, 1)))); + return MORPHO_NIL; +} + +/* ---------- + * Arithmetic + * ---------- */ + +/** Add a vector */ +static value _axpy(vm *v, int nargs, value *args, double alpha) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); // Receiver is left hand operand + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); // Argument is right hand operand + objectmatrix *new = NULL; value out=MORPHO_NIL; - - if (nargs==1 && (MORPHO_ISNIL(MORPHO_GETARG(args, 0)) || - MORPHO_ISNUMBER(MORPHO_GETARG(args, 0)))) { - int i=0; - if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) i=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); - if (MORPHO_ISFLOAT(MORPHO_GETARG(args, 0))) i=(fabs(MORPHO_GETFLOATVALUE(MORPHO_GETARG(args, 0)))ncols==b->ncols && a->nrows==b->nrows) { + new=matrix_clone(a); + if (new) LINALG_ERRCHECKVM(matrix_axpy(alpha, b, new)); + out = morpho_wrapandbind(v, (object *) new); + } else morpho_runtimeerror(v, LINALG_INCOMPATIBLEMATRICES); return out; } -/** Matrix subtract */ -value Matrix_sub(vm *v, int nargs, value *args) { +/** Add a scalar */ +static value _xpa(vm *v, int nargs, value *args, double sgna, double sgnb) { objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectmatrix *new=NULL; value out=MORPHO_NIL; - - if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { - objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); - - if (a->ncols==b->ncols && a->nrows==b->nrows) { - objectmatrix *new = object_newmatrix(a->nrows, a->ncols, false); - if (new) { - out=MORPHO_OBJECT(new); - matrix_sub(a, b, new); - } - } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); - } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) { - double val; - if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) { - objectmatrix *new = object_newmatrix(a->nrows, a->ncols, false); - if (new) { - out=MORPHO_OBJECT(new); - matrix_addscalar(a, 1.0, -val, new); - } - } - } else morpho_runtimeerror(v, MATRIX_ARITHARGS); - if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out); + double beta; + if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &beta)) { + new = matrix_clone(a); + if (new) LINALG_ERRCHECKVM(matrix_addscalar(new, sgna, beta*sgnb)); + out = morpho_wrapandbind(v, (object *) new); + } else morpho_runtimeerror(v, LINALG_INVLDARGS); return out; } -/** Right subtract */ -value Matrix_subr(vm *v, int nargs, value *args) { - objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); - value out=MORPHO_NIL; - - if (nargs==1 && (MORPHO_ISNIL(MORPHO_GETARG(args, 0)) || - MORPHO_ISNUMBER(MORPHO_GETARG(args, 0)))) { - int i=(MORPHO_ISNIL(MORPHO_GETARG(args, 0)) ? 0 : MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0))); +value Matrix_add__matrix(vm *v, int nargs, value *args) { + return _axpy(v,nargs,args,1.0); +} - if (MORPHO_ISFLOAT(MORPHO_GETARG(args, 0))) i=(fabs(MORPHO_GETFLOATVALUE(MORPHO_GETARG(args, 0)))nrows, a->ncols, false); - if (new) { - matrix_addscalar(a, 1.0, -val, new); - // now that did self - arg[0] and we want arg[0] - self so scale the whole thing by -1 - matrix_scale(new, -1.0); - out=MORPHO_OBJECT(new); - morpho_bindobjects(v, 1, &out); - } - } +value Matrix_add__x(vm *v, int nargs, value *args) { + if (matrix_isamatrix(MORPHO_GETARG(args, 0))) return MORPHO_NIL; // Redirect to addr + return _xpa(v,nargs,args,1.0,1.0); +} - } else morpho_runtimeerror(v, VM_INVALIDARGS); - } else morpho_runtimeerror(v, VM_INVALIDARGS); +value Matrix_sub__matrix(vm *v, int nargs, value *args) { + return _axpy(v,nargs,args,-1.0); +} + +value Matrix_sub__x(vm *v, int nargs, value *args) { + if (matrix_isamatrix(MORPHO_GETARG(args, 0))) return MORPHO_NIL; // Redirect to subr + return _xpa(v,nargs,args,1.0,-1.0); +} + +value Matrix_subr__x(vm *v, int nargs, value *args) { + return _xpa(v,nargs,args,-1.0,1.0); +} + +value Matrix_mul__float(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); - return out; + double scale; + if (!morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) return MORPHO_NIL; + + objectmatrix *new = matrix_clone(a); + if (new) matrix_scale(new, scale); + return morpho_wrapandbind(v, (object *) new); } -/** Matrix multiply */ -value Matrix_mul(vm *v, int nargs, value *args) { +value Matrix_mul__matrix(vm *v, int nargs, value *args) { objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); value out=MORPHO_NIL; - - if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { - objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); - - if (a->ncols==b->nrows) { - objectmatrix *new = object_newmatrix(a->nrows, b->ncols, false); - if (new) { - out=MORPHO_OBJECT(new); - matrix_mul(a, b, new); - morpho_bindobjects(v, 1, &out); - } - } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); - } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) { - double scale=1.0; - if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) { - objectmatrix *new = object_clonematrix(a); - if (new) { - out=MORPHO_OBJECT(new); - matrix_scale(new, scale); - morpho_bindobjects(v, 1, &out); - } - } -#ifdef MORPHO_INCLUDE_SPARSE - } else if (nargs==1 && MORPHO_ISSPARSE(MORPHO_GETARG(args, 0))) { - // Returns nil to ensure it gets passed to mulr on Sparse -#endif - } else morpho_runtimeerror(v, MATRIX_ARITHARGS); + if (a->ncols==b->nrows) { + objectmatrix *new = matrix_new(a->nrows, b->ncols, false); + if (new) LINALG_ERRCHECKVM(matrix_mmul(1.0, a, b, 0.0, new)); + out = morpho_wrapandbind(v, (object *) new); + } else morpho_runtimeerror(v, LINALG_INCOMPATIBLEMATRICES); return out; } -/** Called when multiplying on the right */ -value Matrix_mulr(vm *v, int nargs, value *args) { +value Matrix_div__float(vm *v, int nargs, value *args) { objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); - value out=MORPHO_NIL; - - if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) { - double scale=1.0; - if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) { - objectmatrix *new = object_clonematrix(a); - if (new) { - out=MORPHO_OBJECT(new); - matrix_scale(new, scale); - morpho_bindobjects(v, 1, &out); - } - } - } else morpho_runtimeerror(v, MATRIX_ARITHARGS); - return out; + double scale; + if (!morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) return MORPHO_NIL; + scale = 1.0/scale; + if (isnan(scale)) morpho_runtimeerror(v, VM_DVZR); + + objectmatrix *new = matrix_clone(a); + if (new) matrix_scale(new, scale); + return morpho_wrapandbind(v, (object *) new); } -/** Solution of linear system a.x = b (i.e. x = b/a) */ -value Matrix_div(vm *v, int nargs, value *args) { - objectmatrix *b=MORPHO_GETMATRIX(MORPHO_SELF(args)); - value out=MORPHO_NIL; - - if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { - objectmatrix *a=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); - - if (a->ncols==b->nrows) { - objectmatrix *new = object_newmatrix(b->nrows, b->ncols, false); - if (new) { - objectmatrixerror err; - if (MATRIX_ISSMALL(a)) { - err=matrix_divs(a, b, new); - } else { - err=matrix_divl(a, b, new); - } - if (err==MATRIX_SING) { - morpho_runtimeerror(v, MATRIX_SINGULAR); - object_free((object *) new); - } else { - out=MORPHO_OBJECT(new); - morpho_bindobjects(v, 1, &out); - } - } - } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); -#ifdef MORPHO_INCLUDE_SPARSE - } else if (nargs==1 && MORPHO_ISSPARSE(MORPHO_GETARG(args, 0))) { - /* Division by a sparse matrix: redirect to the divr selector of Sparse. */ - value vargs[2]={args[1],args[0]}; - return Sparse_divr(v, nargs, vargs); -#endif - } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) { - /* Division by a scalar */ - double scale=1.0; - if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) { - if (fabs(scale)ncols==b->ncols && a->nrows==b->nrows) { - out=MORPHO_SELF(args); - double lambda=1.0; - morpho_valuetofloat(MORPHO_GETARG(args, 0), &lambda); - matrix_accumulate(a, lambda, b); - } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); - } else morpho_runtimeerror(v, MATRIX_ARITHARGS); + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 1)); + + double alpha=1.0; + if (!morpho_valuetofloat(MORPHO_GETARG(args, 0), &alpha)) { morpho_runtimeerror(v, LINALG_ARITHARGS); return MORPHO_NIL; } + LINALG_ERRCHECKVM(matrix_axpy(alpha, b, a)); return MORPHO_NIL; } -/** Frobenius inner product */ -value Matrix_inner(vm *v, int nargs, value *args) { +/* ---------------- + * Unary operations + * ---------------- */ + +/** Matrix norm */ +value Matrix_norm__x(vm *v, int nargs, value *args) { objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); - value out=MORPHO_NIL; - - if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { - objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); - - double prod=0.0; - if (matrix_inner(a, b, &prod)==MATRIX_OK) { - out = MORPHO_FLOAT(prod); - } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); - } else morpho_runtimeerror(v, MATRIX_ARITHARGS); + double n; - return out; + if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &n)) { + if (fabs(n-1.0)nrows*a->ncols, b->nrows*b->ncols, true); - - if (new && - matrix_outer(a, b, new)==MATRIX_OK) { - out=MORPHO_OBJECT(new); - morpho_bindobjects(v, 1, &out); - } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); - } else morpho_runtimeerror(v, MATRIX_ARITHARGS); - - return out; + return MORPHO_FLOAT(matrix_norm(a, MATRIX_NORM_FROBENIUS)); } -/** Matrix sum */ +/** Sums all matrix values */ value Matrix_sum(vm *v, int nargs, value *args) { objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); - return MORPHO_FLOAT(matrix_sum(a)); + double sum[a->nvals]; + + matrix_sum(a, sum); + return matrix_getinterface(a)->getelfn(v, sum); } -/** Roll a matrix */ -value Matrix_roll(vm *v, int nargs, value *args) { - objectmatrix *slf = MORPHO_GETMATRIX(MORPHO_SELF(args)); - value out = MORPHO_NIL; - int roll, axis=0; +/** Computes the trace */ +value Matrix_trace(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + double out=0.0; + LINALG_ERRCHECKVM(matrix_trace(a, &out)); + return MORPHO_FLOAT(out); +} - if (nargs>0 && - morpho_valuetoint(MORPHO_GETARG(args, 0), &roll)) { - - if (nargs==2 && !morpho_valuetoint(MORPHO_GETARG(args, 1), &axis)) return out; - - objectmatrix *new = matrix_roll(slf, roll, axis); +/** Inverts a matrix */ +value Matrix_transpose(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectmatrix *new = matrix_clone(a); + if (new) { + new->ncols=a->nrows; + new->nrows=a->ncols; + LINALG_ERRCHECKVM(matrix_transpose(a, new)); + } + return morpho_wrapandbind(v, (object *) new); +} - if (new) { - out = MORPHO_OBJECT(new); - morpho_bindobjects(v, 1, &out); - } +/** Inverts a matrix */ +value Matrix_inverse(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectmatrix *new = matrix_clone(a); + if (new) LINALG_ERRCHECKVM(matrix_inverse(new)); + + return morpho_wrapandbind(v, (object *) new); +} - } else morpho_runtimeerror(v, LIST_ADDARGS); +/* ---------------- + * Eigensystem + * ---------------- */ - return out; +static bool _processeigenvalues(vm *v, MatrixIdx_t n, MorphoComplex *w, value *out) { + value ev[n]; + for (int i=0; i DBL_MIN ? fabs(cimag(w[i]))/abs <= MORPHO_EPS : fabs(cimag(w[i])) < DBL_MIN) { + ev[i]=MORPHO_FLOAT(creal(w[i])); + } else { + objectcomplex *new = object_newcomplex(creal(w[i]), cimag(w[i])); + if (new) ev[i]=MORPHO_OBJECT(new); + else goto _processeigenvalues_cleanup; + } + } + + objecttuple *new = object_newtuple(n, ev); + if (!new) goto _processeigenvalues_cleanup; + + *out = MORPHO_OBJECT(new); + return true; + +_processeigenvalues_cleanup: + for (int i=0; incols; + MorphoComplex w[n]; + linalgError_t err=matrix_eigen(a, w, NULL); + if (err==LINALGERR_OK) { + if (_processeigenvalues(v, n, w, &out)) { + morpho_bindobjects(v, 1, &out); + } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); + } else linalg_raiseerror(v, err); return out; } -/** Matrix eigenvalues */ -value Matrix_eigenvalues(vm *v, int nargs, value *args) { +#define _CHK(x) if (!x) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto _eigensystem_cleanup; } + +/** Finds the eigenvalues and eigenvectors of a matrix */ +value Matrix_eigensystem(vm *v, int nargs, value *args) { objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); - value evals=MORPHO_NIL; - if (matrix_eigen(v, a, &evals, NULL)) { - objectlist *new = MORPHO_GETLIST(evals); - list_append(new, evals); // Ensure we retain the List object - morpho_bindobjects(v, new->val.count, new->val.data); - new->val.count--; // And pop it back off + value ev=MORPHO_NIL; // Will hold eigenvalues + objectmatrix *evec=NULL; // Holds eigenvectors + objecttuple *otuple=NULL; // Tuple to return everything + + MatrixIdx_t n=a->ncols; + MorphoComplex w[n]; + + evec=matrix_clone(a); + _CHK(evec); + + linalgError_t err=matrix_eigen(a, w, evec); + if (err!=LINALGERR_OK) { linalg_raiseerror(v, err); goto _eigensystem_cleanup; } + + _CHK(_processeigenvalues(v, n, w, &ev)); + + value outtuple[2] = { ev, MORPHO_OBJECT(evec) }; + otuple = object_newtuple(2, outtuple); + _CHK(otuple); + + return morpho_wrapandbind(v, (object *) otuple); + +_eigensystem_cleanup: + if (evec) object_free((object *) evec); + if (otuple) object_free((object *) otuple); + if (MORPHO_ISOBJECT(ev)) { + value evx; + objecttuple *t = MORPHO_GETTUPLE(ev); + for (int i=0; inrows != u->nrows) || (a->nrows != u->ncols))) return LINALGERR_INCOMPATIBLE_DIM; + if (vt && ((a->ncols != vt->nrows) || (a->ncols != vt->ncols))) return LINALGERR_INCOMPATIBLE_DIM; - if (matrix_eigen(v, a, &evals, &evecs)) { - objectlist *evallist = MORPHO_GETLIST(evals); - - list_append(resultlist, evals); // Create the output list - list_append(resultlist, evecs); - out=MORPHO_OBJECT(resultlist); - - list_append(evallist, evals); // Ensure we bind all objects at once - list_append(evallist, evecs); // by popping them onto the evallist. - list_append(evallist, out); // - morpho_bindobjects(v, evallist->val.count, evallist->val.data); - evallist->val.count-=3; // and then popping them back off. - } + objectmatrix *temp = matrix_clone(a); + if (!temp) return LINALGERR_ALLOC; - return out; + linalgError_t err = matrix_getinterface(a)->svdfn (temp, s, u, vt); + object_free((object *) temp); + return err; } -/** Inverts a matrix */ -value Matrix_inverse(vm *v, int nargs, value *args) { - objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); - value out=MORPHO_NIL; +/* ---------------- + * QR decomposition + * ---------------- */ - // The inverse will have the number of rows and number of columns - // swapped. - objectmatrix *new = object_newmatrix(a->ncols, a->nrows, false); - if (new) { - objectmatrixerror mi = matrix_inverse(a, new); - out=MORPHO_OBJECT(new); - morpho_bindobjects(v, 1, &out); - - if (mi!=MATRIX_OK) matrix_raiseerror(v, mi); - } +/** Interface to QR decomposition */ +linalgError_t matrix_qr(objectmatrix *a, objectmatrix *q, objectmatrix *r) { + if (q && ((a->nrows != q->nrows) || (a->nrows != q->ncols))) return LINALGERR_INCOMPATIBLE_DIM; + if (r && ((a->nrows != r->nrows) || (a->ncols != r->ncols))) return LINALGERR_INCOMPATIBLE_DIM; - return out; + objectmatrix *temp = matrix_clone(a); + if (!temp) return LINALGERR_ALLOC; + + linalgError_t err = matrix_getinterface(a)->qrfn (temp, q, r); + object_free((object *) temp); + return err; } -/** Transpose of a matrix */ -value Matrix_transpose(vm *v, int nargs, value *args) { +/** Processes singular values into a tuple */ +static bool _processsingularvalues(vm *v, MatrixIdx_t n, double *s, value *out) { + value sv[n]; + for (int i = 0; i < n; i++) sv[i] = MORPHO_FLOAT(s[i]); + + objecttuple *new = object_newtuple(n, sv); + if (!new) return false; + + *out = MORPHO_OBJECT(new); + return true; +} + +#define _CHK_SVD(x) if (!x) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto _svd_cleanup; } +/** Singular Value Decomposition */ +value Matrix_svd(vm *v, int nargs, value *args) { + objectmatrix *a = MORPHO_GETMATRIX(MORPHO_SELF(args)); + + value s = MORPHO_NIL; // Will hold singular values + objectmatrix *u = NULL; // Left singular vectors + objectmatrix *vt = NULL; // Right singular vectors (transposed) + objecttuple *otuple = NULL; // Tuple to return everything + + MatrixIdx_t m = a->nrows, n = a->ncols; + MatrixIdx_t minmn = (m < n) ? m : n; + double singular_values[minmn]; + + // Allocate U (m×m) and VT (n×n) matrices + u = matrix_newwithtype(MORPHO_GETOBJECTTYPE(MORPHO_SELF(args)), m, m, a->nvals, false); + _CHK_SVD(u); + + vt = matrix_newwithtype(MORPHO_GETOBJECTTYPE(MORPHO_SELF(args)), n, n, a->nvals, false); + _CHK_SVD(vt); + + linalgError_t err = matrix_svd(a, singular_values, u, vt); + if (err != LINALGERR_OK) { linalg_raiseerror(v, err); goto _svd_cleanup; } + + _CHK_SVD(_processsingularvalues(v, minmn, singular_values, &s)); + + value outtuple[3] = { MORPHO_OBJECT(u), s, MORPHO_OBJECT(vt) }; + otuple = object_newtuple(3, outtuple); + _CHK_SVD(otuple); + + return morpho_wrapandbind(v, (object *) otuple); + +_svd_cleanup: + if (u) object_free((object *) u); + if (vt) object_free((object *) vt); + if (otuple) object_free((object *) otuple); + morpho_freeobject(s); + + return MORPHO_NIL; +} +#undef _CHK_SVD + +/* ---------------- + * QR decomposition + * ---------------- */ + +#define _CHK_QR(x) if (!x) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto _qr_cleanup; } +/** QR Decomposition */ +value Matrix_qr(vm *v, int nargs, value *args) { + objectmatrix *a = MORPHO_GETMATRIX(MORPHO_SELF(args)); + + objectmatrix *q = NULL; // Orthogonal matrix Q + objectmatrix *r = NULL; // Upper triangular matrix R + objecttuple *otuple = NULL; // Tuple to return everything + + MatrixIdx_t m = a->nrows, n = a->ncols; + + // Allocate Q (m×m) and R (m×n) matrices + q = matrix_newwithtype(MORPHO_GETOBJECTTYPE(MORPHO_SELF(args)), m, m, a->nvals, false); + _CHK_QR(q); + + r = matrix_newwithtype(MORPHO_GETOBJECTTYPE(MORPHO_SELF(args)), m, n, a->nvals, false); + _CHK_QR(r); + + linalgError_t err = matrix_qr(a, q, r); + if (err != LINALGERR_OK) { linalg_raiseerror(v, err); goto _qr_cleanup; } + + value outtuple[2] = { MORPHO_OBJECT(q), MORPHO_OBJECT(r) }; + otuple = object_newtuple(2, outtuple); + _CHK_QR(otuple); + + return morpho_wrapandbind(v, (object *) otuple); + +_qr_cleanup: + if (q) object_free((object *) q); + if (r) object_free((object *) r); + if (otuple) object_free((object *) otuple); + + return MORPHO_NIL; +} +#undef _CHK_QR + +/* --------- + * Products + * --------- */ + +/** Frobenius inner product */ +value Matrix_inner(vm *v, int nargs, value *args) { objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); - value out=MORPHO_NIL; - - objectmatrix *new = object_newmatrix(a->ncols, a->nrows, false); - if (new) { - matrix_transpose(a, new); - out=MORPHO_OBJECT(new); - morpho_bindobjects(v, 1, &out); - } + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + double prod=0.0; - return out; + LINALG_ERRCHECKVM(matrix_inner(a, b, &prod)); + + return MORPHO_FLOAT(prod); } +/** Outer product */ +value Matrix_outer(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + + objectmatrix *new=matrix_new(a->nrows*a->ncols, b->nrows*b->ncols, true); + if (new) LINALG_ERRCHECKVM(matrix_r1update(1.0, a, b, new)); + + return morpho_wrapandbind(v, (object *) new); +} + +/* --------- + * Metadata + * --------- */ + /** Reshape a matrix */ value Matrix_reshape(vm *v, int nargs, value *args) { objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + int nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)), + ncols = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1)); - if (nargs==2 && - MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) && - MORPHO_ISINTEGER(MORPHO_GETARG(args, 1))) { - int nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); - int ncols = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1)); - - if (nrows*ncols==a->nrows*a->ncols) { - a->nrows=nrows; - a->ncols=ncols; - } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); - } else morpho_runtimeerror(v, MATRIX_RESHAPEARGS); + if (nrows*ncols==a->nrows*a->ncols) { + a->nrows=nrows; + a->ncols=ncols; + } else morpho_runtimeerror(v, LINALG_INCOMPATIBLEMATRICES); return MORPHO_NIL; } -/** Trace of a matrix */ -value Matrix_trace(vm *v, int nargs, value *args) { - objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); - value out=MORPHO_NIL; +static value _roll(vm *v, objectmatrix *a, int roll, int axis) { + objectmatrix *new = matrix_clone(a); + if (new) matrix_roll(a, roll, axis, new); + return morpho_wrapandbind(v, (object *) new); +} - if (a->nrows==a->ncols) { - double tr; - if (matrix_trace(a, &tr)==MATRIX_OK) out=MORPHO_FLOAT(tr); - } else { - morpho_runtimeerror(v, MATRIX_NOTSQ); - } - - return out; +/** Roll a matrix */ +value Matrix_roll__int_int(vm *v, int nargs, value *args) { + objectmatrix *a = MORPHO_GETMATRIX(MORPHO_SELF(args)); + int roll = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)), + axis = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1)); + return _roll(v, a, roll, axis); +} + +/** Roll a matrix by row */ +value Matrix_roll__int(vm *v, int nargs, value *args) { + objectmatrix *a = MORPHO_GETMATRIX(MORPHO_SELF(args)); + int roll = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + return _roll(v, a, roll, 0); } /** Enumerate protocol */ value Matrix_enumerate(vm *v, int nargs, value *args) { objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + int i = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); value out=MORPHO_NIL; - if (nargs==1) { - if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) { - int i=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); - - if (i<0) out=MORPHO_INTEGER(a->ncols*a->nrows); - else if (incols*a->nrows) out=MORPHO_FLOAT(a->elements[i]); - } + if (i<0) { + out=MORPHO_INTEGER(a->ncols*a->nrows); + } else if (incols*a->nrows) { + out=matrix_getinterface(a)->getelfn(v, a->elements+i*a->nvals); + } else { + linalg_raiseerror(v, LINALGERR_INDX_OUT_OF_BNDS); } return out; } - /** Number of matrix elements */ value Matrix_count(vm *v, int nargs, value *args) { objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); - return MORPHO_INTEGER(a->ncols*a->nrows); } /** Matrix dimensions */ value Matrix_dimensions(vm *v, int nargs, value *args) { objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); - value dim[2]; - value out=MORPHO_NIL; - - dim[0]=MORPHO_INTEGER(a->nrows); - dim[1]=MORPHO_INTEGER(a->ncols); - objectlist *new=object_newlist(2, dim); - if (new) { - out=MORPHO_OBJECT(new); - morpho_bindobjects(v, 1, &out); - } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); + value dim[2] = { MORPHO_INTEGER(a->nrows), MORPHO_INTEGER(a->ncols) }; + objecttuple *new=object_newtuple(2, dim); - return out; -} - -/** Clones a matrix */ -value Matrix_clone(vm *v, int nargs, value *args) { - value out=MORPHO_NIL; - objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); - objectmatrix *new=object_clonematrix(a); - if (new) { - out=MORPHO_OBJECT(new); - morpho_bindobjects(v, 1, &out); - } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); - return out; + return morpho_wrapandbind(v, (object *) new); } MORPHO_BEGINCLASS(Matrix) -MORPHO_METHOD(MORPHO_GETINDEX_METHOD, Matrix_getindex, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_SETINDEX_METHOD, Matrix_setindex, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MATRIX_GETCOLUMN_METHOD, Matrix_getcolumn, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MATRIX_SETCOLUMN_METHOD, Matrix_setcolumn, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_PRINT_METHOD, Matrix_print, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_FORMAT_METHOD, Matrix_format, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_ASSIGN_METHOD, Matrix_assign, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD_SIGNATURE(MORPHO_ADD_METHOD, "Matrix (_)", Matrix_add, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD_SIGNATURE(MORPHO_ADDR_METHOD, "Matrix (_)", Matrix_addr, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD_SIGNATURE(MORPHO_SUB_METHOD, "Matrix (_)", Matrix_sub, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD_SIGNATURE(MORPHO_SUBR_METHOD, "Matrix (_)", Matrix_subr, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_MUL_METHOD, Matrix_mul, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_MULR_METHOD, Matrix_mulr, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_DIV_METHOD, Matrix_div, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_ACC_METHOD, Matrix_acc, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MATRIX_INNER_METHOD, Matrix_inner, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MATRIX_OUTER_METHOD, Matrix_outer, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_SUM_METHOD, Matrix_sum, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MATRIX_NORM_METHOD, Matrix_norm, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MATRIX_INVERSE_METHOD, Matrix_inverse, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MATRIX_TRANSPOSE_METHOD, Matrix_transpose, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MATRIX_RESHAPE_METHOD, Matrix_reshape, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MATRIX_EIGENVALUES_METHOD, Matrix_eigenvalues, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MATRIX_EIGENSYSTEM_METHOD, Matrix_eigensystem, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MATRIX_TRACE_METHOD, Matrix_trace, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_ENUMERATE_METHOD, Matrix_enumerate, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_COUNT_METHOD, Matrix_count, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MATRIX_DIMENSIONS_METHOD, Matrix_dimensions, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_ROLL_METHOD, Matrix_roll, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_CLONE_METHOD, Matrix_clone, BUILTIN_FLAGSEMPTY) +MORPHO_METHOD_SIGNATURE(MORPHO_PRINT_METHOD, "()", Matrix_print, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_FORMAT_METHOD, "(String)", Matrix_format, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ASSIGN_METHOD, "(Matrix)", Matrix_assign, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_CLONE_METHOD, "Matrix ()", Matrix_clone, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_GETINDEX_METHOD, "Float (Int)", Matrix_enumerate, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MORPHO_GETINDEX_METHOD, "Float (Int, Int)", Matrix_index__int_int, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MORPHO_GETINDEX_METHOD, "Matrix (_,_)", Matrix_index__x_x, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MORPHO_GETINDEX_METHOD, "Matrix (...)", Matrix_index__err, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MORPHO_SETINDEX_METHOD, "(Int,_)", Matrix_setindex__int_x, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SETINDEX_METHOD, "(Int,Int,_)", Matrix_setindex__int_int_x, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SETINDEX_METHOD, "(_,_,Matrix)", Matrix_setindex__x_x_matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_GETCOLUMN_METHOD, "Matrix (Int)", Matrix_getcolumn__int, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_SETCOLUMN_METHOD, "(Int, Matrix)", Matrix_setcolumn__int_matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_SETCOLUMN_METHOD_DEPRECATED, "(Int, Matrix)", Matrix_setcolumn__int_matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADD_METHOD, "Matrix (Matrix)", Matrix_add__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADD_METHOD, "Matrix (Nil)", Matrix_add__nil, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADD_METHOD, "Matrix (_)", Matrix_add__x, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADDR_METHOD, "Matrix (_)", Matrix_add__x, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADDR_METHOD, "Matrix (Nil)", Matrix_add__nil, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SUB_METHOD, "Matrix (Matrix)", Matrix_sub__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SUB_METHOD, "Matrix (Nil)", Matrix_add__nil, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SUB_METHOD, "Matrix (_)", Matrix_sub__x, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SUBR_METHOD, "Matrix (_)", Matrix_subr__x, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_MUL_METHOD, "Matrix (_)", Matrix_mul__float, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_MUL_METHOD, "Matrix (Matrix)", Matrix_mul__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_MULR_METHOD, "Matrix (_)", Matrix_mul__float, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_DIV_METHOD, "Matrix (Matrix)", Matrix_div__matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_DIV_METHOD, "Matrix (_)", Matrix_div__float, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ACC_METHOD, "(_, Matrix)", Matrix_acc__x_x_matrix, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_INVERSE_METHOD, "Matrix ()", Matrix_inverse, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_NORM_METHOD, "Float (_)", Matrix_norm__x, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MATRIX_NORM_METHOD, "Float ()", Matrix_norm, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MORPHO_SUM_METHOD, "Float ()", Matrix_sum, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MATRIX_TRACE_METHOD, "Float ()", Matrix_trace, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MATRIX_TRANSPOSE_METHOD, "Matrix ()", Matrix_transpose, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_INNER_METHOD, "Float (Matrix)", Matrix_inner, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MATRIX_OUTER_METHOD, "Matrix (Matrix)", Matrix_outer, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_EIGENVALUES_METHOD, "Tuple ()", Matrix_eigenvalues, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_EIGENSYSTEM_METHOD, "Tuple ()", Matrix_eigensystem, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_SVD_METHOD, "Tuple ()", Matrix_svd, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_QR_METHOD, "Tuple ()", Matrix_qr, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_RESHAPE_METHOD, "(Int,Int)", Matrix_reshape, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_ROLL_METHOD, "Matrix (Int)", Matrix_roll__int, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MATRIX_ROLL_METHOD, "Matrix (Int,Int)", Matrix_roll__int_int, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ENUMERATE_METHOD, "(Int)", Matrix_enumerate, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MORPHO_COUNT_METHOD, "Int ()", Matrix_count, MORPHO_FN_PUREFN), +MORPHO_METHOD_SIGNATURE(MATRIX_DIMENSIONS_METHOD, "Tuple ()", Matrix_dimensions, BUILTIN_FLAGSEMPTY) MORPHO_ENDCLASS /* ********************************************************************** * Initialization - * ********************************************************************* */ + * ********************************************************************** */ void matrix_initialize(void) { objectmatrixtype=object_addtype(&objectmatrixdefn); - - builtin_addfunction(MATRIX_CLASSNAME, matrix_constructor, MORPHO_FN_CONSTRUCTOR); - builtin_addfunction(MATRIX_IDENTITYCONSTRUCTOR, matrix_identityconstructor, BUILTIN_FLAGSEMPTY); + matrix_addinterface(&matrixdefn); objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); @@ -1566,20 +1595,21 @@ void matrix_initialize(void) { value matrixclass=builtin_addclass(MATRIX_CLASSNAME, MORPHO_GETCLASSDEFINITION(Matrix), objclass); object_setveneerclass(OBJECT_MATRIX, matrixclass); - morpho_defineerror(MATRIX_INDICESOUTSIDEBOUNDS, ERROR_HALT, MATRIX_INDICESOUTSIDEBOUNDS_MSG); - morpho_defineerror(MATRIX_INVLDINDICES, ERROR_HALT, MATRIX_INVLDINDICES_MSG); - morpho_defineerror(MATRIX_INVLDNUMINDICES, ERROR_HALT, MATRIX_INVLDNUMINDICES_MSG); - morpho_defineerror(MATRIX_CONSTRUCTOR, ERROR_HALT, MATRIX_CONSTRUCTOR_MSG); - morpho_defineerror(MATRIX_INVLDARRAYINIT, ERROR_HALT, MATRIX_INVLDARRAYINIT_MSG); - morpho_defineerror(MATRIX_ARITHARGS, ERROR_HALT, MATRIX_ARITHARGS_MSG); - morpho_defineerror(MATRIX_RESHAPEARGS, ERROR_HALT, MATRIX_RESHAPEARGS_MSG); - morpho_defineerror(MATRIX_INCOMPATIBLEMATRICES, ERROR_HALT, MATRIX_INCOMPATIBLEMATRICES_MSG); - morpho_defineerror(MATRIX_SINGULAR, ERROR_HALT, MATRIX_SINGULAR_MSG); - morpho_defineerror(MATRIX_NOTSQ, ERROR_HALT, MATRIX_NOTSQ_MSG); - morpho_defineerror(MATRIX_OPFAILED, ERROR_HALT, MATRIX_OPFAILED_MSG); - morpho_defineerror(MATRIX_SETCOLARGS, ERROR_HALT, MATRIX_SETCOLARGS_MSG); - morpho_defineerror(MATRIX_NORMARGS, ERROR_HALT, MATRIX_NORMARGS_MSG); - morpho_defineerror(MATRIX_IDENTCONSTRUCTOR, ERROR_HALT, MATRIX_IDENTCONSTRUCTOR_MSG); + morpho_addfunction(MATRIX_CLASSNAME, "Matrix (Int, Int)", matrix_constructor__int_int, MORPHO_FN_CONSTRUCTOR, NULL); + morpho_addfunction(MATRIX_CLASSNAME, "Matrix (Int)", matrix_constructor__int, MORPHO_FN_CONSTRUCTOR, NULL); + morpho_addfunction(MATRIX_CLASSNAME, "Matrix (Matrix)", matrix_constructor__matrix, MORPHO_FN_CONSTRUCTOR, NULL); + morpho_addfunction(MATRIX_CLASSNAME, "Matrix (List)", matrix_constructor__list, MORPHO_FN_CONSTRUCTOR, NULL); + morpho_addfunction(MATRIX_CLASSNAME, "Matrix (Tuple)", matrix_constructor__list, MORPHO_FN_CONSTRUCTOR, NULL); + morpho_addfunction(MATRIX_CLASSNAME, "Matrix (Array)", matrix_constructor__array, MORPHO_FN_CONSTRUCTOR, NULL); + morpho_addfunction(MATRIX_CLASSNAME, "Matrix (Sparse)", matrix_constructor__sparse, MORPHO_FN_CONSTRUCTOR, NULL); + morpho_addfunction(MATRIX_CLASSNAME, "(...)", matrix_constructor__err, MORPHO_FN_CONSTRUCTOR, NULL); + + morpho_addfunction(MATRIX_IDENTITYCONSTRUCTOR, "Matrix (Int)", matrix_identityconstructor, MORPHO_FN_CONSTRUCTOR, NULL); + + morpho_defineerror(MATRIX_CONSTRUCTOR, ERROR_HALT, MATRIX_CONSTRUCTOR_MSG); + morpho_defineerror(MATRIX_IDENTCONSTRUCTOR, ERROR_HALT, MATRIX_IDENTCONSTRUCTOR_MSG); + + complexmatrix_initialize(); } -#endif +#endif /* MORPHO_INCLUDE_LINALG */ diff --git a/src/linalg/matrix.h b/src/linalg/matrix.h index fe74a515..d911825c 100644 --- a/src/linalg/matrix.h +++ b/src/linalg/matrix.h @@ -29,16 +29,33 @@ #define MATRIX_LAPACK_PRESENT #endif +#ifdef MORPHO_LINALG_USE_LAPACKE +typedef lapack_complex_double linalg_complexdouble_t; +typedef lapack_int linalg_int_t; +#else +typedef __LAPACK_double_complex linalg_complexdouble_t; +typedef __LAPACK_int linalg_int_t; +#endif + #include "cmplx.h" #include "list.h" +#include "linalg.h" + +#define LINALG_MAXMATRIXDEFNS 4 + /* ------------------------------------------------------- - * Matrix objects + * Matrix object type * ------------------------------------------------------- */ extern objecttype objectmatrixtype; #define OBJECT_MATRIX objectmatrixtype +extern objecttypedefn objectmatrixdefn; + +typedef int MatrixIdx_t; // Type used for matrix indices +typedef size_t MatrixCount_t; // Type used to count total number of elements + /** Matrices are a purely numerical collection type oriented toward linear algebra. Elements are stored in column-major format, i.e. [ 1 2 ] @@ -47,8 +64,10 @@ extern objecttype objectmatrixtype; typedef struct { object obj; - unsigned int nrows; - unsigned int ncols; + MatrixIdx_t nrows; // Number of rows + MatrixIdx_t ncols; // Number of columns + MatrixIdx_t nvals; // Number of doubles per entry + MatrixCount_t nels; // Total number of entries (nrows*ncols*nvals) double *elements; double matrixdata[]; } objectmatrix; @@ -59,147 +78,228 @@ typedef struct { /** Gets the object as an matrix */ #define MORPHO_GETMATRIX(val) ((objectmatrix *) MORPHO_GETOBJECT(val)) -/** Creates a matrix object */ -objectmatrix *object_newmatrix(unsigned int nrows, unsigned int ncols, bool zero); - -/** Creates a new matrix from an array */ -objectmatrix *object_matrixfromarray(objectarray *array); - -/** Creates a new matrix from an existing matrix */ -objectmatrix *object_clonematrix(objectmatrix *array); - /** @brief Use to create static matrices on the C stack @details Intended for small matrices; Caller needs to supply a double array of size nr*nc. */ -#define MORPHO_STATICMATRIX(darray, nr, nc) { .obj.type=OBJECT_MATRIX, .obj.status=OBJECT_ISUNMANAGED, .obj.next=NULL, .elements=darray, .nrows=nr, .ncols=nc } +#define MORPHO_STATICMATRIX(darray, nr, nc) { .obj.type=OBJECT_MATRIX, .obj.status=OBJECT_ISUNMANAGED, .obj.next=NULL, .elements=darray, .nrows=nr, .ncols=nc, .nvals=1, .nels=nr*nc } /** Macro to decide if a matrix is 'small' or 'large' and hence static or dynamic allocation should be used. */ #define MATRIX_ISSMALL(m) (m->nrows*m->ncolsncols; j++) { for (int i=0; inrows; i++) { - if (!(matrix_getelement(src, i, j, &val) && + if (!(matrix_getelement(src, i, j, &val)==LINALGERR_OK && sparsedok_insert(dest, i+row0, j+col0, MORPHO_FLOAT(val)))) return false; } } @@ -288,7 +288,7 @@ bool sparsedok_copytomatrix(sparsedok *src, objectmatrix *dest, int row0, int co if (sparsedok_get(src, i, j, &entry)) { double val=0.0; if (!morpho_valuetofloat(entry, &val)) return false; - if (!matrix_setelement(dest, i+row0, j+col0, val)) return false; + if (matrix_setelement(dest, i+row0, j+col0, val)!=LINALGERR_OK) return false; } } @@ -583,7 +583,7 @@ bool sparseccs_copytomatrix(sparseccs *src, objectmatrix *dest, int row0, int co if (!sparseccs_getrowindices(src, i, &nentries, &entries)) return false; for (int j=0; jvalues[k])) return false; + if (matrix_setelement(dest, entries[j]+row0, i+col0, src->values[k]) != LINALGERR_OK) return false; k++; } } @@ -687,6 +687,39 @@ bool sparse_checkupdatedimension(int check, int *dim) { return true; } +/** Recurses into an objectlist to find the dimensions of the array and all child arrays */ +static bool _getlistdimensions(objectlist *list, unsigned int dim[], unsigned int maxdim, unsigned int *ndim) { + unsigned int m=0; + + if (maxdim==0) return false; + + /* Store the length */ + if (list->val.count>dim[0]) dim[0]=list->val.count; + + for (unsigned int i=0; ival.count; i++) { + if (MORPHO_ISLIST(list->val.data[i]) && maxdim>0) { + _getlistdimensions(MORPHO_GETLIST(list->val.data[i]), dim+1, maxdim-1, &m); + } + } + *ndim=m+1; + + return true; +} + +/** Gets a matrix element from a (potentially nested) list. */ +static bool _getlistelement(objectlist *list, unsigned int ndim, unsigned int *indx, value *val) { + value out=MORPHO_NIL; + objectlist *l=list; + for (unsigned int i=0; ival.count) { + out=l->val.data[indx[i]]; + if (i0) icol+=ncols[j]; @@ -828,7 +861,7 @@ objectsparseerror sparse_catmatrix(objectlist *in, objectmatrix **out) { objectsparseerror err=sparse_docat(in, NULL, matrix_catcopyentry, &nrows, &ncols); if (err!=SPARSE_OK) goto sparse_catmatrix_error; - new = object_newmatrix(nrows, ncols, true); + new = matrix_new(nrows, ncols, true); err=sparse_docat(in, new, matrix_catcopyentry, NULL, NULL); if (err==SPARSE_OK) *out = new; @@ -844,11 +877,50 @@ objectsparseerror sparse_catmatrix(objectlist *in, objectmatrix **out) { * Construct sparse matrices * ******************************* */ +/** Recurses into an objectarray to find the dimensions of the array and all child arrays + * @param[in] array - to search + * @param[out] dim - array of dimensions to be filled out (must be zero'd before initial call) + * @param[in] maxdim - maximum number of dimensions + * @param[out] ndim - number of dimensions of the array */ +static bool _getarraydimensions(objectarray *array, unsigned int dim[], unsigned int maxdim, unsigned int *ndim) { + unsigned int n=0, m=0; + for (n=0; nndim; n++) { + int k=MORPHO_GETINTEGERVALUE(array->data[n]); + if (k>dim[n]) dim[n]=k; + } + + if (maxdimndim) return false; + + for (unsigned int i=array->ndim; indim+array->nelements; i++) { + if (MORPHO_ISARRAY(array->data[i])) { + if (!_getarraydimensions(MORPHO_GETARRAY(array->data[i]), dim+n, maxdim-n, &m)) return false; + } + } + *ndim=n+m; + + return true; +} + +/** Looks up an array element recursively if necessary */ +static value _getarrayelement(objectarray *array, unsigned int ndim, unsigned int *indx) { + unsigned int na=array->ndim; + value out; + + if (array_getelement(array, na, indx, &out)==ARRAY_OK) { + if (ndim==na) return out; + if (MORPHO_ISARRAY(out)) { + return _getarrayelement(MORPHO_GETARRAY(out), ndim-na, indx+na); + } + } + + return MORPHO_NIL; +} + /** Create a sparse array from an array */ objectsparse *object_sparsefromarray(objectarray *array) { unsigned int dim[2] = {0,0}, ndim; - if (!matrix_getarraydimensions(array, dim, 2, &ndim)) return NULL; + if (!_getarraydimensions(array, dim, 2, &ndim)) return NULL; objectsparse *new=object_newsparse(NULL, NULL); @@ -856,7 +928,7 @@ objectsparse *object_sparsefromarray(objectarray *array) { value v[3]={MORPHO_NIL, MORPHO_NIL, MORPHO_NIL}; for (unsigned int k=0; kdok, MORPHO_GETINTEGERVALUE(v[0]), MORPHO_GETINTEGERVALUE(v[1]), v[2]); @@ -875,7 +947,7 @@ objectsparseerror object_sparsefromlist(objectlist *list, objectsparse **out) { unsigned int dim[2] = {0,0}, ndim; objectsparseerror err=SPARSE_OK; - if (!matrix_getlistdimensions(list, dim, 2, &ndim)) return SPARSE_INVLDINIT; + if (!_getlistdimensions(list, dim, 2, &ndim)) return SPARSE_INVLDINIT; objectsparse *new=object_newsparse(NULL, NULL); @@ -889,7 +961,7 @@ objectsparseerror object_sparsefromlist(objectlist *list, objectsparse **out) { value v[3]={MORPHO_NIL, MORPHO_NIL, MORPHO_NIL}; for (unsigned int k=0; kdok, MORPHO_GETINTEGERVALUE(v[0]), MORPHO_GETINTEGERVALUE(v[1]), v[2]); @@ -920,11 +992,11 @@ objectsparseerror sparse_tomatrix(objectsparse *in, objectmatrix **out) { objectmatrix *new = NULL; if (sparse_checkformat(in, SPARSE_CCS, false, false)) { - new=object_newmatrix(in->ccs.nrows, in->ccs.ncols, true); + new=matrix_new(in->ccs.nrows, in->ccs.ncols, true); if (!new) return SPARSE_FAILED; if (sparseccs_copytomatrix(&in->ccs, new, 0, 0)) err=SPARSE_OK; } else if (sparse_checkformat(in, SPARSE_DOK, false, false)) { - new=object_newmatrix(in->dok.nrows, in->dok.ncols, true); + new=matrix_new(in->dok.nrows, in->dok.ncols, true); if (!new) return SPARSE_FAILED; if (sparsedok_copytomatrix(&in->dok, new, 0, 0)) err=SPARSE_OK; } @@ -1192,7 +1264,7 @@ size_t sparse_size(objectsparse *a) { void sparse_raiseerror(vm *v, objectsparseerror err) { switch(err) { case SPARSE_OK: break; - case SPARSE_INCMPTBLDIM: morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); break; + case SPARSE_INCMPTBLDIM: morpho_runtimeerror(v, LINALG_INCOMPATIBLEMATRICES); break; case SPARSE_CONVFAILED: morpho_runtimeerror(v, SPARSE_CONVFAILEDERR); break; case SPARSE_FAILED: morpho_runtimeerror(v, SPARSE_OPFAILEDERR); break; case SPARSE_INVLDINIT: morpho_runtimeerror(v, SPARSE_INVLDARRAYINIT); break; @@ -1247,7 +1319,7 @@ value Sparse_getindex(vm *v, int nargs, value *args) { if (array_valuelisttoindices(nargs, args+1, indx)) { sparse_getelement(s, indx[0], indx[1], &out); - } else morpho_runtimeerror(v, MATRIX_INVLDINDICES); + } else morpho_runtimeerror(v, LINALG_INVLDARGS); return out; } @@ -1266,7 +1338,7 @@ value Sparse_setindex(vm *v, int nargs, value *args) { if (osize!=nsize) { morpho_resizeobject(v, (object *) s, osize, nsize); } - } else morpho_runtimeerror(v, MATRIX_INVLDINDICES); + } else morpho_runtimeerror(v, LINALG_INVLDARGS); return MORPHO_NIL; } @@ -1384,7 +1456,7 @@ value Sparse_mul(vm *v, int nargs, value *args) { if (sparse_checkformat(a, SPARSE_CCS, true, true)) { objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); - objectmatrix *out=object_newmatrix(a->ccs.nrows, b->ncols, true); + objectmatrix *out=matrix_new(a->ccs.nrows, b->ncols, true); new = (objectsparse *) out; // Munge type to ensure binding/deallocation if (out) { @@ -1427,7 +1499,7 @@ value Sparse_mulr(vm *v, int nargs, value *args) { int ncols; sparse_getdimensions(b, NULL, &ncols); - objectmatrix *new=object_newmatrix(a->nrows, ncols, true); + objectmatrix *new=matrix_new(a->nrows, ncols, true); if (new) { err=sparse_muldxs(a, b, new); @@ -1460,7 +1532,7 @@ value Sparse_divr(vm *v, int nargs, value *args) { if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); - objectmatrix *new = object_newmatrix(b->nrows, b->ncols, false); + objectmatrix *new = matrix_new(b->nrows, b->ncols, false); if (new) { size_t asize=sparse_size(a); objectsparseerror err =sparse_div(a, b, new); @@ -1586,8 +1658,8 @@ value Sparse_getcolumn(vm *v, int nargs, value *args) { morpho_bindobjects(v, 1, &out); } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); - } else morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); - } else morpho_runtimeerror(v, MATRIX_SETCOLARGS); + } else morpho_runtimeerror(v, LINALG_INDICESOUTSIDEBOUNDS); + } else morpho_runtimeerror(v, LINALG_INVLDARGS); return out; } @@ -1612,7 +1684,7 @@ value Sparse_rowindices(vm *v, int nargs, value *args) { morpho_bindobjects(v, 1, &out); } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); } - } else morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); + } else morpho_runtimeerror(v, LINALG_INDICESOUTSIDEBOUNDS); } } @@ -1640,14 +1712,14 @@ value Sparse_setrowindices(vm *v, int nargs, value *args) { if (list_getelement(list, i, &entry) && MORPHO_ISINTEGER(entry)) { entries[i]=MORPHO_GETINTEGERVALUE(entry); - } else { morpho_runtimeerror(v, MATRIX_INVLDINDICES); return MORPHO_NIL; } + } else { morpho_runtimeerror(v, LINALG_INVLDARGS); return MORPHO_NIL; } } - + if (!sparseccs_setrowindices(&s->ccs, col, nentries, entries)) { - morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); + morpho_runtimeerror(v, LINALG_INCOMPATIBLEMATRICES); } - - } else morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); + + } else morpho_runtimeerror(v, LINALG_INDICESOUTSIDEBOUNDS); } } @@ -1741,14 +1813,14 @@ void sparse_initialize(void) { objectdokkeytype=object_addtype(&objectdokkeydefn); objectsparsetype=object_addtype(&objectsparsedefn); - builtin_addfunction(SPARSE_CLASSNAME, sparse_constructor, MORPHO_FN_CONSTRUCTOR); - objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); value sparseclass=builtin_addclass(SPARSE_CLASSNAME, MORPHO_GETCLASSDEFINITION(Sparse), objclass); object_setveneerclass(OBJECT_SPARSE, sparseclass); + builtin_addfunction(SPARSE_CLASSNAME, sparse_constructor, MORPHO_FN_CONSTRUCTOR); + morpho_defineerror(SPARSE_CONSTRUCTOR, ERROR_HALT, SPARSE_CONSTRUCTOR_MSG); morpho_defineerror(SPARSE_SETFAILED, ERROR_HALT, SPARSE_SETFAILED_MSG); morpho_defineerror(SPARSE_INVLDARRAYINIT, ERROR_HALT, SPARSE_INVLDARRAYINIT_MSG); diff --git a/src/linalg/sparse.h b/src/linalg/sparse.h index 0264cb9d..6ec4d7da 100644 --- a/src/linalg/sparse.h +++ b/src/linalg/sparse.h @@ -13,7 +13,7 @@ #include #include "object.h" #include "morpho.h" -#include "matrix.h" +#include "linalg.h" /* ------------------------------------------------------- * Sparse objects diff --git a/src/linalg/xmatrix.c b/src/linalg/xmatrix.c new file mode 100644 index 00000000..b71e1aed --- /dev/null +++ b/src/linalg/xmatrix.c @@ -0,0 +1,1585 @@ +/** @file matrix.c + * @author T J Atherton + * + * @brief Veneer class over the objectmatrix type that interfaces with blas and lapack + */ + +#include "build.h" +#ifdef MORPHO_INCLUDE_LINALG + +#include +#include "morpho.h" +#include "classes.h" + +#include "matrix.h" +#include "sparse.h" +#include "format.h" + +/* ********************************************************************** + * Matrix objects + * ********************************************************************** */ + +objecttype objectmatrixtype; + +/** Function object definitions */ +size_t objectmatrix_sizefn(object *obj) { + return sizeof(objectmatrix)+sizeof(double) * + ((objectmatrix *) obj)->ncols * + ((objectmatrix *) obj)->nrows; +} + +void objectmatrix_printfn(object *obj, void *v) { + morpho_printf(v, ""); +} + +objecttypedefn objectmatrixdefn = { + .printfn=objectmatrix_printfn, + .markfn=NULL, + .freefn=NULL, + .sizefn=objectmatrix_sizefn, + .hashfn=NULL, + .cmpfn=NULL +}; + +/** Creates a matrix object */ +objectmatrix *object_newmatrix(unsigned int nrows, unsigned int ncols, bool zero) { + unsigned int nel = nrows*ncols; + objectmatrix *new = (objectmatrix *) object_new(sizeof(objectmatrix)+nel*sizeof(double), OBJECT_MATRIX); + + if (new) { + new->ncols=ncols; + new->nrows=nrows; + new->elements=new->matrixdata; + if (zero) { + memset(new->elements, 0, sizeof(double)*nel); + } + } + + return new; +} + +/* ********************************************************************** + * Other constructors + * ********************************************************************** */ + +/* + * Create matrices from array objects + */ + +void matrix_raiseerror(vm *v, objectmatrixerror err) { + switch(err) { + case MATRIX_OK: break; + case MATRIX_INCMPTBLDIM: morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); break; + case MATRIX_SING: morpho_runtimeerror(v, MATRIX_SINGULAR); break; + case MATRIX_INVLD: morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT); break; + case MATRIX_BNDS: morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); break; + case MATRIX_NSQ: morpho_runtimeerror(v, MATRIX_NOTSQ); break; + case MATRIX_FAILED: morpho_runtimeerror(v, MATRIX_OPFAILED); break; + case MATRIX_ALLOC: morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); break; + } +} + +/** Recurses into an objectarray to find the dimensions of the array and all child arrays + * @param[in] array - to search + * @param[out] dim - array of dimensions to be filled out (must be zero'd before initial call) + * @param[in] maxdim - maximum number of dimensions + * @param[out] ndim - number of dimensions of the array */ +bool matrix_getarraydimensions(objectarray *array, unsigned int dim[], unsigned int maxdim, unsigned int *ndim) { + unsigned int n=0, m=0; + for (n=0; nndim; n++) { + int k=MORPHO_GETINTEGERVALUE(array->data[n]); + if (k>dim[n]) dim[n]=k; + } + + if (maxdimndim) return false; + + for (unsigned int i=array->ndim; indim+array->nelements; i++) { + if (MORPHO_ISARRAY(array->data[i])) { + if (!matrix_getarraydimensions(MORPHO_GETARRAY(array->data[i]), dim+n, maxdim-n, &m)) return false; + } + } + *ndim=n+m; + + return true; +} + +/** Looks up an array element recursively if necessary */ +value matrix_getarrayelement(objectarray *array, unsigned int ndim, unsigned int *indx) { + unsigned int na=array->ndim; + value out; + + if (array_getelement(array, na, indx, &out)==ARRAY_OK) { + if (ndim==na) return out; + if (MORPHO_ISARRAY(out)) { + return matrix_getarrayelement(MORPHO_GETARRAY(out), ndim-na, indx+na); + } + } + + return MORPHO_NIL; +} + +/** Creates a new array from a list of values */ +objectmatrix *object_matrixfromarray(objectarray *array) { + unsigned int dim[2]={0,1}; // The 1 is to allow for vector arrays. + unsigned int ndim=0; + objectmatrix *ret=NULL; + + if (matrix_getarraydimensions(array, dim, 2, &ndim)) { + ret=object_newmatrix(dim[0], dim[1], true); + } + + unsigned int indx[2]; + if (ret) for (unsigned int i=0; ielements[j*dim[0]+i]); + } else if (!MORPHO_ISNIL(f)) { + object_free((object *) ret); return NULL; + } + } + } + + return ret; +} + +/* + * Create matrices from lists + */ + +/** Recurses into an objectlist to find the dimensions of the array and all child arrays + * @param[in] list - to search + * @param[out] dim - array of dimensions to be filled out (must be zero'd before initial call) + * @param[in] maxdim - maximum number of dimensions + * @param[out] ndim - number of dimensions of the array */ +bool matrix_getlistdimensions(objectlist *list, unsigned int dim[], unsigned int maxdim, unsigned int *ndim) { + unsigned int m=0; + + if (maxdim==0) return false; + + /* Store the length */ + if (list->val.count>dim[0]) dim[0]=list->val.count; + + for (unsigned int i=0; ival.count; i++) { + if (MORPHO_ISLIST(list->val.data[i]) && maxdim>0) { + matrix_getlistdimensions(MORPHO_GETLIST(list->val.data[i]), dim+1, maxdim-1, &m); + } + } + *ndim=m+1; + + return true; +} + +/** Gets a matrix element from a (potentially nested) list. */ +bool matrix_getlistelement(objectlist *list, unsigned int ndim, unsigned int *indx, value *val) { + value out=MORPHO_NIL; + objectlist *l=list; + for (unsigned int i=0; ival.count) { + out=l->val.data[indx[i]]; + if (i2) return false; + + unsigned int indx[2]; + if (ret) for (unsigned int i=0; ielements[j*dim[0]+i]); + } else { + object_free((object *) ret); + return NULL; + } + } + } + + return ret; +} + +/** Creates a matrix from a list of floats */ +objectmatrix *object_matrixfromfloats(unsigned int nrows, unsigned int ncols, double *list) { + objectmatrix *ret=NULL; + + ret=object_newmatrix(nrows, ncols, true); + if (ret) cblas_dcopy(ncols*nrows, list, 1, ret->elements, 1); + + return ret; +} + +/* + * Clone matrices + */ + +/** Clone a matrix */ +objectmatrix *object_clonematrix(objectmatrix *in) { + objectmatrix *new = object_newmatrix(in->nrows, in->ncols, false); + + if (new) { + cblas_dcopy(in->ncols * in->nrows, in->elements, 1, new->elements, 1); + } + + return new; +} + +/* ********************************************************************** + * Matrix operations + * ********************************************************************* */ + +/** @brief Sets a matrix element. + @returns true if the element is in the range of the matrix, false otherwise */ +bool matrix_setelement(objectmatrix *matrix, unsigned int row, unsigned int col, double value) { + if (colncols && rownrows) { + matrix->elements[col*matrix->nrows+row]=value; + return true; + } + return false; +} + +/** @brief Gets a matrix element + * @returns true if the element is in the range of the matrix, false otherwise */ +bool matrix_getelement(objectmatrix *matrix, unsigned int row, unsigned int col, double *value) { + if (colncols && rownrows) { + if (value) *value=matrix->elements[col*matrix->nrows+row]; + return true; + } + return false; +} + +/** @brief Gets a column's entries + * @param[in] matrix - the matrix + * @param[in] col - column number + * @param[out] v - column entries (matrix->nrows in number) + * @returns true if the element is in the range of the matrix, false otherwise */ +bool matrix_getcolumn(objectmatrix *matrix, unsigned int col, double **v) { + if (colncols) { + *v=&matrix->elements[col*matrix->nrows]; + return true; + } + return false; +} + +/** @brief Sets a column's entries + * @param[in] matrix - the matrix + * @param[in] col - column number + * @param[in] v - column entries (matrix->nrows in number) + * @returns true if the element is in the range of the matrix, false otherwise */ +bool matrix_setcolumn(objectmatrix *matrix, unsigned int col, double *v) { + if (colncols) { + cblas_dcopy(matrix->nrows, v, 1, &matrix->elements[col*matrix->nrows], 1); + return true; + } + return false; +} + +/** @brief Add a vector to a column in a matrix + * @param[in] m - the matrix + * @param[in] col - column number + * @param[in] alpha - scale + * @param[out] v - column entries (matrix->nrows in number) [should have m->nrows entries] + * @returns true on success */ +bool matrix_addtocolumn(objectmatrix *m, unsigned int col, double alpha, double *v) { + if (colncols) { + cblas_daxpy(m->nrows, alpha, v, 1, &m->elements[col*m->nrows], 1); + return true; + } + return false; +} + +/* ********************************************************************** + * Matrix arithmetic + * ********************************************************************* */ + +/** Copies one matrix to another */ +unsigned int matrix_countdof(objectmatrix *a) { + return a->ncols*a->nrows; +} + +/** Copies one matrix to another */ +objectmatrixerror matrix_copy(objectmatrix *a, objectmatrix *out) { + if (a->ncols==out->ncols && a->nrows==out->nrows) { + cblas_dcopy(a->ncols * a->nrows, a->elements, 1, out->elements, 1); + return MATRIX_OK; + } + return MATRIX_INCMPTBLDIM; +} + +/** Copies a matrix to another at an arbitrary point */ +objectmatrixerror matrix_copyat(objectmatrix *a, objectmatrix *out, int row0, int col0) { + if (col0+a->ncols<=out->ncols && row0+a->nrows<=out->nrows) { + for (int j=0; jncols; j++) { + for (int i=0; inrows; i++) { + double value; + if (!matrix_getelement(a, i, j, &value)) return MATRIX_BNDS; + if (!matrix_setelement(out, row0+i, col0+j, value)) return MATRIX_BNDS; + } + } + + return MATRIX_OK; + } + return MATRIX_INCMPTBLDIM; +} + +/** Performs a + b -> out. */ +objectmatrixerror matrix_add(objectmatrix *a, objectmatrix *b, objectmatrix *out) { + if (a->ncols==b->ncols && a->ncols==out->ncols && + a->nrows==b->nrows && a->nrows==out->nrows) { + if (a!=out) cblas_dcopy(a->ncols * a->nrows, a->elements, 1, out->elements, 1); + cblas_daxpy(a->ncols * a->nrows, 1.0, b->elements, 1, out->elements, 1); + return MATRIX_OK; + } + return MATRIX_INCMPTBLDIM; +} + +/** Performs lambda*a + beta -> out. */ +objectmatrixerror matrix_addscalar(objectmatrix *a, double lambda, double beta, objectmatrix *out) { + if (a->ncols==out->ncols && a->nrows==out->nrows) { + for (unsigned int i=0; inrows*out->ncols; i++) { + out->elements[i]=lambda*a->elements[i]+beta; + } + return MATRIX_OK; + } + + return MATRIX_INCMPTBLDIM; +} + +/** Performs a + lambda*b -> a. */ +objectmatrixerror matrix_accumulate(objectmatrix *a, double lambda, objectmatrix *b) { + if (a->ncols==b->ncols && a->nrows==b->nrows ) { + cblas_daxpy(a->ncols * a->nrows, lambda, b->elements, 1, a->elements, 1); + return MATRIX_OK; + } + return MATRIX_INCMPTBLDIM; +} + +/** Performs a - b -> out */ +objectmatrixerror matrix_sub(objectmatrix *a, objectmatrix *b, objectmatrix *out) { + if (a->ncols==b->ncols && a->ncols==out->ncols && + a->nrows==b->nrows && a->nrows==out->nrows) { + if (a!=out) cblas_dcopy(a->ncols * a->nrows, a->elements, 1, out->elements, 1); + cblas_daxpy(a->ncols * a->nrows, -1.0, b->elements, 1, out->elements, 1); + return MATRIX_OK; + } + return MATRIX_INCMPTBLDIM; +} + +/** Performs a * b -> out */ +objectmatrixerror matrix_mul(objectmatrix *a, objectmatrix *b, objectmatrix *out) { + if (a->ncols==b->nrows && a->nrows==out->nrows && b->ncols==out->ncols) { + cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, a->nrows, b->ncols, a->ncols, 1.0, a->elements, a->nrows, b->elements, b->nrows, 0.0, out->elements, out->nrows); + return MATRIX_OK; + } + return MATRIX_INCMPTBLDIM; +} + +/** Finds the Frobenius inner product of two matrices */ +objectmatrixerror matrix_inner(objectmatrix *a, objectmatrix *b, double *out) { + if (a->ncols==b->ncols && a->nrows==b->nrows) { + *out=cblas_ddot(a->ncols*a->nrows, a->elements, 1, b->elements, 1); + return MATRIX_OK; + } + return MATRIX_INCMPTBLDIM; +} + +/** Computes the outer product of two matrices */ +objectmatrixerror matrix_outer(objectmatrix *a, objectmatrix *b, objectmatrix *out) { + int m=a->nrows*a->ncols, n=b->nrows*b->ncols; + if (m==out->nrows && n==out->ncols) { + cblas_dger(CblasColMajor, m, n, 1, a->elements, 1, b->elements, 1, out->elements, out->nrows); + return MATRIX_OK; + } + return MATRIX_INCMPTBLDIM; +} + +/** Solves the system a.x = b + * @param[in] a lhs + * @param[in] b rhs + * @param[in] out - the solution x + * @param[out] lu - LU decomposition of a; you must provide an array the same size as a. + * @param[out] pivot - you must provide an array with the same number of rows as a. + * @returns objectmatrixerror indicating the status; MATRIX_OK indicates success. + * */ +static objectmatrixerror matrix_div(objectmatrix *a, objectmatrix *b, objectmatrix *out, double *lu, int *pivot) { + int n=a->nrows, nrhs = b->ncols, info; + + cblas_dcopy(a->ncols * a->nrows, a->elements, 1, lu, 1); + if (b!=out) cblas_dcopy(b->ncols * b->nrows, b->elements, 1, out->elements, 1); +#ifdef MORPHO_LINALG_USE_LAPACKE + info=LAPACKE_dgesv(LAPACK_COL_MAJOR, n, nrhs, lu, n, pivot, out->elements, n); +#else + dgesv_(&n, &nrhs, lu, &n, pivot, out->elements, &n, &info); +#endif + + return (info==0 ? MATRIX_OK : (info>0 ? MATRIX_SING : MATRIX_INVLD)); +} + +/** Solves the system a.x = b for small matrices (test with MATRIX_ISSMALL) + * @warning Uses the C stack for storage, which avoids malloc but can cause stack overflow */ +objectmatrixerror matrix_divs(objectmatrix *a, objectmatrix *b, objectmatrix *out) { + if (a->ncols==b->nrows && a->ncols == out->nrows) { + int pivot[a->nrows]; + double lu[a->nrows*a->ncols]; + + return matrix_div(a, b, out, lu, pivot); + } + return MATRIX_INCMPTBLDIM; +} + +/** Solves the system a.x = b for large matrices (test with MATRIX_ISSMALL) */ +objectmatrixerror matrix_divl(objectmatrix *a, objectmatrix *b, objectmatrix *out) { + objectmatrixerror ret = MATRIX_ALLOC; // Returned if allocation fails + if (!(a->ncols==b->nrows && a->ncols == out->nrows)) return MATRIX_INCMPTBLDIM; + + int *pivot=MORPHO_MALLOC(sizeof(int)*a->nrows); + double *lu=MORPHO_MALLOC(sizeof(double)*a->nrows*a->ncols); + + if (pivot && lu) ret=matrix_div(a, b, out, lu, pivot); + + if (pivot) MORPHO_FREE(pivot); + if (lu) MORPHO_FREE(lu); + + return ret; +} + +/** Inverts the matrix a + * @param[in] a lhs + * @param[in] out - the solution x + * @returns objectmatrixerror indicating the status; MATRIX_OK indicates success. + * */ +objectmatrixerror matrix_inverse(objectmatrix *a, objectmatrix *out) { + int nrows=a->nrows, ncols=a->ncols, info; + if (!(a->ncols==out->nrows && a->ncols == out->nrows)) return MATRIX_INCMPTBLDIM; + + int pivot[nrows]; + + cblas_dcopy(a->ncols * a->nrows, a->elements, 1, out->elements, 1); +#ifdef MORPHO_LINALG_USE_LAPACKE + info=LAPACKE_dgetrf(LAPACK_COL_MAJOR, nrows, ncols, out->elements, nrows, pivot); +#else + dgetrf_(&nrows, &ncols, out->elements, &nrows, pivot, &info); +#endif + + if (info!=0) return (info>0 ? MATRIX_SING : MATRIX_INVLD); + +#ifdef MORPHO_LINALG_USE_LAPACKE + info=LAPACKE_dgetri(LAPACK_COL_MAJOR, nrows, out->elements, nrows, pivot); +#else + int lwork=nrows*ncols; double work[nrows*ncols]; + dgetri_(&nrows, out->elements, &nrows, pivot, work, &lwork, &info); +#endif + + return (info==0 ? MATRIX_OK : (info>0 ? MATRIX_SING : MATRIX_INVLD)); +} + +/** Compute eigenvalues and eigenvectors of a matrix + * @param[in] a - an objectmatrix to diagonalize of size n + * @param[out] wr - a buffer of size n will hold the real part of the eigenvalues on exit + * @param[out] wi - a buffer of size n will hold the imag part of the eigenvalues on exit + * @param[out] vec - (optional) will be filled out with eigenvectors as columns (should be of size n) + * @returns an error code or MATRIX_OK on success */ +objectmatrixerror matrix_eigensystem(objectmatrix *a, double *wr, double *wi, objectmatrix *vec) { + int info, n=a->nrows; + if (a->nrows!=a->ncols) return MATRIX_NSQ; + if (vec && ((a->nrows!=vec->nrows) || (a->nrows!=vec->ncols))) return MATRIX_INCMPTBLDIM; + + // Copy a to prevent destruction + size_t size = ((size_t) n) * ((size_t) n) * sizeof(double); + double *acopy=MORPHO_MALLOC(size); + if (!acopy) return MATRIX_ALLOC; + cblas_dcopy(n*n, a->elements, 1, acopy, 1); + +#ifdef MORPHO_LINALG_USE_LAPACKE + info=LAPACKE_dgeev(LAPACK_COL_MAJOR, 'N', (vec ? 'V' : 'N'), n, acopy, n, wr, wi, NULL, n, (vec ? vec->elements : NULL), n); +#else + int lwork=4*n; double work[4*n]; + dgeev_("N", (vec ? "V" : "N"), &n, acopy, &n, wr, wi, NULL, &n, (vec ? vec->elements : NULL), &n, work, &lwork, &info); +#endif + + if (acopy) MORPHO_FREE(acopy); // Free up buffer + + if (info!=0) return (info>0 ? MATRIX_FAILED : MATRIX_INVLD); + + return MATRIX_OK; +} + + +/** Sums all elements of a matrix using Kahan summation */ +double matrix_sum(objectmatrix *a) { + unsigned int nel=a->ncols*a->nrows; + double sum=0.0, c=0.0, y,t; + + for (unsigned int i=0; ielements[i]-c; + t=sum+y; + c=(t-sum)-y; + sum=t; + } + return sum; +} + +/** Norms */ + +/** Computes the Frobenius norm of a matrix */ +double matrix_norm(objectmatrix *a) { + double nrm2=cblas_dnrm2(a->ncols*a->nrows, a->elements, 1); + return nrm2; +} + +/** Computes the L1 norm of a matrix */ +double matrix_L1norm(objectmatrix *a) { + unsigned int nel=a->ncols*a->nrows; + double sum=0.0, c=0.0, y,t; + + for (unsigned int i=0; ielements[i])-c; + t=sum+y; + c=(t-sum)-y; + sum=t; + } + return sum; +} + +/** Computes the Ln norm of a matrix */ +double matrix_Lnnorm(objectmatrix *a, double n) { + unsigned int nel=a->ncols*a->nrows; + double sum=0.0, c=0.0, y,t; + + for (unsigned int i=0; ielements[i],n)-c; + t=sum+y; + c=(t-sum)-y; + sum=t; + } + return pow(sum,1.0/n); +} + +/** Computes the Linf norm of a matrix */ +double matrix_Linfnorm(objectmatrix *a) { + unsigned int nel=a->ncols*a->nrows; + double max=0.0; + + for (unsigned int i=0; ielements[i]); + if (y>max) max=y; + } + return max; +} + +/** Transpose a matrix */ +objectmatrixerror matrix_transpose(objectmatrix *a, objectmatrix *out) { + if (!(a->ncols==out->nrows && a->nrows == out->ncols)) return MATRIX_INCMPTBLDIM; + + /* Copy elements a column at a time */ + for (unsigned int i=0; incols; i++) { + cblas_dcopy(a->nrows, a->elements+(i*a->nrows), 1, out->elements+i, a->ncols); + } + return MATRIX_OK; +} + +/** Calculate the trace of a matrix */ +objectmatrixerror matrix_trace(objectmatrix *a, double *out) { + if (a->nrows!=a->ncols) return MATRIX_NSQ; + *out=1.0; + *out=cblas_ddot(a->nrows, a->elements, a->ncols+1, out, 0); + + return MATRIX_OK; +} + +/** Scale a matrix */ +objectmatrixerror matrix_scale(objectmatrix *a, double scale) { + cblas_dscal(a->ncols*a->nrows, scale, a->elements, 1); + + return MATRIX_OK; +} + +/** Load the indentity matrix*/ +objectmatrixerror matrix_identity(objectmatrix *a) { + if (a->ncols!=a->nrows) return MATRIX_NSQ; + memset(a->elements, 0, sizeof(double)*a->nrows*a->ncols); + for (int i=0; inrows; i++) a->elements[i+a->nrows*i]=1.0; + return MATRIX_OK; +} + +/** Sets a matrix to zero */ +objectmatrixerror matrix_zero(objectmatrix *a) { + memset(a->elements, 0, sizeof(double)*a->nrows*a->ncols); + + return MATRIX_OK; +} + +/** Prints a matrix */ +void matrix_print(vm *v, objectmatrix *m) { + for (int i=0; inrows; i++) { // Rows run from 0...m + morpho_printf(v, "[ "); + for (int j=0; jncols; j++) { // Columns run from 0...k + double val; + matrix_getelement(m, i, j, &val); + morpho_printf(v, "%g ", (fabs(val)nrows-1 ? "\n" : "")); + } +} + +/** Prints a matrix to a buffer */ +bool matrix_printtobuffer(objectmatrix *m, char *format, varray_char *out) { + for (int i=0; inrows; i++) { // Rows run from 0...m + varray_charadd(out, "[ ", 2); + + for (int j=0; jncols; j++) { // Columns run from 0...k + double val; + matrix_getelement(m, i, j, &val); + if (!format_printtobuffer(MORPHO_FLOAT(val), format, out)) return false; + varray_charadd(out, " ", 1); + } + varray_charadd(out, "]", 1); + if (inrows-1) varray_charadd(out, "\n", 1); + } + return true; +} + +/** Matrix eigensystem */ +bool matrix_eigen(vm *v, objectmatrix *a, value *evals, value *evecs) { + double *ev = MORPHO_MALLOC(sizeof(double)*a->nrows*2); // Allocate temporary memory for eigenvalues + double *er=ev, *ei=ev+a->nrows; + + objectmatrix *vecs=NULL; // A new matrix for eigenvectors + objectlist *vallist = object_newlist(0, NULL); // List to hold eigenvalues + bool success=false; + + if (evecs) vecs=object_clonematrix(a); // Clones a to hold eigenvectors + + // Check that everything was allocated correctly + if (!(ev && vallist && (!evecs || vecs))) { + morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto matrix_eigen_cleanup; }; + + objectmatrixerror err=matrix_eigensystem(a, er, ei, vecs); + + if (err!=MATRIX_OK) { + matrix_raiseerror(v, err); + goto matrix_eigen_cleanup; + } + + // Now process the eigenvalues + for (int i=0; inrows; i++) { + if (fabs(ei[i])val.count; i++) { + if (MORPHO_ISOBJECT(vallist->val.data[i])) object_free(MORPHO_GETOBJECT(vallist->val.data[i])); + } + object_free((object *) vallist); + } + if (vecs) object_free((object *) vecs); + } + + return success; +} + +/* ********************************************************************** + * Matrix veneer class + * ********************************************************************* */ + +/** Constructs a Matrix object */ +value matrix_constructor(vm *v, int nargs, value *args) { + unsigned int nrows, ncols; + objectmatrix *new=NULL; + value out=MORPHO_NIL; + + if (nargs==2 && + MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) && + MORPHO_ISINTEGER(MORPHO_GETARG(args, 1)) ) { + nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + ncols = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1)); + new=object_newmatrix(nrows, ncols, true); + } else if (nargs==1 && + MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) { + nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + ncols = 1; + new=object_newmatrix(nrows, ncols, true); + } else if (nargs==1 && + MORPHO_ISARRAY(MORPHO_GETARG(args, 0))) { + new=object_matrixfromarray(MORPHO_GETARRAY(MORPHO_GETARG(args, 0))); + if (!new) morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT); +#ifdef MORPHO_INCLUDE_SPARSE + } else if (nargs==1 && + MORPHO_ISLIST(MORPHO_GETARG(args, 0))) { + new=object_matrixfromlist(MORPHO_GETLIST(MORPHO_GETARG(args, 0))); + if (!new) { + /** Could this be a concatenation operation? */ + objectsparseerror err = sparse_catmatrix(MORPHO_GETLIST(MORPHO_GETARG(args, 0)), &new); + if (err==SPARSE_INVLDINIT) { + morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT); + } else if (err!=SPARSE_OK) sparse_raiseerror(v, err); + } +#endif + } else if (nargs==1 && + MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { + new=object_clonematrix(MORPHO_GETMATRIX(MORPHO_GETARG(args, 0))); + if (!new) morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT); +#ifdef MORPHO_INCLUDE_SPARSE + } else if (nargs==1 && + MORPHO_ISSPARSE(MORPHO_GETARG(args, 0))) { + objectsparseerror err=sparse_tomatrix(MORPHO_GETSPARSE(MORPHO_GETARG(args, 0)), &new); + if (err!=SPARSE_OK) morpho_runtimeerror(v, MATRIX_INVLDARRAYINIT); +#endif + } else morpho_runtimeerror(v, MATRIX_CONSTRUCTOR); + + if (new) { + out=MORPHO_OBJECT(new); + morpho_bindobjects(v, 1, &out); + } + + return out; +} + +/** Creates an identity matrix */ +value matrix_identityconstructor(vm *v, int nargs, value *args) { + int n; + objectmatrix *new=NULL; + value out = MORPHO_NIL; + + if (nargs==1 && + MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) { + n = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + new=object_newmatrix(n, n, false); + if (new) { + matrix_identity(new); + } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); + } else morpho_runtimeerror(v, MATRIX_IDENTCONSTRUCTOR); + + if (new) { + out=MORPHO_OBJECT(new); + morpho_bindobjects(v, 1, &out); + } + + return out; +} + +/** Checks that a matrix is indexed with 2 indices with a generic interface */ +bool matrix_slicedim(value * a, unsigned int ndim){ + if (ndim>2||ndim<0) return false; + return true; +} + +/** Constucts a new matrix with a generic interface */ +void matrix_sliceconstructor(unsigned int *slicesize,unsigned int ndim,value* out){ + unsigned int numcol = 1; + if (ndim == 2) { + numcol = slicesize[1]; + } + *out = MORPHO_OBJECT(object_newmatrix(slicesize[0],numcol,false)); +} +/** Copies data from a at indx to out at newindx with a generic interface */ +objectarrayerror matrix_slicecopy(value * a,value * out, unsigned int ndim, unsigned int *indx,unsigned int *newindx){ + double num; // matrices store doubles; + unsigned int colindx = 0; + unsigned int colnewindx = 0; + + if (ndim == 2) { + colindx = indx[1]; + colnewindx = newindx[1]; + } + + if (!(matrix_getelement(MORPHO_GETMATRIX(*a),indx[0],colindx,&num)&& + matrix_setelement(MORPHO_GETMATRIX(*out),newindx[0],colnewindx,num))){ + return ARRAY_OUTOFBOUNDS; + } + return ARRAY_OK; +} + +/** Rolls the matrix list */ +void matrix_rollflat(objectmatrix *a, objectmatrix *b, int nplaces) { + unsigned int N = a->nrows*a->ncols; + int n = abs(nplaces); + if (n>N) n = n % N; + unsigned int Np = N - n; // Number of elements to roll + + if (nplaces<0) { + memcpy(b->matrixdata, a->matrixdata+n, sizeof(double)*Np); + memcpy(b->matrixdata+Np, a->matrixdata, sizeof(double)*n); + } else { + memcpy(b->matrixdata+n, a->matrixdata, sizeof(double)*Np); + if (n>0) memcpy(b->matrixdata, a->matrixdata+Np, sizeof(double)*n); + } +} + +/** Copies arow from matrix a into brow for matrix b */ +void matrix_copyrow(objectmatrix *a, int arow, objectmatrix *b, int brow) { + cblas_dcopy(a->ncols, a->elements+arow, a->nrows, b->elements+brow, a->nrows); +} + +/** Rolls a list by a number of elements */ +objectmatrix *matrix_roll(objectmatrix *a, int nplaces, int axis) { + objectmatrix *new=object_newmatrix(a->nrows, a->ncols, false); + + if (new) { + switch(axis) { + case 0: { // TODO: Could probably be faster + for (int i=0; inrows; i++) { + int j = (i+nplaces); + if (j<0) j+=a->nrows; + matrix_copyrow(a, i, new, j % a->nrows); + } + } + break; + case 1: matrix_rollflat(a, new, nplaces*a->nrows); break; + } + } + + return new; +} + +/** Gets the matrix element with given indices */ +value Matrix_getindex(vm *v, int nargs, value *args) { + objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args)); + unsigned int indx[2]={0,0}; + value out = MORPHO_NIL; + if (nargs>2){ + morpho_runtimeerror(v, MATRIX_INVLDNUMINDICES); + return out; + } + + if (array_valuelisttoindices(nargs, args+1, indx)) { + double outval; + if (!matrix_getelement(m, indx[0], indx[1], &outval)) { + morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); + } else { + out = MORPHO_FLOAT(outval); + } + } else { // now try to get a slice + objectarrayerror err = getslice(&MORPHO_SELF(args), &matrix_slicedim, &matrix_sliceconstructor, &matrix_slicecopy, nargs, &MORPHO_GETARG(args,0), &out); + if (err!=ARRAY_OK) MORPHO_RAISE(v, array_to_matrix_error(err) ); + if (MORPHO_ISOBJECT(out)){ + morpho_bindobjects(v,1,&out); + } else morpho_runtimeerror(v, MATRIX_INVLDINDICES); + } + return out; +} + +/** Sets the matrix element with given indices */ +value Matrix_setindex(vm *v, int nargs, value *args) { + objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args)); + unsigned int indx[2]={0,0}; + + if (array_valuelisttoindices(nargs-1, args+1, indx)) { + double value=0.0; + if (MORPHO_ISFLOAT(args[nargs])) value=MORPHO_GETFLOATVALUE(args[nargs]); + if (MORPHO_ISINTEGER(args[nargs])) value=(double) MORPHO_GETINTEGERVALUE(args[nargs]); + + if (!matrix_setelement(m, indx[0], indx[1], value)) { + morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); + } + } else morpho_runtimeerror(v, MATRIX_INVLDINDICES); + + return MORPHO_NIL; +} + +/** Sets the column of a matrix */ +value Matrix_setcolumn(vm *v, int nargs, value *args) { + objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args)); + + if (nargs==2 && + MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) && + MORPHO_ISMATRIX(MORPHO_GETARG(args, 1))) { + unsigned int col = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + objectmatrix *src = MORPHO_GETMATRIX(MORPHO_GETARG(args, 1)); + + if (colncols) { + if (src && src->ncols*src->nrows==m->nrows) { + matrix_setcolumn(m, col, src->elements); + } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); + } else morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); + } else morpho_runtimeerror(v, MATRIX_SETCOLARGS); + + return MORPHO_NIL; +} + +/** Gets a column of a matrix */ +value Matrix_getcolumn(vm *v, int nargs, value *args) { + objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + if (nargs==1 && + MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) { + unsigned int col = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + + if (colncols) { + double *vals; + if (matrix_getcolumn(m, col, &vals)) { + objectmatrix *new=object_matrixfromfloats(m->nrows, 1, vals); + if (new) { + out=MORPHO_OBJECT(new); + morpho_bindobjects(v, 1, &out); + } + } + } else morpho_runtimeerror(v, MATRIX_INDICESOUTSIDEBOUNDS); + } else morpho_runtimeerror(v, MATRIX_SETCOLARGS); + + return out; +} + +/** Prints a matrix */ +value Matrix_print(vm *v, int nargs, value *args) { + value self = MORPHO_SELF(args); + if (!MORPHO_ISMATRIX(self)) return Object_print(v, nargs, args); + + objectmatrix *m=MORPHO_GETMATRIX(MORPHO_SELF(args)); + matrix_print(v, m); + return MORPHO_NIL; +} + +/** Formatted conversion to a string */ +value Matrix_format(vm *v, int nargs, value *args) { + value out = MORPHO_NIL; + + if (nargs==1 && + MORPHO_ISSTRING(MORPHO_GETARG(args, 0))) { + varray_char str; + varray_charinit(&str); + + if (matrix_printtobuffer(MORPHO_GETMATRIX(MORPHO_SELF(args)), + MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)), + &str)) { + out = object_stringfromvarraychar(&str); + if (MORPHO_ISOBJECT(out)) morpho_bindobjects(v, 1, &out); + } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); + + varray_charclear(&str); + } else { + morpho_runtimeerror(v, VALUE_FRMTARG); + } + + return out; +} + +/** Matrix add */ +value Matrix_assign(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + + if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + + if (a->ncols==b->ncols && a->nrows==b->nrows) { + matrix_copy(b, a); + } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); + } + + return MORPHO_NIL; +} + +/** Matrix add */ +value Matrix_add(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + + if (a->ncols==b->ncols && a->nrows==b->nrows) { + objectmatrix *new = object_newmatrix(a->nrows, a->ncols, false); + if (new) { + out=MORPHO_OBJECT(new); + matrix_add(a, b, new); + } + } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); + } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) { + double val; + if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) { + objectmatrix *new = object_newmatrix(a->nrows, a->ncols, false); + if (new) { + out=MORPHO_OBJECT(new); + matrix_addscalar(a, 1.0, val, new); + } + } + } else morpho_runtimeerror(v, MATRIX_ARITHARGS); + + if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out); + + return out; +} + +/** Right add */ +value Matrix_addr(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + if (nargs==1 && (MORPHO_ISNIL(MORPHO_GETARG(args, 0)) || + MORPHO_ISNUMBER(MORPHO_GETARG(args, 0)))) { + int i=0; + if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) i=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + if (MORPHO_ISFLOAT(MORPHO_GETARG(args, 0))) i=(fabs(MORPHO_GETFLOATVALUE(MORPHO_GETARG(args, 0)))ncols==b->ncols && a->nrows==b->nrows) { + objectmatrix *new = object_newmatrix(a->nrows, a->ncols, false); + if (new) { + out=MORPHO_OBJECT(new); + matrix_sub(a, b, new); + } + } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); + } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) { + double val; + if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &val)) { + objectmatrix *new = object_newmatrix(a->nrows, a->ncols, false); + if (new) { + out=MORPHO_OBJECT(new); + matrix_addscalar(a, 1.0, -val, new); + } + } + } else morpho_runtimeerror(v, MATRIX_ARITHARGS); + + if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out); + + return out; +} + +/** Right subtract */ +value Matrix_subr(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + if (nargs==1 && (MORPHO_ISNIL(MORPHO_GETARG(args, 0)) || + MORPHO_ISNUMBER(MORPHO_GETARG(args, 0)))) { + int i=(MORPHO_ISNIL(MORPHO_GETARG(args, 0)) ? 0 : MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0))); + + if (MORPHO_ISFLOAT(MORPHO_GETARG(args, 0))) i=(fabs(MORPHO_GETFLOATVALUE(MORPHO_GETARG(args, 0)))nrows, a->ncols, false); + if (new) { + matrix_addscalar(a, 1.0, -val, new); + // now that did self - arg[0] and we want arg[0] - self so scale the whole thing by -1 + matrix_scale(new, -1.0); + out=MORPHO_OBJECT(new); + morpho_bindobjects(v, 1, &out); + } + } + + } else morpho_runtimeerror(v, VM_INVALIDARGS); + } else morpho_runtimeerror(v, VM_INVALIDARGS); + + return out; +} + +/** Matrix multiply */ +value Matrix_mul(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + + if (a->ncols==b->nrows) { + objectmatrix *new = object_newmatrix(a->nrows, b->ncols, false); + if (new) { + out=MORPHO_OBJECT(new); + matrix_mul(a, b, new); + morpho_bindobjects(v, 1, &out); + } + } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); + } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) { + double scale=1.0; + if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) { + objectmatrix *new = object_clonematrix(a); + if (new) { + out=MORPHO_OBJECT(new); + matrix_scale(new, scale); + morpho_bindobjects(v, 1, &out); + } + } +#ifdef MORPHO_INCLUDE_SPARSE + } else if (nargs==1 && MORPHO_ISSPARSE(MORPHO_GETARG(args, 0))) { + // Returns nil to ensure it gets passed to mulr on Sparse +#endif + } else morpho_runtimeerror(v, MATRIX_ARITHARGS); + + return out; +} + +/** Called when multiplying on the right */ +value Matrix_mulr(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) { + double scale=1.0; + if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) { + objectmatrix *new = object_clonematrix(a); + if (new) { + out=MORPHO_OBJECT(new); + matrix_scale(new, scale); + morpho_bindobjects(v, 1, &out); + } + } + } else morpho_runtimeerror(v, MATRIX_ARITHARGS); + + return out; +} + +/** Solution of linear system a.x = b (i.e. x = b/a) */ +value Matrix_div(vm *v, int nargs, value *args) { + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + + if (a->ncols==b->nrows) { + objectmatrix *new = object_newmatrix(b->nrows, b->ncols, false); + if (new) { + objectmatrixerror err; + if (MATRIX_ISSMALL(a)) { + err=matrix_divs(a, b, new); + } else { + err=matrix_divl(a, b, new); + } + if (err==MATRIX_SING) { + morpho_runtimeerror(v, MATRIX_SINGULAR); + object_free((object *) new); + } else { + out=MORPHO_OBJECT(new); + morpho_bindobjects(v, 1, &out); + } + } + } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); +#ifdef MORPHO_INCLUDE_SPARSE + } else if (nargs==1 && MORPHO_ISSPARSE(MORPHO_GETARG(args, 0))) { + /* Division by a sparse matrix: redirect to the divr selector of Sparse. */ + value vargs[2]={args[1],args[0]}; + return Sparse_divr(v, nargs, vargs); +#endif + } else if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) { + /* Division by a scalar */ + double scale=1.0; + if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) { + if (fabs(scale)ncols==b->ncols && a->nrows==b->nrows) { + out=MORPHO_SELF(args); + double lambda=1.0; + morpho_valuetofloat(MORPHO_GETARG(args, 0), &lambda); + matrix_accumulate(a, lambda, b); + } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); + } else morpho_runtimeerror(v, MATRIX_ARITHARGS); + + return MORPHO_NIL; +} + +/** Frobenius inner product */ +value Matrix_inner(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + + double prod=0.0; + if (matrix_inner(a, b, &prod)==MATRIX_OK) { + out = MORPHO_FLOAT(prod); + } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); + } else morpho_runtimeerror(v, MATRIX_ARITHARGS); + + return out; +} + +/** Outer product */ +value Matrix_outer(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { + objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); + objectmatrix *new=object_newmatrix(a->nrows*a->ncols, b->nrows*b->ncols, true); + + if (new && + matrix_outer(a, b, new)==MATRIX_OK) { + out=MORPHO_OBJECT(new); + morpho_bindobjects(v, 1, &out); + } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); + } else morpho_runtimeerror(v, MATRIX_ARITHARGS); + + return out; +} + +/** Matrix sum */ +value Matrix_sum(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + return MORPHO_FLOAT(matrix_sum(a)); +} + +/** Roll a matrix */ +value Matrix_roll(vm *v, int nargs, value *args) { + objectmatrix *slf = MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out = MORPHO_NIL; + int roll, axis=0; + + if (nargs>0 && + morpho_valuetoint(MORPHO_GETARG(args, 0), &roll)) { + + if (nargs==2 && !morpho_valuetoint(MORPHO_GETARG(args, 1), &axis)) return out; + + objectmatrix *new = matrix_roll(slf, roll, axis); + + if (new) { + out = MORPHO_OBJECT(new); + morpho_bindobjects(v, 1, &out); + } + + } else morpho_runtimeerror(v, LIST_ADDARGS); + + return out; +} + + +/** Matrix norm */ +value Matrix_norm(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out = MORPHO_NIL; + + if (nargs==1) { + value arg = MORPHO_GETARG(args, 0); + + if (MORPHO_ISNUMBER(arg)) { + double n; + + if (morpho_valuetofloat(arg, &n)) { + if (fabs(n-1.0)val.count, new->val.data); + new->val.count--; // And pop it back off + } + + return evals; +} + +/** Matrix eigensystem */ +value Matrix_eigensystem(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value evals=MORPHO_NIL, evecs=MORPHO_NIL, out=MORPHO_NIL; + objectlist *resultlist = object_newlist(0, NULL); + if (!resultlist) { + morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); + return MORPHO_NIL; + } + + if (matrix_eigen(v, a, &evals, &evecs)) { + objectlist *evallist = MORPHO_GETLIST(evals); + + list_append(resultlist, evals); // Create the output list + list_append(resultlist, evecs); + out=MORPHO_OBJECT(resultlist); + + list_append(evallist, evals); // Ensure we bind all objects at once + list_append(evallist, evecs); // by popping them onto the evallist. + list_append(evallist, out); // + morpho_bindobjects(v, evallist->val.count, evallist->val.data); + evallist->val.count-=3; // and then popping them back off. + } + + return out; +} + +/** Inverts a matrix */ +value Matrix_inverse(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + // The inverse will have the number of rows and number of columns + // swapped. + objectmatrix *new = object_newmatrix(a->ncols, a->nrows, false); + if (new) { + objectmatrixerror mi = matrix_inverse(a, new); + out=MORPHO_OBJECT(new); + morpho_bindobjects(v, 1, &out); + + if (mi!=MATRIX_OK) matrix_raiseerror(v, mi); + } + + return out; +} + +/** Transpose of a matrix */ +value Matrix_transpose(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + objectmatrix *new = object_newmatrix(a->ncols, a->nrows, false); + if (new) { + matrix_transpose(a, new); + out=MORPHO_OBJECT(new); + morpho_bindobjects(v, 1, &out); + } + + return out; +} + +/** Reshape a matrix */ +value Matrix_reshape(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + + if (nargs==2 && + MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)) && + MORPHO_ISINTEGER(MORPHO_GETARG(args, 1))) { + int nrows = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + int ncols = MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 1)); + + if (nrows*ncols==a->nrows*a->ncols) { + a->nrows=nrows; + a->ncols=ncols; + } else morpho_runtimeerror(v, MATRIX_INCOMPATIBLEMATRICES); + } else morpho_runtimeerror(v, MATRIX_RESHAPEARGS); + + return MORPHO_NIL; +} + +/** Trace of a matrix */ +value Matrix_trace(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + if (a->nrows==a->ncols) { + double tr; + if (matrix_trace(a, &tr)==MATRIX_OK) out=MORPHO_FLOAT(tr); + } else { + morpho_runtimeerror(v, MATRIX_NOTSQ); + } + + return out; +} + +/** Enumerate protocol */ +value Matrix_enumerate(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value out=MORPHO_NIL; + + if (nargs==1) { + if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) { + int i=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); + + if (i<0) out=MORPHO_INTEGER(a->ncols*a->nrows); + else if (incols*a->nrows) out=MORPHO_FLOAT(a->elements[i]); + } + } + + return out; +} + + +/** Number of matrix elements */ +value Matrix_count(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + + return MORPHO_INTEGER(a->ncols*a->nrows); +} + +/** Matrix dimensions */ +value Matrix_dimensions(vm *v, int nargs, value *args) { + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + value dim[2]; + value out=MORPHO_NIL; + + dim[0]=MORPHO_INTEGER(a->nrows); + dim[1]=MORPHO_INTEGER(a->ncols); + + objectlist *new=object_newlist(2, dim); + if (new) { + out=MORPHO_OBJECT(new); + morpho_bindobjects(v, 1, &out); + } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); + + return out; +} + +/** Clones a matrix */ +value Matrix_clone(vm *v, int nargs, value *args) { + value out=MORPHO_NIL; + objectmatrix *a=MORPHO_GETMATRIX(MORPHO_SELF(args)); + objectmatrix *new=object_clonematrix(a); + if (new) { + out=MORPHO_OBJECT(new); + morpho_bindobjects(v, 1, &out); + } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); + return out; +} + +MORPHO_BEGINCLASS(Matrix) +MORPHO_METHOD(MORPHO_GETINDEX_METHOD, Matrix_getindex, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_SETINDEX_METHOD, Matrix_setindex, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MATRIX_GETCOLUMN_METHOD, Matrix_getcolumn, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MATRIX_SETCOLUMN_METHOD, Matrix_setcolumn, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_PRINT_METHOD, Matrix_print, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_FORMAT_METHOD, Matrix_format, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_ASSIGN_METHOD, Matrix_assign, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADD_METHOD, "Matrix (_)", Matrix_add, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_ADDR_METHOD, "Matrix (_)", Matrix_addr, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SUB_METHOD, "Matrix (_)", Matrix_sub, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(MORPHO_SUBR_METHOD, "Matrix (_)", Matrix_subr, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_MUL_METHOD, Matrix_mul, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_MULR_METHOD, Matrix_mulr, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_DIV_METHOD, Matrix_div, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_ACC_METHOD, Matrix_acc, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MATRIX_INNER_METHOD, Matrix_inner, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MATRIX_OUTER_METHOD, Matrix_outer, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_SUM_METHOD, Matrix_sum, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MATRIX_NORM_METHOD, Matrix_norm, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MATRIX_INVERSE_METHOD, Matrix_inverse, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MATRIX_TRANSPOSE_METHOD, Matrix_transpose, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MATRIX_RESHAPE_METHOD, Matrix_reshape, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MATRIX_EIGENVALUES_METHOD, Matrix_eigenvalues, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MATRIX_EIGENSYSTEM_METHOD, Matrix_eigensystem, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MATRIX_TRACE_METHOD, Matrix_trace, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_ENUMERATE_METHOD, Matrix_enumerate, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_COUNT_METHOD, Matrix_count, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MATRIX_DIMENSIONS_METHOD, Matrix_dimensions, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_ROLL_METHOD, Matrix_roll, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_CLONE_METHOD, Matrix_clone, BUILTIN_FLAGSEMPTY) +MORPHO_ENDCLASS + +/* ********************************************************************** + * Initialization + * ********************************************************************* */ + +void matrix_initialize(void) { + objectmatrixtype=object_addtype(&objectmatrixdefn); + + builtin_addfunction(MATRIX_CLASSNAME, matrix_constructor, MORPHO_FN_CONSTRUCTOR); + builtin_addfunction(MATRIX_IDENTITYCONSTRUCTOR, matrix_identityconstructor, BUILTIN_FLAGSEMPTY); + + objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); + value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); + + value matrixclass=builtin_addclass(MATRIX_CLASSNAME, MORPHO_GETCLASSDEFINITION(Matrix), objclass); + object_setveneerclass(OBJECT_MATRIX, matrixclass); + + morpho_defineerror(MATRIX_INDICESOUTSIDEBOUNDS, ERROR_HALT, MATRIX_INDICESOUTSIDEBOUNDS_MSG); + morpho_defineerror(MATRIX_INVLDINDICES, ERROR_HALT, MATRIX_INVLDINDICES_MSG); + morpho_defineerror(MATRIX_INVLDNUMINDICES, ERROR_HALT, MATRIX_INVLDNUMINDICES_MSG); + morpho_defineerror(MATRIX_CONSTRUCTOR, ERROR_HALT, MATRIX_CONSTRUCTOR_MSG); + morpho_defineerror(MATRIX_INVLDARRAYINIT, ERROR_HALT, MATRIX_INVLDARRAYINIT_MSG); + morpho_defineerror(MATRIX_ARITHARGS, ERROR_HALT, MATRIX_ARITHARGS_MSG); + morpho_defineerror(MATRIX_RESHAPEARGS, ERROR_HALT, MATRIX_RESHAPEARGS_MSG); + morpho_defineerror(MATRIX_INCOMPATIBLEMATRICES, ERROR_HALT, MATRIX_INCOMPATIBLEMATRICES_MSG); + morpho_defineerror(MATRIX_SINGULAR, ERROR_HALT, MATRIX_SINGULAR_MSG); + morpho_defineerror(MATRIX_NOTSQ, ERROR_HALT, MATRIX_NOTSQ_MSG); + morpho_defineerror(MATRIX_OPFAILED, ERROR_HALT, MATRIX_OPFAILED_MSG); + morpho_defineerror(MATRIX_SETCOLARGS, ERROR_HALT, MATRIX_SETCOLARGS_MSG); + morpho_defineerror(MATRIX_NORMARGS, ERROR_HALT, MATRIX_NORMARGS_MSG); + morpho_defineerror(MATRIX_IDENTCONSTRUCTOR, ERROR_HALT, MATRIX_IDENTCONSTRUCTOR_MSG); +} + +#endif diff --git a/src/linalg/xmatrix.h b/src/linalg/xmatrix.h new file mode 100644 index 00000000..fe74a515 --- /dev/null +++ b/src/linalg/xmatrix.h @@ -0,0 +1,205 @@ +/** @file matrix.h + * @author T J Atherton + * + * @brief Veneer class over the objectmatrix type that interfaces with blas and lapack + */ + +#ifndef matrix_h +#define matrix_h + +#include "build.h" +#ifdef MORPHO_INCLUDE_LINALG + +#include +#include "classes.h" +/** Use Apple's Accelerate library for LAPACK and BLAS */ +#ifdef __APPLE__ +#ifdef MORPHO_LINALG_USE_ACCELERATE +#define ACCELERATE_NEW_LAPACK +#include +#define MATRIX_LAPACK_PRESENT +#endif +#endif + +/** Otherwise, use LAPACKE */ +#ifndef MATRIX_LAPACK_PRESENT +#include +#include +#define MORPHO_LINALG_USE_LAPACKE +#define MATRIX_LAPACK_PRESENT +#endif + +#include "cmplx.h" +#include "list.h" + +/* ------------------------------------------------------- + * Matrix objects + * ------------------------------------------------------- */ + +extern objecttype objectmatrixtype; +#define OBJECT_MATRIX objectmatrixtype + +/** Matrices are a purely numerical collection type oriented toward linear algebra. + Elements are stored in column-major format, i.e. + [ 1 2 ] + [ 3 4 ] + is stored ( 1, 3, 2, 4 ) in memory. This is for compatibility with standard linear algebra packages */ + +typedef struct { + object obj; + unsigned int nrows; + unsigned int ncols; + double *elements; + double matrixdata[]; +} objectmatrix; + +/** Tests whether an object is a matrix */ +#define MORPHO_ISMATRIX(val) object_istype(val, OBJECT_MATRIX) + +/** Gets the object as an matrix */ +#define MORPHO_GETMATRIX(val) ((objectmatrix *) MORPHO_GETOBJECT(val)) + +/** Creates a matrix object */ +objectmatrix *object_newmatrix(unsigned int nrows, unsigned int ncols, bool zero); + +/** Creates a new matrix from an array */ +objectmatrix *object_matrixfromarray(objectarray *array); + +/** Creates a new matrix from an existing matrix */ +objectmatrix *object_clonematrix(objectmatrix *array); + +/** @brief Use to create static matrices on the C stack + @details Intended for small matrices; Caller needs to supply a double array of size nr*nc. */ +#define MORPHO_STATICMATRIX(darray, nr, nc) { .obj.type=OBJECT_MATRIX, .obj.status=OBJECT_ISUNMANAGED, .obj.next=NULL, .elements=darray, .nrows=nr, .ncols=nc } + +/** Macro to decide if a matrix is 'small' or 'large' and hence static or dynamic allocation should be used. */ +#define MATRIX_ISSMALL(m) (m->nrows*m->ncols + #include +#else + #define _POSIX_C_SOURCE 199309L + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + #include #include #include @@ -18,22 +36,6 @@ #include "platform.h" #include "error.h" -#ifdef _WIN32 -#include -#include -#else -#define _POSIX_C_SOURCE 199309L -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - /* ********************************************************************** * Platform name * ********************************************************************** */ @@ -51,6 +53,43 @@ const char *platform_name(void) { return NULL; // Unrecognized platform } +/* ********************************************************************** + * Re-entrant qsort + * ********************************************************************** */ + +typedef struct _sadapt { + void *context; + platform_qsort_r_comparefn cmp; +} _adaptinfo; + +/** Adapter function to patch macOS, BSD and windows variants of qsort_r */ +static int _comparefn_adapter(void *in, const void *a, const void *b) { + _adaptinfo *info = (_adaptinfo *) in; + return info->cmp(a,b,info->context); +} + +/** Fallback function for use with regular qsort @warning not thread-safe */ +static _adaptinfo _globalinfo; +static int _comparefn_fallback(const void *a, const void *b) { + return _globalinfo.cmp(a,b,_globalinfo.context); +} + +/** Platform independent re-entrant qsort function */ +void platform_qsort_r(void *base, size_t nel, size_t width, void *context, platform_qsort_r_comparefn cmp) { +#if defined(__GLIBC__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + qsort_r(base, nel, width, cmp, context); +#elif defined(__APPLE__) + _adaptinfo info = { .context = context, .cmp = cmp }; + qsort_r(base, nel, width, &info, _comparefn_adapter); +#elif defined(_WIN32) + _adaptinfo info = { .context = context, .cmp = cmp }; + qsort_s(base, nel, width, _comparefn_adapter, &info); +#else + _globalinfo.cmp = cmp; _globalinfo.context = context; + qsort(base, nel, width, _comparefn_fallback); +#endif +} + /* ********************************************************************** * Random numbers * ********************************************************************** */ diff --git a/src/support/platform.h b/src/support/platform.h index 2d07d90c..bf56ab19 100644 --- a/src/support/platform.h +++ b/src/support/platform.h @@ -30,6 +30,15 @@ const char *platform_name(void); +/* ------------------------------------------------------- + * Re-entrant qsort + * ------------------------------------------------------- */ + +typedef int (*platform_qsort_r_comparefn)(const void *, const void *, void *); + +/** Sort elements with additional context passed to the comparator function */ +void platform_qsort_r(void *base, size_t nel, size_t width, void *context, platform_qsort_r_comparefn cmp); + /* ------------------------------------------------------- * Random numbers * ------------------------------------------------------- */ diff --git a/test/functionals/err_integrand.morpho b/test/functionals/err_integrand.morpho index e155366e..59f52125 100644 --- a/test/functionals/err_integrand.morpho +++ b/test/functionals/err_integrand.morpho @@ -19,4 +19,4 @@ var defectsGrad = Field(m, fn(x,y,z) phdgrad(x, y, z, 0.25, 0, 0)+mhdgrad(x, y, var directorIntegral = LineIntegral(fn (x,n) 1/(2*Pi) * n.inner(tangent()), defectsGrad) var fe = directorIntegral.integrand(m) -//expect error 'MtrxIncmptbl' \ No newline at end of file +//expect error 'LnAlgMtrxIncmptbl' \ No newline at end of file diff --git a/test/functionals/hydrogel/hydrogel2D.morpho b/test/functionals/hydrogel/hydrogel2D.morpho index ead6733a..48ffb575 100644 --- a/test/functionals/hydrogel/hydrogel2D.morpho +++ b/test/functionals/hydrogel/hydrogel2D.morpho @@ -32,7 +32,7 @@ var m = mb.build() // Expand m by a linear factor var f = 1.2 var vert = m.vertexmatrix() -for (i in 0...m.count()) m.setvertexposition(i, f*vert.column(i)) +for (i in 0...m.count()) m.setvertexposition(i, vert.column(i)*f) var phi = phi0/(f^2) // New phi will be inversely proportional to the area var vol = vol0 * f^2 diff --git a/test/linalg/arithmetic/complexmatrix_acc.morpho b/test/linalg/arithmetic/complexmatrix_acc.morpho new file mode 100644 index 00000000..850a1d90 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_acc.morpho @@ -0,0 +1,13 @@ +// In-place accumulate + +var A = ComplexMatrix(2,2) +A[0,0]=1+im +A[0,1]=1-im +A[1,0]=1-im +A[1,1]=1+im + +A.acc(2,A) + +print A +// expect: [ 3 + 3im 3 - 3im ] +// expect: [ 3 - 3im 3 + 3im ] diff --git a/test/linalg/arithmetic/complexmatrix_add_complexmatrix.morpho b/test/linalg/arithmetic/complexmatrix_add_complexmatrix.morpho new file mode 100644 index 00000000..fe894b45 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_add_complexmatrix.morpho @@ -0,0 +1,7 @@ +// Add a complexmatrix to a complexmatrix + +var A = ComplexMatrix(((1+1im, 2+2im), (3+3im, 4+4im))) + +print A + A +// expect: [ 2 + 2im 4 + 4im ] +// expect: [ 6 + 6im 8 + 8im ] diff --git a/test/linalg/arithmetic/complexmatrix_add_matrix.morpho b/test/linalg/arithmetic/complexmatrix_add_matrix.morpho new file mode 100644 index 00000000..dbea7249 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_add_matrix.morpho @@ -0,0 +1,8 @@ +// Add a matrix to a complex matrix + +var A = ComplexMatrix(((1+1im, 2+2im), (3+3im, 4+4im))) +var B = Matrix(((1, 2), (3, 4))) + +print A + B +// expect: [ 2 + 1im 4 + 2im ] +// expect: [ 6 + 3im 8 + 4im ] diff --git a/test/linalg/arithmetic/complexmatrix_add_nil.morpho b/test/linalg/arithmetic/complexmatrix_add_nil.morpho new file mode 100644 index 00000000..13650933 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_add_nil.morpho @@ -0,0 +1,7 @@ +// Add a scalar + +var A = ComplexMatrix(2,2) + +print A + nil +// expect: [ 0 + 0im 0 + 0im ] +// expect: [ 0 + 0im 0 + 0im ] diff --git a/test/linalg/arithmetic/complexmatrix_add_scalar.morpho b/test/linalg/arithmetic/complexmatrix_add_scalar.morpho new file mode 100644 index 00000000..0b0e440d --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_add_scalar.morpho @@ -0,0 +1,9 @@ +// Add a scalar + +var A = ComplexMatrix(2,2) +A[0,0]=1+im +A[1,1]=1+im + +print A + 2 +// expect: [ 3 + 1im 2 + 0im ] +// expect: [ 2 + 0im 3 + 1im ] diff --git a/test/linalg/arithmetic/complexmatrix_addr_matrix.morpho b/test/linalg/arithmetic/complexmatrix_addr_matrix.morpho new file mode 100644 index 00000000..364dd703 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_addr_matrix.morpho @@ -0,0 +1,8 @@ +// Add a matrix to a complex matrix + +var A = ComplexMatrix(((1+1im, 2+2im), (3+3im, 4+4im))) +var B = Matrix(((1, 2), (3, 4))) + +print B + A +// expect: [ 2 + 1im 4 + 2im ] +// expect: [ 6 + 3im 8 + 4im ] diff --git a/test/linalg/arithmetic/complexmatrix_addr_nil.morpho b/test/linalg/arithmetic/complexmatrix_addr_nil.morpho new file mode 100644 index 00000000..d9c3edb9 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_addr_nil.morpho @@ -0,0 +1,7 @@ +// Add a scalar + +var A = ComplexMatrix(2,2) + +print nil + A +// expect: [ 0 + 0im 0 + 0im ] +// expect: [ 0 + 0im 0 + 0im ] diff --git a/test/linalg/arithmetic/complexmatrix_div_complexmatrix.morpho b/test/linalg/arithmetic/complexmatrix_div_complexmatrix.morpho new file mode 100644 index 00000000..84246a80 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_div_complexmatrix.morpho @@ -0,0 +1,19 @@ +// Divide ComplexMatrix by ComplexMatrix (solve linear system) + +var A = ComplexMatrix(2,2) +A[0,0]=1 +A[0,1]=1-im +A[1,0]=1 +A[1,1]=1+1im + +var b = ComplexMatrix(2,1) +b[0,0]=1+1im +b[1,0]=2 + +print b / A +// expect: [ 2 + 1im ] +// expect: [ -0.5 - 0.5im ] + +print b +// expect: [ 1 + 1im ] +// expect: [ 2 + 0im ] diff --git a/test/linalg/arithmetic/complexmatrix_div_matrix.morpho b/test/linalg/arithmetic/complexmatrix_div_matrix.morpho new file mode 100644 index 00000000..7fb130c5 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_div_matrix.morpho @@ -0,0 +1,13 @@ +// Divide ComplexMatrix by Matrix (solve linear system) + +var A = Matrix(((1,2),(-2,1))) + +var b = ComplexMatrix((1+im, 2)) + +print b / A +// expect: [ -0.6 + 0.2im ] +// expect: [ 0.8 + 0.4im ] + +print b +// expect: [ 1 + 1im ] +// expect: [ 2 + 0im ] diff --git a/test/linalg/arithmetic/complexmatrix_div_scalar.morpho b/test/linalg/arithmetic/complexmatrix_div_scalar.morpho new file mode 100644 index 00000000..115ed9d5 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_div_scalar.morpho @@ -0,0 +1,10 @@ +// Divide ComplexMatrix by scalar + +var A = ComplexMatrix(2,2) +A[0,0]=2+2im +A[1,1]=4+4im + +print A / 2 +// expect: [ 1 + 1im 0 + 0im ] +// expect: [ 0 + 0im 2 + 2im ] + diff --git a/test/linalg/arithmetic/complexmatrix_divr_matrix.morpho b/test/linalg/arithmetic/complexmatrix_divr_matrix.morpho new file mode 100644 index 00000000..ca33e774 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_divr_matrix.morpho @@ -0,0 +1,9 @@ +// Divide Matrix by ComplexMatrix (solve linear system) + +var A = ComplexMatrix(((1,2+im),(2-im,1))) + +var b = Matrix((1, 2)) + +print b / A +// expect: [ 0.75 + 0.5im ] +// expect: [ 0 - 0.25im ] diff --git a/test/linalg/arithmetic/complexmatrix_mul_complex.morpho b/test/linalg/arithmetic/complexmatrix_mul_complex.morpho new file mode 100644 index 00000000..399b87d0 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_mul_complex.morpho @@ -0,0 +1,7 @@ +// Multiply ComplexMatrix by scalar + +var A = ComplexMatrix(((1,2),(3,4))) + +print A * (1+im) +// expect: [ 1 + 1im 2 + 2im ] +// expect: [ 3 + 3im 4 + 4im ] diff --git a/test/linalg/arithmetic/complexmatrix_mul_complexmatrix.morpho b/test/linalg/arithmetic/complexmatrix_mul_complexmatrix.morpho new file mode 100644 index 00000000..3bf75db9 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_mul_complexmatrix.morpho @@ -0,0 +1,18 @@ +// Multiply two ComplexMatrices + +var A = ComplexMatrix(2,2) +A[0,0]=1+1im +A[0,1]=2+2im +A[1,0]=3+3im +A[1,1]=4+4im + +var B = ComplexMatrix(2,2) +B[0,0]=1 +B[0,1]=1im +B[1,0]=-1im +B[1,1]=1 + +print A * B +// expect: [ 3 - 1im 1 + 3im ] +// expect: [ 7 - 1im 1 + 7im ] + diff --git a/test/linalg/arithmetic/complexmatrix_mul_matrix.morpho b/test/linalg/arithmetic/complexmatrix_mul_matrix.morpho new file mode 100644 index 00000000..d7d862b8 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_mul_matrix.morpho @@ -0,0 +1,8 @@ +// Multiply two ComplexMatrices + +var A = ComplexMatrix(((1+1im, 2+2im),(3+3im,4+4im))) +var B = Matrix(((4,3),(2,1))) + +print A * B +// expect: [ 8 + 8im 5 + 5im ] +// expect: [ 20 + 20im 13 + 13im ] diff --git a/test/linalg/arithmetic/complexmatrix_mul_scalar.morpho b/test/linalg/arithmetic/complexmatrix_mul_scalar.morpho new file mode 100644 index 00000000..d3d404ed --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_mul_scalar.morpho @@ -0,0 +1,10 @@ +// Multiply ComplexMatrix by scalar + +var A = ComplexMatrix(2,2) +A[0,0]=1+1im +A[1,1]=2+2im + +print A * 2 +// expect: [ 2 + 2im 0 + 0im ] +// expect: [ 0 + 0im 4 + 4im ] + diff --git a/test/linalg/arithmetic/complexmatrix_mulr_matrix.morpho b/test/linalg/arithmetic/complexmatrix_mulr_matrix.morpho new file mode 100644 index 00000000..35c26580 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_mulr_matrix.morpho @@ -0,0 +1,14 @@ +// Multiply two ComplexMatrices + +var A = ComplexMatrix(((1+1im, 2+2im),(3+3im,4+4im))) +var B = Matrix(((4,3),(2,1))) + +print B * A +// expect: [ 13 + 13im 20 + 20im ] +// expect: [ 5 + 5im 8 + 8im ] + +var C = ComplexMatrix([[1+1im],[3+3im]]) + +print B * C +// expect: [ 13 + 13im ] +// expect: [ 5 + 5im ] diff --git a/test/linalg/arithmetic/complexmatrix_sub_complexmatrix.morpho b/test/linalg/arithmetic/complexmatrix_sub_complexmatrix.morpho new file mode 100644 index 00000000..9ad9e528 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_sub_complexmatrix.morpho @@ -0,0 +1,8 @@ +// Subtract a complexmatrix from a complexmatrix + +var A = ComplexMatrix(((1+1im, 2+2im), (3+3im, 4+4im))) +var B = ComplexMatrix(((4+4im, 3+3im), (2+2im, 1+1im))) + +print A - B +// expect: [ -3 - 3im -1 - 1im ] +// expect: [ 1 + 1im 3 + 3im ] diff --git a/test/linalg/arithmetic/complexmatrix_sub_matrix.morpho b/test/linalg/arithmetic/complexmatrix_sub_matrix.morpho new file mode 100644 index 00000000..f80d0e42 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_sub_matrix.morpho @@ -0,0 +1,8 @@ +// Subtract a matrix from a complexmatrix + +var A = ComplexMatrix(((1+1im, 2+2im), (3+3im, 4+4im))) +var B = Matrix(((4, 3), (2, 1))) + +print A - B +// expect: [ -3 + 1im -1 + 2im ] +// expect: [ 1 + 3im 3 + 4im ] diff --git a/test/linalg/arithmetic/complexmatrix_sub_scalar.morpho b/test/linalg/arithmetic/complexmatrix_sub_scalar.morpho new file mode 100644 index 00000000..bfc60320 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_sub_scalar.morpho @@ -0,0 +1,9 @@ +// Subtract a scalar + +var A = ComplexMatrix(2,2) +A[0,0]=2+2im +A[1,1]=2+2im + +print A - 2 +// expect: [ 0 + 2im -2 + 0im ] +// expect: [ -2 + 0im 0 + 2im ] diff --git a/test/linalg/arithmetic/complexmatrix_subr_matrix.morpho b/test/linalg/arithmetic/complexmatrix_subr_matrix.morpho new file mode 100644 index 00000000..17219a27 --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_subr_matrix.morpho @@ -0,0 +1,8 @@ +// Subtract a matrix from a complexmatrix + +var A = ComplexMatrix(((1+1im, 2+2im), (3+3im, 4+4im))) +var B = Matrix(((4, 3), (2, 1))) + +print B - A +// expect: [ 3 - 1im 1 - 2im ] +// expect: [ -1 - 3im -3 - 4im ] diff --git a/test/linalg/arithmetic/complexmatrix_subr_scalar.morpho b/test/linalg/arithmetic/complexmatrix_subr_scalar.morpho new file mode 100644 index 00000000..4ea9a1dd --- /dev/null +++ b/test/linalg/arithmetic/complexmatrix_subr_scalar.morpho @@ -0,0 +1,9 @@ +// Subtract a scalar + +var A = ComplexMatrix(2,2) +A[0,0]=1+im +A[1,1]=1+im + +print 2 - A +// expect: [ 1 - 1im 2 + 0im ] +// expect: [ 2 + 0im 1 - 1im ] diff --git a/test/linalg/arithmetic/matrix_acc.morpho b/test/linalg/arithmetic/matrix_acc.morpho new file mode 100644 index 00000000..d00b5a96 --- /dev/null +++ b/test/linalg/arithmetic/matrix_acc.morpho @@ -0,0 +1,13 @@ +// In-place accumulate + +var A = Matrix(2,2) +A[0,0]=1 +A[0,1]=1 +A[1,0]=1 +A[1,1]=1 + +A.acc(2,A) + +print A +// expect: [ 3 3 ] +// expect: [ 3 3 ] diff --git a/test/linalg/arithmetic/matrix_add_matrix.morpho b/test/linalg/arithmetic/matrix_add_matrix.morpho new file mode 100644 index 00000000..b52bd49b --- /dev/null +++ b/test/linalg/arithmetic/matrix_add_matrix.morpho @@ -0,0 +1,10 @@ +// Matrix arithmetic + +var a = Matrix([[1, 2], [3, 4]]) +var b = Matrix([[0, 1], [1, 0]]) + +print "A+B:" +print a+b +// expect: A+B: +// expect: [ 1 3 ] +// expect: [ 4 4 ] diff --git a/test/linalg/arithmetic/matrix_add_nil.morpho b/test/linalg/arithmetic/matrix_add_nil.morpho new file mode 100644 index 00000000..d44a45f6 --- /dev/null +++ b/test/linalg/arithmetic/matrix_add_nil.morpho @@ -0,0 +1,7 @@ +// Add a scalar + +var A = Matrix(2,2) + +print A + nil +// expect: [ 0 0 ] +// expect: [ 0 0 ] diff --git a/test/linalg/arithmetic/matrix_add_scalar.morpho b/test/linalg/arithmetic/matrix_add_scalar.morpho new file mode 100644 index 00000000..01fed20d --- /dev/null +++ b/test/linalg/arithmetic/matrix_add_scalar.morpho @@ -0,0 +1,7 @@ +// Add a scalar + +var A = Matrix(2,2) + +print A + 2 +// expect: [ 2 2 ] +// expect: [ 2 2 ] diff --git a/test/linalg/arithmetic/matrix_addr_nil.morpho b/test/linalg/arithmetic/matrix_addr_nil.morpho new file mode 100644 index 00000000..b7c63e21 --- /dev/null +++ b/test/linalg/arithmetic/matrix_addr_nil.morpho @@ -0,0 +1,8 @@ +// Add nil from the right + +var A = Matrix(2,2) +A += 1 + +print nil + A +// expect: [ 1 1 ] +// expect: [ 1 1 ] diff --git a/test/linalg/arithmetic/matrix_addr_scalar.morpho b/test/linalg/arithmetic/matrix_addr_scalar.morpho new file mode 100644 index 00000000..7a543255 --- /dev/null +++ b/test/linalg/arithmetic/matrix_addr_scalar.morpho @@ -0,0 +1,7 @@ +// Add a scalar from the right + +var A = Matrix(2,2) + +print 2 + A +// expect: [ 2 2 ] +// expect: [ 2 2 ] diff --git a/test/linalg/arithmetic/matrix_div_matrix.morpho b/test/linalg/arithmetic/matrix_div_matrix.morpho new file mode 100644 index 00000000..731d9633 --- /dev/null +++ b/test/linalg/arithmetic/matrix_div_matrix.morpho @@ -0,0 +1,16 @@ +// Divide Matrix by Matrix (solve linear system) + +var A = Matrix(2,2) +A[0,0]=1 +A[0,1]=2 +A[1,0]=3 +A[1,1]=4 + +var b = Matrix(2,1) +b[0]=1 +b[1]=2 + +print b / A +// expect: [ 0 ] +// expect: [ 0.5 ] + diff --git a/test/linalg/arithmetic/matrix_div_scalar.morpho b/test/linalg/arithmetic/matrix_div_scalar.morpho new file mode 100644 index 00000000..56b9cb61 --- /dev/null +++ b/test/linalg/arithmetic/matrix_div_scalar.morpho @@ -0,0 +1,10 @@ +// Divide Matrix by scalar + +var A = Matrix(2,2) +A[0,0]=2 +A[1,1]=4 + +print A / 2 +// expect: [ 1 0 ] +// expect: [ 0 2 ] + diff --git a/test/linalg/arithmetic/matrix_mul_matrix.morpho b/test/linalg/arithmetic/matrix_mul_matrix.morpho new file mode 100644 index 00000000..24ff63ed --- /dev/null +++ b/test/linalg/arithmetic/matrix_mul_matrix.morpho @@ -0,0 +1,35 @@ +// Multiply two Matrices + +var A = Matrix(2,2) +A[0,0]=1 +A[0,1]=2 +A[1,0]=3 +A[1,1]=4 + +var B = Matrix(2,2) +B[0,0]=1 +B[0,1]=0 +B[1,0]=0 +B[1,1]=1 + +print A * B +// expect: [ 1 2 ] +// expect: [ 3 4 ] + +var a = Matrix([[1, 2], [3, 4]]) +var b = Matrix([[0, 1], [1, 0]]) + +print "A*B:" +print a*b +// expect: A*B: +// expect: [ 2 1 ] +// expect: [ 4 3 ] + +var c = Matrix([[1,2,3], [4,5,6]]) +var d = Matrix([[1,2], [3,4], [5,6]]) + +print "C*D:" +print c*d +// expect: C*D: +// expect: [ 22 28 ] +// expect: [ 49 64 ] diff --git a/test/linalg/arithmetic/matrix_mul_scalar.morpho b/test/linalg/arithmetic/matrix_mul_scalar.morpho new file mode 100644 index 00000000..3d7f6c9e --- /dev/null +++ b/test/linalg/arithmetic/matrix_mul_scalar.morpho @@ -0,0 +1,10 @@ +// Multiply Matrix by scalar + +var A = Matrix(2,2) +A[0,0]=1 +A[1,1]=2 + +print A * 2 +// expect: [ 2 0 ] +// expect: [ 0 4 ] + diff --git a/test/linalg/arithmetic/matrix_negate.morpho b/test/linalg/arithmetic/matrix_negate.morpho new file mode 100644 index 00000000..82fec405 --- /dev/null +++ b/test/linalg/arithmetic/matrix_negate.morpho @@ -0,0 +1,7 @@ +// Negate + +var a = Matrix([[1,2], [3,4]]) + +print -a +// expect: [ -1 -2 ] +// expect: [ -3 -4 ] diff --git a/test/linalg/arithmetic/matrix_sub_matrix.morpho b/test/linalg/arithmetic/matrix_sub_matrix.morpho new file mode 100644 index 00000000..571b9876 --- /dev/null +++ b/test/linalg/arithmetic/matrix_sub_matrix.morpho @@ -0,0 +1,12 @@ +// Matrix arithmetic + +var a = Matrix([[1, 2], [3, 4]]) +var b = Matrix([[0, 1], [1, 0]]) + + +print "A-B:" +print a-b +// expect: A-B: +// expect: [ 1 1 ] +// expect: [ 2 4 ] + diff --git a/test/linalg/arithmetic/matrix_sub_scalar.morpho b/test/linalg/arithmetic/matrix_sub_scalar.morpho new file mode 100644 index 00000000..c01149bb --- /dev/null +++ b/test/linalg/arithmetic/matrix_sub_scalar.morpho @@ -0,0 +1,7 @@ +// Subtract a scalar + +var A = Matrix(2,2) + +print A - 2 +// expect: [ -2 -2 ] +// expect: [ -2 -2 ] diff --git a/test/linalg/arithmetic/matrix_subr_scalar.morpho b/test/linalg/arithmetic/matrix_subr_scalar.morpho new file mode 100644 index 00000000..b12d6437 --- /dev/null +++ b/test/linalg/arithmetic/matrix_subr_scalar.morpho @@ -0,0 +1,9 @@ +// Subtract a scalar + +var A = Matrix(2,2) +A[0,0]=1 +A[1,1]=1 + +print 2 - A +// expect: [ 1 2 ] +// expect: [ 2 1 ] diff --git a/test/linalg/assign/complexmatrix_assign.morpho b/test/linalg/assign/complexmatrix_assign.morpho new file mode 100644 index 00000000..087bd5cd --- /dev/null +++ b/test/linalg/assign/complexmatrix_assign.morpho @@ -0,0 +1,15 @@ +// Assign one ComplexMatrix to another + +var A = ComplexMatrix(2,2) +A[0,0] = 1+1im +A[0,1] = 2+2im +A[1,0] = 3+3im +A[1,1] = 4+4im + +var B = ComplexMatrix(2,2) + +B.assign(A) + +print B +// expect: [ 1 + 1im 2 + 2im ] +// expect: [ 3 + 3im 4 + 4im ] diff --git a/test/linalg/assign/complexmatrix_clone.morpho b/test/linalg/assign/complexmatrix_clone.morpho new file mode 100644 index 00000000..2bc18f9f --- /dev/null +++ b/test/linalg/assign/complexmatrix_clone.morpho @@ -0,0 +1,18 @@ +// Clone a ComplexMatrix + +var A = ComplexMatrix(2,2) +A[0,0]=1+1im +A[0,1]=2+2im +A[1,0]=3+3im +A[1,1]=4+4im + +var B = A.clone() + +// Modify original +A[0,0]=9+9im + +// Clone should be unchanged +print B +// expect: [ 1 + 1im 2 + 2im ] +// expect: [ 3 + 3im 4 + 4im ] + diff --git a/test/linalg/assign/matrix_assign.morpho b/test/linalg/assign/matrix_assign.morpho new file mode 100644 index 00000000..ee4d5ecd --- /dev/null +++ b/test/linalg/assign/matrix_assign.morpho @@ -0,0 +1,18 @@ +// Assign one matrix to another + +var A = Matrix(2,2) +A[0,0] = 1 +A[0,1] = 2 +A[1,0] = 3 +A[1,1] = 4 + +var B = Matrix(2,2) +B.assign(A) + +print B +// expect: [ 1 2 ] +// expect: [ 3 4 ] + +var C = Matrix(1,2) +B.assign(C) +// expect error 'LnAlgMtrxIncmptbl' diff --git a/test/linalg/constructors/complexmatrix_array_constructor.morpho b/test/linalg/constructors/complexmatrix_array_constructor.morpho new file mode 100644 index 00000000..949aadaa --- /dev/null +++ b/test/linalg/constructors/complexmatrix_array_constructor.morpho @@ -0,0 +1,13 @@ +// Create a Matrix from an Array + +var a[2,2] +a[0,0]=1+im +a[1,0]=2-2im +a[0,1]=3+3im +a[1,1]=4+4im + +var A = ComplexMatrix(a) + +print A +// expect: [ 1 + 1im 3 + 3im ] +// expect: [ 2 - 2im 4 + 4im ] diff --git a/test/linalg/constructors/complexmatrix_array_constructor_invalid_dimensions.morpho b/test/linalg/constructors/complexmatrix_array_constructor_invalid_dimensions.morpho new file mode 100644 index 00000000..6a6ba5fb --- /dev/null +++ b/test/linalg/constructors/complexmatrix_array_constructor_invalid_dimensions.morpho @@ -0,0 +1,11 @@ +// ComplexMatrix constructor from Array with invalid dimensions + +// Try to construct from a 1D array (should fail - requires 2D) +var a[4] +a[0] = 1 +a[1] = 2 +a[2] = 3 +a[3] = 4 + +print ComplexMatrix(a) +// expect error 'LnAlgMtrxInvldArg' diff --git a/test/linalg/constructors/complexmatrix_constructor.morpho b/test/linalg/constructors/complexmatrix_constructor.morpho new file mode 100644 index 00000000..8723c996 --- /dev/null +++ b/test/linalg/constructors/complexmatrix_constructor.morpho @@ -0,0 +1,8 @@ +// Create a ComplexMatrix + +var A = ComplexMatrix(2,2) + +print A +// expect: [ 0 + 0im 0 + 0im ] +// expect: [ 0 + 0im 0 + 0im ] + diff --git a/test/linalg/constructors/complexmatrix_constructor_edge_cases.morpho b/test/linalg/constructors/complexmatrix_constructor_edge_cases.morpho new file mode 100644 index 00000000..84a1a761 --- /dev/null +++ b/test/linalg/constructors/complexmatrix_constructor_edge_cases.morpho @@ -0,0 +1,47 @@ +// ComplexMatrix constructor edge cases + +// Zero dimension matrix (0x0) +var A = ComplexMatrix(0, 0) +print A.dimensions() +// expect: (0, 0) +print A.count() +// expect: 0 + +// Zero rows, non-zero columns +var B = ComplexMatrix(0, 3) +print B.dimensions() +// expect: (0, 3) +print B.count() +// expect: 0 + +// Non-zero rows, zero columns +var C = ComplexMatrix(3, 0) +print C.dimensions() +// expect: (3, 0) +print C.count() +// expect: 0 + +// Single element matrix (1x1) +var D = ComplexMatrix(1, 1) +D[0,0] = 42+10im +print D +// expect: [ 42 + 10im ] + +// Single row matrix (1xN) +var E = ComplexMatrix(1, 3) +E[0,0] = 1+im +E[0,1] = 2+2im +E[0,2] = 3+3im +print E +// expect: [ 1 + 1im 2 + 2im 3 + 3im ] + +// Single column matrix (Nx1) +var F = ComplexMatrix(3, 1) +F[0,0] = 1+im +F[1,0] = 2+2im +F[2,0] = 3+3im +print F +// expect: [ 1 + 1im ] +// expect: [ 2 + 2im ] +// expect: [ 3 + 3im ] + diff --git a/test/linalg/constructors/complexmatrix_constructor_invalid_args.morpho b/test/linalg/constructors/complexmatrix_constructor_invalid_args.morpho new file mode 100644 index 00000000..9e7e894a --- /dev/null +++ b/test/linalg/constructors/complexmatrix_constructor_invalid_args.morpho @@ -0,0 +1,5 @@ +// ComplexMatrix constructor with invalid arguments + +// Try to construct with invalid argument types +print ComplexMatrix("invalid") +// expect error 'MltplDsptchFld' diff --git a/test/linalg/constructors/complexmatrix_list_constructor.morpho b/test/linalg/constructors/complexmatrix_list_constructor.morpho new file mode 100644 index 00000000..7857d52d --- /dev/null +++ b/test/linalg/constructors/complexmatrix_list_constructor.morpho @@ -0,0 +1,7 @@ +// Create a ComplexMatrix from a List of Lists + +var A = ComplexMatrix([[1+im,2-2im],[3+3im,4-4im]]) + +print A +// expect: [ 1 + 1im 2 - 2im ] +// expect: [ 3 + 3im 4 - 4im ] diff --git a/test/linalg/constructors/complexmatrix_list_vector_constructor.morpho b/test/linalg/constructors/complexmatrix_list_vector_constructor.morpho new file mode 100644 index 00000000..1e1cd1a7 --- /dev/null +++ b/test/linalg/constructors/complexmatrix_list_vector_constructor.morpho @@ -0,0 +1,16 @@ +// Create a ComplexMatrix from a List of Values + +var A = ComplexMatrix([1+im, 2-2im, 3+3im, 4-4im]) + +print A +// expect: [ 1 + 1im ] +// expect: [ 2 - 2im ] +// expect: [ 3 + 3im ] +// expect: [ 4 - 4im ] + +var B = ComplexMatrix(((1+im), (2), (3), (4-4im))) +print B +// expect: [ 1 + 1im ] +// expect: [ 2 + 0im ] +// expect: [ 3 + 0im ] +// expect: [ 4 - 4im ] diff --git a/test/linalg/constructors/complexmatrix_matrix_constructor.morpho b/test/linalg/constructors/complexmatrix_matrix_constructor.morpho new file mode 100644 index 00000000..fbb03772 --- /dev/null +++ b/test/linalg/constructors/complexmatrix_matrix_constructor.morpho @@ -0,0 +1,9 @@ +// Create a Matrix from an Array + +var A = Matrix(((1,2), (3,4))) + +var B = ComplexMatrix(A) + +print B +// expect: [ 1 + 0im 2 + 0im ] +// expect: [ 3 + 0im 4 + 0im ] diff --git a/test/linalg/constructors/complexmatrix_tuple_column_constructor.morpho b/test/linalg/constructors/complexmatrix_tuple_column_constructor.morpho new file mode 100644 index 00000000..9a8eca72 --- /dev/null +++ b/test/linalg/constructors/complexmatrix_tuple_column_constructor.morpho @@ -0,0 +1,7 @@ +// Create a column vector from a list of tuples + +var C = ComplexMatrix(((1+1im),(3+3im))) + +print C +// expect: [ 1 + 1im ] +// expect: [ 3 + 3im ] diff --git a/test/linalg/constructors/complexmatrix_tuple_constructor.morpho b/test/linalg/constructors/complexmatrix_tuple_constructor.morpho new file mode 100644 index 00000000..10bbd9fa --- /dev/null +++ b/test/linalg/constructors/complexmatrix_tuple_constructor.morpho @@ -0,0 +1,7 @@ +// Create a ComplexMatrix from a List of Lists + +var A = ComplexMatrix(((1+im,2-2im),(3+3im,4-4im))) + +print A +// expect: [ 1 + 1im 2 - 2im ] +// expect: [ 3 + 3im 4 - 4im ] diff --git a/test/linalg/constructors/complexmatrix_vector_constructor.morpho b/test/linalg/constructors/complexmatrix_vector_constructor.morpho new file mode 100644 index 00000000..4d83b2af --- /dev/null +++ b/test/linalg/constructors/complexmatrix_vector_constructor.morpho @@ -0,0 +1,8 @@ +// Create a ComplexMatrix column vector + +var A = ComplexMatrix(2) + +print A +// expect: [ 0 + 0im ] +// expect: [ 0 + 0im ] + diff --git a/test/linalg/constructors/matrix_array_constructor.morpho b/test/linalg/constructors/matrix_array_constructor.morpho new file mode 100644 index 00000000..08e97a0f --- /dev/null +++ b/test/linalg/constructors/matrix_array_constructor.morpho @@ -0,0 +1,14 @@ +// Create a Matrix from an Array + +var a[2,2] +a[0,0]=1 +a[1,0]=3 +a[0,1]=2 +a[1,1]=4 + +var A = Matrix(a) + +print A +// expect: [ 1 2 ] +// expect: [ 3 4 ] + diff --git a/test/linalg/constructors/matrix_array_constructor_invalid_dimensions.morpho b/test/linalg/constructors/matrix_array_constructor_invalid_dimensions.morpho new file mode 100644 index 00000000..4a9abbfc --- /dev/null +++ b/test/linalg/constructors/matrix_array_constructor_invalid_dimensions.morpho @@ -0,0 +1,11 @@ +// Matrix constructor from Array with invalid dimensions + +// Try to construct from a 1D array (should fail - requires 2D) +var a[4] +a[0] = 1 +a[1] = 2 +a[2] = 3 +a[3] = 4 + +print Matrix(a) +// expect error 'LnAlgMtrxInvldArg' diff --git a/test/linalg/constructors/matrix_constructor.morpho b/test/linalg/constructors/matrix_constructor.morpho new file mode 100644 index 00000000..5d35ef53 --- /dev/null +++ b/test/linalg/constructors/matrix_constructor.morpho @@ -0,0 +1,8 @@ +// Create a Matrix + +var A = Matrix(2,2) + +print A +// expect: [ 0 0 ] +// expect: [ 0 0 ] + diff --git a/test/linalg/constructors/matrix_constructor_edge_cases.morpho b/test/linalg/constructors/matrix_constructor_edge_cases.morpho new file mode 100644 index 00000000..d0810cdb --- /dev/null +++ b/test/linalg/constructors/matrix_constructor_edge_cases.morpho @@ -0,0 +1,47 @@ +// Matrix constructor edge cases + +// Zero dimension matrix (0x0) +var A = Matrix(0, 0) +print A.dimensions() +// expect: (0, 0) +print A.count() +// expect: 0 + +// Zero rows, non-zero columns +var B = Matrix(0, 3) +print B.dimensions() +// expect: (0, 3) +print B.count() +// expect: 0 + +// Non-zero rows, zero columns +var C = Matrix(3, 0) +print C.dimensions() +// expect: (3, 0) +print C.count() +// expect: 0 + +// Single element matrix (1x1) +var D = Matrix(1, 1) +D[0,0] = 42 +print D +// expect: [ 42 ] + +// Single row matrix (1xN) +var E = Matrix(1, 3) +E[0,0] = 1 +E[0,1] = 2 +E[0,2] = 3 +print E +// expect: [ 1 2 3 ] + +// Single column matrix (Nx1) +var F = Matrix(3, 1) +F[0,0] = 1 +F[1,0] = 2 +F[2,0] = 3 +print F +// expect: [ 1 ] +// expect: [ 2 ] +// expect: [ 3 ] + diff --git a/test/linalg/constructors/matrix_identity_constructor.morpho b/test/linalg/constructors/matrix_identity_constructor.morpho new file mode 100644 index 00000000..155ff8bc --- /dev/null +++ b/test/linalg/constructors/matrix_identity_constructor.morpho @@ -0,0 +1,12 @@ +// IdentityMatrix constructor + +var I = IdentityMatrix(3) + +print I +// expect: [ 1 0 0 ] +// expect: [ 0 1 0 ] +// expect: [ 0 0 1 ] + +var I2 = IdentityMatrix(1) +print I2 +// expect: [ 1 ] diff --git a/test/linalg/constructors/matrix_list_constructor.morpho b/test/linalg/constructors/matrix_list_constructor.morpho new file mode 100644 index 00000000..edb28f03 --- /dev/null +++ b/test/linalg/constructors/matrix_list_constructor.morpho @@ -0,0 +1,7 @@ +// Create a Matrix from a List of Lists + +var A = Matrix([[1,2],[3,4]]) + +print A +// expect: [ 1 2 ] +// expect: [ 3 4 ] diff --git a/test/linalg/constructors/matrix_list_vector_constructor.morpho b/test/linalg/constructors/matrix_list_vector_constructor.morpho new file mode 100644 index 00000000..802efd43 --- /dev/null +++ b/test/linalg/constructors/matrix_list_vector_constructor.morpho @@ -0,0 +1,7 @@ +// Create a column vector from a list of tuples + +var C = Matrix(((1),(2))) + +print C +// expect: [ 1 ] +// expect: [ 2 ] diff --git a/test/linalg/constructors/matrix_tuple_constructor.morpho b/test/linalg/constructors/matrix_tuple_constructor.morpho new file mode 100644 index 00000000..5b18ceb8 --- /dev/null +++ b/test/linalg/constructors/matrix_tuple_constructor.morpho @@ -0,0 +1,14 @@ +// Create a Matrix from a Tuple of Tuples + +var A = Matrix(((1,2),(3,4))) + +print A +// expect: [ 1 2 ] +// expect: [ 3 4 ] + +// Mix Tuples and Lists +var B = Matrix(([1,2],[3,4])) + +print B +// expect: [ 1 2 ] +// expect: [ 3 4 ] diff --git a/test/linalg/constructors/matrix_vector_constructor.morpho b/test/linalg/constructors/matrix_vector_constructor.morpho new file mode 100644 index 00000000..a82c4f36 --- /dev/null +++ b/test/linalg/constructors/matrix_vector_constructor.morpho @@ -0,0 +1,7 @@ +// Create a Matrix + +var A = Matrix(2) + +print A +// expect: [ 0 ] +// expect: [ 0 ] diff --git a/test/linalg/constructors/vector_constructor.morpho b/test/linalg/constructors/vector_constructor.morpho new file mode 100644 index 00000000..2d5c07bc --- /dev/null +++ b/test/linalg/constructors/vector_constructor.morpho @@ -0,0 +1,8 @@ +// Create a column vector + +var A = Matrix(2) + +print A +// expect: [ 0 ] +// expect: [ 0 ] + diff --git a/test/linalg/errors/complexmatrix_incompatible_dimensions.morpho b/test/linalg/errors/complexmatrix_incompatible_dimensions.morpho new file mode 100644 index 00000000..ab1e19d2 --- /dev/null +++ b/test/linalg/errors/complexmatrix_incompatible_dimensions.morpho @@ -0,0 +1,12 @@ +// Incompatible dimensions error + +var A = ComplexMatrix(2,2) +A[0,0]=1+1im + +var B = ComplexMatrix(3,3) +B[0,0]=1+1im + +// Try to add incompatible matrices +print A + B +// expect error 'LnAlgMtrxIncmptbl' + diff --git a/test/linalg/errors/complexmatrix_index_out_of_bounds.morpho b/test/linalg/errors/complexmatrix_index_out_of_bounds.morpho new file mode 100644 index 00000000..56f30644 --- /dev/null +++ b/test/linalg/errors/complexmatrix_index_out_of_bounds.morpho @@ -0,0 +1,9 @@ +// Index out of bounds error + +var A = ComplexMatrix(2,2) +A[0,0]=1+1im + +// Try to access out of bounds +print A[5,5] +// expect error 'LnAlgMtrxIndxBnds' + diff --git a/test/linalg/errors/complexmatrix_non_square_error.morpho b/test/linalg/errors/complexmatrix_non_square_error.morpho new file mode 100644 index 00000000..dd526a45 --- /dev/null +++ b/test/linalg/errors/complexmatrix_non_square_error.morpho @@ -0,0 +1,9 @@ +// Non-square matrix err. (for operations requiring square matrices) + +var A = ComplexMatrix(2,3) +A[0,0]=1+1im + +// Try trace on non-square matrix +print A.trace() +// expect error 'LnAlgMtrxNtSq' + diff --git a/test/linalg/index/complexmatrix_getcolumn.morpho b/test/linalg/index/complexmatrix_getcolumn.morpho new file mode 100644 index 00000000..b8b74afa --- /dev/null +++ b/test/linalg/index/complexmatrix_getcolumn.morpho @@ -0,0 +1,17 @@ +// Get columns of a ComplexMatrix + +var A = ComplexMatrix(2,2) +A[0,0] = 1+1im +A[0,1] = 2+2im +A[1,0] = 3+3im +A[1,1] = 4+4im + +print A.column(0) +// expect: [ 1 + 1im ] +// expect: [ 3 + 3im ] + +print A.column(1) +// expect: [ 2 + 2im ] +// expect: [ 4 + 4im ] + +print A.column(2) // expect error 'LnAlgMtrxIndxBnds' diff --git a/test/linalg/index/complexmatrix_getindex.morpho b/test/linalg/index/complexmatrix_getindex.morpho new file mode 100644 index 00000000..23069538 --- /dev/null +++ b/test/linalg/index/complexmatrix_getindex.morpho @@ -0,0 +1,19 @@ +// Get elements of a ComplexMatrix + +var A = ComplexMatrix(2,2) +A[0,0] = 1+1im +A[1,0] = 2+2im +A[0,1] = 3+3im +A[1,1] = 4+4im + +// Array-like access +print A[0,0] // expect: 1 + 1im +print A[1,0] // expect: 2 + 2im +print A[0,1] // expect: 3 + 3im +print A[1,1] // expect: 4 + 4im + +// Vector-like access +print A[0] // expect: 1 + 1im +print A[1] // expect: 2 + 2im +print A[2] // expect: 3 + 3im +print A[3] // expect: 4 + 4im diff --git a/test/linalg/index/complexmatrix_setcolumn.morpho b/test/linalg/index/complexmatrix_setcolumn.morpho new file mode 100644 index 00000000..71cd496a --- /dev/null +++ b/test/linalg/index/complexmatrix_setcolumn.morpho @@ -0,0 +1,20 @@ +// Set columns of a Matrix + +var A = ComplexMatrix(2,2) + +var b = ComplexMatrix(2,1) +b[0] = 1+1im +b[1] = 3+3im + +var c = ComplexMatrix(2,1) +c[0] = 2+2im +c[1] = 4+4im + +A.setColumn(0,b) +A.setColumn(1,c) + +print A +// expect: [ 1 + 1im 2 + 2im ] +// expect: [ 3 + 3im 4 + 4im ] + +print A.setColumn(2,b) // expect error 'LnAlgMtrxIndxBnds' diff --git a/test/linalg/index/complexmatrix_setindex.morpho b/test/linalg/index/complexmatrix_setindex.morpho new file mode 100644 index 00000000..50fac8e2 --- /dev/null +++ b/test/linalg/index/complexmatrix_setindex.morpho @@ -0,0 +1,19 @@ +// Set elements of a ComplexMatrix + +var A = ComplexMatrix(2,2) + +// Set using two indices +A[0,0] = 1+1im +A[0,1] = 2+2im +A[1,0] = 3+3im +A[1,1] = 4+4im + +print A +// expect: [ 1 + 1im 2 + 2im ] +// expect: [ 3 + 3im 4 + 4im ] + +// Set using single index (vector-like) +A[0] = 5+5im +print A[0,0] +// expect: 5 + 5im + diff --git a/test/linalg/index/complexmatrix_setindex_real.morpho b/test/linalg/index/complexmatrix_setindex_real.morpho new file mode 100644 index 00000000..775b9e55 --- /dev/null +++ b/test/linalg/index/complexmatrix_setindex_real.morpho @@ -0,0 +1,14 @@ +// Set elements of a ComplexMatrix with mixture of Complex and Real args + +var A = ComplexMatrix(2,2) + +// Set using two indices +A[0,0] = 1+im // Make sure imag part is zero'd out +A[0,0] = 1 +A[0,1] = 2+2im +A[1,0] = 3.0 +A[1,1] = 4+4im + +print A +// expect: [ 1 + 0im 2 + 2im ] +// expect: [ 3 + 0im 4 + 4im ] diff --git a/test/linalg/index/complexmatrix_setslice.morpho b/test/linalg/index/complexmatrix_setslice.morpho new file mode 100644 index 00000000..fc67163e --- /dev/null +++ b/test/linalg/index/complexmatrix_setslice.morpho @@ -0,0 +1,49 @@ +// Copy elements of a ComplexMatrix using slices + +var A = ComplexMatrix(((1+1im,2+2im),(3+3im,4+4im))) + +var B = ComplexMatrix(4,4) + +B[0..1, 0..1] = A +print B +// expect: [ 1 + 1im 2 + 2im 0 + 0im 0 + 0im ] +// expect: [ 3 + 3im 4 + 4im 0 + 0im 0 + 0im ] +// expect: [ 0 + 0im 0 + 0im 0 + 0im 0 + 0im ] +// expect: [ 0 + 0im 0 + 0im 0 + 0im 0 + 0im ] + +var C = ComplexMatrix(4,4) +C[0..2:2, 0..2:2] = A +print C +// expect: [ 1 + 1im 0 + 0im 2 + 2im 0 + 0im ] +// expect: [ 0 + 0im 0 + 0im 0 + 0im 0 + 0im ] +// expect: [ 3 + 3im 0 + 0im 4 + 4im 0 + 0im ] +// expect: [ 0 + 0im 0 + 0im 0 + 0im 0 + 0im ] + +var D = ComplexMatrix(4,4) +D[1..2, 1..2] = A +print D +// expect: [ 0 + 0im 0 + 0im 0 + 0im 0 + 0im ] +// expect: [ 0 + 0im 1 + 1im 2 + 2im 0 + 0im ] +// expect: [ 0 + 0im 3 + 3im 4 + 4im 0 + 0im ] +// expect: [ 0 + 0im 0 + 0im 0 + 0im 0 + 0im ] + +var E = ComplexMatrix(4,4) +E[1..3:2, 1..3:2] = A +print E +// expect: [ 0 + 0im 0 + 0im 0 + 0im 0 + 0im ] +// expect: [ 0 + 0im 1 + 1im 0 + 0im 2 + 2im ] +// expect: [ 0 + 0im 0 + 0im 0 + 0im 0 + 0im ] +// expect: [ 0 + 0im 3 + 3im 0 + 0im 4 + 4im ] + +var F = ComplexMatrix(4,4) +F[0..1, 0] = A[0..1, 1] +print F +// expect: [ 2 + 2im 0 + 0im 0 + 0im 0 + 0im ] +// expect: [ 4 + 4im 0 + 0im 0 + 0im 0 + 0im ] +// expect: [ 0 + 0im 0 + 0im 0 + 0im 0 + 0im ] +// expect: [ 0 + 0im 0 + 0im 0 + 0im 0 + 0im ] + +B[0..5, 0..5] = A +print B +// expect error 'LnAlgMtrxIndxBnds' + diff --git a/test/linalg/index/complexmatrix_slice.morpho b/test/linalg/index/complexmatrix_slice.morpho new file mode 100644 index 00000000..813ca13b --- /dev/null +++ b/test/linalg/index/complexmatrix_slice.morpho @@ -0,0 +1,24 @@ +// Slice a ComplexMatrix + +var A = ComplexMatrix(((1,2,3,4),(5,6,7,8),(9,10,11,12),(13,14,15,16)))*(1+1im) + +print A[0..1, 0..1] +// expect: [ 1 + 1im 2 + 2im ] +// expect: [ 5 + 5im 6 + 6im ] + +print A[0..2, 0] +// expect: [ 1 + 1im ] +// expect: [ 5 + 5im ] +// expect: [ 9 + 9im ] + +print A[2..0:-1, 0] +// expect: [ 9 + 9im ] +// expect: [ 5 + 5im ] +// expect: [ 1 + 1im ] + +print A[0..3:2, 0..3:2] +// expect: [ 1 + 1im 3 + 3im ] +// expect: [ 9 + 9im 11 + 11im ] + +print A[0..1:2, "Foo"] +// expect error 'LnAlgMtrxNnNmrclArg' diff --git a/test/linalg/index/matrix_getcolumn.morpho b/test/linalg/index/matrix_getcolumn.morpho new file mode 100644 index 00000000..a064f70b --- /dev/null +++ b/test/linalg/index/matrix_getcolumn.morpho @@ -0,0 +1,17 @@ +// Get columns of a Matrix + +var A = Matrix(2,2) +A[0,0] = 1 +A[0,1] = 2 +A[1,0] = 3 +A[1,1] = 4 + +print A.column(0) +// expect: [ 1 ] +// expect: [ 3 ] + +print A.column(1) +// expect: [ 2 ] +// expect: [ 4 ] + +print A.column(2) // expect error 'LnAlgMtrxIndxBnds' diff --git a/test/linalg/index/matrix_getindex.morpho b/test/linalg/index/matrix_getindex.morpho new file mode 100644 index 00000000..d32c767d --- /dev/null +++ b/test/linalg/index/matrix_getindex.morpho @@ -0,0 +1,19 @@ +// Get elements of a Matrix + +var A = Matrix(2,2) +A[0,0] = 1 +A[1,0] = 2 +A[0,1] = 3 +A[1,1] = 4 + +// Array-like access +print A[0,0] // expect: 1 +print A[1,0] // expect: 2 +print A[0,1] // expect: 3 +print A[1,1] // expect: 4 + +// Vector-like access +print A[0] // expect: 1 +print A[1] // expect: 2 +print A[2] // expect: 3 +print A[3] // expect: 4 diff --git a/test/linalg/index/matrix_setcolumn.morpho b/test/linalg/index/matrix_setcolumn.morpho new file mode 100644 index 00000000..7a82bf3e --- /dev/null +++ b/test/linalg/index/matrix_setcolumn.morpho @@ -0,0 +1,20 @@ +// Set columns of a Matrix + +var A = Matrix(2,2) + +var b = Matrix(2,1) +b[0] = 1 +b[1] = 3 + +var c = Matrix(2,1) +c[0] = 2 +c[1] = 4 + +A.setColumn(0,b) +A.setColumn(1,c) + +print A +// expect: [ 1 2 ] +// expect: [ 3 4 ] + +print A.setColumn(2,b) // expect error 'LnAlgMtrxIndxBnds' diff --git a/test/linalg/index/matrix_setindex.morpho b/test/linalg/index/matrix_setindex.morpho new file mode 100644 index 00000000..f657f348 --- /dev/null +++ b/test/linalg/index/matrix_setindex.morpho @@ -0,0 +1,11 @@ +// Set elements of a Matrix + +var A = Matrix(2,2) +A[0,0] = 1 +A[0,1] = 2 +A[1,0] = 3 +A[1,1] = 4 + +print A +// expect: [ 1 2 ] +// expect: [ 3 4 ] diff --git a/test/linalg/index/matrix_setslice.morpho b/test/linalg/index/matrix_setslice.morpho new file mode 100644 index 00000000..0eb54497 --- /dev/null +++ b/test/linalg/index/matrix_setslice.morpho @@ -0,0 +1,48 @@ +// Copy elements of a Matrix using slices + +var A = Matrix(((1,2),(3,4))) + +var B = Matrix(4,4) + +B[0..1, 0..1] = A +print B +// expect: [ 1 2 0 0 ] +// expect: [ 3 4 0 0 ] +// expect: [ 0 0 0 0 ] +// expect: [ 0 0 0 0 ] + +var C = Matrix(4,4) +C[0..2:2, 0..2:2] = A +print C +// expect: [ 1 0 2 0 ] +// expect: [ 0 0 0 0 ] +// expect: [ 3 0 4 0 ] +// expect: [ 0 0 0 0 ] + +var D = Matrix(4,4) +D[1..2, 1..2] = A +print D +// expect: [ 0 0 0 0 ] +// expect: [ 0 1 2 0 ] +// expect: [ 0 3 4 0 ] +// expect: [ 0 0 0 0 ] + +var E = Matrix(4,4) +E[1..3:2, 1..3:2] = A +print E +// expect: [ 0 0 0 0 ] +// expect: [ 0 1 0 2 ] +// expect: [ 0 0 0 0 ] +// expect: [ 0 3 0 4 ] + +var F = Matrix(4,4) +F[0..1, 0] = A[0..1, 1] +print F +// expect: [ 2 0 0 0 ] +// expect: [ 4 0 0 0 ] +// expect: [ 0 0 0 0 ] +// expect: [ 0 0 0 0 ] + +B[0..5, 0..5] = A +print B +// expect error 'LnAlgMtrxIndxBnds' diff --git a/test/linalg/index/matrix_slice.morpho b/test/linalg/index/matrix_slice.morpho new file mode 100644 index 00000000..27d1aec7 --- /dev/null +++ b/test/linalg/index/matrix_slice.morpho @@ -0,0 +1,24 @@ +// Slice a Matrix + +var A = Matrix(((1,2,3,4),(5,6,7,8),(9,10,11,12),(13,14,15,16))) + +print A[0..1, 0..1] +// expect: [ 1 2 ] +// expect: [ 5 6 ] + +print A[0..2, 0] +// expect: [ 1 ] +// expect: [ 5 ] +// expect: [ 9 ] + +print A[2..0:-1, 0] +// expect: [ 9 ] +// expect: [ 5 ] +// expect: [ 1 ] + +print A[0..3:2, 0..3:2] +// expect: [ 1 3 ] +// expect: [ 9 11 ] + +print A[0..1:2, "Foo"] +// expect error 'LnAlgMtrxNnNmrclArg' diff --git a/test/linalg/index/matrix_slice_bounds.morpho b/test/linalg/index/matrix_slice_bounds.morpho new file mode 100644 index 00000000..f0bce0a7 --- /dev/null +++ b/test/linalg/index/matrix_slice_bounds.morpho @@ -0,0 +1,6 @@ +// Slice a Matrix out of bounds + +var A = Matrix(((1,2,3,4),(5,6,7,8),(9,10,11,12),(13,14,15,16))) + +print A[0..5, 0..2] +// expect error 'LnAlgMtrxIndxBnds' diff --git a/test/linalg/index/matrix_slice_infinite_range.morpho b/test/linalg/index/matrix_slice_infinite_range.morpho new file mode 100644 index 00000000..63c2bf84 --- /dev/null +++ b/test/linalg/index/matrix_slice_infinite_range.morpho @@ -0,0 +1,6 @@ +// Slice a Matrix with a range that doesn't halt + +var A = Matrix(((1,2,3,4),(5,6,7,8),(9,10,11,12),(13,14,15,16))) + +print A[0..3:-1, 0] +// expect error 'LnAlgMtrxInvldArg' diff --git a/test/linalg/methods/complexmatrix_conj.morpho b/test/linalg/methods/complexmatrix_conj.morpho new file mode 100644 index 00000000..c02d0f8c --- /dev/null +++ b/test/linalg/methods/complexmatrix_conj.morpho @@ -0,0 +1,11 @@ +// Conjugate of a ComplexMatrix + +var A = ComplexMatrix(2,2) +A[0,0]=1+4im +A[0,1]=2+3im +A[1,0]=3+2im +A[1,1]=4+1im + +print A.conj() +// expect: [ 1 - 4im 2 - 3im ] +// expect: [ 3 - 2im 4 - 1im ] diff --git a/test/linalg/methods/complexmatrix_conjTranspose.morpho b/test/linalg/methods/complexmatrix_conjTranspose.morpho new file mode 100644 index 00000000..d1e1b836 --- /dev/null +++ b/test/linalg/methods/complexmatrix_conjTranspose.morpho @@ -0,0 +1,21 @@ +// Conjugate of a ComplexMatrix + +var A = ComplexMatrix(2,2) +A[0,0]=1+4im +A[0,1]=2+3im +A[1,0]=3+2im +A[1,1]=4+1im + +var B=A.conjTranspose() +print B +// expect: [ 1 - 4im 3 - 2im ] +// expect: [ 2 - 3im 4 - 1im ] + +for (ev in A.eigenvalues()) print isnumber(ev) +// expect: false +// expect: false + +var C=A+B // create a hermitian matrix +for (ev in C.eigenvalues()) print isnumber(ev) +// expect: true +// expect: true diff --git a/test/linalg/methods/complexmatrix_count.morpho b/test/linalg/methods/complexmatrix_count.morpho new file mode 100644 index 00000000..ce77d224 --- /dev/null +++ b/test/linalg/methods/complexmatrix_count.morpho @@ -0,0 +1,13 @@ +// Count elements in ComplexMatrix + +var A = ComplexMatrix(2,3) +A[0,0]=1+1im +A[0,1]=2+2im +A[0,2]=3+3im +A[1,0]=4+4im +A[1,1]=5+5im +A[1,2]=6+6im + +print A.count() +// expect: 6 + diff --git a/test/linalg/methods/complexmatrix_dimensions.morpho b/test/linalg/methods/complexmatrix_dimensions.morpho new file mode 100644 index 00000000..123adf13 --- /dev/null +++ b/test/linalg/methods/complexmatrix_dimensions.morpho @@ -0,0 +1,8 @@ +// Get dimensions of ComplexMatrix + +var A = ComplexMatrix(2,3) +A[0,0]=1+1im + +print A.dimensions() +// expect: (2, 3) + diff --git a/test/linalg/methods/complexmatrix_eigensystem.morpho b/test/linalg/methods/complexmatrix_eigensystem.morpho new file mode 100644 index 00000000..ffd0b63d --- /dev/null +++ b/test/linalg/methods/complexmatrix_eigensystem.morpho @@ -0,0 +1,24 @@ +// Eigenvalues and eigenvectors + +var A = ComplexMatrix(2,2) +A[0,0]=0im +A[0,1]=im +A[1,0]=im +A[1,1]=0im + +var es=A.eigensystem() +print es +// expect: ((0 + 1im, 0 - 1im), ) + +print es[0] +// expect: (0 + 1im, 0 - 1im) + +// Compare to analytical eigenvectors +var v = ComplexMatrix(2,2) +v[0,0]=sqrt(2)/2 +v[1,0]=sqrt(2)/2 +v[0,1]=sqrt(2)/2 +v[1,1]=-sqrt(2)/2 + +print abs((es[1]-v).sum()) < 1e-15 +// expect: true diff --git a/test/linalg/methods/complexmatrix_eigenvalues.morpho b/test/linalg/methods/complexmatrix_eigenvalues.morpho new file mode 100644 index 00000000..74f858dc --- /dev/null +++ b/test/linalg/methods/complexmatrix_eigenvalues.morpho @@ -0,0 +1,10 @@ +// Eigenvalues + +var A = ComplexMatrix(2,2) +A[0,0]=0+0im +A[0,1]=im +A[1,0]=im +A[1,1]=0+0im + +print A.eigenvalues() +// expect: (0 + 1im, 0 - 1im) diff --git a/test/linalg/methods/complexmatrix_enumerate.morpho b/test/linalg/methods/complexmatrix_enumerate.morpho new file mode 100644 index 00000000..dcce1447 --- /dev/null +++ b/test/linalg/methods/complexmatrix_enumerate.morpho @@ -0,0 +1,13 @@ +// Enumerate elements of a matrix + +var A = ComplexMatrix(2,2) +A[0,0] = 1+im +A[0,1] = 0+0im +A[1,0] = 0+0im +A[1,1] = 1-im + +for (x in A) print x +// expect: 1 + 1im +// expect: 0 + 0im +// expect: 0 + 0im +// expect: 1 - 1im diff --git a/test/linalg/methods/complexmatrix_format.morpho b/test/linalg/methods/complexmatrix_format.morpho new file mode 100644 index 00000000..32d32dd9 --- /dev/null +++ b/test/linalg/methods/complexmatrix_format.morpho @@ -0,0 +1,12 @@ +// Format +import constants + +var A = ComplexMatrix(2,2) +A[0,0]=1+1im +A[0,1]=exp(im*Pi/4) +A[1,0]=exp(-im*Pi/4) +A[1,1]=-1.5*(1+1im) + +print A.format("%5.2f") +// expect: [ 1.00 + 1.00im 0.71 + 0.71im ] +// expect: [ 0.71 - 0.71im -1.50 - 1.50im ] diff --git a/test/linalg/methods/complexmatrix_imag.morpho b/test/linalg/methods/complexmatrix_imag.morpho new file mode 100644 index 00000000..26afc044 --- /dev/null +++ b/test/linalg/methods/complexmatrix_imag.morpho @@ -0,0 +1,11 @@ +// Inner product of ComplexMatrices (Frobenius inner product with conjugation) + +var A = ComplexMatrix(2,2) +A[0,0]=1+4im +A[0,1]=2+3im +A[1,0]=3+2im +A[1,1]=4+1im + +print A.imag() +// expect: [ 4 3 ] +// expect: [ 2 1 ] diff --git a/test/linalg/methods/complexmatrix_inner.morpho b/test/linalg/methods/complexmatrix_inner.morpho new file mode 100644 index 00000000..68150dd5 --- /dev/null +++ b/test/linalg/methods/complexmatrix_inner.morpho @@ -0,0 +1,16 @@ +// Inner product of ComplexMatrices (Frobenius inner product with conjugation) + +var A = ComplexMatrix(2,2) +A[0,0]=1+1im +A[0,1]=2+2im +A[1,0]=3+3im +A[1,1]=4+4im + +var B = ComplexMatrix(2,2) +B[0,0]=1 +B[0,1]=1im +B[1,0]=-1im +B[1,1]=1 + +print A.inner(B) +// expect: 4 - 6im diff --git a/test/linalg/methods/complexmatrix_inverse.morpho b/test/linalg/methods/complexmatrix_inverse.morpho new file mode 100644 index 00000000..d6de0b08 --- /dev/null +++ b/test/linalg/methods/complexmatrix_inverse.morpho @@ -0,0 +1,11 @@ +// Inverse + +var A = ComplexMatrix(2,2) +A[0,0]=0+0im +A[0,1]=1+im +A[1,0]=1-im +A[1,1]=0+0im + +print A.inverse() +// expect: [ 0 + 0im 0.5 + 0.5im ] +// expect: [ 0.5 - 0.5im 0 + 0im ] diff --git a/test/linalg/methods/complexmatrix_inverse_singular.morpho b/test/linalg/methods/complexmatrix_inverse_singular.morpho new file mode 100644 index 00000000..49b34a86 --- /dev/null +++ b/test/linalg/methods/complexmatrix_inverse_singular.morpho @@ -0,0 +1,11 @@ +// Inverse of singular ComplexMatrix + +var A = ComplexMatrix(2,2) +A[0,0]=1+0im +A[0,1]=2+0im +A[1,0]=2+0im +A[1,1]=4+0im + +print A.inverse() +// expect error 'LnAlgMtrxSnglr' + diff --git a/test/linalg/methods/complexmatrix_norm.morpho b/test/linalg/methods/complexmatrix_norm.morpho new file mode 100644 index 00000000..09729729 --- /dev/null +++ b/test/linalg/methods/complexmatrix_norm.morpho @@ -0,0 +1,20 @@ +// Norm of a ComplexMatrix +import constants + +var A = ComplexMatrix(2,2) +A[0,0]=1+im +A[0,1]=2+im +A[1,0]=3-3im +A[1,1]=4-4im + +print abs(A.norm(1) - (4*sqrt(2) + sqrt(5))) < 1e-15 +// expect: true + +print abs(A.norm(Inf) - 7*sqrt(2)) < 1e-15 +// expect: true + +print abs(A.norm() - sqrt(57)) < 1e-15 +// expect: true + +print A.norm(5) +// expect error 'LnAlgMtrxNrmArgs' diff --git a/test/linalg/methods/complexmatrix_outer.morpho b/test/linalg/methods/complexmatrix_outer.morpho new file mode 100644 index 00000000..4a2e6ffb --- /dev/null +++ b/test/linalg/methods/complexmatrix_outer.morpho @@ -0,0 +1,9 @@ +// Outer product of two vectors + +var A = ComplexMatrix((1+1im,2-2im,3+3im)) +var B = ComplexMatrix((4+4im,5-5im)) + +print A.outer(B) +// expect: [ 0 + 8im 10 + 0im ] +// expect: [ 16 + 0im 0 - 20im ] +// expect: [ 0 + 24im 30 + 0im ] diff --git a/test/linalg/methods/complexmatrix_qr.morpho b/test/linalg/methods/complexmatrix_qr.morpho new file mode 100644 index 00000000..7d509bb6 --- /dev/null +++ b/test/linalg/methods/complexmatrix_qr.morpho @@ -0,0 +1,144 @@ +// QR Decomposition for ComplexMatrix + +// Test with a square complex matrix +var A = ComplexMatrix(((1.0+1.0im, 2.0+0.0im, 3.0-1.0im), + (4.0+2.0im, 5.0+1.0im, 6.0+0.0im), + (7.0+0.0im, 8.0-1.0im, 9.0+2.0im))) + +var qr = A.qr() + +print qr +// expect: (, ) + +var Q = qr[0] +var R = qr[1] + +print Q.dimensions() +// expect: (3, 3) + +print R.dimensions() +// expect: (3, 3) + +// Verify Q is unitary: Q^H * Q should be approximately I (conjugate transpose) +var QHQ = Q.conjTranspose() * Q +var I = ComplexMatrix(3,3) +for (var i = 0; i < 3; i = i + 1) { + I[i,i] = 1.0 + 0.0im +} + +print (QHQ - I).norm() < 1e-10 +// expect: true + +// Verify R is upper triangular (check lower triangle is zero) +var R_lower_norm = 0.0 +for (var i = 0; i < 3; i = i + 1) { + for (var j = 0; j < i; j = j + 1) { + var val = R[i,j] + R_lower_norm += val.abs() + } +} +print R_lower_norm < 1e-10 +// expect: true + +// Verify Q * R has the right structure +var QR = Q * R +print QR.dimensions() +// expect: (3, 3) + +// Test with a non-square matrix (tall matrix) +var B = ComplexMatrix(4,2) +B[0,0] = 1.0 + 1.0im +B[0,1] = 2.0 + 0.0im +B[1,0] = 3.0 - 1.0im +B[1,1] = 4.0 + 2.0im +B[2,0] = 5.0 + 0.0im +B[2,1] = 6.0 - 1.0im +B[3,0] = 7.0 + 1.0im +B[3,1] = 8.0 + 0.0im + +var qr2 = B.qr() +var Q2 = qr2[0] +var R2 = qr2[1] + +print Q2.dimensions() +// expect: (4, 4) + +print R2.dimensions() +// expect: (4, 2) + +// Verify Q2 first 2 columns are orthonormal (unitary) +// For tall matrices, only the first min(m,n) columns from ZUNGQR are orthonormal +// The remaining columns are zero +var Q2_col0 = Q2.column(0) +var Q2_col1 = Q2.column(1) +// Check norms +var norm0 = Q2_col0.norm() +var norm1 = Q2_col1.norm() + +print abs(norm0-1) < 1e-7 // expect: true +print abs(norm1-1) < 1e-7 // expect: true + +// Check orthogonality: inner product should be close to zero +var inner01 = Q2_col0.inner(Q2_col1) +var inner01_mag = inner01.abs() +print inner01_mag < 1e-10 +// expect: true + +// Verify R2 is upper triangular +var R2_lower_norm = 0.0 +for (var i = 0; i < 4; i = i + 1) { + for (var j = 0; j < 2; j = j + 1) { + if (i > j) { + var val = R2[i,j] + R2_lower_norm = val.abs() + } + } +} +print R2_lower_norm < 1e-10 +// expect: true + +// Test with a wide matrix +var C = ComplexMatrix(2,4) +C[0,0] = 1.0 + 1.0im +C[0,1] = 2.0 + 0.0im +C[0,2] = 3.0 - 1.0im +C[0,3] = 4.0 + 2.0im +C[1,0] = 5.0 + 0.0im +C[1,1] = 6.0 - 1.0im +C[1,2] = 7.0 + 1.0im +C[1,3] = 8.0 + 0.0im + +var qr3 = C.qr() +var Q3 = qr3[0] +var R3 = qr3[1] + +print Q3.dimensions() +// expect: (2, 2) + +print R3.dimensions() +// expect: (2, 4) + +// Verify Q3 is unitary +var Q3HQ3 = Q3.conjTranspose() * Q3 +var I2 = ComplexMatrix(2,2) +for (var i = 0; i < 2; i = i + 1) { + I2[i,i] = 1.0 + 0.0im +} +print (Q3HQ3 - I2).norm() < 1e-10 +// expect: true + +// Test with real-valued complex matrix (should work like real matrix) +var D = ComplexMatrix(((1.0+0.0im, 0.0+0.0im), + (0.0+0.0im, 2.0+0.0im))) +var qr4 = D.qr() +var Q4 = qr4[0] +var R4 = qr4[1] + +// Verify Q4 is unitary +var Q4HQ4 = Q4.conjTranspose() * Q4 +var I2b = ComplexMatrix(2,2) +for (var i = 0; i < 2; i = i + 1) { + I2b[i,i] = 1.0 + 0.0im +} +print (Q4HQ4 - I2b).norm() < 1e-10 +// expect: true diff --git a/test/linalg/methods/complexmatrix_real.morpho b/test/linalg/methods/complexmatrix_real.morpho new file mode 100644 index 00000000..620f2503 --- /dev/null +++ b/test/linalg/methods/complexmatrix_real.morpho @@ -0,0 +1,11 @@ +// Real part of a ComplexMatrix + +var A = ComplexMatrix(2,2) +A[0,0]=1+4im +A[0,1]=2+3im +A[1,0]=3+2im +A[1,1]=4+1im + +print A.real() +// expect: [ 1 2 ] +// expect: [ 3 4 ] diff --git a/test/linalg/methods/complexmatrix_reshape.morpho b/test/linalg/methods/complexmatrix_reshape.morpho new file mode 100644 index 00000000..cf4ea782 --- /dev/null +++ b/test/linalg/methods/complexmatrix_reshape.morpho @@ -0,0 +1,18 @@ +// Reshape ComplexMatrix + +var A = ComplexMatrix(2,2) +A[0,0]=1+1im +A[1,0]=2+2im +A[0,1]=3+3im +A[1,1]=4+4im +print A +// expect: [ 1 + 1im 3 + 3im ] +// expect: [ 2 + 2im 4 + 4im ] + +A.reshape(4,1) +print A +// expect: [ 1 + 1im ] +// expect: [ 2 + 2im ] +// expect: [ 3 + 3im ] +// expect: [ 4 + 4im ] + diff --git a/test/linalg/methods/complexmatrix_roll.morpho b/test/linalg/methods/complexmatrix_roll.morpho new file mode 100644 index 00000000..c8ecb690 --- /dev/null +++ b/test/linalg/methods/complexmatrix_roll.morpho @@ -0,0 +1,50 @@ +// Roll contents of a matrix + +var A = ComplexMatrix(3,3) +var k=0 +for (i in 0...3) for (j in 0...3) { A[i,j] = Complex(k,k); k+=1 } + +print A +// expect: [ 0 + 0im 1 + 1im 2 + 2im ] +// expect: [ 3 + 3im 4 + 4im 5 + 5im ] +// expect: [ 6 + 6im 7 + 7im 8 + 8im ] + +print A.roll(1,1) +// expect: [ 2 + 2im 0 + 0im 1 + 1im ] +// expect: [ 5 + 5im 3 + 3im 4 + 4im ] +// expect: [ 8 + 8im 6 + 6im 7 + 7im ] + +print A.roll(2,1) +// expect: [ 1 + 1im 2 + 2im 0 + 0im ] +// expect: [ 4 + 4im 5 + 5im 3 + 3im ] +// expect: [ 7 + 7im 8 + 8im 6 + 6im ] + +print A.roll(3,1) +// expect: [ 0 + 0im 1 + 1im 2 + 2im ] +// expect: [ 3 + 3im 4 + 4im 5 + 5im ] +// expect: [ 6 + 6im 7 + 7im 8 + 8im ] + +print A.roll(4,1) +// expect: [ 2 + 2im 0 + 0im 1 + 1im ] +// expect: [ 5 + 5im 3 + 3im 4 + 4im ] +// expect: [ 8 + 8im 6 + 6im 7 + 7im ] + +print A.roll(-1,0) +// expect: [ 3 + 3im 4 + 4im 5 + 5im ] +// expect: [ 6 + 6im 7 + 7im 8 + 8im ] +// expect: [ 0 + 0im 1 + 1im 2 + 2im ] + +print A.roll(-2,0) +// expect: [ 6 + 6im 7 + 7im 8 + 8im ] +// expect: [ 0 + 0im 1 + 1im 2 + 2im ] +// expect: [ 3 + 3im 4 + 4im 5 + 5im ] + +print A.roll(-3,0) +// expect: [ 0 + 0im 1 + 1im 2 + 2im ] +// expect: [ 3 + 3im 4 + 4im 5 + 5im ] +// expect: [ 6 + 6im 7 + 7im 8 + 8im ] + +print A.roll(-4,0) +// expect: [ 3 + 3im 4 + 4im 5 + 5im ] +// expect: [ 6 + 6im 7 + 7im 8 + 8im ] +// expect: [ 0 + 0im 1 + 1im 2 + 2im ] diff --git a/test/linalg/methods/complexmatrix_roll_negative.morpho b/test/linalg/methods/complexmatrix_roll_negative.morpho new file mode 100644 index 00000000..ef2e2a81 --- /dev/null +++ b/test/linalg/methods/complexmatrix_roll_negative.morpho @@ -0,0 +1,12 @@ +// Negative roll values for ComplexMatrix + +var A = ComplexMatrix(3,1) +A[0,0]=1+1im +A[1,0]=2+2im +A[2,0]=3+3im + +print A.roll(-1) +// expect: [ 2 + 2im ] +// expect: [ 3 + 3im ] +// expect: [ 1 + 1im ] + diff --git a/test/linalg/methods/complexmatrix_sum.morpho b/test/linalg/methods/complexmatrix_sum.morpho new file mode 100644 index 00000000..64369ace --- /dev/null +++ b/test/linalg/methods/complexmatrix_sum.morpho @@ -0,0 +1,12 @@ +// Sum + +var A = ComplexMatrix(3,2) +A[0,0]=1+im +A[0,1]=2+2im +A[1,0]=3+3im +A[1,1]=4+4im +A[2,0]=5+5im +A[2,1]=6+6im + +print A.sum() +// expect: 21 + 21im diff --git a/test/linalg/methods/complexmatrix_svd.morpho b/test/linalg/methods/complexmatrix_svd.morpho new file mode 100644 index 00000000..388681fa --- /dev/null +++ b/test/linalg/methods/complexmatrix_svd.morpho @@ -0,0 +1,21 @@ +// Singular Value Decomposition + +var A = ComplexMatrix(((1+1im,0+0im),(0+0im,2+2im))) + +var svd = A.svd() + +// Test reconstruction: U * S * V^T should approximately equal A +var U = svd[0] +var S = svd[1] +var V = svd[2] + +// Create diagonal matrix from singular values +var Sdiag = ComplexMatrix(2,2) +Sdiag[0,0] = S[0] +Sdiag[1,1] = S[1] + +var VT = V.transpose() +var reconstructed = U * Sdiag * VT + +print (reconstructed - A).norm() < 1e-10 +// expect: true diff --git a/test/linalg/methods/complexmatrix_trace.morpho b/test/linalg/methods/complexmatrix_trace.morpho new file mode 100644 index 00000000..4a01913f --- /dev/null +++ b/test/linalg/methods/complexmatrix_trace.morpho @@ -0,0 +1,10 @@ +// Inverse + +var A = ComplexMatrix(2,2) +A[0,0]=1+4im +A[0,1]=2+3im +A[1,0]=3+2im +A[1,1]=4+1im + +print A.trace() +// expect: 5 + 5im diff --git a/test/linalg/methods/complexmatrix_transpose.morpho b/test/linalg/methods/complexmatrix_transpose.morpho new file mode 100644 index 00000000..92049585 --- /dev/null +++ b/test/linalg/methods/complexmatrix_transpose.morpho @@ -0,0 +1,13 @@ +// Inverse + +var A = ComplexMatrix(3,2) +A[0,0]=1+1im +A[0,1]=2+2im +A[1,0]=3+3im +A[1,1]=4+4im +A[2,0]=5+5im +A[2,1]=6+6im + +print A.transpose() +// expect: [ 1 + 1im 3 + 3im 5 + 5im ] +// expect: [ 2 + 2im 4 + 4im 6 + 6im ] diff --git a/test/linalg/methods/matrix_count.morpho b/test/linalg/methods/matrix_count.morpho new file mode 100644 index 00000000..46d5a971 --- /dev/null +++ b/test/linalg/methods/matrix_count.morpho @@ -0,0 +1,13 @@ +// Count elements in Matrix + +var A = Matrix(2,3) +A[0,0]=1 +A[0,1]=2 +A[0,2]=3 +A[1,0]=4 +A[1,1]=5 +A[1,2]=6 + +print A.count() +// expect: 6 + diff --git a/test/linalg/methods/matrix_dimensions.morpho b/test/linalg/methods/matrix_dimensions.morpho new file mode 100644 index 00000000..9fa7f8b5 --- /dev/null +++ b/test/linalg/methods/matrix_dimensions.morpho @@ -0,0 +1,9 @@ +// Get dimensions of Matrix + +var A = Matrix(2,3) +print A.dimensions() +// expect: (2, 3) + +var a = Matrix([[0.8, -0.4], [0.4, 0.8]]) +print a.dimensions() +// expect: (2, 2) diff --git a/test/linalg/methods/matrix_eigensystem.morpho b/test/linalg/methods/matrix_eigensystem.morpho new file mode 100644 index 00000000..957f676d --- /dev/null +++ b/test/linalg/methods/matrix_eigensystem.morpho @@ -0,0 +1,19 @@ +// Eigenvalues and eigenvectors + +var A = Matrix(2,2) +A[0,0]=0 +A[0,1]=1 +A[1,0]=1 +A[1,1]=0 + +var es=A.eigensystem() + +print es +// expect: ((1, -1), ) + +print es[0] +// expect: (1, -1) + +print es[1].format("%.2g") +// expect: [ 0.71 -0.71 ] +// expect: [ 0.71 0.71 ] diff --git a/test/linalg/methods/matrix_eigenvalues.morpho b/test/linalg/methods/matrix_eigenvalues.morpho new file mode 100644 index 00000000..e45399eb --- /dev/null +++ b/test/linalg/methods/matrix_eigenvalues.morpho @@ -0,0 +1,10 @@ +// Eigenvalues + +var A = Matrix(2,2) +A[0,0]=0 +A[0,1]=1 +A[1,0]=1 +A[1,1]=0 + +print A.eigenvalues() +// expect: (1, -1) diff --git a/test/linalg/methods/matrix_enumerate.morpho b/test/linalg/methods/matrix_enumerate.morpho new file mode 100644 index 00000000..37d1cafa --- /dev/null +++ b/test/linalg/methods/matrix_enumerate.morpho @@ -0,0 +1,13 @@ +// Enumerate elements of a matrix + +var A = Matrix(2,2) +A[0,0] = 1 +A[1,0] = 2 +A[0,1] = 3 +A[1,1] = 4 + +for (x in A) print x +// expect: 1 +// expect: 2 +// expect: 3 +// expect: 4 diff --git a/test/linalg/methods/matrix_format.morpho b/test/linalg/methods/matrix_format.morpho new file mode 100644 index 00000000..999c831e --- /dev/null +++ b/test/linalg/methods/matrix_format.morpho @@ -0,0 +1,12 @@ +// Format +import constants + +var A = Matrix(2,2) +A[0,0]=1 +A[0,1]=Pi/2 +A[1,0]=Pi/2 +A[1,1]=-1.5 + +print A.format("%5.2f") +// expect: [ 1.00 1.57 ] +// expect: [ 1.57 -1.50 ] diff --git a/test/linalg/methods/matrix_inner.morpho b/test/linalg/methods/matrix_inner.morpho new file mode 100644 index 00000000..4c789d22 --- /dev/null +++ b/test/linalg/methods/matrix_inner.morpho @@ -0,0 +1,17 @@ +// Inner product of Matrices + +var A = Matrix(2,2) +A[0,0]=1 +A[0,1]=2 +A[1,0]=3 +A[1,1]=4 + +var B = Matrix(2,2) +B[0,0]=1 +B[0,1]=0 +B[1,0]=0 +B[1,1]=1 + +print A.inner(B) +// expect: 5 + diff --git a/test/linalg/methods/matrix_inverse.morpho b/test/linalg/methods/matrix_inverse.morpho new file mode 100644 index 00000000..85b4e02a --- /dev/null +++ b/test/linalg/methods/matrix_inverse.morpho @@ -0,0 +1,11 @@ +// Inverse + +var A = Matrix(2,2) +A[0,0]=1 +A[0,1]=2 +A[1,0]=3 +A[1,1]=4 + +print A.inverse() +// expect: [ -2 1 ] +// expect: [ 1.5 -0.5 ] diff --git a/test/linalg/methods/matrix_inverse_singular.morpho b/test/linalg/methods/matrix_inverse_singular.morpho new file mode 100644 index 00000000..d3dea86a --- /dev/null +++ b/test/linalg/methods/matrix_inverse_singular.morpho @@ -0,0 +1,10 @@ +// Inverse + +var A = Matrix(2,2) +A[0,0]=1 +A[0,1]=2 +A[1,0]=2 +A[1,1]=4 + +print A.inverse() +// expect error 'LnAlgMtrxSnglr' diff --git a/test/linalg/methods/matrix_norm.morpho b/test/linalg/methods/matrix_norm.morpho new file mode 100644 index 00000000..b1a4cd67 --- /dev/null +++ b/test/linalg/methods/matrix_norm.morpho @@ -0,0 +1,20 @@ +// Norm of an Matrix +import constants + +var A = Matrix(2,2) +A[0,0]=1 +A[0,1]=-2 +A[1,0]=3 +A[1,1]=-4 + +print A.norm(1) +// expect: 6 + +print A.norm(Inf) +// expect: 7 + +print abs(A.norm() - sqrt(30)) < 1e-15 +// expect: true + +print A.norm(5) +// expect error 'LnAlgMtrxNrmArgs' diff --git a/test/linalg/methods/matrix_outer.morpho b/test/linalg/methods/matrix_outer.morpho new file mode 100644 index 00000000..bb08c58c --- /dev/null +++ b/test/linalg/methods/matrix_outer.morpho @@ -0,0 +1,9 @@ +// Outer product of two vectors + +var A = Matrix((1,2,3)) +var B = Matrix((4,5)) + +print A.outer(B) +// expect: [ 4 5 ] +// expect: [ 8 10 ] +// expect: [ 12 15 ] diff --git a/test/linalg/methods/matrix_qr.morpho b/test/linalg/methods/matrix_qr.morpho new file mode 100644 index 00000000..9dfc2b26 --- /dev/null +++ b/test/linalg/methods/matrix_qr.morpho @@ -0,0 +1,134 @@ +// QR Decomposition + +// Test with a square matrix (this one is singular, so R will have a zero on the diagonal) +var A = Matrix(((1.0,2.0,3.0),(4.0,5.0,6.0),(7.0,8.0,9.0))) + +var qr = A.qr() + +print qr +// expect: (, ) + +var Q = qr[0] +var R = qr[1] + +print Q.dimensions() +// expect: (3, 3) + +print R.dimensions() +// expect: (3, 3) + +// Verify Q is orthogonal: Q^T * Q should be approximately I +var QTQ = Q.transpose() * Q +var I = IdentityMatrix(3) + +print (QTQ - I).norm() < 1e-10 +// expect: true + +// Verify R is upper triangular (check lower triangle is zero) +var R_lower_norm = 0.0 +for (var i = 0; i < 3; i = i + 1) { + for (var j = 0; j < i; j = j + 1) { + R_lower_norm = R_lower_norm + R[i,j]*R[i,j] + } +} +print R_lower_norm < 1e-10 +// expect: true + +// Check R's diagonal - since A is singular, one diagonal element should be (close to) zero +// This indicates the matrix has rank 2 (not full rank) +print abs(R[2,2]) < 1e-8 +// expect: true + +// Verify Q * R reconstruction +var QR = Q * R +print (QR - A).norm() < 1e-10 +// expect: true + +// Test with a non-square matrix (tall matrix) +var B = Matrix(4,2) +B[0,0] = 1.0 +B[0,1] = 2.0 +B[1,0] = 3.0 +B[1,1] = 4.0 +B[2,0] = 5.0 +B[2,1] = 6.0 +B[3,0] = 7.0 +B[3,1] = 8.0 + +var qr2 = B.qr() +var Q2 = qr2[0] +var R2 = qr2[1] + +print Q2.dimensions() +// expect: (4, 4) + +print R2.dimensions() +// expect: (4, 2) + +// Verify Q2 first 2 columns are orthonormal +// For tall matrices, only the first min(m,n) columns from DORGQR are orthonormal +// The remaining columns are zero +var Q2_col0 = Q2.column(0) +var Q2_col1 = Q2.column(1) +// Check norms +print Q2_col0.norm() < 1.0 + 1e-10 and Q2_col0.norm() > 1.0 - 1e-10 +// expect: true +print Q2_col1.norm() < 1.0 + 1e-10 and Q2_col1.norm() > 1.0 - 1e-10 +// expect: true +// Check orthogonality: dot product should be close to zero +var dot01 = Q2_col0.inner(Q2_col1) +print dot01 < 1e-10 and dot01 > -1e-10 +// expect: true + +// Verify R2 is upper triangular +var R2_lower_norm = 0.0 +for (var i = 0; i < 4; i = i + 1) { + for (var j = 0; j < 2; j = j + 1) { + if (i > j) { + R2_lower_norm = R2_lower_norm + R2[i,j]*R2[i,j] + } + } +} +print R2_lower_norm < 1e-10 +// expect: true + +// Test with a wide matrix +var C = Matrix(2,4) +C[0,0] = 1.0 +C[0,1] = 2.0 +C[0,2] = 3.0 +C[0,3] = 4.0 +C[1,0] = 5.0 +C[1,1] = 6.0 +C[1,2] = 7.0 +C[1,3] = 8.0 + +var qr3 = C.qr() +var Q3 = qr3[0] +var R3 = qr3[1] + +print Q3.dimensions() +// expect: (2, 2) + +print R3.dimensions() +// expect: (2, 4) + +// Verify Q3 is orthogonal +var Q3TQ3 = Q3.transpose() * Q3 +var I2 = IdentityMatrix(2) +print (Q3TQ3 - I2).norm() < 1e-10 +// expect: true + +// Test with identity matrix +var I3 = IdentityMatrix(3) +var qr4 = I3.qr() +var Q4 = qr4[0] +var R4 = qr4[1] + +// Q should be close to identity +print (Q4 - I3).norm() < 1e-10 +// expect: true + +// R should be close to identity +print (R4 - I3).norm() < 1e-10 +// expect: true diff --git a/test/linalg/methods/matrix_reshape.morpho b/test/linalg/methods/matrix_reshape.morpho new file mode 100644 index 00000000..d49bbe5b --- /dev/null +++ b/test/linalg/methods/matrix_reshape.morpho @@ -0,0 +1,18 @@ +// Reshape Matrix + +var A = Matrix(2,2) +A[0,0]=1 +A[1,0]=2 +A[0,1]=3 +A[1,1]=4 +print A +// expect: [ 1 3 ] +// expect: [ 2 4 ] + +A.reshape(4,1) +print A +// expect: [ 1 ] +// expect: [ 2 ] +// expect: [ 3 ] +// expect: [ 4 ] + diff --git a/test/linalg/methods/matrix_roll.morpho b/test/linalg/methods/matrix_roll.morpho new file mode 100644 index 00000000..b0a74648 --- /dev/null +++ b/test/linalg/methods/matrix_roll.morpho @@ -0,0 +1,50 @@ +// Roll contents of a matrix + +var A = Matrix(3,3) +var k=0 +for (i in 0...3) for (j in 0...3) { A[i,j] = k; k+=1 } + +print A +// expect: [ 0 1 2 ] +// expect: [ 3 4 5 ] +// expect: [ 6 7 8 ] + +print A.roll(1,1) +// expect: [ 2 0 1 ] +// expect: [ 5 3 4 ] +// expect: [ 8 6 7 ] + +print A.roll(2,1) +// expect: [ 1 2 0 ] +// expect: [ 4 5 3 ] +// expect: [ 7 8 6 ] + +print A.roll(3,1) +// expect: [ 0 1 2 ] +// expect: [ 3 4 5 ] +// expect: [ 6 7 8 ] + +print A.roll(4,1) +// expect: [ 2 0 1 ] +// expect: [ 5 3 4 ] +// expect: [ 8 6 7 ] + +print A.roll(-1,0) +// expect: [ 3 4 5 ] +// expect: [ 6 7 8 ] +// expect: [ 0 1 2 ] + +print A.roll(-2,0) +// expect: [ 6 7 8 ] +// expect: [ 0 1 2 ] +// expect: [ 3 4 5 ] + +print A.roll(-3,0) +// expect: [ 0 1 2 ] +// expect: [ 3 4 5 ] +// expect: [ 6 7 8 ] + +print A.roll(-4,0) +// expect: [ 3 4 5 ] +// expect: [ 6 7 8 ] +// expect: [ 0 1 2 ] diff --git a/test/linalg/methods/matrix_sum.morpho b/test/linalg/methods/matrix_sum.morpho new file mode 100644 index 00000000..0ccdee3b --- /dev/null +++ b/test/linalg/methods/matrix_sum.morpho @@ -0,0 +1,12 @@ +// Sum + +var A = Matrix(3,2) +A[0,0]=1 +A[0,1]=2 +A[1,0]=3 +A[1,1]=4 +A[2,0]=5 +A[2,1]=6 + +print A.sum() +// expect: 21 diff --git a/test/linalg/methods/matrix_svd.morpho b/test/linalg/methods/matrix_svd.morpho new file mode 100644 index 00000000..23cdac47 --- /dev/null +++ b/test/linalg/methods/matrix_svd.morpho @@ -0,0 +1,53 @@ +// Singular Value Decomposition + +var A = Matrix(((1,0),(0,2))) + +var svd = A.svd() + +print svd +// expect: (, (2, 1), ) + +print (svd[0] - Matrix(((0,1),(1,0)))).norm() < 1e-10 +// expect: true + +print svd[1] +// expect: (2, 1) + +print (svd[2] - Matrix(((0,1),(1,0)))).norm() < 1e-10 +// expect: true + +// Test reconstruction: U * S * V^T should approximately equal A +var U = svd[0] +var S = svd[1] +var V = svd[2] + +// Create diagonal matrix from singular values +var Sdiag = Matrix(2,2) +Sdiag[0,0] = S[0] +Sdiag[1,1] = S[1] + +var VT = V.transpose() +var reconstructed = U * Sdiag * VT + +print (reconstructed - A).norm() < 1e-10 +// expect: true + +// Test with a non-square matrix +var B = Matrix(3,2) +B[0,0]=1 +B[0,1]=0 +B[1,0]=0 +B[1,1]=2 +B[2,0]=0 +B[2,1]=0 + +var svd2 = B.svd() + +print svd2[0].dimensions() +// expect: (3, 3) + +print svd2[1] +// expect: (2, 1) + +print svd2[2].dimensions() +// expect: (2, 2) diff --git a/test/linalg/methods/matrix_trace.morpho b/test/linalg/methods/matrix_trace.morpho new file mode 100644 index 00000000..2fe81a5f --- /dev/null +++ b/test/linalg/methods/matrix_trace.morpho @@ -0,0 +1,10 @@ +// Inverse + +var A = Matrix(2,2) +A[0,0]=1 +A[0,1]=2 +A[1,0]=3 +A[1,1]=4 + +print A.trace() +// expect: 5 diff --git a/test/linalg/methods/matrix_transpose.morpho b/test/linalg/methods/matrix_transpose.morpho new file mode 100644 index 00000000..4043881e --- /dev/null +++ b/test/linalg/methods/matrix_transpose.morpho @@ -0,0 +1,13 @@ +// Inverse + +var A = Matrix(3,2) +A[0,0]=1 +A[0,1]=2 +A[1,0]=3 +A[1,1]=4 +A[2,0]=5 +A[2,1]=6 + +print A.transpose() +// expect: [ 1 3 5 ] +// expect: [ 2 4 6 ] diff --git a/test/matrix/Lnorm.morpho b/test/matrix/Lnorm.morpho index e3fb763b..ac6e7094 100644 --- a/test/matrix/Lnorm.morpho +++ b/test/matrix/Lnorm.morpho @@ -5,9 +5,5 @@ var a = Matrix([1,2,3]) print a.norm(1) // expect: 6 -print abs(a.norm(2) - 3.74166) < 1e-4 // expect: true - -print abs(a.norm(3) - 3.30193) < 1e-4 // expect: true - print a.norm(Inf) // expect: 3 diff --git a/test/matrix/assign.morpho b/test/matrix/assign.morpho index 68aa43f9..c18d55f5 100644 --- a/test/matrix/assign.morpho +++ b/test/matrix/assign.morpho @@ -14,4 +14,4 @@ print b var c = Matrix(1,2) b.assign(c) -// expect error 'MtrxIncmptbl' +// expect error 'LnAlgMtrxIncmptbl' diff --git a/test/matrix/blockmatrix_constructor.morpho b/test/matrix/blockmatrix_constructor.morpho index ea012bc0..e5a45cb3 100644 --- a/test/matrix/blockmatrix_constructor.morpho +++ b/test/matrix/blockmatrix_constructor.morpho @@ -1,4 +1,4 @@ // Block matrix constructor with single list print Matrix([Matrix(2)]) -// expect error 'MtrxInvldInit' +// expect error 'LnAlgMtrxInvldArg' diff --git a/test/matrix/concatenate.morpho b/test/matrix/concatenate.morpho index ba00ec79..c88f253a 100644 --- a/test/matrix/concatenate.morpho +++ b/test/matrix/concatenate.morpho @@ -28,4 +28,4 @@ print c // expect: [ 2 3 2 3 0 ] var c = Matrix([[a, b], [b, 0]]) -// expect error 'MtrxIncmptbl' +// expect error 'LnAlgMtrxIncmptbl' diff --git a/test/matrix/concatenate_sparse.morpho b/test/matrix/concatenate_sparse.morpho index 100d0136..66fb4f6e 100644 --- a/test/matrix/concatenate_sparse.morpho +++ b/test/matrix/concatenate_sparse.morpho @@ -24,4 +24,4 @@ print c // expect: [ 2 3 2 3 0 ] var c = Matrix([[a, b], [b, 0]]) -// expect error 'MtrxIncmptbl' +// expect error 'LnAlgMtrxIncmptbl' diff --git a/test/matrix/dimensions.morpho b/test/matrix/dimensions.morpho index 2ef71212..2fe92517 100644 --- a/test/matrix/dimensions.morpho +++ b/test/matrix/dimensions.morpho @@ -4,7 +4,7 @@ var a = Matrix([[0.8, -0.4], [0.4, 0.8]]) var v = Matrix([0.2, 0.3]) print a.dimensions() -// expect: [ 2, 2 ] +// expect: (2, 2) print v.dimensions() -// expect: [ 2, 1 ] +// expect: (2, 1) diff --git a/test/matrix/eigensystem.morpho b/test/matrix/eigensystem.morpho index 17e1e55b..351eece3 100644 --- a/test/matrix/eigensystem.morpho +++ b/test/matrix/eigensystem.morpho @@ -4,5 +4,6 @@ var a = Matrix([[ -0.083929, 0.102945, 0.213477 ], [ 0.102945, -0.108697, 0.189335 ], [ 0.213477, 0.189335, 0.192626 ]]) -print a.eigensystem() -// expect: [ , ] \ No newline at end of file +var b = a.eigensystem() +print b[0].clss() // expect: @Tuple +print b[1].clss() // expect: @Matrix diff --git a/test/matrix/eigenvalues.morpho b/test/matrix/eigenvalues.morpho index 562f3d62..b874b407 100644 --- a/test/matrix/eigenvalues.morpho +++ b/test/matrix/eigenvalues.morpho @@ -3,9 +3,8 @@ var a = Matrix([[1,-1,0], [-1,1,0], [0,0,1]]) var ev = a.eigenvalues() -ev.sort() -print ev -// expect: [ 0, 1, 2 ] +print ev.sort() +// expect: (0, 1, 2) var b = Matrix([[1,2,0], [-2,1,0], [0,0,1]]) @@ -22,7 +21,7 @@ print a // ensure a is not overwritten var es = a.eigensystem() print es[0] -// expect: [ 2, 0, 1 ] +// expect: (2, 0, 1) print es[1] // expect: [ 0.707107 0.707107 0 ] // expect: [ -0.707107 0.707107 0 ] diff --git a/test/matrix/get_column.morpho b/test/matrix/get_column.morpho index 94ba0314..d8d66621 100644 --- a/test/matrix/get_column.morpho +++ b/test/matrix/get_column.morpho @@ -17,4 +17,4 @@ print a.column(2) // expect: [ 12 ] print a.column(10) -// expect Error 'MtrxBnds' +// expect Error 'LnAlgMtrxIndxBnds' diff --git a/test/matrix/incompatible_add.morpho b/test/matrix/incompatible_add.morpho index 0045a01e..650250b3 100644 --- a/test/matrix/incompatible_add.morpho +++ b/test/matrix/incompatible_add.morpho @@ -4,4 +4,4 @@ var a = Matrix([[1, 2, 1], [3, 4, 1]]) var b = Matrix([[0, 1], [1, 0]]) print a+b -// expect Error: 'MtrxIncmptbl' +// expect Error: 'LnAlgMtrxIncmptbl' diff --git a/test/matrix/incompatible_mul.morpho b/test/matrix/incompatible_mul.morpho index 769698df..dc681121 100644 --- a/test/matrix/incompatible_mul.morpho +++ b/test/matrix/incompatible_mul.morpho @@ -4,4 +4,4 @@ var a = Matrix([[1, 2, 1], [3, 4, 1]]) var b = Matrix([[0, 1], [1, 0]]) print a*b -// expect Error: 'MtrxIncmptbl' +// expect Error: 'LnAlgMtrxIncmptbl' diff --git a/test/matrix/incompatible_sub.morpho b/test/matrix/incompatible_sub.morpho index d056a961..751af5d6 100644 --- a/test/matrix/incompatible_sub.morpho +++ b/test/matrix/incompatible_sub.morpho @@ -4,4 +4,4 @@ var a = Matrix([[1, 2, 1], [3, 4, 1]]) var b = Matrix([[0, 1], [1, 0]]) print a*b -// expect Error: 'MtrxIncmptbl' +// expect Error: 'LnAlgMtrxIncmptbl' diff --git a/test/matrix/initializer.morpho b/test/matrix/initializer.morpho index 5d24e1d8..846ad9a6 100644 --- a/test/matrix/initializer.morpho +++ b/test/matrix/initializer.morpho @@ -33,5 +33,5 @@ print w // expect: [ 3 4 ] var w = Matrix([[1,[2,3]]]) -// expect Error 'MtrxInvldInit' +// expect Error 'LnAlgMtrxInvldArg' print w diff --git a/test/matrix/inverse.morpho b/test/matrix/inverse.morpho index 628acad3..de8879e1 100644 --- a/test/matrix/inverse.morpho +++ b/test/matrix/inverse.morpho @@ -15,4 +15,4 @@ print ((mi[1,1] - a/det)<1e-8) // expect: true var m = Matrix([[1,0,0],[0,1,0],[0,0,0]]) -print m.inverse() // expect error 'MtrxSnglr' +print m.inverse() // expect error 'LnAlgMtrxSnglr' diff --git a/test/matrix/linearsolve.morpho b/test/matrix/linearsolve.morpho index f22e45d0..4f27656c 100644 --- a/test/matrix/linearsolve.morpho +++ b/test/matrix/linearsolve.morpho @@ -21,4 +21,4 @@ print a var b = Matrix([[3, 4], [6, 8]]) print v/b -// expect error 'MtrxSnglr' +// expect error 'LnAlgMtrxSnglr' diff --git a/test/matrix/nonnum_indices.morpho b/test/matrix/nonnum_indices.morpho index 4ef54278..7f1b7d10 100644 --- a/test/matrix/nonnum_indices.morpho +++ b/test/matrix/nonnum_indices.morpho @@ -3,4 +3,4 @@ var a = Matrix([[1,2], [3,4], [5,6]]) print a["Hello", "Squirrel"] -// expect error: 'MtrxInvldIndx' +// expect error: 'LnAlgMtrxNnNmrclArg' diff --git a/test/matrix/nonnum_initializer.morpho b/test/matrix/nonnum_initializer.morpho index 68fd001b..b94e4f48 100644 --- a/test/matrix/nonnum_initializer.morpho +++ b/test/matrix/nonnum_initializer.morpho @@ -1,4 +1,4 @@ // Non numerical initializer var m = Matrix([[1,2], [3,"oops"]]) -// expect error: 'MtrxInvldInit' +// expect error: 'LnAlgMtrxInvldArg' diff --git a/test/matrix/reshape.morpho b/test/matrix/reshape.morpho index 12adcf5e..e3470ce7 100644 --- a/test/matrix/reshape.morpho +++ b/test/matrix/reshape.morpho @@ -31,4 +31,4 @@ print a // expect: [ 3 6 ] a.reshape(3,3) -// expect error 'MtrxIncmptbl' +// expect error 'LnAlgMtrxIncmptbl' diff --git a/test/matrix/trace.morpho b/test/matrix/trace.morpho index cca52b46..48165412 100644 --- a/test/matrix/trace.morpho +++ b/test/matrix/trace.morpho @@ -7,4 +7,4 @@ print a.trace() var b = Matrix([[1, 2]]) print b.trace() -// expect error 'MtrxNtSq' +// expect error 'LnAlgMtrxNtSq' diff --git a/test/slice/matrixSlicing.morpho b/test/slice/matrixSlicing.morpho index f1247ef7..75f031b4 100644 --- a/test/slice/matrixSlicing.morpho +++ b/test/slice/matrixSlicing.morpho @@ -23,108 +23,116 @@ print A[0..1,3] // expect: [ 7 ] // mix of list and int -print A[2,[0,1,2,1,2]] -// expect: [ 8 9 10 9 10 ] +// print A[2,[0,1,2,1,2]] +// notexpect: [ 8 9 10 9 10 ] +// Bug in method resolution -print A[0..1] +print A[0..1,0] // expect: [ 0 ] // expect: [ 4 ] +print A[-1..2,1..2] +// expect: [ 9 10 ] +// expect: [ 1 2 ] +// expect: [ 5 6 ] +// expect: [ 9 10 ] + // range out of bounds try{ - print A[-1..2,1..2] + print A[-20..2,1..2] } catch{ - "MtrxBnds": print "MtrxBnds" -// expect: MtrxBnds + "LnAlgMtrxIndxBnds": print "LnAlgMtrxIndxBnds" +// expect: LnAlgMtrxIndxBnds } try{ print A[0..3,1..2] } catch{ - "MtrxBnds": print "MtrxBnds" -// expect: MtrxBnds + "LnAlgMtrxIndxBnds": print "LnAlgMtrxIndxBnds" +// expect: LnAlgMtrxIndxBnds } +print A[0.0..2,1..2] +// expect: [ 1 2 ] +// expect: [ 5 6 ] +// expect: [ 9 10 ] -// range is not int -try{ - print A[0.0..2,1..2] -} catch{ - "MtrxInvldIndx": print "MtrxInvldIndx" -// expect: MtrxInvldIndx -} +print A[[-1,1,-1],1..2] +// expect: [ 9 10 ] +// expect: [ 5 6 ] +// expect: [ 9 10 ] // list is out of bounds try{ - print A[[-1,2,-1],1..2] + print A[[-4,2,-1],1..2] } catch{ - "MtrxBnds": print "MtrxBnds" -// expect: MtrxBnds + "LnAlgMtrxIndxBnds": print "LnAlgMtrxIndxBnds" +// expect: LnAlgMtrxIndxBnds } try{ print A[[100],1..2] } catch{ - "MtrxBnds": print "MtrxBnds" -// expect: MtrxBnds + "LnAlgMtrxIndxBnds": print "LnAlgMtrxIndxBnds" +// expect: LnAlgMtrxIndxBnds } -// list is not int -try{ - print A[[1,2,1],[0.2,0.1]] -} catch{ - "MtrxInvldIndx": print "MtrxInvldIndx" -// expect: MtrxInvldIndx -} +// index in list is not int +print A[[1,2,1],[0.2,0.1]] +// expect: [ 4 4 ] +// expect: [ 8 8 ] +// expect: [ 4 4 ] + +// index in list is not slicable try{ print A[[1,2,1],[[1]]] } catch{ - "MtrxInvldIndx": print "MtrxInvldIndx" -// expect: MtrxInvldIndx + "LnAlgMtrxInvldArg": print "LnAlgMtrxInvldArg" +// expect: LnAlgMtrxInvldArg } -// int + list but int is out of bounds -try{ - print A[[0,2,1],-1] -} catch{ - "MtrxBnds": print "MtrxBnds" -// expect: MtrxBnds -} +// int + list with negative indexing +print A[[0,2,1],-1] +// expect: [ 3 ] +// expect: [ 11 ] +// expect: [ 7 ] + try{ print A[[0,2,1],100] } catch{ - "MtrxBnds": print "MtrxBnds" -// expect: MtrxBnds -} -try{ - print A[[0,2,1],0.0] -} catch{ - "MtrxInvldIndx": print "MtrxInvldIndx" -// expect: MtrxInvldIndx + "LnAlgMtrxIndxBnds": print "LnAlgMtrxIndxBnds" +// expect: LnAlgMtrxIndxBnds } +print A[[0,2,1],0.0] +// expect: [ 0 ] +// expect: [ 8 ] +// expect: [ 4 ] + //wrong dim try{ print A[[1,2],[2,3],[0,1]] } catch{ - "MtrxInvldNumIndx": print "MtrxInvldNumIndx" -// expect: MtrxInvldNumIndx + "LnAlgInvldIndx": print "LnAlgInvldIndx" +// expect: LnAlgInvldIndx } -// garbage in +// garbage in try{ print A[A] } catch{ - "MtrxInvldIndx": print "MtrxInvldIndx" -// expect: MtrxInvldIndx + "LnAlgInvldIndx": print "LnAlgInvldIndx" +// expect: LnAlgInvldIndx + } try{ print A[nil] } catch{ - "MtrxInvldIndx": print "MtrxInvldIndx" -// expect: MtrxInvldIndx + "LnAlgInvldIndx": print "LnAlgInvldIndx" +// expect: LnAlgInvldIndx } + try{ A[1,2,3,4,5] } catch{ - "MtrxInvldNumIndx": print "MtrxInvldNumIndx" -// expect: MtrxInvldNumIndx + "LnAlgInvldIndx": print "LnAlgInvldIndx" +// expect: LnAlgInvldIndx } diff --git a/test/sparse/concatenate.morpho b/test/sparse/concatenate.morpho index 9ea35027..063af577 100644 --- a/test/sparse/concatenate.morpho +++ b/test/sparse/concatenate.morpho @@ -24,4 +24,4 @@ print c // expect: [ 2 3 2 3 0 ] var c = Sparse([[a, b], [b, 0]]) -// expect error 'MtrxIncmptbl' +// expect error 'LnAlgMtrxIncmptbl' diff --git a/test/sparse/incompatible_add.morpho b/test/sparse/incompatible_add.morpho index 5e85bd39..8cbcb014 100644 --- a/test/sparse/incompatible_add.morpho +++ b/test/sparse/incompatible_add.morpho @@ -4,4 +4,4 @@ var a = Sparse([[0,0,1],[1,1,1],[1,2,-1],[2,1,-1],[2,2,1],[3,3,1]]) var b = Sparse([[0,1,1],[1,0,1]]) print a+b -// expect Error: 'MtrxIncmptbl' +// expect Error: 'LnAlgMtrxIncmptbl' diff --git a/test/sparse/incompatible_mul.morpho b/test/sparse/incompatible_mul.morpho index 50750282..c6b4437c 100644 --- a/test/sparse/incompatible_mul.morpho +++ b/test/sparse/incompatible_mul.morpho @@ -4,4 +4,4 @@ var a = Sparse([[0,0,1],[1,1,1],[1,2,-1],[2,1,-1],[2,2,1],[3,3,1]]) var b = Sparse([[0,1,1],[1,0,1]]) print a*b -// expect Error: 'MtrxIncmptbl' +// expect Error: 'LnAlgMtrxIncmptbl' diff --git a/test/sparse/set_row_indices.morpho b/test/sparse/set_row_indices.morpho index e26e15b1..6284a3af 100644 --- a/test/sparse/set_row_indices.morpho +++ b/test/sparse/set_row_indices.morpho @@ -20,4 +20,4 @@ print a // expect: [ 1 -1 0 0 ] a.setrowindices(3, [1,1]) -// expect error 'MtrxIncmptbl' +// expect error 'LnAlgMtrxIncmptbl' diff --git a/test/sparse/sparse_dense_mul.morpho b/test/sparse/sparse_dense_mul.morpho index 3c4c2abb..6e086085 100644 --- a/test/sparse/sparse_dense_mul.morpho +++ b/test/sparse/sparse_dense_mul.morpho @@ -13,4 +13,4 @@ print A*B.transpose() // expect: [ 8 16 ] print A*B -// expect error 'MtrxIncmptbl' \ No newline at end of file +// expect error 'LnAlgMtrxIncmptbl' \ No newline at end of file diff --git a/test/sparse/sparse_dense_mul_dimensions.morpho b/test/sparse/sparse_dense_mul_dimensions.morpho index 19471eae..af985fd8 100644 --- a/test/sparse/sparse_dense_mul_dimensions.morpho +++ b/test/sparse/sparse_dense_mul_dimensions.morpho @@ -11,9 +11,9 @@ for (i in 0...N) { var b = Matrix(List(1..N)) print A.dimensions() // expect: [ 6, 3 ] -print b.dimensions() // expect: [ 3, 1 ] +print b.dimensions() // expect: (3, 1) -print (A*b).dimensions() // expect: [ 6, 1 ] +print (A*b).dimensions() // expect: (6, 1) print A*b // expect: [ 1 ] diff --git a/test/tuple/tuple_sort.morpho b/test/tuple/tuple_sort.morpho new file mode 100644 index 00000000..9757d05e --- /dev/null +++ b/test/tuple/tuple_sort.morpho @@ -0,0 +1,10 @@ +// Tuple sort + +var a = ( 4, 3, 2, 7, 8, 1, 10, 9, 6, 5 ) +var b = a.sort() + +print b +// expect: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +print a==b +// expect: false diff --git a/test/tuple/tuple_sort_fn.morpho b/test/tuple/tuple_sort_fn.morpho new file mode 100644 index 00000000..555a35ff --- /dev/null +++ b/test/tuple/tuple_sort_fn.morpho @@ -0,0 +1,14 @@ +// Tuple sort with comparison function + +fn cmp(a, b) { + return -(a-b) +} + +var a = ( 4, 3, 2, 7, 8, 1, 10, 9, 6, 5 ) +var b = a.sort(cmp) + +print b +// expect: (10, 9, 8, 7, 6, 5, 4, 3, 2, 1) + +print a==b +// expect: false diff --git a/test/types/multiple_dispatch/namespace_for_new.morpho b/test/types/multiple_dispatch/namespace_for_new.morpho index 672f6e36..f32fe3c4 100644 --- a/test/types/multiple_dispatch/namespace_for_new.morpho +++ b/test/types/multiple_dispatch/namespace_for_new.morpho @@ -6,8 +6,7 @@ import "namespace.xmorpho" for f fn f(Matrix a) { print a.dimensions() - } f(Matrix(2,2)) -// expect: [ 2, 2 ] +// expect: (2, 2)