# Buggy code
def _lower_parfor_parallel(lowerer, parfor):
    """Lowerer that handles LLVM code generation for parfor.
    This function lowers a parfor IR node to LLVM.
    The general approach is as follows:
    1) The code from the parfor's init block is lowered normally
       in the context of the current function.
    2) The body of the parfor is transformed into a gufunc function.
    3) Code is inserted into the main function that calls do_scheduling
       to divide the iteration space for each thread, allocates
       reduction arrays, calls the gufunc function, and then invokes
       the reduction function across the reduction arrays to produce
       the final reduction values.
    """
    from .parallel import get_thread_count

    ensure_parallel_support()
    typingctx = lowerer.context.typing_context
    targetctx = lowerer.context
    # We copy the typemap here because for race condition variable we'll
    # update their type to array so they can be updated by the gufunc.
    orig_typemap = lowerer.fndesc.typemap
    # replace original typemap with copy and restore the original at the end.
    lowerer.fndesc.typemap = copy.copy(orig_typemap)
    typemap = lowerer.fndesc.typemap
    varmap = lowerer.varmap

    if config.DEBUG_ARRAY_OPT:
        print("_lower_parfor_parallel")
        parfor.dump()

    loc = parfor.init_block.loc
    scope = parfor.init_block.scope

    # produce instructions for init_block
    if config.DEBUG_ARRAY_OPT:
        print("init_block = ", parfor.init_block, " ", type(parfor.init_block))
    for instr in parfor.init_block.body:
        if config.DEBUG_ARRAY_OPT:
            print("lower init_block instr = ", instr)
        lowerer.lower_inst(instr)

    for racevar in parfor.races:
        if racevar not in varmap:
            rvtyp = typemap[racevar]
            rv = ir.Var(scope, racevar, loc)
            lowerer._alloca_var(rv.name, rvtyp)

    alias_map = {}
    arg_aliases = {}
    numba.parfor.find_potential_aliases_parfor(
        parfor, parfor.params, typemap, lowerer.func_ir, alias_map, arg_aliases
    )
    if config.DEBUG_ARRAY_OPT:
        print("alias_map", alias_map)
        print("arg_aliases", arg_aliases)

    # run get_parfor_outputs() and get_parfor_reductions() before gufunc creation
    # since Jumps are modified so CFG of loop_body dict will become invalid
    assert parfor.params != None

    parfor_output_arrays = numba.parfor.get_parfor_outputs(parfor, parfor.params)
    parfor_redvars, parfor_reddict = numba.parfor.get_parfor_reductions(
        parfor, parfor.params, lowerer.fndesc.calltypes
    )

    # init reduction array allocation here.
    nredvars = len(parfor_redvars)
    redarrs = {}
    if nredvars > 0:
        # reduction arrays outer dimension equal to thread count
        thread_count = get_thread_count()
        scope = parfor.init_block.scope
        loc = parfor.init_block.loc

        # For each reduction variable...
        for i in range(nredvars):
            redvar_typ = lowerer.fndesc.typemap[parfor_redvars[i]]
            redvar = ir.Var(scope, parfor_redvars[i], loc)
            redarrvar_typ = redtyp_to_redarraytype(redvar_typ)
            reddtype = redarrvar_typ.dtype
            if config.DEBUG_ARRAY_OPT:
                print(
                    "redvar_typ",
                    redvar_typ,
                    redarrvar_typ,
                    reddtype,
                    types.DType(reddtype),
                )

            # If this is reduction over an array,
            # the reduction array has just one added per-worker dimension.
            if isinstance(redvar_typ, types.npytypes.Array):
                redarrdim = redvar_typ.ndim + 1
            else:
                redarrdim = 1

            # Reduction array is created and initialized to the initial reduction value.

            # First create a var for the numpy empty ufunc.
            empty_func = ir.Var(scope, mk_unique_var("empty_func"), loc)
            ff_fnty = get_np_ufunc_typ(np.empty)
            ff_sig = typingctx.resolve_function_type(
                ff_fnty,
                (types.UniTuple(types.intp, redarrdim), types.DType(reddtype)),
                {},
            )
            typemap[empty_func.name] = ff_fnty
            empty_assign = ir.Assign(
                ir.Global("empty", np.empty, loc=loc), empty_func, loc
            )
            lowerer.lower_inst(empty_assign)

            # Create var for outer dimension size of reduction array equal to number of threads.
            num_threads_var = ir.Var(scope, mk_unique_var("num_threads"), loc)
            num_threads_assign = ir.Assign(
                ir.Const(thread_count, loc), num_threads_var, loc
            )
            typemap[num_threads_var.name] = types.intp
            lowerer.lower_inst(num_threads_assign)

            # Empty call takes tuple of sizes.  Create here and fill in outer dimension (num threads).
            size_var = ir.Var(scope, mk_unique_var("tuple_size_var"), loc)
            typemap[size_var.name] = types.UniTuple(types.intp, redarrdim)
            size_var_list = [num_threads_var]

            # If this is a reduction over an array...
            if isinstance(redvar_typ, types.npytypes.Array):
                # Add code to get the shape of the array being reduced over.
                redshape_var = ir.Var(scope, mk_unique_var("redarr_shape"), loc)
                typemap[redshape_var.name] = types.UniTuple(types.intp, redvar_typ.ndim)
                redshape_getattr = ir.Expr.getattr(redvar, "shape", loc)
                redshape_assign = ir.Assign(redshape_getattr, redshape_var, loc)
                lowerer.lower_inst(redshape_assign)

                # Add the dimension sizes of the array being reduced over to the tuple of sizes pass to empty.
                for j in range(redvar_typ.ndim):
                    onedimvar = ir.Var(scope, mk_unique_var("redshapeonedim"), loc)
                    onedimgetitem = ir.Expr.static_getitem(redshape_var, j, None, loc)
                    typemap[onedimvar.name] = types.intp
                    onedimassign = ir.Assign(onedimgetitem, onedimvar, loc)
                    lowerer.lower_inst(onedimassign)
                    size_var_list += [onedimvar]

            size_call = ir.Expr.build_tuple(size_var_list, loc)
            size_assign = ir.Assign(size_call, size_var, loc)
            lowerer.lower_inst(size_assign)

            # Add call to empty passing the size var tuple.
            empty_call = ir.Expr.call(empty_func, [size_var], {}, loc=loc)
            redarr_var = ir.Var(scope, mk_unique_var("redarr"), loc)
            typemap[redarr_var.name] = redarrvar_typ
            empty_call_assign = ir.Assign(empty_call, redarr_var, loc)
            lowerer.fndesc.calltypes[empty_call] = ff_sig
            lowerer.lower_inst(empty_call_assign)

            # Remember mapping of original reduction array to the newly created per-worker reduction array.
            redarrs[redvar.name] = redarr_var

            init_val = parfor_reddict[parfor_redvars[i]][0]
            if init_val != None:
                if isinstance(redvar_typ, types.npytypes.Array):
                    # Create an array of identity values for the reduction.
                    # First, create a variable for np.full.
                    full_func = ir.Var(scope, mk_unique_var("full_func"), loc)
                    full_fnty = get_np_ufunc_typ(np.full)
                    full_sig = typingctx.resolve_function_type(
                        full_fnty,
                        (
                            types.UniTuple(types.intp, redvar_typ.ndim),
                            reddtype,
                            types.DType(reddtype),
                        ),
                        {},
                    )
                    typemap[full_func.name] = full_fnty
                    full_assign = ir.Assign(
                        ir.Global("full", np.full, loc=loc), full_func, loc
                    )
                    lowerer.lower_inst(full_assign)

                    # Then create a var with the identify value.
                    init_val_var = ir.Var(scope, mk_unique_var("init_val"), loc)
                    init_val_assign = ir.Assign(
                        ir.Const(init_val, loc), init_val_var, loc
                    )
                    typemap[init_val_var.name] = reddtype
                    lowerer.lower_inst(init_val_assign)

                    # Then, call np.full with the shape of the reduction array and the identity value.
                    full_call = ir.Expr.call(
                        full_func, [redshape_var, init_val_var], {}, loc=loc
                    )
                    lowerer.fndesc.calltypes[full_call] = full_sig
                    redtoset = ir.Var(scope, mk_unique_var("redtoset"), loc)
                    redtoset_assign = ir.Assign(full_call, redtoset, loc)
                    typemap[redtoset.name] = redvar_typ
                    lowerer.lower_inst(redtoset_assign)
                else:
                    redtoset = ir.Var(scope, mk_unique_var("redtoset"), loc)
                    redtoset_assign = ir.Assign(ir.Const(init_val, loc), redtoset, loc)
                    typemap[redtoset.name] = reddtype
                    lowerer.lower_inst(redtoset_assign)
            else:
                redtoset = redvar

            # For each thread, initialize the per-worker reduction array to the current reduction array value.
            for j in range(get_thread_count()):
                index_var = ir.Var(scope, mk_unique_var("index_var"), loc)
                index_var_assign = ir.Assign(ir.Const(j, loc), index_var, loc)
                typemap[index_var.name] = types.uintp
                lowerer.lower_inst(index_var_assign)

                redsetitem = ir.SetItem(redarr_var, index_var, redtoset, loc)
                lowerer.fndesc.calltypes[redsetitem] = signature(
                    types.none,
                    typemap[redarr_var.name],
                    typemap[index_var.name],
                    redvar_typ,
                )
                lowerer.lower_inst(redsetitem)

    # compile parfor body as a separate function to be used with GUFuncWrapper
    flags = copy.copy(parfor.flags)
    flags.set("error_model", "numpy")
    # Can't get here unless  flags.set('auto_parallel', ParallelOptions(True))
    index_var_typ = typemap[parfor.loop_nests[0].index_variable.name]
    # index variables should have the same type, check rest of indices
    for l in parfor.loop_nests[1:]:
        assert typemap[l.index_variable.name] == index_var_typ
    numba.parfor.sequential_parfor_lowering = True
    func, func_args, func_sig, redargstartdim, func_arg_types = (
        _create_gufunc_for_parfor_body(
            lowerer,
            parfor,
            typemap,
            typingctx,
            targetctx,
            flags,
            {},
            bool(alias_map),
            index_var_typ,
            parfor.races,
        )
    )
    numba.parfor.sequential_parfor_lowering = False

    # get the shape signature
    func_args = ["sched"] + func_args
    num_reductions = len(parfor_redvars)
    num_inputs = len(func_args) - len(parfor_output_arrays) - num_reductions
    if config.DEBUG_ARRAY_OPT:
        print("func_args = ", func_args)
        print("num_inputs = ", num_inputs)
        print("parfor_outputs = ", parfor_output_arrays)
        print("parfor_redvars = ", parfor_redvars)
        print("num_reductions = ", num_reductions)
    gu_signature = _create_shape_signature(
        parfor.get_shape_classes,
        num_inputs,
        num_reductions,
        func_args,
        redargstartdim,
        func_sig,
        parfor.races,
        typemap,
    )
    if config.DEBUG_ARRAY_OPT:
        print("gu_signature = ", gu_signature)

    # call the func in parallel by wrapping it with ParallelGUFuncBuilder
    loop_ranges = [(l.start, l.stop, l.step) for l in parfor.loop_nests]
    if config.DEBUG_ARRAY_OPT:
        print("loop_nests = ", parfor.loop_nests)
        print("loop_ranges = ", loop_ranges)
    call_parallel_gufunc(
        lowerer,
        func,
        gu_signature,
        func_sig,
        func_args,
        func_arg_types,
        loop_ranges,
        parfor_redvars,
        parfor_reddict,
        redarrs,
        parfor.init_block,
        index_var_typ,
        parfor.races,
    )
    if config.DEBUG_ARRAY_OPT:
        sys.stdout.flush()

    if nredvars > 0:
        # Perform the final reduction across the reduction array created above.
        thread_count = get_thread_count()
        scope = parfor.init_block.scope
        loc = parfor.init_block.loc

        # For each reduction variable...
        for i in range(nredvars):
            name = parfor_redvars[i]
            redarr = redarrs[name]
            redvar_typ = lowerer.fndesc.typemap[name]

            if config.DEBUG_ARRAY_OPT_RUNTIME:
                res_print_str = "res_print"
                strconsttyp = types.StringLiteral(res_print_str)
                lhs = ir.Var(scope, mk_unique_var("str_const"), loc)
                assign_lhs = ir.Assign(
                    value=ir.Const(value=res_print_str, loc=loc), target=lhs, loc=loc
                )
                typemap[lhs.name] = strconsttyp
                lowerer.lower_inst(assign_lhs)

                res_print = ir.Print(args=[lhs, redarr], vararg=None, loc=loc)
                lowerer.fndesc.calltypes[res_print] = signature(
                    types.none, typemap[lhs.name], typemap[redarr.name]
                )
                print("res_print", res_print)
                lowerer.lower_inst(res_print)

            # For each element in the reduction array created above.
            for j in range(get_thread_count()):
                # Create index var to access that element.
                index_var = ir.Var(scope, mk_unique_var("index_var"), loc)
                index_var_assign = ir.Assign(ir.Const(j, loc), index_var, loc)
                typemap[index_var.name] = types.uintp
                lowerer.lower_inst(index_var_assign)

                # Read that element from the array into oneelem.
                oneelem = ir.Var(scope, mk_unique_var("redelem"), loc)
                oneelemgetitem = ir.Expr.getitem(redarr, index_var, loc)
                typemap[oneelem.name] = redvar_typ
                lowerer.fndesc.calltypes[oneelemgetitem] = signature(
                    redvar_typ, typemap[redarr.name], typemap[index_var.name]
                )
                oneelemassign = ir.Assign(oneelemgetitem, oneelem, loc)
                lowerer.lower_inst(oneelemassign)

                init_var = ir.Var(scope, name + "#init", loc)
                init_assign = ir.Assign(oneelem, init_var, loc)
                if name + "#init" not in typemap:
                    typemap[init_var.name] = redvar_typ
                lowerer.lower_inst(init_assign)

                if config.DEBUG_ARRAY_OPT_RUNTIME:
                    res_print_str = "one_res_print"
                    strconsttyp = types.StringLiteral(res_print_str)
                    lhs = ir.Var(scope, mk_unique_var("str_const"), loc)
                    assign_lhs = ir.Assign(
                        value=ir.Const(value=res_print_str, loc=loc),
                        target=lhs,
                        loc=loc,
                    )
                    typemap[lhs.name] = strconsttyp
                    lowerer.lower_inst(assign_lhs)

                    res_print = ir.Print(
                        args=[lhs, index_var, oneelem, init_var], vararg=None, loc=loc
                    )
                    lowerer.fndesc.calltypes[res_print] = signature(
                        types.none,
                        typemap[lhs.name],
                        typemap[index_var.name],
                        typemap[oneelem.name],
                        typemap[init_var.name],
                    )
                    print("res_print", res_print)
                    lowerer.lower_inst(res_print)

                # generate code for combining reduction variable with thread output
                for inst in parfor_reddict[name][1]:
                    # If we have a case where a parfor body has an array reduction like A += B
                    # and A and B have different data types then the reduction in the parallel
                    # region will operate on those differeing types.  However, here, after the
                    # parallel region, we are summing across the reduction array and that is
                    # guaranteed to have the same data type so we need to change the reduction
                    # nodes so that the right-hand sides have a type equal to the reduction-type
                    # and therefore the left-hand side.
                    if isinstance(inst, ir.Assign):
                        rhs = inst.value
                        # We probably need to generalize this since it only does substitutions in
                        # inplace_binops.
                        if (
                            isinstance(rhs, ir.Expr)
                            and rhs.op == "inplace_binop"
                            and rhs.rhs.name == init_var.name
                        ):
                            if config.DEBUG_ARRAY_OPT:
                                print("Adding call to reduction", rhs)
                            if rhs.fn == operator.isub:
                                rhs.fn = operator.iadd
                                rhs.immutable_fn = operator.add
                            if (
                                rhs.fn == operator.itruediv
                                or rhs.fn == operator.ifloordiv
                            ):
                                rhs.fn = operator.imul
                                rhs.immutable_fn = operator.mul
                            if config.DEBUG_ARRAY_OPT:
                                print("After changing sub to add or div to mul", rhs)
                            # Get calltype of rhs.
                            ct = lowerer.fndesc.calltypes[rhs]
                            assert len(ct.args) == 2
                            # Create new arg types replace the second arg type with the reduction var type.
                            ctargs = (ct.args[0], redvar_typ)
                            # Update the signature of the call.
                            ct = ct.replace(args=ctargs)
                            # Remove so we can re-insert since calltypes is unique dict.
                            lowerer.fndesc.calltypes.pop(rhs)
                            # Add calltype back in for the expr with updated signature.
                            lowerer.fndesc.calltypes[rhs] = ct
                    lowerer.lower_inst(inst)

        # Cleanup reduction variable
        for v in redarrs.values():
            lowerer.lower_inst(ir.Del(v.name, loc=loc))
    # Restore the original typemap of the function that was replaced temporarily at the
    # Beginning of this function.
    lowerer.fndesc.typemap = orig_typemap


