Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complex py wrap #452

Merged
merged 1 commit into from
Jan 6, 2025
Merged

Complex py wrap #452

merged 1 commit into from
Jan 6, 2025

Conversation

j8xixo12
Copy link
Collaborator

@j8xixo12 j8xixo12 commented Jan 2, 2025

In this I have added modmesh complex implementation python wrapper, and containing python unit test code.
And it is in preparation for further fft GUI related development.

Comment on lines 32 to 103
template <typename T, typename = std::enable_if_t<std::is_arithmetic<T>::value>>
struct ComplexImpl
{
T real_v;
T imag_v;

explicit ComplexImpl()
: ComplexImpl(0.0, 0.0)
{
}

explicit ComplexImpl(T r, T i)
: real_v(r)
, imag_v(i)
{
}

explicit ComplexImpl(const ComplexImpl & other)
: real_v(other.real_v)
, imag_v(other.imag_v)
{
}

ComplexImpl operator+(const ComplexImpl & other) const
{
return ComplexImpl(real_v + other.real_v, imag_v + other.imag_v);
}

ComplexImpl operator-(const ComplexImpl & other) const
{
return ComplexImpl(real_v - other.real_v, imag_v - other.imag_v);
}

ComplexImpl operator*(const ComplexImpl & other) const
{
return ComplexImpl(real_v * other.real_v - imag_v * other.imag_v, real_v * other.imag_v + imag_v * other.real_v);
}

ComplexImpl operator/(const T & other) const
{
return ComplexImpl(real_v / other, imag_v / other);
}

ComplexImpl & operator=(const ComplexImpl & other)
{
if (this != &other) // Check for self-assignment
{
real_v = other.real_v;
imag_v = other.imag_v;
}
return *this;
}

ComplexImpl & operator+=(const ComplexImpl & other)
{
real_v += other.real_v;
imag_v += other.imag_v;
return *this;
}

ComplexImpl & operator-=(const ComplexImpl & other)
{
real_v -= other.real_v;
imag_v -= other.imag_v;
return *this;
}

T real() const { return real_v; }
T imag() const { return imag_v; }
T norm() const { return real_v * real_v + imag_v * imag_v; }
}; /* end class ComplexImpl */

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move ComplexImpl into mathtype folder for creating python wrapper.

Comment on lines 37 to 107
template <typename T, typename = std::enable_if_t<std::is_arithmetic<T>::value>>
struct ComplexImpl
{
T real_v;
T imag_v;

explicit ComplexImpl()
: ComplexImpl(0.0, 0.0)
{
}

explicit ComplexImpl(T r, T i)
: real_v(r)
, imag_v(i)
{
}

explicit ComplexImpl(const ComplexImpl & other)
: real_v(other.real_v)
, imag_v(other.imag_v)
{
}

ComplexImpl operator+(const ComplexImpl & other) const
{
return ComplexImpl(real_v + other.real_v, imag_v + other.imag_v);
}

ComplexImpl operator-(const ComplexImpl & other) const
{
return ComplexImpl(real_v - other.real_v, imag_v - other.imag_v);
}

ComplexImpl operator*(const ComplexImpl & other) const
{
return ComplexImpl(real_v * other.real_v - imag_v * other.imag_v, real_v * other.imag_v + imag_v * other.real_v);
}

ComplexImpl operator/(const T & other) const
{
return ComplexImpl(real_v / other, imag_v / other);
}

ComplexImpl & operator=(const ComplexImpl & other)
{
if (this != &other) // Check for self-assignment
{
real_v = other.real_v;
imag_v = other.imag_v;
}
return *this;
}

ComplexImpl & operator+=(const ComplexImpl & other)
{
real_v += other.real_v;
imag_v += other.imag_v;
return *this;
}

ComplexImpl & operator-=(const ComplexImpl & other)
{
real_v -= other.real_v;
imag_v -= other.imag_v;
return *this;
}

T real() const { return real_v; }
T imag() const { return imag_v; }
T norm() const { return real_v * real_v + imag_v * imag_v; }
}; /* end class ComplexImpl */
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meanwhile, I'm trying to integrate with SimpleArray and create an array type Python wrapper and make it compatible with the NumPy buffer protocol, but I got a static assert

return pybind11::detail::npy_format_descriptor<T>::dtype().is(arr.dtype());

Which is that ComplexImpl<T> is not a POD type. Currently, I don't have a firm plan to move forward.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ending mark should be /* end structure ComplexImpl */.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ComplexImpl<T> looks like POD. What does it mean to say t is not a POD type?

Comment on lines 78 to 80
.def("__complex__",
[](wrapped_type const & self)
{ return std::complex<T>(self.real(), self.imag()); });
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making it compatible with python built-in complex type and cmath function.

@j8xixo12
Copy link
Collaborator Author

