Python: Add support for @ infix operator matrix multiplication

This differential revision implements the code for T56276

Reviewers: campbellbarton

Reviewed By: campbellbarton

Differential Revision: https://developer.blender.org/D3587
This commit is contained in:
Andrew Hale 2018-08-10 14:53:38 +02:00
parent 693ecdf7d3
commit aa5a96430e
Notes: blender-bot 2023-02-14 19:27:40 +01:00
Referenced by issue blender/blender-addons#56276, Python: Add support for @ infix operator matrix multiplication
4 changed files with 529 additions and 95 deletions

View File

@ -2321,7 +2321,7 @@ static PyObject *Matrix_sub(PyObject *m1, PyObject *m2)
return Matrix_CreatePyObject(mat, mat1->num_col, mat1->num_row, Py_TYPE(mat1));
}
/*------------------------obj * obj------------------------------
* multiplication */
* element-wise multiplication */
static PyObject *matrix_mul_float(MatrixObject *mat, const float scalar)
{
float tmat[MATRIX_MAX_DIM * MATRIX_MAX_DIM];
@ -2332,6 +2332,114 @@ static PyObject *matrix_mul_float(MatrixObject *mat, const float scalar)
static PyObject *Matrix_mul(PyObject *m1, PyObject *m2)
{
float scalar;
MatrixObject *mat1 = NULL, *mat2 = NULL;
if (MatrixObject_Check(m1)) {
mat1 = (MatrixObject *)m1;
if (BaseMath_ReadCallback(mat1) == -1)
return NULL;
}
if (MatrixObject_Check(m2)) {
mat2 = (MatrixObject *)m2;
if (BaseMath_ReadCallback(mat2) == -1)
return NULL;
}
if (mat1 && mat2) {
#ifdef USE_MATHUTILS_ELEM_MUL
/* MATRIX * MATRIX */
float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM];
if ((mat1->num_row != mat2->num_row) || (mat1->num_col != mat2->num_col)) {
PyErr_SetString(PyExc_ValueError,
"matrix1 * matrix2: matrix1 number of rows/columns "
"and the matrix2 number of rows/columns must be the same");
return NULL;
}
mul_vn_vnvn(mat, mat1->matrix, mat2->matrix, mat1->num_col * mat1->num_row);
return Matrix_CreatePyObject(mat, mat2->num_col, mat1->num_row, Py_TYPE(mat1));
#endif
}
else if (mat2) {
/*FLOAT/INT * MATRIX */
if (((scalar = PyFloat_AsDouble(m1)) == -1.0f && PyErr_Occurred()) == 0) {
return matrix_mul_float(mat2, scalar);
}
}
else if (mat1) {
/* MATRIX * FLOAT/INT */
if (((scalar = PyFloat_AsDouble(m2)) == -1.0f && PyErr_Occurred()) == 0) {
return matrix_mul_float(mat1, scalar);
}
}
PyErr_Format(PyExc_TypeError,
"Element-wise multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(m1)->tp_name, Py_TYPE(m2)->tp_name);
return NULL;
}
/*------------------------obj *= obj------------------------------
* Inplace element-wise multiplication */
static PyObject *Matrix_imul(PyObject *m1, PyObject *m2)
{
float scalar;
MatrixObject *mat1 = NULL, *mat2 = NULL;
if (MatrixObject_Check(m1)) {
mat1 = (MatrixObject *)m1;
if (BaseMath_ReadCallback(mat1) == -1)
return NULL;
}
if (MatrixObject_Check(m2)) {
mat2 = (MatrixObject *)m2;
if (BaseMath_ReadCallback(mat2) == -1)
return NULL;
}
if (mat1 && mat2) {
#ifdef USE_MATHUTILS_ELEM_MUL
/* MATRIX *= MATRIX */
if ((mat1->num_row != mat2->num_row) || (mat1->num_col != mat2->num_col)) {
PyErr_SetString(PyExc_ValueError,
"matrix1 *= matrix2: matrix1 number of rows/columns "
"and the matrix2 number of rows/columns must be the same");
return NULL;
}
mul_vn_vn(mat1->matrix, mat2->matrix, mat1->num_col * mat1->num_row);
#else
PyErr_Format(PyExc_TypeError,
"Inplace element-wise multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(m1)->tp_name, Py_TYPE(m2)->tp_name);
return NULL;
#endif
}
else if (mat1 && (((scalar = PyFloat_AsDouble(m2)) == -1.0f && PyErr_Occurred()) == 0)) {
/* MATRIX *= FLOAT/INT */
mul_vn_fl(mat1->matrix, mat1->num_row * mat1->num_col, scalar);
}
else {
PyErr_Format(PyExc_TypeError,
"Inplace element-wise multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(m1)->tp_name, Py_TYPE(m2)->tp_name);
return NULL;
}
(void)BaseMath_WriteCallback(mat1);
Py_INCREF(m1);
return m1;
}
/*------------------------obj @ obj------------------------------
* matrix multiplication */
static PyObject *Matrix_matmul(PyObject *m1, PyObject *m2)
{
int vec_size;
MatrixObject *mat1 = NULL, *mat2 = NULL;
@ -2348,15 +2456,15 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2)
}
if (mat1 && mat2) {
/* MATRIX * MATRIX */
/* MATRIX @ MATRIX */
float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM];
int col, row, item;
if (mat1->num_col != mat2->num_row) {
PyErr_SetString(PyExc_ValueError,
"matrix1 * matrix2: matrix1 number of columns "
"and the matrix2 number of rows must be the same");
"matrix1 * matrix2: matrix1 number of columns "
"and the matrix2 number of rows must be the same");
return NULL;
}
@ -2372,14 +2480,8 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2)
return Matrix_CreatePyObject(mat, mat2->num_col, mat1->num_row, Py_TYPE(mat1));
}
else if (mat2) {
/*FLOAT/INT * MATRIX */
if (((scalar = PyFloat_AsDouble(m1)) == -1.0f && PyErr_Occurred()) == 0) {
return matrix_mul_float(mat2, scalar);
}
}
else if (mat1) {
/* MATRIX * VECTOR */
/* MATRIX @ VECTOR */
if (VectorObject_Check(m2)) {
VectorObject *vec2 = (VectorObject *)m2;
float tvec[MATRIX_MAX_DIM];
@ -2398,21 +2500,70 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2)
return Vector_CreatePyObject(tvec, vec_size, Py_TYPE(m2));
}
/*FLOAT/INT * MATRIX */
else if (((scalar = PyFloat_AsDouble(m2)) == -1.0f && PyErr_Occurred()) == 0) {
return matrix_mul_float(mat1, scalar);
}
}
else {
BLI_assert(!"internal error");
}
PyErr_Format(PyExc_TypeError,
"Matrix multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(m1)->tp_name, Py_TYPE(m2)->tp_name);
"Matrix multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(m1)->tp_name, Py_TYPE(m2)->tp_name);
return NULL;
}
/*------------------------obj @= obj------------------------------
* inplace matrix multiplication */
static PyObject *Matrix_imatmul(PyObject *m1, PyObject *m2)
{
MatrixObject *mat1 = NULL, *mat2 = NULL;
if (MatrixObject_Check(m1)) {
mat1 = (MatrixObject *)m1;
if (BaseMath_ReadCallback(mat1) == -1)
return NULL;
}
if (MatrixObject_Check(m2)) {
mat2 = (MatrixObject *)m2;
if (BaseMath_ReadCallback(mat2) == -1)
return NULL;
}
if (mat1 && mat2) {
/* MATRIX @= MATRIX */
float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM];
int col, row, item;
if (mat1->num_col != mat2->num_row) {
PyErr_SetString(PyExc_ValueError,
"matrix1 * matrix2: matrix1 number of columns "
"and the matrix2 number of rows must be the same");
return NULL;
}
for (col = 0; col < mat2->num_col; col++) {
for (row = 0; row < mat1->num_row; row++) {
double dot = 0.0f;
for (item = 0; item < mat1->num_col; item++) {
dot += (double)(MATRIX_ITEM(mat1, row, item) * MATRIX_ITEM(mat2, item, col));
}
/* store in new matrix as overwriting original at this point will cause
* subsequent iterations to use incorrect values */
mat[(col * mat1->num_row) + row] = (float)dot;
}
}
/* copy matrix back */
memcpy(mat1->matrix, mat, mat1->num_row * mat1->num_col);
}
else {
PyErr_Format(PyExc_TypeError,
"Inplace matrix multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(m1)->tp_name, Py_TYPE(m2)->tp_name);
return NULL;
}
(void)BaseMath_WriteCallback(mat1);
Py_INCREF(m1);
return m1;
}
/*-----------------PROTOCOL DECLARATIONS--------------------------*/
static PySequenceMethods Matrix_SeqMethods = {
@ -2527,7 +2678,7 @@ static PyNumberMethods Matrix_NumMethods = {
NULL, /*nb_float*/
NULL, /* nb_inplace_add */
NULL, /* nb_inplace_subtract */
NULL, /* nb_inplace_multiply */
(binaryfunc) Matrix_imul, /* nb_inplace_multiply */
NULL, /* nb_inplace_remainder */
NULL, /* nb_inplace_power */
NULL, /* nb_inplace_lshift */
@ -2540,6 +2691,8 @@ static PyNumberMethods Matrix_NumMethods = {
NULL, /* nb_inplace_floor_divide */
NULL, /* nb_inplace_true_divide */
NULL, /* nb_index */
(binaryfunc) Matrix_matmul, /* nb_matrix_multiply */
(binaryfunc) Matrix_imatmul, /* nb_inplace_matrix_multiply */
};
PyDoc_STRVAR(Matrix_translation_doc,

View File

@ -834,7 +834,7 @@ static PyObject *quat_mul_float(QuaternionObject *quat, const float scalar)
* multiplication */
static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2)
{
float quat[QUAT_SIZE], scalar;
float scalar;
QuaternionObject *quat1 = NULL, *quat2 = NULL;
if (QuaternionObject_Check(q1)) {
@ -848,9 +848,12 @@ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2)
return NULL;
}
if (quat1 && quat2) { /* QUAT * QUAT (cross product) */
mul_qt_qtqt(quat, quat1->quat, quat2->quat);
if (quat1 && quat2) { /* QUAT * QUAT (element-wise product) */
#ifdef USE_MATHUTILS_ELEM_MUL
float quat[QUAT_SIZE];
mul_vn_vnvn(quat, quat1->quat, quat2->quat, QUAT_SIZE);
return Quaternion_CreatePyObject(quat, Py_TYPE(q1));
#endif
}
/* the only case this can happen (for a supported type is "FLOAT * QUAT") */
else if (quat2) { /* FLOAT * QUAT */
@ -858,17 +861,96 @@ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2)
return quat_mul_float(quat2, scalar);
}
}
else if (quat1) { /* QUAT * FLOAT */
if ((((scalar = PyFloat_AsDouble(q2)) == -1.0f && PyErr_Occurred()) == 0)) {
return quat_mul_float(quat1, scalar);
}
}
PyErr_Format(PyExc_TypeError,
"Element-wise multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(q1)->tp_name, Py_TYPE(q2)->tp_name);
return NULL;
}
/*------------------------obj *= obj------------------------------
* inplace multiplication */
static PyObject *Quaternion_imul(PyObject *q1, PyObject *q2)
{
float scalar;
QuaternionObject *quat1 = NULL, *quat2 = NULL;
if (QuaternionObject_Check(q1)) {
quat1 = (QuaternionObject *)q1;
if (BaseMath_ReadCallback(quat1) == -1)
return NULL;
}
if (QuaternionObject_Check(q2)) {
quat2 = (QuaternionObject *)q2;
if (BaseMath_ReadCallback(quat2) == -1)
return NULL;
}
if (quat1 && quat2) { /* QUAT *= QUAT (inplace element-wise product) */
#ifdef USE_MATHUTILS_ELEM_MUL
mul_vn_vn(quat1->quat, quat2->quat, QUAT_SIZE);
#else
PyErr_Format(PyExc_TypeError,
"Inplace element-wise multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(q1)->tp_name, Py_TYPE(q2)->tp_name);
return NULL;
#endif
}
else if (quat1 && (((scalar = PyFloat_AsDouble(q2)) == -1.0f && PyErr_Occurred()) == 0)) {
/* QUAT *= FLOAT */
mul_qt_fl(quat1->quat, scalar);
}
else {
PyErr_Format(PyExc_TypeError,
"Element-wise multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(q1)->tp_name, Py_TYPE(q2)->tp_name);
return NULL;
}
(void)BaseMath_WriteCallback(quat1);
Py_INCREF(q1);
return q1;
}
/*------------------------obj @ obj------------------------------
* quaternion multiplication */
static PyObject *Quaternion_matmul(PyObject *q1, PyObject *q2)
{
float quat[QUAT_SIZE];
QuaternionObject *quat1 = NULL, *quat2 = NULL;
if (QuaternionObject_Check(q1)) {
quat1 = (QuaternionObject *)q1;
if (BaseMath_ReadCallback(quat1) == -1)
return NULL;
}
if (QuaternionObject_Check(q2)) {
quat2 = (QuaternionObject *)q2;
if (BaseMath_ReadCallback(quat2) == -1)
return NULL;
}
if (quat1 && quat2) { /* QUAT @ QUAT (cross product) */
mul_qt_qtqt(quat, quat1->quat, quat2->quat);
return Quaternion_CreatePyObject(quat, Py_TYPE(q1));
}
else if (quat1) {
/* QUAT * VEC */
/* QUAT @ VEC */
if (VectorObject_Check(q2)) {
VectorObject *vec2 = (VectorObject *)q2;
float tvec[3];
if (vec2->size != 3) {
PyErr_SetString(PyExc_ValueError,
"Vector multiplication: "
"only 3D vector rotations (with quats) "
"currently supported");
"Vector multiplication: "
"only 3D vector rotations (with quats) "
"currently supported");
return NULL;
}
if (BaseMath_ReadCallback(vec2) == -1) {
@ -880,21 +962,48 @@ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2)
return Vector_CreatePyObject(tvec, 3, Py_TYPE(vec2));
}
/* QUAT * FLOAT */
else if ((((scalar = PyFloat_AsDouble(q2)) == -1.0f && PyErr_Occurred()) == 0)) {
return quat_mul_float(quat1, scalar);
}
}
else {
BLI_assert(!"internal error");
}
PyErr_Format(PyExc_TypeError,
"Quaternion multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(q1)->tp_name, Py_TYPE(q2)->tp_name);
"Quaternion multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(q1)->tp_name, Py_TYPE(q2)->tp_name);
return NULL;
}
/*------------------------obj @= obj------------------------------
* inplace quaternion multiplication */
static PyObject *Quaternion_imatmul(PyObject *q1, PyObject *q2)
{
float quat[QUAT_SIZE];
QuaternionObject *quat1 = NULL, *quat2 = NULL;
if (QuaternionObject_Check(q1)) {
quat1 = (QuaternionObject *)q1;
if (BaseMath_ReadCallback(quat1) == -1)
return NULL;
}
if (QuaternionObject_Check(q2)) {
quat2 = (QuaternionObject *)q2;
if (BaseMath_ReadCallback(quat2) == -1)
return NULL;
}
if (quat1 && quat2) { /* QUAT @ QUAT (cross product) */
mul_qt_qtqt(quat, quat1->quat, quat2->quat);
copy_qt_qt(quat1->quat, quat);
}
else {
PyErr_Format(PyExc_TypeError,
"Inplace quaternion multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(q1)->tp_name, Py_TYPE(q2)->tp_name);
return NULL;
}
(void)BaseMath_WriteCallback(quat1);
Py_INCREF(q1);
return q1;
}
/* -obj
* returns the negative of this object*/
@ -952,7 +1061,7 @@ static PyNumberMethods Quaternion_NumMethods = {
NULL, /*nb_float*/
NULL, /* nb_inplace_add */
NULL, /* nb_inplace_subtract */
NULL, /* nb_inplace_multiply */
(binaryfunc) Quaternion_imul, /* nb_inplace_multiply */
NULL, /* nb_inplace_remainder */
NULL, /* nb_inplace_power */
NULL, /* nb_inplace_lshift */
@ -965,6 +1074,8 @@ static PyNumberMethods Quaternion_NumMethods = {
NULL, /* nb_inplace_floor_divide */
NULL, /* nb_inplace_true_divide */
NULL, /* nb_index */
(binaryfunc) Quaternion_matmul, /* nb_matrix_multiply */
(binaryfunc) Quaternion_imatmul, /* nb_inplace_matrix_multiply */
};
PyDoc_STRVAR(Quaternion_axis_doc,

View File

@ -1706,11 +1706,132 @@ static PyObject *vector_mul_float(VectorObject *vec, const float scalar)
mul_vn_vn_fl(tvec, vec->vec, vec->size, scalar);
return Vector_CreatePyObject_alloc(tvec, vec->size, Py_TYPE(vec));
}
#ifdef USE_MATHUTILS_ELEM_MUL
static PyObject *vector_mul_vec(VectorObject *vec1, VectorObject *vec2)
{
float *tvec = PyMem_Malloc(vec1->size * sizeof(float));
if (tvec == NULL) {
PyErr_SetString(PyExc_MemoryError,
"vec * vec: "
"problem allocating pointer space");
return NULL;
}
mul_vn_vnvn(tvec, vec1->vec, vec2->vec, vec1->size);
return Vector_CreatePyObject_alloc(tvec, vec1->size, Py_TYPE(vec1));
}
#endif
static PyObject *Vector_mul(PyObject *v1, PyObject *v2)
{
VectorObject *vec1 = NULL, *vec2 = NULL;
float scalar;
if (VectorObject_Check(v1)) {
vec1 = (VectorObject *)v1;
if (BaseMath_ReadCallback(vec1) == -1)
return NULL;
}
if (VectorObject_Check(v2)) {
vec2 = (VectorObject *)v2;
if (BaseMath_ReadCallback(vec2) == -1)
return NULL;
}
/* Intentionally don't support (Quaternion) here, uses reverse order instead. */
/* make sure v1 is always the vector */
if (vec1 && vec2) {
#ifdef USE_MATHUTILS_ELEM_MUL
if (vec1->size != vec2->size) {
PyErr_SetString(PyExc_ValueError,
"Vector multiplication: "
"vectors must have the same dimensions for this operation");
return NULL;
}
/* element-wise product */
return vector_mul_vec(vec1, vec2);
#endif
}
else if (vec1) {
if (((scalar = PyFloat_AsDouble(v2)) == -1.0f && PyErr_Occurred()) == 0) { /* VEC * FLOAT */
return vector_mul_float(vec1, scalar);
}
}
else if (vec2) {
if (((scalar = PyFloat_AsDouble(v1)) == -1.0f && PyErr_Occurred()) == 0) { /* FLOAT * VEC */
return vector_mul_float(vec2, scalar);
}
}
PyErr_Format(PyExc_TypeError,
"Element-wise multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
return NULL;
}
/* multiplication in-place: obj *= obj */
static PyObject *Vector_imul(PyObject *v1, PyObject *v2)
{
VectorObject *vec1 = NULL, *vec2 = NULL;
float scalar;
if (VectorObject_Check(v1)) {
vec1 = (VectorObject *)v1;
if (BaseMath_ReadCallback(vec1) == -1)
return NULL;
}
if (VectorObject_Check(v2)) {
vec2 = (VectorObject *)v2;
if (BaseMath_ReadCallback(vec2) == -1)
return NULL;
}
if (BaseMath_ReadCallback_ForWrite(vec1) == -1)
return NULL;
/* Intentionally don't support (Quaternion, Matrix) here, uses reverse order instead. */
if (vec1 && vec2) {
#ifdef USE_MATHUTILS_ELEM_MUL
if (vec1->size != vec2->size) {
PyErr_SetString(PyExc_ValueError,
"Vector multiplication: "
"vectors must have the same dimensions for this operation");
return NULL;
}
/* element-wise product inplace */
mul_vn_vn(vec1->vec, vec2->vec, vec1->size);
#else
PyErr_Format(PyExc_TypeError,
"Inplace element-wise multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
return NULL;
#endif
}
else if (vec1 && (((scalar = PyFloat_AsDouble(v2)) == -1.0f && PyErr_Occurred()) == 0)) { /* VEC *= FLOAT */
mul_vn_fl(vec1->vec, vec1->size, scalar);
}
else {
PyErr_Format(PyExc_TypeError,
"Inplace element-wise multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
return NULL;
}
(void)BaseMath_WriteCallback(vec1);
Py_INCREF(v1);
return v1;
}
static PyObject *Vector_matmul(PyObject *v1, PyObject *v2)
{
VectorObject *vec1 = NULL, *vec2 = NULL;
int vec_size;
if (VectorObject_Check(v1)) {
@ -1731,8 +1852,8 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2)
if (vec1 && vec2) {
if (vec1->size != vec2->size) {
PyErr_SetString(PyExc_ValueError,
"Vector multiplication: "
"vectors must have the same dimensions for this operation");
"Vector multiplication: "
"vectors must have the same dimensions for this operation");
return NULL;
}
@ -1741,7 +1862,7 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2)
}
else if (vec1) {
if (MatrixObject_Check(v2)) {
/* VEC * MATRIX */
/* VEC @ MATRIX */
float tvec[MAX_DIMENSIONS];
if (BaseMath_ReadCallback((MatrixObject *)v2) == -1)
@ -1759,53 +1880,22 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2)
return Vector_CreatePyObject(tvec, vec_size, Py_TYPE(vec1));
}
else if (((scalar = PyFloat_AsDouble(v2)) == -1.0f && PyErr_Occurred()) == 0) { /* VEC * FLOAT */
return vector_mul_float(vec1, scalar);
}
}
else if (vec2) {
if (((scalar = PyFloat_AsDouble(v1)) == -1.0f && PyErr_Occurred()) == 0) { /* FLOAT * VEC */
return vector_mul_float(vec2, scalar);
}
}
else {
BLI_assert(!"internal error");
}
PyErr_Format(PyExc_TypeError,
"Vector multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
"Vector multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
return NULL;
}
/* multiplication in-place: obj *= obj */
static PyObject *Vector_imul(PyObject *v1, PyObject *v2)
static PyObject *Vector_imatmul(PyObject *v1, PyObject *v2)
{
VectorObject *vec = (VectorObject *)v1;
float scalar;
if (BaseMath_ReadCallback_ForWrite(vec) == -1)
return NULL;
/* Intentionally don't support (Quaternion, Matrix) here, uses reverse order instead. */
/* only support 'vec *= float'
* vec*=vec result is a float so that wont work */
if (((scalar = PyFloat_AsDouble(v2)) == -1.0f && PyErr_Occurred()) == 0) { /* VEC *= FLOAT */
mul_vn_fl(vec->vec, vec->size, scalar);
}
else {
PyErr_Format(PyExc_TypeError,
"Vector multiplication: (%s *= %s) "
"invalid type for this operation",
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
return NULL;
}
(void)BaseMath_WriteCallback(vec);
Py_INCREF(v1);
return v1;
PyErr_Format(PyExc_TypeError,
"Inplace vector multiplication: "
"not supported between '%.200s' and '%.200s' types",
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
return NULL;
}
/* divid: obj / obj */
@ -2119,6 +2209,8 @@ static PyNumberMethods Vector_NumMethods = {
NULL, /* nb_inplace_floor_divide */
Vector_idiv, /* nb_inplace_true_divide */
NULL, /* nb_index */
(binaryfunc) Vector_matmul, /* nb_matrix_multiply */
(binaryfunc) Vector_imatmul, /* nb_inplace_matrix_multiply */
};
/*------------------PY_OBECT DEFINITION--------------------------*/

View File

@ -104,7 +104,7 @@ class MatrixTesting(unittest.TestCase):
self.assertEqual(mat[1][3], 2)
self.assertEqual(mat[2][3], 3)
def test_non_square_mult(self):
def test_matrix_non_square_matmul(self):
mat1 = Matrix(((1, 2, 3),
(4, 5, 6)))
mat2 = Matrix(((1, 2),
@ -117,10 +117,10 @@ class MatrixTesting(unittest.TestCase):
(19, 26, 33),
(29, 40, 51)))
self.assertEqual(mat1 * mat2, prod_mat1)
self.assertEqual(mat2 * mat1, prod_mat2)
self.assertEqual(mat1 @ mat2, prod_mat1)
self.assertEqual(mat2 @ mat1, prod_mat2)
def test_mat4x4_vec3D_mult(self):
def test_mat4x4_vec3D_matmul(self):
mat = Matrix(((1, 0, 2, 0),
(0, 6, 0, 0),
(0, 0, 1, 1),
@ -131,23 +131,58 @@ class MatrixTesting(unittest.TestCase):
prod_mat_vec = Vector((7, 12, 4))
prod_vec_mat = Vector((1, 12, 5))
self.assertEqual(mat * vec, prod_mat_vec)
self.assertEqual(vec * mat, prod_vec_mat)
self.assertEqual(mat @ vec, prod_mat_vec)
self.assertEqual(vec @ mat, prod_vec_mat)
def test_mat_vec_mult(self):
def test_mat_vec_matmul(self):
mat1 = Matrix()
vec = Vector((1, 2))
self.assertRaises(ValueError, mat1.__mul__, vec)
self.assertRaises(ValueError, vec.__mul__, mat1)
self.assertRaises(ValueError, mat1.__matmul__, vec)
self.assertRaises(ValueError, vec.__matmul__, mat1)
mat2 = Matrix(((1, 2),
(-2, 3)))
prod = Vector((5, 4))
self.assertEqual(mat2 * vec, prod)
self.assertEqual(mat2 @ vec, prod)
def test_matrix_square_matmul(self):
mat1 = Matrix(((1, 0),
(1, 2)))
mat2 = Matrix(((1, 2),
(-2, 3)))
prod1 = Matrix(((1, 2),
(-3, 8)))
prod2 = Matrix(((3, 4),
(1, 6)))
self.assertEqual(mat1 @ mat2, prod1)
self.assertEqual(mat2 @ mat1, prod2)
"""
# tests for element-wise multiplication
def test_matrix_mul(self):
mat1 = Matrix(((1, 0),
(1, 2)))
mat2 = Matrix(((1, 2),
(-2, 3)))
mat3 = Matrix(((1, 0, 2, 0),
(0, 6, 0, 0),
(0, 0, 1, 1),
(0, 0, 0, 1)))
prod = Matrix(((1, 0),
(-2, 6)))
self.assertEqual(mat1 * mat2, prod)
self.assertEqual(mat2 * mat1, prod)
self.assertRaises(ValueError, mat1.__mul__, mat3)
"""
def test_matrix_inverse(self):
mat = Matrix(((1, 4, 0, -1),
@ -185,7 +220,7 @@ class MatrixTesting(unittest.TestCase):
self.assertEqual(mat.inverted_safe(), inv_mat_safe)
def test_matrix_mult(self):
def test_matrix_matmult(self):
mat = Matrix(((1, 4, 0, -1),
(2, -1, 2, -2),
(0, 3, 8, 3),
@ -196,7 +231,7 @@ class MatrixTesting(unittest.TestCase):
(0, 48, 73, 18),
(16, -14, 26, -13)))
self.assertEqual(mat * mat, prod_mat)
self.assertEqual(mat @ mat, prod_mat)
class VectorTesting(unittest.TestCase):
@ -209,6 +244,49 @@ class VectorTesting(unittest.TestCase):
if v.length_squared != 0.0:
self.assertAlmostEqual(v.angle(v.orthogonal()), angle_90d)
def test_vector_matmul(self):
# produces dot product for vectors
vec1 = Vector((1, 3, 5))
vec2 = Vector((1, 2))
self.assertRaises(ValueError, vec1.__matmul__, vec2)
self.assertEqual(vec1 @ vec1, 35)
self.assertEqual(vec2 @ vec2, 5)
def test_vector_imatmul(self):
vec = Vector((1, 3, 5))
with self.assertRaises(TypeError):
vec @= vec
"""
# tests for element-wise multiplication
def test_vector_mul(self):
# element-wise multiplication
vec1 = Vector((1, 3, 5))
vec2 = Vector((1, 2))
prod1 = Vector((1, 9, 25))
prod2 = Vector((2, 6, 10))
self.assertRaises(ValueError, vec1.__mul__, vec2)
self.assertEqual(vec1 * vec1, prod1)
self.assertEqual(2 * vec1, prod2)
def test_vector_imul(self):
# inplace element-wise multiplication
vec = Vector((1, 3, 5))
prod1 = Vector((1, 9, 25))
prod2 = Vector((2, 18, 50))
vec *= vec
self.assertEqual(vec, prod1)
vec *= 2
self.assertEqual(vec, prod2)
"""
class QuaternionTesting(unittest.TestCase):