eclipse / xacc

XACC - eXtreme-scale Accelerator programming framework
https://xacc.readthedocs.io
BSD 3-Clause "New" or "Revised" License
165 stars 85 forks source link

Add one-qubit IRTransformation for ion trap backend #497

Closed ausbin closed 2 years ago

ausbin commented 2 years ago

This PR is for the one-qubit IRTransformation which decomposes sequences of single-qubit unitaries into R_phi(pi/2) gates. Described in Section III-C of https://arxiv.org/pdf/2111.00146.pdf

Next PR is the actual Accelerator

Gradients from Mathematica

For future reference, I created the gradients hardcoded in IonTrapOptFunction.cpp using the following Mathematica notebook (gzipped so GitHub will let me upload it): r-decomp-gradients.nb.gz. It's not very organized, but it's only used to generate C++ code by running the gradients it finds through the following Python I had in a Jupyter notebook:

def textToCpp(text, indent=''):
    text = text.strip('{}')
    terms = text.split(',')
    repl = [('Sqrt[', 'std::sqrt('),
            ('Conjugate[', 'std::conj('),
            ('Sin[', 'std::sin('),
            ('Cos[', 'std::cos('),
            ('phi1', 'phi(0,0)'),
            ('phi2', 'phi(1,0)'),
            ('phi3', 'phi(2,0)'),
            ('phi4', 'phi(3,0)'),
            (' ', '*'),
            ('I', 'i'),
            ('E^(', 'std::exp('),
            (']', ')'),
            ('u11', 'goal(0,0)'),
            ('u12', 'goal(0,1)'),
            ('u21', 'goal(1,0)'),
            ('u22', 'goal(1,1)')]
    re_repl = [(r'(?P<digit>\d)(?P<sym>[/*+-])', '\g<digit>.0\g<sym>'),
               (r'(?P<sym>[/*+-])(?P<digits>\d+)(?!\.)', '\g<sym>\g<digits>.0'),
               (r'\((?P<digits>\d+)\)', '(\g<digits>.0)'),
               (r'\)(?P<char>\w)', ')*\g<char>'),]
    re_repl = [(re.compile(pat), subst) for pat, subst in re_repl]

    for i in range(len(terms)):
        for old, new in repl:
            terms[i] = terms[i].replace(old, new)
        for pat, new in re_repl:
            terms[i] = pat.sub(new, terms[i])
        terms[i] = '{}gh({},0) = {};\n'.format(indent, i, terms[i])
    text = '\n'.join(terms)
    return text

def genNabla(suffix, grads):
    templ = '''void GTRIPulseTransform::GTRIOptFunction::nablaH{}_{}(const arma::mat &phi, arma::cx_mat &gh) {{
    const auto i = std::complex<double>(0, 1);

'''
    indent = ' '*4
    footer = '}\n'
    funcs = [templ.format(suffix, i+1) + textToCpp(line, indent) + footer for i, line in enumerate(grads) if line]
    return '\n'.join(funcs)

# example
print(genNabla('Rx', [
    '{-(1/2) I (Conjugate[u12]+Conjugate[u21]) Cos[phi1/2]-1/2 (Conjugate[u11]+Conjugate[u22]) Sin[phi1/2]}',
    '{1/Sqrt[2] E^(-I phi1) (-((Co...',
    # ...
]))

I'm not sure how best to include all that in the repository, so I've left it out for now. Ideally, we'd have a closed form decomposition of an arbitrary unitary into R_phi(pi/2) gates (possibly followed by or preceded by RX/RZ gates) and wouldn't need the optimizer or these gradients in the first place. Even if we never managed to find that that decomposition, there could still be a different optimizer that doesn't need gradients (e.g. the scipy BFGS optimizer), making all this gradient business obsolete as well

Testing

ctest. I added a test for the one-qubit pass. Tried to make it have pretty reasonable expectations so that in the future, the optimizer won't flake out and fail the test randomly and annoy people

gitpod-io[bot] commented 2 years ago

1tnguyen commented 2 years ago

This looks great. Thanks, @ausbin!