j8xixo12 commented Jan 2, 2025

@yungyuc This PR is ready for review!

Copy link
Member

@yungyuc yungyuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good move. I would like to have tightened structure of the code:

  • cpp/modmesh/math.hpp: Use cpp/modmesh/math/ to conform to the existing name cpp/modmesh/math.hpp. Do not create another directory name (mathtype).
  • test_mathtype.py: Random number is not necessary. Don't use it.


import modmesh as mm

from random import random
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is in stdlib and should be grouped with import unittest at L27.

class ComplexTC(unittest.TestCase):

def setUp(self):
self.real1_32 = np.float32(random())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please avoid random number in unit tests unless there is not another way. Using random number introduces a hidden dependency. If random number goes wrong, the tests here will issue false alarm. The false alarm is noise that burdens maintenance.

The random number is not printed in code and maintainers need to print it themselves when troubleshooting. This is another burden.


In this case, there are ways other than generating random number here, so random number should not be used.

You can randomly generate fixture and print the number here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should move cpp/modmesh/math.hpp to cpp/modmesh/math/math.hpp. The directory should be named as cpp/modmesh/math/, not cpp/modmesh/mathtype/.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After changing the new directory to be cpp/modmesh/math/, this should be renamed as test_math.py.

modmesh/core.py Outdated
@@ -97,6 +97,8 @@
'WorldFp32',
'WorldFp64',
'testhelper',
'ComplexFloat32',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two lines should be placed before SimpleArray because they are more fundamental.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be replaced by moving cpp/modmesh/math.hpp here.

Comment on lines 37 to 107
template <typename T, typename = std::enable_if_t<std::is_arithmetic<T>::value>>
struct ComplexImpl
{
T real_v;
T imag_v;

explicit ComplexImpl()
: ComplexImpl(0.0, 0.0)
{
}

explicit ComplexImpl(T r, T i)
: real_v(r)
, imag_v(i)
{
}

explicit ComplexImpl(const ComplexImpl & other)
: real_v(other.real_v)
, imag_v(other.imag_v)
{
}

ComplexImpl operator+(const ComplexImpl & other) const
{
return ComplexImpl(real_v + other.real_v, imag_v + other.imag_v);
}

ComplexImpl operator-(const ComplexImpl & other) const
{
return ComplexImpl(real_v - other.real_v, imag_v - other.imag_v);
}

ComplexImpl operator*(const ComplexImpl & other) const
{
return ComplexImpl(real_v * other.real_v - imag_v * other.imag_v, real_v * other.imag_v + imag_v * other.real_v);
}

ComplexImpl operator/(const T & other) const
{
return ComplexImpl(real_v / other, imag_v / other);
}

ComplexImpl & operator=(const ComplexImpl & other)
{
if (this != &other) // Check for self-assignment
{
real_v = other.real_v;
imag_v = other.imag_v;
}
return *this;
}

ComplexImpl & operator+=(const ComplexImpl & other)
{
real_v += other.real_v;
imag_v += other.imag_v;
return *this;
}

ComplexImpl & operator-=(const ComplexImpl & other)
{
real_v -= other.real_v;
imag_v -= other.imag_v;
return *this;
}

T real() const { return real_v; }
T imag() const { return imag_v; }
T norm() const { return real_v * real_v + imag_v * imag_v; }
}; /* end class ComplexImpl */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ending mark should be /* end structure ComplexImpl */.

Comment on lines 37 to 107
template <typename T, typename = std::enable_if_t<std::is_arithmetic<T>::value>>
struct ComplexImpl
{
T real_v;
T imag_v;

explicit ComplexImpl()
: ComplexImpl(0.0, 0.0)
{
}

explicit ComplexImpl(T r, T i)
: real_v(r)
, imag_v(i)
{
}

explicit ComplexImpl(const ComplexImpl & other)
: real_v(other.real_v)
, imag_v(other.imag_v)
{
}

ComplexImpl operator+(const ComplexImpl & other) const
{
return ComplexImpl(real_v + other.real_v, imag_v + other.imag_v);
}

ComplexImpl operator-(const ComplexImpl & other) const
{
return ComplexImpl(real_v - other.real_v, imag_v - other.imag_v);
}

ComplexImpl operator*(const ComplexImpl & other) const
{
return ComplexImpl(real_v * other.real_v - imag_v * other.imag_v, real_v * other.imag_v + imag_v * other.real_v);
}

ComplexImpl operator/(const T & other) const
{
return ComplexImpl(real_v / other, imag_v / other);
}

ComplexImpl & operator=(const ComplexImpl & other)
{
if (this != &other) // Check for self-assignment
{
real_v = other.real_v;
imag_v = other.imag_v;
}
return *this;
}

ComplexImpl & operator+=(const ComplexImpl & other)
{
real_v += other.real_v;
imag_v += other.imag_v;
return *this;
}

ComplexImpl & operator-=(const ComplexImpl & other)
{
real_v -= other.real_v;
imag_v -= other.imag_v;
return *this;
}

T real() const { return real_v; }
T imag() const { return imag_v; }
T norm() const { return real_v * real_v + imag_v * imag_v; }
}; /* end class ComplexImpl */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ComplexImpl<T> looks like POD. What does it mean to say t is not a POD type?