# Traceback (AssertionError)
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/errors.py in new_error_context(fmt_, *args, **kwargs)
661     try:
--> 662         yield
663     except NumbaError as e:

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/lowering.py in lower_block(self, block)
257                                    loc=self.loc, errcls_=defaulterrcls):
--> 258                 self.lower_inst(inst)
259

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/lowering.py in lower_inst(self, inst)
409                 if isinstance(inst, _class):
--> 410                     func(self, inst)
411                     return

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/npyufunc/parfor.py in _lower_parfor_parallel(lowerer, parfor)
97     parfor_redvars, parfor_reddict = numba.parfor.get_parfor_reductions(
---> 98         parfor, parfor.params, lowerer.fndesc.calltypes)
99

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/parfor.py in get_parfor_reductions(parfor, parfor_params, calltypes, reductions, reduce_varnames, param_uses, param_nodes, var_to_param)
2968             param_nodes[param].reverse()
-> 2969             reduce_nodes = get_reduce_nodes(param, param_nodes[param])
2970             init_val = guard(get_reduction_init, reduce_nodes)

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/parfor.py in get_reduce_nodes(name, nodes)
3017                 non_red_args = [ x for (x, y) in args if y.name != name ]
-> 3018                 assert len(non_red_args) == 1
3019                 args = [ (x, y) for (x, y) in args if x != y.name ]

