Update self._variables_shim with the final values to be patched into the gate parameters,
        according to the arithmetic expressions in the original program.

        For example:

            DECLARE theta REAL
            DECLARE beta REAL
            RZ(3 * theta) 0
            RZ(beta+theta) 0

        gets translated to:

            DECLARE theta REAL
            DECLARE __P REAL[2]
            RZ(__P[0]) 0
            RZ(__P[1]) 0

        and the recalculation table will contain:

        {
            ParameterAref('__P', 0): Mul(3.0, <MemoryReference theta[0]>),
            ParameterAref('__P', 1): Add(<MemoryReference beta[0]>, <MemoryReference theta[0]>)
        }

        Let's say we've made the following two function calls:

            qpu.write_memory(region_name='theta', value=0.5)
            qpu.write_memory(region_name='beta', value=0.1)

        After executing this function, our self.variables_shim in the above example would contain
        the following:

        {
            ParameterAref('theta', 0): 0.5,
            ParameterAref('beta', 0): 0.1,
            ParameterAref('__P', 0): 1.5,       # (3.0) * theta[0]
            ParameterAref('__P', 1): 0.6        # beta[0] + theta[0]
        }

        Once the _variables_shim is filled, execution continues as with regular binary patching.

def _update_variables_shim_with_recalculation_table(self):
        """
        Update self._variables_shim with the final values to be patched into the gate parameters,
        according to the arithmetic expressions in the original program.

        For example:

            DECLARE theta REAL
            DECLARE beta REAL
            RZ(3 * theta) 0
            RZ(beta+theta) 0

        gets translated to:

            DECLARE theta REAL
            DECLARE __P REAL[2]
            RZ(__P[0]) 0
            RZ(__P[1]) 0

        and the recalculation table will contain:

        {
            ParameterAref('__P', 0): Mul(3.0, <MemoryReference theta[0]>),
            ParameterAref('__P', 1): Add(<MemoryReference beta[0]>, <MemoryReference theta[0]>)
        }

        Let's say we've made the following two function calls:

            qpu.write_memory(region_name='theta', value=0.5)
            qpu.write_memory(region_name='beta', value=0.1)

        After executing this function, our self.variables_shim in the above example would contain
        the following:

        {
            ParameterAref('theta', 0): 0.5,
            ParameterAref('beta', 0): 0.1,
            ParameterAref('__P', 0): 1.5,       # (3.0) * theta[0]
            ParameterAref('__P', 1): 0.6        # beta[0] + theta[0]
        }

        Once the _variables_shim is filled, execution continues as with regular binary patching.
        """
        if not hasattr(self._executable, "recalculation_table"):
            # No recalculation table, no work to be done here.
            return
        for memory_reference, expression in self._executable.recalculation_table.items():
            # Replace the user-declared memory references with any values the user has written,
            # coerced to a float because that is how we declared it.
            self._variables_shim[memory_reference] = float(self._resolve_memory_references(expression))