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

xapyb pet #895

Closed
wants to merge 17 commits into from
Closed

xapyb pet #895

wants to merge 17 commits into from

Conversation

gfardell
Copy link
Collaborator

@gfardell gfardell commented Apr 20, 2021

  • There is no xapyb where a and b are mixed scalar and vector, therefore I've implemented this in python with the vector multiplication first before the scalar version of axpby is called. If we want to implement this at a lower level I am happy to, but it'll need it to be added to STIR too.
  • I have not implemented anything in matlab, I don't have a matlab to test the code and don't know matlab well at all, so it would be good if someone else could handle this
  • I've added unit tests at the python level
  • I haven't implemented the vector xapyb versions for gadgetron data containers
  • Where python tries to call the vector version and this is not implemented the error is caught and python is used for the calculation instead however it still prints:
File: /home/sirfuser/devel/install/python/sirf/SIRF.py
Line: 254
check_status found the following message sent from the engine:

I'm not sure if there's a way to handle this try/except better.

@KrisThielemans KrisThielemans linked an issue Apr 20, 2021 that may be closed by this pull request
Copy link
Member

@KrisThielemans KrisThielemans left a comment

Choose a reason for hiding this comment

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

this was quite a lot of work! thanks

Comment on lines 1642 to 1645
const NiftiImageData<dataType>& a = dynamic_cast<const NiftiImageData<dataType>&>(a_a);
const NiftiImageData<dataType>& b = dynamic_cast<const NiftiImageData<dataType>&>(a_b);
const NiftiImageData<dataType>& x = dynamic_cast<const NiftiImageData<dataType>&>(a_x);
const NiftiImageData<dataType>& y = dynamic_cast<const NiftiImageData<dataType>&>(a_y);
Copy link
Member

Choose a reason for hiding this comment

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

could potentially throw unexpectedly if the type isn't ok. This is fine, but will confuse the user (certainly so a Python user).

should we cope with cases where the data-type isn't NiftiImageData? Might be quite hard. But if we don't, I think we should put the whole thing in a try catch block with some sensible error.

Also, in future, this can be shorter as

auto& a = dynamic_cast<const NiftiImageData<dataType>&>(a_a);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

At the python end sapyb for vectors is in a try/except so it will default to python if there's an issue.

It does seem a bit strange restricting a and b to the same types, but I don't know how data is organised so whether a numpy array (for example) is compatible with nifty or stir arrays.

Copy link
Member

Choose a reason for hiding this comment

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

ok. Nevertheless, it's the C++ library that should give sensible errors as much as we can, not the "client" Python/MATLAB

