Skip to content
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

feat!: implement faster lambdification #281

Merged
merged 10 commits into from Jun 1, 2021
Merged

feat!: implement faster lambdification #281

merged 10 commits into from Jun 1, 2021

Conversation

redeboer
Copy link
Member

@redeboer redeboer commented Jun 1, 2021

@redeboer redeboer added this to the 0.2.7 milestone Jun 1, 2021
@redeboer redeboer requested a review from Leongrim June 1, 2021 18:00
@redeboer redeboer self-assigned this Jun 1, 2021
@codecov
Copy link

codecov bot commented Jun 1, 2021

Codecov Report

Merging #281 (6e1a860) into main (9be08e4) will decrease coverage by 3.19%.
The diff coverage is 29.78%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #281      +/-   ##
==========================================
- Coverage   84.36%   81.17%   -3.20%     
==========================================
  Files          13       13              
  Lines         761      802      +41     
==========================================
+ Hits          642      651       +9     
- Misses        119      151      +32     
Flag Coverage Δ
unittests 81.17% <29.78%> (-3.20%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/tensorwaves/model.py 71.72% <29.78%> (-13.61%) ⬇️

@redeboer redeboer mentioned this pull request Jun 1, 2021
@redeboer
Copy link
Member Author

redeboer commented Jun 1, 2021

Also gave multithreading a try. Unfortunately, lambdified expressions can't be pickled, so multiprocessing won't work (also not with pathos/dill).

Original snippet

def optimized_lambdify(
args: Sequence[sp.Symbol],
expression: sp.Expr,
modules: Optional[Union[str, tuple, dict]] = None,
*,
min_complexity: int = 0,
max_complexity: int,
) -> Callable:
"""Speed up `~sympy.utilities.lambdify.lambdify` with `.split_expression`.
.. seealso:: :doc:`/usage/faster-lambdify`
"""
top_expression, definitions = split_expression(
expression,
min_complexity=min_complexity,
max_complexity=max_complexity,
)
top_symbols = sorted(definitions, key=lambda s: s.name)
top_lambdified = sp.lambdify(top_symbols, top_expression, modules)
sub_lambdified = [ # same order as positional arguments in top_lambdified
sp.lambdify(args, definitions[symbol], modules)
for symbol in tqdm(
iterable=top_symbols,
desc="Lambdifying sub-expressions",
unit="expr",
disable=logging.getLogger().level > logging.WARNING,
)
]
def recombined_function(*args): # type: ignore
new_args = [sub_expr(*args) for sub_expr in sub_lambdified]
return top_lambdified(*new_args)
return recombined_function

Rewritten with multi-threading
# import multiprocessing
from pathos import multiprocessing

...

def optimized_lambdify(
    args: Sequence[sp.Symbol],
    expression: sp.Expr,
    modules: Optional[Union[str, tuple, dict]] = None,
    *,
    min_complexity: int = 0,
    max_complexity: int,
    n_threads: int = 1,
) -> Callable:
    """Speed up `~sympy.utilities.lambdify.lambdify` with `.split_expression`.

    .. seealso:: :doc:`/usage/faster-lambdify`
    """
    top_expression, definitions = split_expression(
        expression,
        min_complexity=min_complexity,
        max_complexity=max_complexity,
    )
    top_symbols = sorted(definitions, key=lambda s: s.name)
    top_lambdified = sp.lambdify(top_symbols, top_expression, modules)

    progress_bar = tqdm(
        total=len(top_symbols),
        desc="Lambdifying sub-expressions",
        unit="expr",
        disable=logging.getLogger().level > logging.WARNING,
    )

    def local_lambdify(symbol: sp.Symbol) -> Tuple[sp.Symbol, Callable]:
        lambdified_expression = sp.lambdify(args, definitions[symbol], modules)
        progress_bar.update()
        return symbol, lambdified_expression

    sub_expr_mapping: Dict[sp.Symbol, Callable] = {}
    if n_threads > 1:
        with multiprocessing.Pool(n_threads) as pool:
            for symbol, lambdified_sub_expr in pool.imap_unordered(
                local_lambdify, top_symbols
            ):
                sub_expr_mapping[symbol] = lambdified_sub_expr
    else:
        for symbol in top_symbols:
            _, lambdified_sub_expr = local_lambdify(symbol)
            sub_expr_mapping[symbol] = lambdified_sub_expr

    # retrieve order of positional arguments in top_lambdified
    sub_lambdified = [sub_expr_mapping[s] for s in top_symbols]

    def recombined_function(*args):  # type: ignore
        new_args = [sub_expr(*args) for sub_expr in sub_lambdified]
        return top_lambdified(*new_args)

    return recombined_function

@redeboer redeboer enabled auto-merge (squash) June 1, 2021 19:59
@redeboer redeboer merged commit 44fc055 into main Jun 1, 2021
@redeboer redeboer deleted the speed-up-lambdify branch June 1, 2021 20:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Speed up lambdifying
2 participants