-
Notifications
You must be signed in to change notification settings - Fork 46
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
add complex array support #468
base: master
Are you sure you want to change the base?
Conversation
This PR is still under development, I have problem about
@yungyuc @tigercosmos Do you have any idea? |
@@ -78,7 +78,7 @@ endif() | |||
|
|||
include(Flake8) | |||
|
|||
set(CMAKE_CXX_STANDARD 17) | |||
set(CMAKE_CXX_STANDARD 20) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because I have used designate initialiser, this feature is supported by c++20, therefore change it to c++ standard 20.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason that you have to use designated initializer? Please let us know what it is.
If there is not, then just don't use designated initializer. I have no problem using it, but it alone is not a good reason to upgrade to C++20.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just want to mark the initial value for each structure member during structure initialization, but I did not consider what side effects upgrading to C++20 may bring.
if constexpr (is_complex_v<value_type>) | ||
{ | ||
initial = value_type(); | ||
} | ||
else | ||
{ | ||
initial = 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the element type is complex
, different initialization should be performed here.
constexpr bool valid_conversion = (!is_complex_v<T> && !is_complex_v<D>) || (is_complex_v<T> && is_complex_v<D> && std::is_same_v<T, D>); | ||
|
||
if constexpr (valid_conversion) | ||
{ | ||
arr_out.at(offset_out) = static_cast<out_type>(*ptr_in); | ||
} | ||
else | ||
{ | ||
throw std::runtime_error("Cannot convert between complex and non-complex types"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, only support float
complex to float
complex and double
complex to double
complex conversion.
template <typename T> | ||
bool operator<(const Complex<T> & lhs, const Complex<T> & rhs) | ||
{ | ||
return lhs.norm() < rhs.norm(); | ||
} | ||
|
||
template <typename T> | ||
bool operator>(const Complex<T> & lhs, const Complex<T> & rhs) | ||
{ | ||
return lhs.norm() > rhs.norm(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These comparison operator would be used in SimpleArray::min()
, SimpleArray::max()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is worth being a code comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will leave a code comment for it.
.def("dtype", | ||
[]() | ||
{ return py::dtype::of<wrapped_type>(); }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add dtype
getter is convenient for user to assign numpy dtype
while initial numpy arrary.
@@ -65,7 +66,9 @@ static std::unordered_map<std::string, DataType, DataTypeHasher> string_data_typ | |||
{"uint32", DataType::Uint32}, | |||
{"uint64", DataType::Uint64}, | |||
{"float32", DataType::Float32}, | |||
{"float64", DataType::Float64}}; | |||
{"float64", DataType::Float64}, | |||
{"ComplexFloat32", DataType::ComplexFloat32}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just call it "complex32"
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea to drop Float
from the complex type name, but we should use the correct bit count. For a complex number using two 32-bit float, it should be called complex64
. A complex using two 64-bit float, it should be called complex128
.
It follows numpy convention: https://numpy.org/doc/stable/user/basics.types.html#relationship-between-numpy-data-types-and-c-data-types
@@ -14,9 +14,9 @@ class ParsevalTest : public ::testing::Test | |||
const size_t VN = 1024; | |||
|
|||
modmesh::SimpleArray<modmesh::Complex<T>> signal{ | |||
modmesh::small_vector<size_t>{VN}, modmesh::Complex<T>(0.0, 0.0)}; | |||
modmesh::small_vector<size_t>{VN}, modmesh::Complex<T>{.real_v = 0.0, .imag_v = 0.0}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally don't like designated initializers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The designated initializer does not seem to be necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will remove designated initializer.
regarding |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Stay with C++17 if possible.
- Name the complex data type as
complex64
andcomplex128
, orComplex64
andComplex128
, if capitalization is desired.
@@ -65,7 +66,9 @@ static std::unordered_map<std::string, DataType, DataTypeHasher> string_data_typ | |||
{"uint32", DataType::Uint32}, | |||
{"uint64", DataType::Uint64}, | |||
{"float32", DataType::Float32}, | |||
{"float64", DataType::Float64}}; | |||
{"float64", DataType::Float64}, | |||
{"ComplexFloat32", DataType::ComplexFloat32}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea to drop Float
from the complex type name, but we should use the correct bit count. For a complex number using two 32-bit float, it should be called complex64
. A complex using two 64-bit float, it should be called complex128
.
It follows numpy convention: https://numpy.org/doc/stable/user/basics.types.html#relationship-between-numpy-data-types-and-c-data-types
{ | ||
if (!pybind11::isinstance<Complex<float>>(value)) | ||
{ | ||
throw pybind11::type_error("Data type mismatch, expected complex float"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message should use the type name.
template <typename T> | ||
bool operator<(const Complex<T> & lhs, const Complex<T> & rhs) | ||
{ | ||
return lhs.norm() < rhs.norm(); | ||
} | ||
|
||
template <typename T> | ||
bool operator>(const Complex<T> & lhs, const Complex<T> & rhs) | ||
{ | ||
return lhs.norm() > rhs.norm(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree
@@ -14,9 +14,9 @@ class ParsevalTest : public ::testing::Test | |||
const size_t VN = 1024; | |||
|
|||
modmesh::SimpleArray<modmesh::Complex<T>> signal{ | |||
modmesh::small_vector<size_t>{VN}, modmesh::Complex<T>(0.0, 0.0)}; | |||
modmesh::small_vector<size_t>{VN}, modmesh::Complex<T>{.real_v = 0.0, .imag_v = 0.0}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The designated initializer does not seem to be necessary.
@@ -40,50 +41,68 @@ struct ComplexImpl | |||
T real_v; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the rationale to append the member name with _v
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because ComplexImpl
has member function real()
and imag()
, so I added the _v
to avoid confusion between function name and variable name.
Btw, can you check if |
No, it doesn't work.
The reason is And
|
I have added complex array with
SimpleArray
, the complex array will be used for fourier transform.