AssertionError:

During handling of the above exception, another exception occurred:

LoweringError                             Traceback (most recent call last)
<ipython-input-79-3ff90f149e7e> in <module>
1 n_sims = 100
----> 2 level_data_simulations = run_simulations(n_players, n_sims, t_levels, t_player, player, max_level)

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/dispatcher.py in _compile_for_args(self, *args, **kws)
393                     e.patch_message(''.join(e.args) + help_msg)
394             # ignore the FULL_TRACEBACKS config, this needs reporting!
--> 395             raise e
396
397     def inspect_llvm(self, signature=None):

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/dispatcher.py in _compile_for_args(self, *args, **kws)
350                 argtypes.append(self.typeof_pyval(a))
351         try:
--> 352             return self.compile(tuple(argtypes))
353         except errors.TypingError as e:
354             # Intercept typing error that may be due to an argument

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/compiler_lock.py in _acquire_compile_lock(*args, **kwargs)
30         def _acquire_compile_lock(*args, **kwargs):
31             with self:
---> 32                 return func(*args, **kwargs)
33         return _acquire_compile_lock
34

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/dispatcher.py in compile(self, sig)
691
692             self._cache_misses[sig] += 1
--> 693             cres = self._compiler.compile(args, return_type)
694             self.add_overload(cres)
695             self._cache.save_overload(sig, cres)

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/dispatcher.py in compile(self, args, return_type)
74
75     def compile(self, args, return_type):
---> 76         status, retval = self._compile_cached(args, return_type)
77         if status:
78             return retval

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/dispatcher.py in _compile_cached(self, args, return_type)
88
89         try:
---> 90             retval = self._compile_core(args, return_type)
91         except errors.TypingError as e:
92             self._failed_cache[key] = e

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/dispatcher.py in _compile_core(self, args, return_type)
106                                       args=args, return_type=return_type,
107                                       flags=flags, locals=self.locals,
--> 108                                       pipeline_class=self.pipeline_class)
109         # Check typing error if object mode is used
110         if cres.typing_error is not None and not flags.enable_pyobject:

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/compiler.py in compile_extra(typingctx, targetctx, func, args, return_type, flags, locals, library, pipeline_class)
970     pipeline = pipeline_class(typingctx, targetctx, library,
971                               args, return_type, flags, locals)
--> 972     return pipeline.compile_extra(func)
973
974

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/compiler.py in compile_extra(self, func)
388         self.lifted = ()
389         self.lifted_from = None
--> 390         return self._compile_bytecode()
391
392     def compile_ir(self, func_ir, lifted=(), lifted_from=None):

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/compiler.py in _compile_bytecode(self)
901         """
902         assert self.func_ir is None
--> 903         return self._compile_core()
904
905     def _compile_ir(self):

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/compiler.py in _compile_core(self)
888         self.define_pipelines(pm)
889         pm.finalize()
--> 890         res = pm.run(self.status)
891         if res is not None:
892             # Early pipeline completion

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/compiler_lock.py in _acquire_compile_lock(*args, **kwargs)
30         def _acquire_compile_lock(*args, **kwargs):
31             with self:
---> 32                 return func(*args, **kwargs)
33         return _acquire_compile_lock
34

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/compiler.py in run(self, status)
264                     # No more fallback pipelines?
265                     if is_final_pipeline:
--> 266                         raise patched_exception
267                     # Go to next fallback pipeline
268                     else:

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/compiler.py in run(self, status)
255                 try:
256                     event("-- %s" % stage_name)
--> 257                     stage()
258                 except _EarlyPipelineCompletion as e:
259                     return e.result

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/compiler.py in stage_nopython_backend(self)
762         """
763         lowerfn = self.backend_nopython_mode
--> 764         self._backend(lowerfn, objectmode=False)
765
766     def stage_compile_interp_mode(self):

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/compiler.py in _backend(self, lowerfn, objectmode)
701             self.library.enable_object_caching()
702
--> 703         lowered = lowerfn()
704         signature = typing.signature(self.return_type, *self.args)
705         self.cr = compile_result(

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/compiler.py in backend_nopython_mode(self)
688                 self.calltypes,
689                 self.flags,
--> 690                 self.metadata)
691
692     def _backend(self, lowerfn, objectmode):

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/compiler.py in native_lowering_stage(targetctx, library, interp, typemap, restype, calltypes, flags, metadata)
1141         lower = lowering.Lower(targetctx, library, fndesc, interp,
1142                                metadata=metadata)
-> 1143         lower.lower()
1144         if not flags.no_cpython_wrapper:
1145             lower.create_cpython_wrapper(flags.release_gil)

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/lowering.py in lower(self)
175         if self.generator_info is None:
176             self.genlower = None
--> 177             self.lower_normal_function(self.fndesc)
178         else:
179             self.genlower = self.GeneratorLower(self)

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/lowering.py in lower_normal_function(self, fndesc)
216         # Init argument values
217         self.extract_function_arguments()
--> 218         entry_block_tail = self.lower_function_body()
219
220         # Close tail of entry block

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/lowering.py in lower_function_body(self)
241             bb = self.blkmap[offset]
242             self.builder.position_at_end(bb)
--> 243             self.lower_block(block)
244
245         self.post_lower()

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/lowering.py in lower_block(self, block)
256             with new_error_context('lowering "{inst}" at {loc}', inst=inst,
257                                    loc=self.loc, errcls_=defaulterrcls):
--> 258                 self.lower_inst(inst)
259
260     def create_cpython_wrapper(self, release_gil=False):

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/contextlib.py in __exit__(self, type, value, traceback)
128                 value = type()
129             try:
--> 130                 self.gen.throw(type, value, traceback)
131             except StopIteration as exc:
132                 # Suppress StopIteration *unless* it's the same exception that

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/errors.py in new_error_context(fmt_, *args, **kwargs)
668         from numba import config
669         tb = sys.exc_info()[2] if config.FULL_TRACEBACKS else None
--> 670         six.reraise(type(newerr), newerr, tb)
671
672

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/six.py in reraise(tp, value, tb)
657         if value.__traceback__ is not tb:
658             raise value.with_traceback(tb)
--> 659         raise value
660
661 else:

LoweringError: Failed in nopython mode pipeline (step: nopython mode backend)


File "<ipython-input-78-9c8bcba8955a>", line 4:
def run_simulations(n_p, n_s, t_lvl, t_plr, plr,mx_lvl):
<source elided>
#    lvl_sims = np.empty(shape=(np.shape(lvl)[0], np.shape(lvl)[1], n_s))
for s in prange(n_s):
^

[1] During: lowering "id=0[LoopNest(index_variable = parfor_index.10, range = (0, n_s, 1))]{12: <ir.Block at <ipython-input-78-9c8bcba8955a> (4)>}Var(parfor_index.10, <ipython-input-78-9c8bcba8955a> (4))" at <ipython-input-78-9c8bcba8955a> (4)
-------------------------------------------------------------------------------
This should not have happened, a problem has occurred in Numba's internals.
You are currently using Numba version 0.45.1.

Please report the error message and traceback, along with a minimal reproducer
at: https://github.com/numba/numba/issues/new

If more help is needed please feel free to speak to the Numba core developers
directly at: https://gitter.im/numba/numba

Thanks in advance for your help in improving Numba!

# Fixed code
def _lower_parfor_parallel(lowerer, parfor):
    """Lowerer that handles LLVM code generation for parfor.
    This function lowers a parfor IR node to LLVM.
    The general approach is as follows:
    1) The code from the parfor's init block is lowered normally
       in the context of the current function.
    2) The body of the parfor is transformed into a gufunc function.
    3) Code is inserted into the main function that calls do_scheduling
       to divide the iteration space for each thread, allocates
       reduction arrays, calls the gufunc function, and then invokes
       the reduction function across the reduction arrays to produce
       the final reduction values.
    """
    from .parallel import get_thread_count

    ensure_parallel_support()
    typingctx = lowerer.context.typing_context
    targetctx = lowerer.context
    # We copy the typemap here because for race condition variable we'll
    # update their type to array so they can be updated by the gufunc.
    orig_typemap = lowerer.fndesc.typemap
    # replace original typemap with copy and restore the original at the end.
    lowerer.fndesc.typemap = copy.copy(orig_typemap)
    typemap = lowerer.fndesc.typemap
    varmap = lowerer.varmap

    if config.DEBUG_ARRAY_OPT:
        print("_lower_parfor_parallel")
        parfor.dump()

    loc = parfor.init_block.loc
    scope = parfor.init_block.scope

    # produce instructions for init_block
    if config.DEBUG_ARRAY_OPT:
        print("init_block = ", parfor.init_block, " ", type(parfor.init_block))
    for instr in parfor.init_block.body:
        if config.DEBUG_ARRAY_OPT:
            print("lower init_block instr = ", instr)
        lowerer.lower_inst(instr)

    for racevar in parfor.races:
        if racevar not in varmap:
            rvtyp = typemap[racevar]
            rv = ir.Var(scope, racevar, loc)
            lowerer._alloca_var(rv.name, rvtyp)

    alias_map = {}
    arg_aliases = {}
    numba.parfor.find_potential_aliases_parfor(
        parfor, parfor.params, typemap, lowerer.func_ir, alias_map, arg_aliases
    )
    if config.DEBUG_ARRAY_OPT:
        print("alias_map", alias_map)
        print("arg_aliases", arg_aliases)

    # run get_parfor_outputs() and get_parfor_reductions() before gufunc creation
    # since Jumps are modified so CFG of loop_body dict will become invalid
    assert parfor.params != None

    parfor_output_arrays = numba.parfor.get_parfor_outputs(parfor, parfor.params)
    parfor_redvars, parfor_reddict = numba.parfor.get_parfor_reductions(
        lowerer.func_ir, parfor, parfor.params, lowerer.fndesc.calltypes
    )

    # init reduction array allocation here.
    nredvars = len(parfor_redvars)
    redarrs = {}
    if nredvars > 0:
        # reduction arrays outer dimension equal to thread count
        thread_count = get_thread_count()
        scope = parfor.init_block.scope
        loc = parfor.init_block.loc

        # For each reduction variable...
        for i in range(nredvars):
            redvar_typ = lowerer.fndesc.typemap[parfor_redvars[i]]
            redvar = ir.Var(scope, parfor_redvars[i], loc)
            redarrvar_typ = redtyp_to_redarraytype(redvar_typ)
            reddtype = redarrvar_typ.dtype
            if config.DEBUG_ARRAY_OPT:
                print(
                    "redvar_typ",
                    redvar_typ,
                    redarrvar_typ,
                    reddtype,
                    types.DType(reddtype),
                )

            # If this is reduction over an array,
            # the reduction array has just one added per-worker dimension.
            if isinstance(redvar_typ, types.npytypes.Array):
                redarrdim = redvar_typ.ndim + 1
            else:
                redarrdim = 1

            # Reduction array is created and initialized to the initial reduction value.

            # First create a var for the numpy empty ufunc.
            empty_func = ir.Var(scope, mk_unique_var("empty_func"), loc)
            ff_fnty = get_np_ufunc_typ(np.empty)
            ff_sig = typingctx.resolve_function_type(
                ff_fnty,
                (types.UniTuple(types.intp, redarrdim), types.DType(reddtype)),
                {},
            )
            typemap[empty_func.name] = ff_fnty
            empty_assign = ir.Assign(
                ir.Global("empty", np.empty, loc=loc), empty_func, loc
            )
            lowerer.lower_inst(empty_assign)

            # Create var for outer dimension size of reduction array equal to number of threads.
            num_threads_var = ir.Var(scope, mk_unique_var("num_threads"), loc)
            num_threads_assign = ir.Assign(
                ir.Const(thread_count, loc), num_threads_var, loc
            )
            typemap[num_threads_var.name] = types.intp
            lowerer.lower_inst(num_threads_assign)

            # Empty call takes tuple of sizes.  Create here and fill in outer dimension (num threads).
            size_var = ir.Var(scope, mk_unique_var("tuple_size_var"), loc)
            typemap[size_var.name] = types.UniTuple(types.intp, redarrdim)
            size_var_list = [num_threads_var]

            # If this is a reduction over an array...
            if isinstance(redvar_typ, types.npytypes.Array):
                # Add code to get the shape of the array being reduced over.
                redshape_var = ir.Var(scope, mk_unique_var("redarr_shape"), loc)
                typemap[redshape_var.name] = types.UniTuple(types.intp, redvar_typ.ndim)
                redshape_getattr = ir.Expr.getattr(redvar, "shape", loc)
                redshape_assign = ir.Assign(redshape_getattr, redshape_var, loc)
                lowerer.lower_inst(redshape_assign)

                # Add the dimension sizes of the array being reduced over to the tuple of sizes pass to empty.
                for j in range(redvar_typ.ndim):
                    onedimvar = ir.Var(scope, mk_unique_var("redshapeonedim"), loc)
                    onedimgetitem = ir.Expr.static_getitem(redshape_var, j, None, loc)
                    typemap[onedimvar.name] = types.intp
                    onedimassign = ir.Assign(onedimgetitem, onedimvar, loc)
                    lowerer.lower_inst(onedimassign)
                    size_var_list += [onedimvar]

            size_call = ir.Expr.build_tuple(size_var_list, loc)
            size_assign = ir.Assign(size_call, size_var, loc)
            lowerer.lower_inst(size_assign)

            # Add call to empty passing the size var tuple.
            empty_call = ir.Expr.call(empty_func, [size_var], {}, loc=loc)
            redarr_var = ir.Var(scope, mk_unique_var("redarr"), loc)
            typemap[redarr_var.name] = redarrvar_typ
            empty_call_assign = ir.Assign(empty_call, redarr_var, loc)
            lowerer.fndesc.calltypes[empty_call] = ff_sig
            lowerer.lower_inst(empty_call_assign)

            # Remember mapping of original reduction array to the newly created per-worker reduction array.
            redarrs[redvar.name] = redarr_var

            init_val = parfor_reddict[parfor_redvars[i]][0]
            if init_val != None:
                if isinstance(redvar_typ, types.npytypes.Array):
                    # Create an array of identity values for the reduction.
                    # First, create a variable for np.full.
                    full_func = ir.Var(scope, mk_unique_var("full_func"), loc)
                    full_fnty = get_np_ufunc_typ(np.full)
                    full_sig = typingctx.resolve_function_type(
                        full_fnty,
                        (
                            types.UniTuple(types.intp, redvar_typ.ndim),
                            reddtype,
                            types.DType(reddtype),
                        ),
                        {},
                    )
                    typemap[full_func.name] = full_fnty
                    full_assign = ir.Assign(
                        ir.Global("full", np.full, loc=loc), full_func, loc
                    )
                    lowerer.lower_inst(full_assign)

                    # Then create a var with the identify value.
                    init_val_var = ir.Var(scope, mk_unique_var("init_val"), loc)
                    init_val_assign = ir.Assign(
                        ir.Const(init_val, loc), init_val_var, loc
                    )
                    typemap[init_val_var.name] = reddtype
                    lowerer.lower_inst(init_val_assign)

                    # Then, call np.full with the shape of the reduction array and the identity value.
                    full_call = ir.Expr.call(
                        full_func, [redshape_var, init_val_var], {}, loc=loc
                    )
                    lowerer.fndesc.calltypes[full_call] = full_sig
                    redtoset = ir.Var(scope, mk_unique_var("redtoset"), loc)
                    redtoset_assign = ir.Assign(full_call, redtoset, loc)
                    typemap[redtoset.name] = redvar_typ
                    lowerer.lower_inst(redtoset_assign)
                else:
                    redtoset = ir.Var(scope, mk_unique_var("redtoset"), loc)
                    redtoset_assign = ir.Assign(ir.Const(init_val, loc), redtoset, loc)
                    typemap[redtoset.name] = reddtype
                    lowerer.lower_inst(redtoset_assign)
            else:
                redtoset = redvar

            # For each thread, initialize the per-worker reduction array to the current reduction array value.
            for j in range(get_thread_count()):
                index_var = ir.Var(scope, mk_unique_var("index_var"), loc)
                index_var_assign = ir.Assign(ir.Const(j, loc), index_var, loc)
                typemap[index_var.name] = types.uintp
                lowerer.lower_inst(index_var_assign)

                redsetitem = ir.SetItem(redarr_var, index_var, redtoset, loc)
                lowerer.fndesc.calltypes[redsetitem] = signature(
                    types.none,
                    typemap[redarr_var.name],
                    typemap[index_var.name],
                    redvar_typ,
                )
                lowerer.lower_inst(redsetitem)

    # compile parfor body as a separate function to be used with GUFuncWrapper
    flags = copy.copy(parfor.flags)
    flags.set("error_model", "numpy")
    # Can't get here unless  flags.set('auto_parallel', ParallelOptions(True))
    index_var_typ = typemap[parfor.loop_nests[0].index_variable.name]
    # index variables should have the same type, check rest of indices
    for l in parfor.loop_nests[1:]:
        assert typemap[l.index_variable.name] == index_var_typ
    numba.parfor.sequential_parfor_lowering = True
    func, func_args, func_sig, redargstartdim, func_arg_types = (
        _create_gufunc_for_parfor_body(
            lowerer,
            parfor,
            typemap,
            typingctx,
            targetctx,
            flags,
            {},
            bool(alias_map),
            index_var_typ,
            parfor.races,
        )
    )
    numba.parfor.sequential_parfor_lowering = False

    # get the shape signature
    func_args = ["sched"] + func_args
    num_reductions = len(parfor_redvars)
    num_inputs = len(func_args) - len(parfor_output_arrays) - num_reductions
    if config.DEBUG_ARRAY_OPT:
        print("func_args = ", func_args)
        print("num_inputs = ", num_inputs)
        print("parfor_outputs = ", parfor_output_arrays)
        print("parfor_redvars = ", parfor_redvars)
        print("num_reductions = ", num_reductions)
    gu_signature = _create_shape_signature(
        parfor.get_shape_classes,
        num_inputs,
        num_reductions,
        func_args,
        redargstartdim,
        func_sig,
        parfor.races,
        typemap,
    )
    if config.DEBUG_ARRAY_OPT:
        print("gu_signature = ", gu_signature)

    # call the func in parallel by wrapping it with ParallelGUFuncBuilder
    loop_ranges = [(l.start, l.stop, l.step) for l in parfor.loop_nests]
    if config.DEBUG_ARRAY_OPT:
        print("loop_nests = ", parfor.loop_nests)
        print("loop_ranges = ", loop_ranges)
    call_parallel_gufunc(
        lowerer,
        func,
        gu_signature,
        func_sig,
        func_args,
        func_arg_types,
        loop_ranges,
        parfor_redvars,
        parfor_reddict,
        redarrs,
        parfor.init_block,
        index_var_typ,
        parfor.races,
    )
    if config.DEBUG_ARRAY_OPT:
        sys.stdout.flush()

    if nredvars > 0:
        # Perform the final reduction across the reduction array created above.
        thread_count = get_thread_count()
        scope = parfor.init_block.scope
        loc = parfor.init_block.loc

        # For each reduction variable...
        for i in range(nredvars):
            name = parfor_redvars[i]
            redarr = redarrs[name]
            redvar_typ = lowerer.fndesc.typemap[name]

            if config.DEBUG_ARRAY_OPT_RUNTIME:
                res_print_str = "res_print"
                strconsttyp = types.StringLiteral(res_print_str)
                lhs = ir.Var(scope, mk_unique_var("str_const"), loc)
                assign_lhs = ir.Assign(
                    value=ir.Const(value=res_print_str, loc=loc), target=lhs, loc=loc
                )
                typemap[lhs.name] = strconsttyp
                lowerer.lower_inst(assign_lhs)

                res_print = ir.Print(args=[lhs, redarr], vararg=None, loc=loc)
                lowerer.fndesc.calltypes[res_print] = signature(
                    types.none, typemap[lhs.name], typemap[redarr.name]
                )
                print("res_print", res_print)
                lowerer.lower_inst(res_print)

            # For each element in the reduction array created above.
            for j in range(get_thread_count()):
                # Create index var to access that element.
                index_var = ir.Var(scope, mk_unique_var("index_var"), loc)
                index_var_assign = ir.Assign(ir.Const(j, loc), index_var, loc)
                typemap[index_var.name] = types.uintp
                lowerer.lower_inst(index_var_assign)

                # Read that element from the array into oneelem.
                oneelem = ir.Var(scope, mk_unique_var("redelem"), loc)
                oneelemgetitem = ir.Expr.getitem(redarr, index_var, loc)
                typemap[oneelem.name] = redvar_typ
                lowerer.fndesc.calltypes[oneelemgetitem] = signature(
                    redvar_typ, typemap[redarr.name], typemap[index_var.name]
                )
                oneelemassign = ir.Assign(oneelemgetitem, oneelem, loc)
                lowerer.lower_inst(oneelemassign)

                init_var = ir.Var(scope, name + "#init", loc)
                init_assign = ir.Assign(oneelem, init_var, loc)
                if name + "#init" not in typemap:
                    typemap[init_var.name] = redvar_typ
                lowerer.lower_inst(init_assign)

                if config.DEBUG_ARRAY_OPT_RUNTIME:
                    res_print_str = "one_res_print"
                    strconsttyp = types.StringLiteral(res_print_str)
                    lhs = ir.Var(scope, mk_unique_var("str_const"), loc)
                    assign_lhs = ir.Assign(
                        value=ir.Const(value=res_print_str, loc=loc),
                        target=lhs,
                        loc=loc,
                    )
                    typemap[lhs.name] = strconsttyp
                    lowerer.lower_inst(assign_lhs)

                    res_print = ir.Print(
                        args=[lhs, index_var, oneelem, init_var], vararg=None, loc=loc
                    )
                    lowerer.fndesc.calltypes[res_print] = signature(
                        types.none,
                        typemap[lhs.name],
                        typemap[index_var.name],
                        typemap[oneelem.name],
                        typemap[init_var.name],
                    )
                    print("res_print", res_print)
                    lowerer.lower_inst(res_print)

                # generate code for combining reduction variable with thread output
                for inst in parfor_reddict[name][1]:
                    # If we have a case where a parfor body has an array reduction like A += B
                    # and A and B have different data types then the reduction in the parallel
                    # region will operate on those differeing types.  However, here, after the
                    # parallel region, we are summing across the reduction array and that is
                    # guaranteed to have the same data type so we need to change the reduction
                    # nodes so that the right-hand sides have a type equal to the reduction-type
                    # and therefore the left-hand side.
                    if isinstance(inst, ir.Assign):
                        rhs = inst.value
                        # We probably need to generalize this since it only does substitutions in
                        # inplace_binops.
                        if (
                            isinstance(rhs, ir.Expr)
                            and rhs.op == "inplace_binop"
                            and rhs.rhs.name == init_var.name
                        ):
                            if config.DEBUG_ARRAY_OPT:
                                print("Adding call to reduction", rhs)
                            if rhs.fn == operator.isub:
                                rhs.fn = operator.iadd
                                rhs.immutable_fn = operator.add
                            if (
                                rhs.fn == operator.itruediv
                                or rhs.fn == operator.ifloordiv
                            ):
                                rhs.fn = operator.imul
                                rhs.immutable_fn = operator.mul
                            if config.DEBUG_ARRAY_OPT:
                                print("After changing sub to add or div to mul", rhs)
                            # Get calltype of rhs.
                            ct = lowerer.fndesc.calltypes[rhs]
                            assert len(ct.args) == 2
                            # Create new arg types replace the second arg type with the reduction var type.
                            ctargs = (ct.args[0], redvar_typ)
                            # Update the signature of the call.
                            ct = ct.replace(args=ctargs)
                            # Remove so we can re-insert since calltypes is unique dict.
                            lowerer.fndesc.calltypes.pop(rhs)
                            # Add calltype back in for the expr with updated signature.
                            lowerer.fndesc.calltypes[rhs] = ct
                    lowerer.lower_inst(inst)

        # Cleanup reduction variable
        for v in redarrs.values():
            lowerer.lower_inst(ir.Del(v.name, loc=loc))
    # Restore the original typemap of the function that was replaced temporarily at the
    # Beginning of this function.
    lowerer.fndesc.typemap = orig_typemap