def value_of(
        self, value: cirq.TParamKey | cirq.TParamValComplex, recursive: bool = True
    ) -> cirq.TParamValComplex:
        """Attempt to resolve a parameter to its assigned value.

        Scalars are returned without modification.  Strings are resolved via
        the parameter dictionary with exact match only.  Otherwise, strings
        are considered to be sympy.Symbols with the name as the input string.

        A sympy.Symbol is first checked for exact match in the parameter
        dictionary. Otherwise, it is treated as a sympy.Basic.

        A sympy.Basic is resolved using sympy substitution.

        Note that passing a formula to this resolver can be slow due to the
        underlying sympy library.  For circuits relying on quick performance,
        it is recommended that all formulas are flattened before-hand using
        cirq.flatten or other means so that formula resolution is avoided.
        If unable to resolve a sympy.Symbol, returns it unchanged.
        If unable to resolve a name, returns a sympy.Symbol with that name.

        Args:
            value: The parameter to try to resolve.
            recursive: Whether to recursively evaluate formulas.

        Returns:
            The value of the parameter as resolved by this resolver.

        Raises:
            RecursionError: If the ParamResolver detects a loop in recursive
                resolution.
            sympy.SympifyError: If the resulting value cannot be interpreted.
        """

        # Input is a pass through type, no resolution needed: return early
        v = _resolve_value(value)
        if v is not NotImplemented:
            return v

        # Handle string or symbol
        if isinstance(value, (str, sympy.Symbol)):
            string = value if isinstance(value, str) else value.name
            symbol = value if isinstance(value, sympy.Symbol) else sympy.Symbol(value)
            param_value = self._param_dict.get(string, _NOT_FOUND)
            if param_value is _NOT_FOUND:
                param_value = self._param_dict.get(symbol, _NOT_FOUND)
            if param_value is _NOT_FOUND:
                # Symbol or string cannot be resolved if not in param dict; return as symbol.
                return symbol
            v = _resolve_value(param_value)
            if v is not NotImplemented:
                return v
            if isinstance(param_value, str):
                param_value = sympy.Symbol(param_value)
            elif not isinstance(param_value, sympy.Basic):
                return value
            if recursive:
                param_value = self._value_of_recursive(value)
            return param_value

        if not isinstance(value, sympy.Basic):
            # No known way to resolve this variable, return unchanged.
            return value

        # The following resolves common sympy expressions
        # If sympy did its job and wasn't slower than molasses,
        # we wouldn't need the following block.
        if isinstance(value, sympy.Float):
            return float(value)
        if isinstance(value, sympy.Add):
            summation = self.value_of(value.args[0], recursive)
            for addend in value.args[1:]:
                summation += self.value_of(addend, recursive)
            return summation
        if isinstance(value, sympy.Mul):
            product = self.value_of(value.args[0], recursive)
            for factor in value.args[1:]:
                product *= self.value_of(factor, recursive)
            return product
        if isinstance(value, sympy.Pow) and len(value.args) == 2:
            base = self.value_of(value.args[0], recursive)
            exponent = self.value_of(value.args[1], recursive)
            # Casts because numpy can handle expressions (by delegating to __pow__), but does
            # not have signature that will support this.
            if isinstance(base, numbers.Number):
                return np.float_power(cast(complex, base), cast(complex, exponent))
            return np.power(cast(complex, base), cast(complex, exponent))

        # Input is either a sympy formula or the dictionary maps to a
        # formula.  Use sympy to resolve the value.
        # Note that sympy.subs() is slow, so we want to avoid this and
        # only use it for cases that require complicated resolution.
        if not recursive:
            # Resolves one step at a time. For example:
            # a.subs({a: b, b: c}) == b
            #
            # Note that a sympy.SympifyError here likely means
            # that one of the expressions was not parsable by sympy
            # (such as a function returning NotImplemented)
            v = value.subs(self._param_dict, simultaneous=True)

            if v.free_symbols:
                return v
            elif sympy.im(v):
                # Technically, this should not return complex, but changing
                # type signature to complex would cause many cascading issues
                return complex(v)
            else:
                return float(v)

        return self._value_of_recursive(value)
Attempt to resolve a parameter to its assigned value.

        Scalars are returned without modification.  Strings are resolved via
        the parameter dictionary with exact match only.  Otherwise, strings
        are considered to be sympy.Symbols with the name as the input string.

        A sympy.Symbol is first checked for exact match in the parameter
        dictionary. Otherwise, it is treated as a sympy.Basic.

        A sympy.Basic is resolved using sympy substitution.

        Note that passing a formula to this resolver can be slow due to the
        underlying sympy library.  For circuits relying on quick performance,
        it is recommended that all formulas are flattened before-hand using
        cirq.flatten or other means so that formula resolution is avoided.
        If unable to resolve a sympy.Symbol, returns it unchanged.
        If unable to resolve a name, returns a sympy.Symbol with that name.

        Args:
            value: The parameter to try to resolve.
            recursive: Whether to recursively evaluate formulas.

        Returns:
            The value of the parameter as resolved by this resolver.

        Raises:
            RecursionError: If the ParamResolver detects a loop in recursive
                resolution.
            sympy.SympifyError: If the resulting value cannot be interpreted.
        
cirq-core/cirq/study/resolver.py