#NEWLINE# def bottom_up_until_type_break( expr , scope , **kwargs ) : #NEWLINE# #INDENT# " Traverse bottom up until data changes significantly\n\n    Parameters\n    ----------\n\n    expr: Expression\n        Expression to compute\n    scope: dict\n        namespace matching leaves of expression to data\n\n    Returns\n    -------\n\n    expr: Expression\n        New expression with lower subtrees replaced with leaves\n    scope: dict\n        New scope with entries for those leaves\n\n    Examples\n    --------\n\n    >>> import numpy as np\n\n    >>> s = symbol('s', 'var * {name: string, amount: int}')\n    >>> data = np.array([('Alice', 100), ('Bob', 200), ('Charlie', 300)],\n    ...                 dtype=[('name', 'S7'), ('amount', 'i8')])\n\n    This computation completes without changing type.  We get back a leaf\n    symbol and a computational result\n\n    >>> e = (s.amount + 1).distinct()\n    >>> bottom_up_until_type_break(e, {s: data}) # doctest: +SKIP\n    (amount, {amount: array([101, 201, 301])})\n\n    This computation has a type change midstream (``list`` to ``int``), so we\n    stop and get the unfinished computation.\n\n    >>> e = s.amount.sum() + 1\n    >>> bottom_up_until_type_break(e, {s: data})\n    (amount_sum + 1, {<`amount_sum` symbol; dshape='int64'>: 600})\n    " #NEWLINE# if ( expr in scope ) : #NEWLINE# #INDENT# leaf = makeleaf ( expr ) #NEWLINE# return ( leaf , { #NEWLINE# #INDENT# leaf : expr [ expr ] , #NEWLINE# #UNINDENT# } ) #NEWLINE# #UNINDENT# inputs = list ( unique ( expr . _inputs ) ) #NEWLINE# ( exprs , new_scopes ) = zip ( * [ bottom_up_until_type_break ( i , scope , ** kwargs ) for i in inputs ] ) #NEWLINE# new_scope = toolz . merge ( new_scopes ) #NEWLINE# new_expr = expr . _subs ( { i : e for ( i , e ) in zip ( inputs , exprs ) if ( not i . isidentical ( e ) ) } ) #NEWLINE# old_expr_leaves = expr . _leaves ( ) #NEWLINE# old_data_leaves = [ scope . get ( leaf ) for leaf in old_expr_leaves ] #NEWLINE# key = ( lambda x : str ( type ( x ) ) ) #NEWLINE# if type_change ( sorted ( new_scope . values ( ) , key = key ) , sorted ( old_data_leaves , key = key ) ) : #NEWLINE# #INDENT# return ( new_expr , new_scope ) #NEWLINE# #UNINDENT# try : #NEWLINE# #INDENT# leaf = makeleaf ( expr ) #NEWLINE# _data = [ new_scope [ i ] for i in new_expr . _inputs ] #NEWLINE# #UNINDENT# except KeyError : #NEWLINE# #INDENT# return ( new_expr , new_scope ) #NEWLINE# #UNINDENT# try : #NEWLINE# #INDENT# return ( leaf , { #NEWLINE# #INDENT# leaf : compute_up ( new_expr , * _data , scope = new_scope , ** kwargs ) , #NEWLINE# #UNINDENT# } ) #NEWLINE# #UNINDENT# except NotImplementedError : #NEWLINE# #INDENT# return ( new_expr , new_scope )