@j8xixo12
Copy link
Collaborator Author

j8xixo12 commented Jan 4, 2025

@yungyuc This PR is ready for the next review.

I have made the following change:

  • Rename mathtype folder to math and move cpp/modmesh/math.hpp to cpp/modmesh/math
  • Rename test_mathtype.py to test_math.py
  • Using fix number instead of random number in test_math.py
  • Moving ComplexFloat32 and ComplexFloat64 to before SimpleArray
  • Fix typo in cpp/modmesh/math/Complex.hpp struct end mark

For the array type of complex number, I plan to use another PR.

@yungyuc yungyuc added array Multi-dimensional array implementation enhancement New feature or request labels Jan 4, 2025
Copy link
Member

@yungyuc yungyuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the array type of complex number, I plan to use another PR.

Sounds like a plan. Still having issues to be worked out:

@@ -30,6 +30,7 @@
#include <modmesh/spacetime/pymod/spacetime_pymod.hpp>
#include <modmesh/toggle/pymod/toggle_pymod.hpp>
#include <modmesh/universe/pymod/universe_pymod.hpp>
#include <modmesh/math/pymod/math_pymod.hpp>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a blank line. (This issue appears at multiple locations. Please fix them all.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also found that some include order do not follow the LLVM include style, for example

#include <iostream>

should I fix them ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for spotting the inclusion order. It should not be done with the enhancement PR.

We are not strictly following inclusion order. It's not important atm, but we should optimize the inclusion at some point. If anyone cares about it, it can be done in a standalone PR.

def setUp(self):
self.real1_32 = np.float32(1.0)
self.imag1_32 = np.float32(1.0)
self.real2_32 = np.float32(1.0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

real/imag 1/2 are both 1+i. Please use use different value.

Copy link
Collaborator Author

@j8xixo12 j8xixo12 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yungyuc This PR is ready for the next review.

I have squashed commits, fixed missing blank lines after inclusion and using different number for test fixtures.

Comment on lines +37 to +45
self.real1_32 = np.float32(0.7)
self.imag1_32 = np.float32(1.6)
self.real2_32 = np.float32(2.5)
self.imag2_32 = np.float32(3.4)

self.real1_64 = np.float64(4.3)
self.imag1_64 = np.float64(5.2)
self.real2_64 = np.float64(6.1)
self.imag2_64 = np.float64(7.0)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using different number for fixtures.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good.

Copy link
Member

@yungyuc yungyuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Do not change blank lines in unrelated files.

Please revert all unnecessary style changes and squash.

@@ -29,6 +29,7 @@
#include <modmesh/buffer/pymod/buffer_pymod.hpp> // Must be the first include.

#include <modmesh/buffer/pymod/array_common.hpp>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not change formatting in unrelated files. Adding/removing blank lines is refactoring. Refactoring change should not be included in the same PR as feature development.

Comment on lines +37 to +45
self.real1_32 = np.float32(0.7)
self.imag1_32 = np.float32(1.6)
self.real2_32 = np.float32(2.5)
self.imag2_32 = np.float32(3.4)

self.real1_64 = np.float64(4.3)
self.imag1_64 = np.float64(5.2)
self.real2_64 = np.float64(6.1)
self.imag2_64 = np.float64(7.0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good.

@tigercosmos
Copy link
Collaborator

Do you have any plan to support complex array?

@yungyuc
Copy link
Member

yungyuc commented Jan 5, 2025

Do you have any plan to support complex array?

It has been discussed and the conclusion is to handle it in another PR.

[...]
For the array type of complex number, I plan to use another PR.

@j8xixo12
Copy link
Collaborator Author

j8xixo12 commented Jan 5, 2025

@yungyuc This PR is ready for the next review, I have reverted all unnecessary style changes and only applied style change within related files.

Copy link
Member

@yungyuc yungyuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It now looks good. Thanks for the work.

@@ -96,7 +98,7 @@
'Bezier3dFp64',
'WorldFp32',
'WorldFp64',
'testhelper',
'testhelper'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a comma after each line in a Python list. Please be careful about it next time.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I will be careful about this next time.

@yungyuc yungyuc merged commit de6f173 into solvcon:master Jan 6, 2025
12 checks passed
@yungyuc yungyuc linked an issue Jan 6, 2025 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
array Multi-dimensional array implementation enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Prototype for Fourier transform
3 participants