SyneRBI / SIRF

Main repository for the CCP SynerBI software
http://www.ccpsynerbi.ac.uk
Other
59 stars 29 forks source link

Added optional argument out in all gradient-computing methods #1246

Closed evgueni-ovtchinnikov closed 4 months ago

evgueni-ovtchinnikov commented 5 months ago

Changes in this pull request

Added optional argument out in all gradient-computing methods for compatibility with CIL.

Testing performed

Related issues

Fixes #1242.

Checklist before requesting a review

Contribution Notes

Please read and adhere to the contribution guidelines.

Please tick the following:

evgueni-ovtchinnikov commented 5 months ago

I fixed it this way:

        if out is None:
            grad = ImageData() # does next to nothing
        else:
            grad = out
        grad.handle = pystir.cSTIR_priorGradient(self.handle, image.handle)
        check_status(grad.handle)
        return grad
KrisThielemans commented 5 months ago

How does this remove the copy? The C function will still do the fill into a temporary object (as it doesn't get the grad handle)?

evgueni-ovtchinnikov commented 5 months ago

I got rid of out.fill(grad). Replacing the image copy constructor with something like get_uniform_copy(0) (which we do not have in SIRF at C++ level - does STIR have it?), is a separate issue.

KrisThielemans commented 5 months ago

I might misunderstand the handle details, but as far as I can see

        grad.handle = pystir.cSTIR_priorGradient(self.handle, image.handle)

gets essentially rid of any allocated memory that out was pointing to (I guess the handle destructor will be called). In any case, https://github.com/SyneRBI/SIRF/blob/2c66faff3bc0f12c864cfc2a2931eba5ade60ba0/src/xSTIR/cSTIR/cstir.cpp#L1249 still has allocation of a new object, as opposed to reusing whatever was in out.

So, while this doesn't have the fill anymore, it still doesn't reuse memory out. I think that can only be done by passing the out.handle to the C function.

evgueni-ovtchinnikov commented 5 months ago

ok, see your point now, thanks!

trying this now:

        if out is None:
            out = ImageData()
        if out.handle is None:
            out.handle = pystir.cSTIR_priorGradient(self.handle, image.handle)
        else:
            assert_validities(image, out)
            pystir.cSTIR_computePriorGradient(self.handle, image.handle, out.handle)
        check_status(out.handle)
        return out

not sure I could manage to avoid copies with PLSPriorGradient though

KrisThielemans commented 5 months ago

can't check right now, but unfortunate to have the duplication. Cannot pass 0 or an empty handle (or default constructed one)?

we do need a test though.

KrisThielemans commented 5 months ago

Something like

void*
cSTIR_computeObjectiveFunctionGradient(void* ptr_f, void* ptr_i, int subset, void* ptr_g)
{
    try {
        ObjectiveFunction3DF& fun = objectFromHandle< ObjectiveFunction3DF>(ptr_f);
        STIRImageData& id = objectFromHandle<STIRImageData>(ptr_i);
               shared_ptr<STIRImageData> sptr;
        if (ptr_g==0)
        {
            Image3DF& image = id.data();
            STIRImageData* ptr_id = new STIRImageData(image);
            sptr.reset(ptr_id);
            ptr_g = newObjectHandle(sptr);
        }
        else
        {
            sptr = getObjectSptrFromHandle(ptr_g);
        }
        Image3DF& gd = sptr->data();
                ...
        return ptr_g;
}

Of course, there's large scope for using auto here.

KrisThielemans commented 5 months ago

not sure I could manage to avoid copies with PLSPriorGradient though

It's a bug Edit: it's a bad name. See #1248

KrisThielemans commented 4 months ago

Thanks! Please create an issue such that we don't forget to add tests, as well as reduce code duplication.