src/Registration/cReg/NiftiImageData.cpp Outdated Show resolved Hide resolved
@@ -202,6 +202,8 @@ def add(self, other, out=None):
return z
def axpby(self, a, b, y, out=None, **kwargs):
'''
This function has been deprecated and will be removed in a future release. Please use `sapyb` instead.
Copy link
Member

Choose a reason for hiding this comment

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

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'm happy to use this. But I'll need to add the Python-Deprecated to the requirements, could you point out where these live?

Copy link
Member

Choose a reason for hiding this comment

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

@evgueni-ovtchinnikov
Copy link
Contributor

@KrisThielemans

#define THROW(msg) throw LocalisedException(msg, __FILE__, __LINE__)
#define ASSERT(condition, msg) if (!(condition)) THROW(msg)

@KrisThielemans
Copy link
Member

ok!

@gfardell
Copy link
Collaborator Author

this was quite a lot of work! thanks

400+ lines of code does seem a little excessive for basically some wrappers, but at least I understand SIRF a little now!

src/common/SIRF.py Outdated Show resolved Hide resolved
Comment on lines 261 to 263
tmp = self.multiply(a)
y.multiply(b, out=z)
z.add(tmp, out=z)
Copy link
Contributor

Choose a reason for hiding this comment

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

should better check the error message, and if it is "xapyb has not been implemented yet" then return NotImplemented, otherwise throw (something else happened)

Copy link
Contributor

Choose a reason for hiding this comment

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

Frankly, I feel uneasy about your trying to cover lots of cases (axpby, xapby and essentially also axpyb and xapby + self variants) by one method - are all of them really needed? If they all are, I would rather have had several methods.

Copy link
Contributor

Choose a reason for hiding this comment

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

besides, we normally try to do as little as possible in Python and Matlab interfaces, if only to reduce duplication

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

axpby previously covered vector and scalar methods, but with python computing the vector case, so I don't think it's covering more cases. But yes, it is a busy method.

In terms of the users passing a data-container to out that is also one of the inputs, I guess we'd have to check on the ID to stop this, but as the operation is pixel-wise anyway I assumed it would be safe to allow. It doesn't change the implementation apart from the python code order avoids an intermediary step writing to out.

I think CIL currently wraps the C++ code with ctypes and passes the type of a and b as parameters. I guess this would be an option to simplify the python and matlab ends. but sapyb needs to be consistent in behaviour across SIRF and CIL, I'm not sure we'd want to split it in to sapyb_vv or sapyb_ss at the python end as there's algorithms where a and b could be either.

Copy link
Contributor

Choose a reason for hiding this comment

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

using input container as out is ok - see e.g. __iadd__.

I still believe unnecessary copies can be avoided, but let us leave this for now - after all, as I mentioned, in SIRF we try to do all computation in C++, so sapyb will slim down eventually.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The only unnecessary copy is when a and b are of different types, and the user has passed out, as it will pre-calculate the vector * vector side. Is this where you mean?

There is a specific case if the users passes y and out as the same object, where a is a vector and b a scalar. Then an intermediate step x.multiply(a, out=out) would overwrite y before it was used. So without checking on the datacontainer IDs it seems safer that where it has to calculate in steps it writes initially for a temporary container rather than out. Of course if it was implemented in the C++ code it would be pixel-wise and not an issue.

@KrisThielemans
Copy link
Member

Turns out we have a DEPRECATED macro already in

// Deprecation function. With C++14, could use [[deprecated("some message")]]

I like it better that it has its own file though. I think SIRF_DEPRECATED might be better than DEPRECATED, but that seems to have to be for a different PR

@KrisThielemans
Copy link
Member

@gfardell @evgueni-ovtchinnikov I have no clear feeling if this is feasible to merge in the next few days, or if we better postpone it (which would be fine). can you comment?

@gfardell
Copy link
Collaborator Author

@gfardell @evgueni-ovtchinnikov I have no clear feeling if this is feasible to merge in the next few days, or if we better postpone it (which would be fine). can you comment?

I am updating it now and don't imagine it will take too long for it's current functionality, however if we want to delay it until the gadegtron xapyb is implemented I guess it would be more complete.

@evgueni-ovtchinnikov
Copy link
Contributor

@KrisThielemans I would postpone it - for one thing, I believe we should move the computation in Python sapyb to C++ and do Matlab and MR before merge. (Now that Johannes took over Resampler from me, I can take over xapyb from Gemma :-))

@KrisThielemans KrisThielemans added this to the v3.1 milestone Apr 21, 2021
@gfardell
Copy link
Collaborator Author

I can't figure out why this stopped building. I can build and run the tests locally still.

@KrisThielemans
Copy link
Member

As the error is with recent changes in master, I think it is best to merge master on here again as the DEPRECATED stuff changed there. Presumably might give a conflict (although github doesn't think so).

@gfardell
Copy link
Collaborator Author

gfardell commented May 4, 2021

I've made a stab at the matlab wrappers but I haven't managed to get a license from SCD yet to run and test it. I've also not used matlab much so have no clue on error handling, when I get a license I'll be able to figure that out.

tmp = x.same_object();

if a_scalar
tmp = b.dot(y);
Copy link
Contributor

Choose a reason for hiding this comment

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

should be b.times(y), dot computes scalar product

z.handle_ = calllib('msirf', 'mSIRF_axpby', ...
ptr_a, x.handle_, 1.0, tmp.handle_);
else
tmp = a.dot(x);
Copy link
Contributor

Choose a reason for hiding this comment

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

as above

Comment on lines +220 to +221
z.handle_ = calllib('msirf', 'mSIRF_axpby', ...
ptr_a, x.handle_, ptr_b, y.handle_);
Copy link
Contributor

Choose a reason for hiding this comment

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

if neither a nor b is scalar, should call mSIRF_xapyb_vv

Comment on lines +149 to +176
void*
cSIRF_xapyb_ss(
const void* ptr_x, const void* ptr_a,
const void* ptr_y, const void* ptr_b
) {
try {
DataContainer& x =
objectFromHandle<DataContainer >(ptr_x);
DataContainer& y =
objectFromHandle<DataContainer >(ptr_y);
void* h = x.new_data_container_handle();
DataContainer& z = objectFromHandle<DataContainer>(h);
z.xapyb(x, ptr_a, y, ptr_b);
return h;
}
CATCH;
}

extern "C"
void*
cSIRF_xapyb_vv(
const void* ptr_x, const void* ptr_a,
const void* ptr_y, const void* ptr_b
) {
try {
DataContainer& x =
objectFromHandle<DataContainer >(ptr_x);
DataContainer& y =
Copy link
Contributor

Choose a reason for hiding this comment

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

these two functions have substantial overlap - why not have one cSIRF_xapyb with extra const char* args parameter that may have values "ss" or "vv" (and potentially other values, making it future proof)?

this would also solve lesser problem of breaking our naming scheme cSIRF_nameOfFunctionInCamelCase

Comment on lines +46 to +53
void* cSIRF_xapyb_ss(const void* ptr_x, const PTR_FLOAT ptr_a,
const void* ptr_y, const PTR_FLOAT ptr_b);
void* cSIRF_xapyb_ss_Alt(const void* ptr_x, const PTR_FLOAT ptr_a,
const void* ptr_y, const PTR_FLOAT ptr_b, void* ptr_z);
void* cSIRF_xapyb_vv(const void* ptr_x, const void* ptr_a,
const void* ptr_y, const void* ptr_b);
void* cSIRF_xapyb_vv_Alt(const void* ptr_x, const void* ptr_a,
const void* ptr_y, const void* ptr_b, void* ptr_z);
Copy link
Contributor

Choose a reason for hiding this comment

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

see the above suggestion to replace these four functions with two functions with an extra const char* args parameter

@KrisThielemans
Copy link
Member

merged via another branch

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

axpby renaming to xapby
3 participants