Skip to content

added calcAmpSum frontend #610

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

Merged
merged 1 commit into from
May 6, 2025
Merged

added calcAmpSum frontend #610

merged 1 commit into from
May 6, 2025

Conversation

TysonRayJones
Copy link
Member

@TysonRayJones TysonRayJones commented May 6, 2025

Example PR A

Sub PR 4

This PR demonstrates how to add new QuEST API functions which directly return a qcomp.

extern "C" {

    // here be dragons
    qcomp calcAmpSum(Qureg qureg) {
        qcomp value = internalFunc(qureg);
        return value;
    }

}

The problem

QuEST is agnostic to C and C++ using a myriad of tricks. This includes resolving the qcomp type to the natural, respective C and C++ complex types. While a C user may believe the qcomp is defined as

#include <complex.h>
typedef double _Complex qcomp;

a C++ user (and the internal QuEST library) may meanwhile believe it to be

#include <complex>
typedef std::complex<double> qcomp;

As such, a C user might pass a _Complex to a C++ function expecting a std::complex<double>. Alas, this is undefined behaviour! For historical reasons, complex primitives were never integrated into the ABI. It is ergo illegal for a C user to directly pass a qcomp to its internal C++ functions.

The solution

While the type qcomp differs between C and C++, they do have the same memory layout. This is guaranteed by the C99 and C++11 standards as indicated here, and means it is okay to pass pointers to qcomp over the ABI. Ergo, QuEST's C++ functions can safely accept and return qcomp* and be directly invoked by C code.

Passing superfluous pointers is gross though, and we still wish for the C and C++ interfaces to be identical. So, to define a new API function which returns a qcomp, we...

  1. Declare the function in the user-facing header (e.g. quest/include/calculations.h) without extern "C".
    qcomp calcAmpSum(Qureg qureg);

    Both the user's C compiler and the backend's C++ compiler will interpret qcomp as their native types.

  2. Define the function's C++-only version in the corresponding source file (e.g. quest/src/api/calculatons.cpp), also without extern "C".
    qcomp calcAmpSum(Qureg qureg) {
        qcomp value = internalFunc(qureg);
        return value;
    }

    The C++ compiler will name-mangle this definition, ensuring it is never directly invoked from C. So far, C++ users can call this function - we now need to spoof it for C users.

  3. Define a de-mangled C-compatible version of the function just under the above function, which calls the above function, but safely "returns" the qcomp by overwriting a pointer.
    extern "C" void _wrap_calcAmpSum(Qureg qureg, qcomp* out) {
        *out = calcAmpSum(qureg);
    }

    This could be directly called by a C user, but it's ugly and has a differing signature to the C++ version! To address this...

  4. Define a C-only version of the function in quest/include/wrappers.h with an identical signature to the C++ definition but which invokes _wrap_calcAmpSum above.
    #ifndef __cplusplus
    
    extern void _wrap_calcAmpSum(Qureg qureg, qcomp* out);
    
    qcomp calcAmpSum(Qureg qureg) {
    
        qcomp out;
        _wrap_calcAmpSum(qureg, &out);
        return out;
    }
    
    #endif

    Since only C parses this header-only definition, only C user's will invoke it! C++ users will invoke the original calcAmpSum definition.

A regrettable side-effect of this trick is that the intendedly private function _wrap_calcAmpSum is declared (as extern) in the header. This is necessary to prevent the the C compiler parsing wrappers.h from panicking about the missing symbol, but means C users can see and call _wrap_calcAmpSum. Although calling it is harmless, hopefully the ugly prefix dissuades IDEs from listing the routine!

This PR

We generalise the existing calcRealAmpSum (which returned a real-valued primitive without problem) to return qcomp, retaining a C and C++ agnostic API via the above process. We also define its unit test for completeness.

The combined changes of this and the previous example PRs can be seen in #615.

and unit test, for completeness
@TysonRayJones TysonRayJones added the demo A demonstration of a development process label May 6, 2025
@TysonRayJones TysonRayJones merged commit b2524fa into example-pr-3 May 6, 2025
@TysonRayJones TysonRayJones deleted the example-pr-4 branch May 6, 2025 16:15
This was referenced May 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
demo A demonstration of a development process
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant