XanaduAI / strawberryfields

Strawberry Fields is a full-stack Python library for designing, simulating, and optimizing continuous variable (CV) quantum optical circuits.
https://strawberryfields.ai
Apache License 2.0
747 stars 186 forks source link

Reduce the use of SymPy lambdify #579

Closed antalszava closed 3 years ago

antalszava commented 3 years ago

Context: Some optimization tasks using Strawberry Fields were showing increased memory allocation, although no state was being left (i.e., the engine was reset, state objects recreated, etc.).

Parameters in Strawberry Fields might be numeric or symbolic. To represent both, SymPy is used. When using gate parameters that are internally created, Strawberry Fields evaluates the parameter to get the numeric value using SymPy.

Evaluating the parameters depends on the sympy.lambdify function, which internally caches data using linecache. The memory increase was found to be due to this caching.

Description of the Change: Changes the par_evaluate function which does the parameter evaluation such that:

Benefits:

Possible Drawbacks: N/A

Related GitHub Issues: N/A

See profiling data here.

codecov[bot] commented 3 years ago

Codecov Report

Merging #579 (c60dc89) into master (127085e) will increase coverage by 0.00%. The diff coverage is 100.00%.

@@           Coverage Diff           @@
##           master     #579   +/-   ##
=======================================
  Coverage   98.38%   98.38%           
=======================================
  Files          75       75           
  Lines        8410     8414    +4     
=======================================
+ Hits         8274     8278    +4     
  Misses        136      136           
Impacted Files Coverage Δ
strawberryfields/parameters.py 99.10% <100.00%> (+0.03%) :arrow_up:

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 127085e...c60dc89. Read the comment docs.

antalszava commented 3 years ago

Profiling

The following script was profiled for both memory and time:

import strawberryfields as sf
from strawberryfields.ops import *
from strawberryfields.parameters import par_funcs as pf
import numpy as np

eng = sf.Engine("fock", backend_options={"cutoff_dim": 2})

def evx(param, n):
    prog = sf.Program(2)    
    with prog.context as q:
        ntrot = 10
        for i in range(n): 
            step = param/ntrot
            for itrot in range(ntrot):

                s = step
                r = pf.asinh(-s / 2)
                theta = 0.5 * pf.atan2(-1.0 / pf.cosh(r), -pf.tanh(r))

                for _ in range(20):
                    # Decomposition of CXgate
                    BSgate(theta, 0) |  (q[0], q[1]),
                    Sgate(r, 0) | q[0],
                    Sgate(-r, 0) | q[1],
                    BSgate(theta + np.pi / 2, 0) | (q[0], q[1]),

        state = eng.run(prog).state
        return state.number_expectation([0])

parm = 0.05
for i in range(10):

    if eng.run_progs:
        eng.reset()

    res = evx(parm, 40)
    print(i, flush=True)

master branch:

memory: without_clearing_cache

time: 88.01 secs

calling linecache.clearcache():

with_clearing_cache

time: 87.23 secs

fewer_lambdify branch (calling linecache.clearcache() & no call to lambdify if not symbolic):

time: 34.86 secs

nquesada commented 3 years ago

@antalszava : don't forget to update the changelog. Also you might want to make the CodeFactor bot happy.