lintrans.matrices package
Module contents
This package supplies classes and functions to parse, evaluate, and wrap matrices.
Expression syntax
Note
All whitespace is ignored, except the whitespace between numbers in anonymous matrices.
Documentation on correct expression syntax is given here. This is a basic summary:
A single matrix is written as a capital letter like
A
,rot(x)
, wherex
is a real number, or as an anonymous matrixAn anonymous matrix is of the form
[a b; c d]
wherea
,b
,c
, andd
are real numbersWhen a matrix is given as
rot(x)
, this means that it represents an anticlockwise rotation byx
degreesMatrix A multiplied by matrix B is written as
AB
Matrix A plus matrix B is written as
A+B
Matrix A minus matrix B is written as
A-B
Matrix A to the power of
n
is written asA^n
orA^{n}
wheren
is a positive or negative integerThe transpose of matrix A is written as
A^T
orA^{T}
Any matrix may be multiplied by a real constant, like
3A
, or1.2B
Note
A^2 3B
will be interpreted as A^{23}B
, not A^{2}3B
. Braces are needed to clarify
this. However, A^2 + 3B
will be interpreted as expected.
Here is the technical BNF schema used by lintrans.matrices.parse.parse_matrix_expression()
and lintrans.matrices.parse.validate_matrix_expression()
:
expression ::= [ "-" ] matrices { ( "+" | "-" ) matrices };
matrices ::= matrix { matrix };
matrix ::= [ real_number ] matrix_identifier [ index ] | "(" expression ")" | anonymous_matrix;
matrix_identifier ::= "A" .. "Z" | "rot(" [ "-" ] real_number ")";
index ::= "^{" index_content "}" | "^" index_content;
index_content ::= [ "-" ] integer_not_zero | "T";
anonymous_matrix ::= "[" real_number " " real_number ";" real_number " " real_number "]";
digit_no_zero ::= "1" .. "9";
digit ::= "0" | digit_no_zero;
digits ::= digit | digits digit;
integer_not_zero ::= digit_no_zero [ digits ];
real_number ::= ( integer_not_zero [ "." digits ] | "0" "." digits );
Note
In the GUI, commas are also acceptable in an input expression, as long as everything between commas is valid on its own. These commas are used exclusively in the animation to animate an expression in steps.
Submodules
lintrans.matrices.parse module
This module provides functions to parse and validate matrix expressions.
- class lintrans.matrices.parse.ExpressionParser[source]
Bases:
object
A class to hold state during parsing.
Most of the methods in this class are class-internal and should not be used from outside.
This class should be used like this:
>>> ExpressionParser('3A^-1B').parse() [[('3', 'A', '-1'), ('', 'B', '')]] >>> ExpressionParser('4(M^TA^2)^-2').parse() [[('4', 'M^{T}A^{2}', '-2')]]
- __init__(expression: str)[source]
Create an instance of the parser with the given expression and initialise variables to use during parsing.
- _parse_anonymous_identifer() None [source]
Parse an anonymous matrix, including the square brackets.
- _parse_exponent() None [source]
Parse a matrix exponent from the expression and pointer.
The exponent must be an integer or
T
for transpose.- Raises
MatrixParseError – If we fail to parse this part of the token
- _parse_matrix() bool [source]
Parse a full matrix using
_parse_matrix_part()
.This method will parse an optional multiplier, an identifier, and an optional exponent. If we do this successfully, we return True. If we fail to parse a matrix (maybe we’ve reached the end of the current multiplication group and the next char is
+
), then we return False.- Returns bool
Success or failure
- _parse_matrix_part() bool [source]
Parse part of a matrix (multiplier, identifier, or exponent).
Which part of the matrix we parse is dependent on the current value of the pointer and the expression. This method will parse whichever part of matrix token that it can. If it can’t parse a part of a matrix, or it’s reached the next matrix, then we just return False. If we succeeded to parse a matrix part, then we return True.
- Returns bool
Success or failure
- Raises
MatrixParseError – If we fail to parse this part of the matrix
- _parse_multiplication_group() None [source]
Parse a group of matrices to be multiplied together.
This method just parses matrices until we get to a
+
.
- _parse_multiplier() None [source]
Parse a multiplier from the expression and pointer.
This method just parses a numerical multiplier, which can include zero or one
.
character and optionally a-
at the start.- Raises
MatrixParseError – If we fail to parse this part of the matrix
- _parse_rot_identifier() None [source]
Parse a
rot()
-style identifier from the expression and pointer.This method will just parse something like
rot(12.5)
. The angle number must be a real number.- Raises
MatrixParseError – If we fail to parse this part of the matrix
- _parse_sub_expression() None [source]
Parse a parenthesized sub-expression as the identifier.
This method will also validate the expression in the parentheses.
- Raises
MatrixParseError – If we fail to parse this part of the matrix
- parse() MatrixParseList [source]
Fully parse the instance’s matrix expression and return the
MatrixParseList
.This method uses all the private methods of this class to parse the expression in parts. All private methods mutate the instance variables.
- Returns
The parsed expression
- Return type
- exception lintrans.matrices.parse.MatrixParseError[source]
Bases:
Exception
A simple exception to be raised when an error is found when parsing.
- class lintrans.matrices.parse.MatrixToken[source]
Bases:
object
A simple dataclass to hold information about a matrix token being parsed.
- __eq__(other)
Return self==value.
- __hash__ = None
- __repr__()
Return repr(self).
- lintrans.matrices.parse.NAIVE_CHARACTER_CLASS = '[-+\\sA-Z0-9.rot()^{}\\[\\];]'
This is a RegEx character class that just holds all the valid characters for an expression.
See
validate_matrix_expression()
to actually validate matrix expressions.
- lintrans.matrices.parse.compile_naive_expression_pattern() Pattern[str] [source]
Compile the single RegEx pattern that will match a valid matrix expression.
- lintrans.matrices.parse.find_sub_expressions(expression: str) List[str] [source]
Find all the sub-expressions in the given expression.
This function only goes one level deep, so may return strings like
'A(BC)D'
.- Raises
MatrixParseError – If there are unbalanced parentheses
- lintrans.matrices.parse.get_matrix_identifiers(expression: str) Set[str] [source]
Return all the matrix identifiers used in the given expression.
This method works recursively with sub-expressions.
- lintrans.matrices.parse.parse_matrix_expression(expression: str) MatrixParseList [source]
Parse the matrix expression and return a
MatrixParseList
.- Example
>>> parse_matrix_expression('A') [[('', 'A', '')]] >>> parse_matrix_expression('-3M^2') [[('-3', 'M', '2')]] >>> parse_matrix_expression('1.2rot(12)^{3}2B^T') [[('1.2', 'rot(12)', '3'), ('2', 'B', 'T')]] >>> parse_matrix_expression('A^2 + 3B') [[('', 'A', '2')], [('3', 'B', '')]] >>> parse_matrix_expression('-3A^{-1}3B^T - 45M^2') [[('-3', 'A', '-1'), ('3', 'B', 'T')], [('-45', 'M', '2')]] >>> parse_matrix_expression('5.3A^{4} 2.6B^{-2} + 4.6D^T 8.9E^{-1}') [[('5.3', 'A', '4'), ('2.6', 'B', '-2')], [('4.6', 'D', 'T'), ('8.9', 'E', '-1')]] >>> parse_matrix_expression('2(A+B^TC)^2D') [[('2', 'A+B^{T}C', '2'), ('', 'D', '')]]
- Parameters
expression (str) – The expression to be parsed
- Returns
A list of parsed components
- Return type
- lintrans.matrices.parse.strip_whitespace(expression: str) str [source]
Strip the whitespace from the given expression, preserving whitespace in anonymous matrices.
Whitespace in anonymous matrices is preserved such that there is exactly one space in the middle of each pair of numbers, but no space after the semi-colon, like so:
[1 -2;3.4 5]
.
- lintrans.matrices.parse.validate_matrix_expression(expression: str) bool [source]
Validate the given matrix expression.
This function simply checks the expression against the BNF schema documented in Expression syntax. It is not aware of which matrices are actually defined in a wrapper. For an aware version of this function, use the
is_valid_expression()
method onMatrixWrapper
.- Parameters
expression (str) – The expression to be validated
- Returns bool
Whether the expression is valid according to the schema
lintrans.matrices.utility module
This module provides simple utility methods for matrix and vector manipulation.
- lintrans.matrices.utility.create_rotation_matrix(angle: float, *, degrees: bool = True) MatrixType [source]
Create a matrix representing a rotation (anticlockwise) by the given angle.
- Example
>>> create_rotation_matrix(30) array([[ 0.8660254, -0.5 ], [ 0.5 , 0.8660254]]) >>> create_rotation_matrix(45) array([[ 0.70710678, -0.70710678], [ 0.70710678, 0.70710678]]) >>> create_rotation_matrix(np.pi / 3, degrees=False) array([[ 0.5 , -0.8660254], [ 0.8660254, 0.5 ]])
- lintrans.matrices.utility.is_valid_float(string: str) bool [source]
Check if the string is a valid float (or anything that can be cast to a float, such as an int).
This function simply checks that
float(string)
doesn’t raise an error.Note
An empty string is not a valid float, so will return False.
- Parameters
string (str) – The string to check
- Returns bool
Whether the string is a valid float
- lintrans.matrices.utility.polar_coords(x: float, y: float, *, degrees: bool = False) Tuple[float, float] [source]
Return the polar coordinates of a given (x, y) Cartesian coordinate.
Note
We’re returning the angle in the range \([0, 2\pi)\)
- lintrans.matrices.utility.rect_coords(radius: float, angle: float, *, degrees: bool = False) Tuple[float, float] [source]
Return the rectilinear coordinates of a given polar coordinate.
- lintrans.matrices.utility.rotate_coord(x: float, y: float, angle: float, *, degrees: bool = False) Tuple[float, float] [source]
Rotate a rectilinear coordinate by the given angle.
lintrans.matrices.wrapper module
This module contains the main MatrixWrapper
class and a function to create a matrix from an angle.
- class lintrans.matrices.wrapper.MatrixWrapper[source]
Bases:
object
A wrapper class to hold all possible matrices and allow access to them.
Note
When defining a custom matrix, its name must be a capital letter and cannot be
I
.The contained matrices can be accessed and assigned to using square bracket notation.
- Example
>>> wrapper = MatrixWrapper() >>> wrapper['I'] array([[1., 0.], [0., 1.]]) >>> wrapper['M'] # Returns None >>> wrapper['M'] = np.array([[1, 2], [3, 4]]) >>> wrapper['M'] array([[1., 2.], [3., 4.]])
- __eq__(other: Any) bool [source]
Check for equality in wrappers by comparing dictionaries.
- Parameters
other (Any) – The object to compare this wrapper to
- __getitem__(name: str) Optional[MatrixType] [source]
Get the matrix with the given identifier.
If it is a simple name, it will just be fetched from the dictionary. If the identifier is
rot(x)
, with a given angle in degrees, then we return a new matrix representing a rotation by that angle. If the identifier is something like[1 2;3 4]
, then we will evaluate this matrix (we assume it will have whitespace exactly like the example; seelintrans.matrices.parse.strip_whitespace()
).Note
If the named matrix is defined as an expression, then this method will return its evaluation. If you want the expression itself, use
get_expression()
.
- __init__()[source]
Initialize a
MatrixWrapper
object with a dictionary of matrices which can be accessed.
- __repr__() str [source]
Return a nice string repr of the
MatrixWrapper
for debugging.
- __setitem__(name: str, new_matrix: Optional[Union[MatrixType, str]]) None [source]
Set the value of matrix
name
with the new_matrix.The new matrix may be a simple 2x2 NumPy array, or it could be a string, representing an expression in terms of other, previously defined matrices.
- Parameters
- Raises
NameError – If the name isn’t a legal matrix name
TypeError – If the matrix isn’t a valid 2x2 NumPy array or expression in terms of other defined matrices
ValueError – If you attempt to define a matrix in terms of itself
- evaluate_expression(expression: str) MatrixType [source]
Evaluate a given expression and return the matrix evaluation.
- Parameters
expression (str) – The expression to be parsed
- Returns MatrixType
The matrix result of the expression
- Raises
ValueError – If the expression is invalid
- get_defined_matrices() List[Tuple[str, Union[MatrixType, str]]] [source]
Return a list of tuples containing the name and value of all defined matrices in the wrapper.
- get_expression(name: str) Optional[str] [source]
If the named matrix is defined as an expression, return that expression, else return None.
- get_expression_dependencies(expression: str) Set[str] [source]
Return all the matrices that the given expression depends on.
This method just calls
get_matrix_dependencies()
on each matrix identifier in the expression. See that method for details.If an expression contains a matrix that has no dependencies, then the expression is not considered to depend on that matrix. But it is considered to depend on any matrix that has its own dependencies.
- get_matrix_dependencies(matrix_name: str) Set[str] [source]
Return all the matrices (as identifiers) that the given matrix (indirectly) depends on.
If A depends on nothing, B directly depends on A, and C directly depends on B, then we say C depends on B and A.
- is_valid_expression(expression: str) bool [source]
Check if the given expression is valid, using the context of the wrapper.
This method calls
lintrans.matrices.parse.validate_matrix_expression()
, but also ensures that all the matrices in the expression are defined in the wrapper.- Parameters
expression (str) – The expression to validate
- Returns bool
Whether the expression is valid in this wrapper
- Raises
LinAlgError – If a matrix is defined in terms of the inverse of a singular matrix