# Buggy code
def _create_gufunc_for_parfor_body(
    lowerer,
    parfor,
    typemap,
    typingctx,
    targetctx,
    flags,
    locals,
    has_aliases,
    index_var_typ,
    races,
):
    """
    Takes a parfor and creates a gufunc function for its body.
    There are two parts to this function.
    1) Code to iterate across the iteration space as defined by the schedule.
    2) The parfor body that does the work for a single point in the iteration space.
    Part 1 is created as Python text for simplicity with a sentinel assignment to mark the point
    in the IR where the parfor body should be added.
    This Python text is 'exec'ed into existence and its IR retrieved with run_frontend.
    The IR is scanned for the sentinel assignment where that basic block is split and the IR
    for the parfor body inserted.
    """

    loc = parfor.init_block.loc

    # The parfor body and the main function body share ir.Var nodes.
    # We have to do some replacements of Var names in the parfor body to make them
    # legal parameter names.  If we don't copy then the Vars in the main function also
    # would incorrectly change their name.
    loop_body = copy.copy(parfor.loop_body)
    remove_dels(loop_body)

    parfor_dim = len(parfor.loop_nests)
    loop_indices = [l.index_variable.name for l in parfor.loop_nests]

    # Get all the parfor params.
    parfor_params = parfor.params
    # Get just the outputs of the parfor.
    parfor_outputs = numba.parfor.get_parfor_outputs(parfor, parfor_params)
    # Get all parfor reduction vars, and operators.
    typemap = lowerer.fndesc.typemap
    parfor_redvars, parfor_reddict = numba.parfor.get_parfor_reductions(
        parfor, parfor_params, lowerer.fndesc.calltypes
    )
    # Compute just the parfor inputs as a set difference.
    parfor_inputs = sorted(
        list(set(parfor_params) - set(parfor_outputs) - set(parfor_redvars))
    )

    races = races.difference(set(parfor_redvars))
    for race in races:
        msg = (
            "Variable %s used in parallel loop may be written "
            "to simultaneously by multiple workers and may result "
            "in non-deterministic or unintended results." % race
        )
        warnings.warn(NumbaParallelSafetyWarning(msg, loc))
    replace_var_with_array(races, loop_body, typemap, lowerer.fndesc.calltypes)

    if config.DEBUG_ARRAY_OPT >= 1:
        print("parfor_params = ", parfor_params, " ", type(parfor_params))
        print("parfor_outputs = ", parfor_outputs, " ", type(parfor_outputs))
        print("parfor_inputs = ", parfor_inputs, " ", type(parfor_inputs))
        print("parfor_redvars = ", parfor_redvars, " ", type(parfor_redvars))

    # Reduction variables are represented as arrays, so they go under
    # different names.
    parfor_redarrs = []
    parfor_red_arg_types = []
    for var in parfor_redvars:
        arr = var + "_arr"
        parfor_redarrs.append(arr)
        redarraytype = redtyp_to_redarraytype(typemap[var])
        parfor_red_arg_types.append(redarraytype)
        redarrsig = redarraytype_to_sig(redarraytype)
        if arr in typemap:
            assert typemap[arr] == redarrsig
        else:
            typemap[arr] = redarrsig

    # Reorder all the params so that inputs go first then outputs.
    parfor_params = parfor_inputs + parfor_outputs + parfor_redarrs

    if config.DEBUG_ARRAY_OPT >= 1:
        print("parfor_params = ", parfor_params, " ", type(parfor_params))
        print("loop_indices = ", loop_indices, " ", type(loop_indices))
        print("loop_body = ", loop_body, " ", type(loop_body))
        _print_body(loop_body)

    # Some Var are not legal parameter names so create a dict of potentially illegal
    # param name to guaranteed legal name.
    param_dict = legalize_names_with_typemap(parfor_params + parfor_redvars, typemap)
    if config.DEBUG_ARRAY_OPT >= 1:
        print("param_dict = ", sorted(param_dict.items()), " ", type(param_dict))

    # Some loop_indices are not legal parameter names so create a dict of potentially illegal
    # loop index to guaranteed legal name.
    ind_dict = legalize_names_with_typemap(loop_indices, typemap)
    # Compute a new list of legal loop index names.
    legal_loop_indices = [ind_dict[v] for v in loop_indices]
    if config.DEBUG_ARRAY_OPT >= 1:
        print("ind_dict = ", sorted(ind_dict.items()), " ", type(ind_dict))
        print(
            "legal_loop_indices = ", legal_loop_indices, " ", type(legal_loop_indices)
        )
        for pd in parfor_params:
            print("pd = ", pd)
            print("pd type = ", typemap[pd], " ", type(typemap[pd]))

    # Get the types of each parameter.
    param_types = [to_scalar_from_0d(typemap[v]) for v in parfor_params]
    # Calculate types of args passed to gufunc.
    func_arg_types = [
        typemap[v] for v in (parfor_inputs + parfor_outputs)
    ] + parfor_red_arg_types

    # Replace illegal parameter names in the loop body with legal ones.
    replace_var_names(loop_body, param_dict)
    # remember the name before legalizing as the actual arguments
    parfor_args = parfor_params
    # Change parfor_params to be legal names.
    parfor_params = [param_dict[v] for v in parfor_params]
    parfor_params_orig = parfor_params

    parfor_params = []
    ascontig = False
    for pindex in range(len(parfor_params_orig)):
        if (
            ascontig
            and pindex < len(parfor_inputs)
            and isinstance(param_types[pindex], types.npytypes.Array)
        ):
            parfor_params.append(parfor_params_orig[pindex] + "param")
        else:
            parfor_params.append(parfor_params_orig[pindex])

    # Change parfor body to replace illegal loop index vars with legal ones.
    replace_var_names(loop_body, ind_dict)
    loop_body_var_table = get_name_var_table(loop_body)
    sentinel_name = get_unused_var_name("__sentinel__", loop_body_var_table)

    if config.DEBUG_ARRAY_OPT >= 1:
        print("legal parfor_params = ", parfor_params, " ", type(parfor_params))

    # Determine the unique names of the scheduling and gufunc functions.
    # sched_func_name = "__numba_parfor_sched_%s" % (hex(hash(parfor)).replace("-", "_"))
    gufunc_name = "__numba_parfor_gufunc_%s" % (hex(hash(parfor)).replace("-", "_"))
    if config.DEBUG_ARRAY_OPT:
        # print("sched_func_name ", type(sched_func_name), " ", sched_func_name)
        print("gufunc_name ", type(gufunc_name), " ", gufunc_name)

    gufunc_txt = ""

    # Create the gufunc function.
    gufunc_txt += (
        "def " + gufunc_name + "(sched, " + (", ".join(parfor_params)) + "):\n"
    )

    for pindex in range(len(parfor_inputs)):
        if ascontig and isinstance(param_types[pindex], types.npytypes.Array):
            gufunc_txt += (
                "    "
                + parfor_params_orig[pindex]
                + " = np.ascontiguousarray("
                + parfor_params[pindex]
                + ")\n"
            )

    # Add initialization of reduction variables
    for arr, var in zip(parfor_redarrs, parfor_redvars):
        # If reduction variable is a scalar then save current value to
        # temp and accumulate on that temp to prevent false sharing.
        if redtyp_is_scalar(typemap[var]):
            gufunc_txt += "    " + param_dict[var] + "=" + param_dict[arr] + "[0]\n"
        else:
            # The reduction variable is an array so np.copy it to a temp.
            gufunc_txt += (
                "    " + param_dict[var] + "=np.copy(" + param_dict[arr] + ")\n"
            )

    # For each dimension of the parfor, create a for loop in the generated gufunc function.
    # Iterate across the proper values extracted from the schedule.
    # The form of the schedule is start_dim0, start_dim1, ..., start_dimN, end_dim0,
    # end_dim1, ..., end_dimN
    for eachdim in range(parfor_dim):
        for indent in range(eachdim + 1):
            gufunc_txt += "    "
        sched_dim = eachdim
        gufunc_txt += (
            "for "
            + legal_loop_indices[eachdim]
            + " in range(sched["
            + str(sched_dim)
            + "], sched["
            + str(sched_dim + parfor_dim)
            + "] + np.uint8(1)):\n"
        )

    if config.DEBUG_ARRAY_OPT_RUNTIME:
        for indent in range(parfor_dim + 1):
            gufunc_txt += "    "
        gufunc_txt += "print("
        for eachdim in range(parfor_dim):
            gufunc_txt += (
                '"'
                + legal_loop_indices[eachdim]
                + '",'
                + legal_loop_indices[eachdim]
                + ","
            )
        gufunc_txt += ")\n"

    # Add the sentinel assignment so that we can find the loop body position
    # in the IR.
    for indent in range(parfor_dim + 1):
        gufunc_txt += "    "
    gufunc_txt += sentinel_name + " = 0\n"
    # Add assignments of reduction variables (for returning the value)
    redargstartdim = {}
    for arr, var in zip(parfor_redarrs, parfor_redvars):
        # After the gufunc loops, copy the accumulated temp value back to reduction array.
        if redtyp_is_scalar(typemap[var]):
            gufunc_txt += "    " + param_dict[arr] + "[0] = " + param_dict[var] + "\n"
            redargstartdim[arr] = 1
        else:
            # After the gufunc loops, copy the accumulated temp array back to reduction array with ":"
            gufunc_txt += (
                "    " + param_dict[arr] + "[:] = " + param_dict[var] + "[:]\n"
            )
            redargstartdim[arr] = 0
    gufunc_txt += "    return None\n"

    if config.DEBUG_ARRAY_OPT:
        print("gufunc_txt = ", type(gufunc_txt), "\n", gufunc_txt)
    # Force gufunc outline into existence.
    globls = {"np": np}
    locls = {}
    exec_(gufunc_txt, globls, locls)
    gufunc_func = locls[gufunc_name]

    if config.DEBUG_ARRAY_OPT:
        print("gufunc_func = ", type(gufunc_func), "\n", gufunc_func)
    # Get the IR for the gufunc outline.
    gufunc_ir = compiler.run_frontend(gufunc_func)

    if config.DEBUG_ARRAY_OPT:
        print("gufunc_ir dump ", type(gufunc_ir))
        gufunc_ir.dump()
        print("loop_body dump ", type(loop_body))
        _print_body(loop_body)

    # rename all variables in gufunc_ir afresh
    var_table = get_name_var_table(gufunc_ir.blocks)
    new_var_dict = {}
    reserved_names = [sentinel_name] + list(param_dict.values()) + legal_loop_indices
    for name, var in var_table.items():
        if not (name in reserved_names):
            new_var_dict[name] = mk_unique_var(name)
    replace_var_names(gufunc_ir.blocks, new_var_dict)
    if config.DEBUG_ARRAY_OPT:
        print("gufunc_ir dump after renaming ")
        gufunc_ir.dump()

    gufunc_param_types = [
        numba.types.npytypes.Array(index_var_typ, 1, "C")
    ] + param_types
    if config.DEBUG_ARRAY_OPT:
        print(
            "gufunc_param_types = ", type(gufunc_param_types), "\n", gufunc_param_types
        )

    gufunc_stub_last_label = max(gufunc_ir.blocks.keys()) + 1

    # Add gufunc stub last label to each parfor.loop_body label to prevent
    # label conflicts.
    loop_body = add_offset_to_labels(loop_body, gufunc_stub_last_label)
    # new label for splitting sentinel block
    new_label = max(loop_body.keys()) + 1

    # If enabled, add a print statement after every assignment.
    if config.DEBUG_ARRAY_OPT_RUNTIME:
        for label, block in loop_body.items():
            new_block = block.copy()
            new_block.clear()
            loc = block.loc
            scope = block.scope
            for inst in block.body:
                new_block.append(inst)
                # Append print after assignment
                if isinstance(inst, ir.Assign):
                    # Only apply to numbers
                    if typemap[inst.target.name] not in types.number_domain:
                        continue

                    # Make constant string
                    strval = "{} =".format(inst.target.name)
                    strconsttyp = types.StringLiteral(strval)

                    lhs = ir.Var(scope, mk_unique_var("str_const"), loc)
                    assign_lhs = ir.Assign(
                        value=ir.Const(value=strval, loc=loc), target=lhs, loc=loc
                    )
                    typemap[lhs.name] = strconsttyp
                    new_block.append(assign_lhs)

                    # Make print node
                    print_node = ir.Print(args=[lhs, inst.target], vararg=None, loc=loc)
                    new_block.append(print_node)
                    sig = numba.typing.signature(
                        types.none, typemap[lhs.name], typemap[inst.target.name]
                    )
                    lowerer.fndesc.calltypes[print_node] = sig
            loop_body[label] = new_block

    if config.DEBUG_ARRAY_OPT:
        print("parfor loop body")
        _print_body(loop_body)

    wrapped_blocks = wrap_loop_body(loop_body)
    hoisted, not_hoisted = hoist(parfor_params, loop_body, typemap, wrapped_blocks)
    start_block = gufunc_ir.blocks[min(gufunc_ir.blocks.keys())]
    start_block.body = start_block.body[:-1] + hoisted + [start_block.body[-1]]
    unwrap_loop_body(loop_body)

    # store hoisted into diagnostics
    diagnostics = lowerer.metadata["parfor_diagnostics"]
    diagnostics.hoist_info[parfor.id] = {"hoisted": hoisted, "not_hoisted": not_hoisted}

    if config.DEBUG_ARRAY_OPT:
        print("After hoisting")
        _print_body(loop_body)

    # Search all the block in the gufunc outline for the sentinel assignment.
    for label, block in gufunc_ir.blocks.items():
        for i, inst in enumerate(block.body):
            if isinstance(inst, ir.Assign) and inst.target.name == sentinel_name:
                # We found the sentinel assignment.
                loc = inst.loc
                scope = block.scope
                # split block across __sentinel__
                # A new block is allocated for the statements prior to the sentinel
                # but the new block maintains the current block label.
                prev_block = ir.Block(scope, loc)
                prev_block.body = block.body[:i]
                # The current block is used for statements after the sentinel.
                block.body = block.body[i + 1 :]
                # But the current block gets a new label.
                body_first_label = min(loop_body.keys())

                # The previous block jumps to the minimum labelled block of the
                # parfor body.
                prev_block.append(ir.Jump(body_first_label, loc))
                # Add all the parfor loop body blocks to the gufunc function's
                # IR.
                for l, b in loop_body.items():
                    gufunc_ir.blocks[l] = b
                body_last_label = max(loop_body.keys())
                gufunc_ir.blocks[new_label] = block
                gufunc_ir.blocks[label] = prev_block
                # Add a jump from the last parfor body block to the block containing
                # statements after the sentinel.
                gufunc_ir.blocks[body_last_label].append(ir.Jump(new_label, loc))
                break
        else:
            continue
        break

    if config.DEBUG_ARRAY_OPT:
        print("gufunc_ir last dump before renaming")
        gufunc_ir.dump()

    gufunc_ir.blocks = rename_labels(gufunc_ir.blocks)
    remove_dels(gufunc_ir.blocks)

    if config.DEBUG_ARRAY_OPT:
        print("gufunc_ir last dump")
        gufunc_ir.dump()
        print("flags", flags)
        print("typemap", typemap)

    old_alias = flags.noalias
    if not has_aliases:
        if config.DEBUG_ARRAY_OPT:
            print("No aliases found so adding noalias flag.")
        flags.noalias = True
    kernel_func = compiler.compile_ir(
        typingctx, targetctx, gufunc_ir, gufunc_param_types, types.none, flags, locals
    )

    flags.noalias = old_alias

    kernel_sig = signature(types.none, *gufunc_param_types)
    if config.DEBUG_ARRAY_OPT:
        print("kernel_sig = ", kernel_sig)

    return kernel_func, parfor_args, kernel_sig, redargstartdim, func_arg_types


# 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 _create_gufunc_for_parfor_body(
    lowerer,
    parfor,
    typemap,
    typingctx,
    targetctx,
    flags,
    locals,
    has_aliases,
    index_var_typ,
    races,
):
    """
    Takes a parfor and creates a gufunc function for its body.
    There are two parts to this function.
    1) Code to iterate across the iteration space as defined by the schedule.
    2) The parfor body that does the work for a single point in the iteration space.
    Part 1 is created as Python text for simplicity with a sentinel assignment to mark the point
    in the IR where the parfor body should be added.
    This Python text is 'exec'ed into existence and its IR retrieved with run_frontend.
    The IR is scanned for the sentinel assignment where that basic block is split and the IR
    for the parfor body inserted.
    """

    loc = parfor.init_block.loc

    # The parfor body and the main function body share ir.Var nodes.
    # We have to do some replacements of Var names in the parfor body to make them
    # legal parameter names.  If we don't copy then the Vars in the main function also
    # would incorrectly change their name.
    loop_body = copy.copy(parfor.loop_body)
    remove_dels(loop_body)

    parfor_dim = len(parfor.loop_nests)
    loop_indices = [l.index_variable.name for l in parfor.loop_nests]

    # Get all the parfor params.
    parfor_params = parfor.params
    # Get just the outputs of the parfor.
    parfor_outputs = numba.parfor.get_parfor_outputs(parfor, parfor_params)
    # Get all parfor reduction vars, and operators.
    typemap = lowerer.fndesc.typemap
    parfor_redvars, parfor_reddict = numba.parfor.get_parfor_reductions(
        lowerer.func_ir, parfor, parfor_params, lowerer.fndesc.calltypes
    )
    # Compute just the parfor inputs as a set difference.
    parfor_inputs = sorted(
        list(set(parfor_params) - set(parfor_outputs) - set(parfor_redvars))
    )

    races = races.difference(set(parfor_redvars))
    for race in races:
        msg = (
            "Variable %s used in parallel loop may be written "
            "to simultaneously by multiple workers and may result "
            "in non-deterministic or unintended results." % race
        )
        warnings.warn(NumbaParallelSafetyWarning(msg, loc))
    replace_var_with_array(races, loop_body, typemap, lowerer.fndesc.calltypes)

    if config.DEBUG_ARRAY_OPT >= 1:
        print("parfor_params = ", parfor_params, " ", type(parfor_params))
        print("parfor_outputs = ", parfor_outputs, " ", type(parfor_outputs))
        print("parfor_inputs = ", parfor_inputs, " ", type(parfor_inputs))
        print("parfor_redvars = ", parfor_redvars, " ", type(parfor_redvars))

    # Reduction variables are represented as arrays, so they go under
    # different names.
    parfor_redarrs = []
    parfor_red_arg_types = []
    for var in parfor_redvars:
        arr = var + "_arr"
        parfor_redarrs.append(arr)
        redarraytype = redtyp_to_redarraytype(typemap[var])
        parfor_red_arg_types.append(redarraytype)
        redarrsig = redarraytype_to_sig(redarraytype)
        if arr in typemap:
            assert typemap[arr] == redarrsig
        else:
            typemap[arr] = redarrsig

    # Reorder all the params so that inputs go first then outputs.
    parfor_params = parfor_inputs + parfor_outputs + parfor_redarrs

    if config.DEBUG_ARRAY_OPT >= 1:
        print("parfor_params = ", parfor_params, " ", type(parfor_params))
        print("loop_indices = ", loop_indices, " ", type(loop_indices))
        print("loop_body = ", loop_body, " ", type(loop_body))
        _print_body(loop_body)

    # Some Var are not legal parameter names so create a dict of potentially illegal
    # param name to guaranteed legal name.
    param_dict = legalize_names_with_typemap(parfor_params + parfor_redvars, typemap)
    if config.DEBUG_ARRAY_OPT >= 1:
        print("param_dict = ", sorted(param_dict.items()), " ", type(param_dict))

    # Some loop_indices are not legal parameter names so create a dict of potentially illegal
    # loop index to guaranteed legal name.
    ind_dict = legalize_names_with_typemap(loop_indices, typemap)
    # Compute a new list of legal loop index names.
    legal_loop_indices = [ind_dict[v] for v in loop_indices]
    if config.DEBUG_ARRAY_OPT >= 1:
        print("ind_dict = ", sorted(ind_dict.items()), " ", type(ind_dict))
        print(
            "legal_loop_indices = ", legal_loop_indices, " ", type(legal_loop_indices)
        )
        for pd in parfor_params:
            print("pd = ", pd)
            print("pd type = ", typemap[pd], " ", type(typemap[pd]))

    # Get the types of each parameter.
    param_types = [to_scalar_from_0d(typemap[v]) for v in parfor_params]
    # Calculate types of args passed to gufunc.
    func_arg_types = [
        typemap[v] for v in (parfor_inputs + parfor_outputs)
    ] + parfor_red_arg_types

    # Replace illegal parameter names in the loop body with legal ones.
    replace_var_names(loop_body, param_dict)
    # remember the name before legalizing as the actual arguments
    parfor_args = parfor_params
    # Change parfor_params to be legal names.
    parfor_params = [param_dict[v] for v in parfor_params]
    parfor_params_orig = parfor_params

    parfor_params = []
    ascontig = False
    for pindex in range(len(parfor_params_orig)):
        if (
            ascontig
            and pindex < len(parfor_inputs)
            and isinstance(param_types[pindex], types.npytypes.Array)
        ):
            parfor_params.append(parfor_params_orig[pindex] + "param")
        else:
            parfor_params.append(parfor_params_orig[pindex])

    # Change parfor body to replace illegal loop index vars with legal ones.
    replace_var_names(loop_body, ind_dict)
    loop_body_var_table = get_name_var_table(loop_body)
    sentinel_name = get_unused_var_name("__sentinel__", loop_body_var_table)

    if config.DEBUG_ARRAY_OPT >= 1:
        print("legal parfor_params = ", parfor_params, " ", type(parfor_params))

    # Determine the unique names of the scheduling and gufunc functions.
    # sched_func_name = "__numba_parfor_sched_%s" % (hex(hash(parfor)).replace("-", "_"))
    gufunc_name = "__numba_parfor_gufunc_%s" % (hex(hash(parfor)).replace("-", "_"))
    if config.DEBUG_ARRAY_OPT:
        # print("sched_func_name ", type(sched_func_name), " ", sched_func_name)
        print("gufunc_name ", type(gufunc_name), " ", gufunc_name)

    gufunc_txt = ""

    # Create the gufunc function.
    gufunc_txt += (
        "def " + gufunc_name + "(sched, " + (", ".join(parfor_params)) + "):\n"
    )

    for pindex in range(len(parfor_inputs)):
        if ascontig and isinstance(param_types[pindex], types.npytypes.Array):
            gufunc_txt += (
                "    "
                + parfor_params_orig[pindex]
                + " = np.ascontiguousarray("
                + parfor_params[pindex]
                + ")\n"
            )

    # Add initialization of reduction variables
    for arr, var in zip(parfor_redarrs, parfor_redvars):
        # If reduction variable is a scalar then save current value to
        # temp and accumulate on that temp to prevent false sharing.
        if redtyp_is_scalar(typemap[var]):
            gufunc_txt += "    " + param_dict[var] + "=" + param_dict[arr] + "[0]\n"
        else:
            # The reduction variable is an array so np.copy it to a temp.
            gufunc_txt += (
                "    " + param_dict[var] + "=np.copy(" + param_dict[arr] + ")\n"
            )

    # For each dimension of the parfor, create a for loop in the generated gufunc function.
    # Iterate across the proper values extracted from the schedule.
    # The form of the schedule is start_dim0, start_dim1, ..., start_dimN, end_dim0,
    # end_dim1, ..., end_dimN
    for eachdim in range(parfor_dim):
        for indent in range(eachdim + 1):
            gufunc_txt += "    "
        sched_dim = eachdim
        gufunc_txt += (
            "for "
            + legal_loop_indices[eachdim]
            + " in range(sched["
            + str(sched_dim)
            + "], sched["
            + str(sched_dim + parfor_dim)
            + "] + np.uint8(1)):\n"
        )

    if config.DEBUG_ARRAY_OPT_RUNTIME:
        for indent in range(parfor_dim + 1):
            gufunc_txt += "    "
        gufunc_txt += "print("
        for eachdim in range(parfor_dim):
            gufunc_txt += (
                '"'
                + legal_loop_indices[eachdim]
                + '",'
                + legal_loop_indices[eachdim]
                + ","
            )
        gufunc_txt += ")\n"

    # Add the sentinel assignment so that we can find the loop body position
    # in the IR.
    for indent in range(parfor_dim + 1):
        gufunc_txt += "    "
    gufunc_txt += sentinel_name + " = 0\n"
    # Add assignments of reduction variables (for returning the value)
    redargstartdim = {}
    for arr, var in zip(parfor_redarrs, parfor_redvars):
        # After the gufunc loops, copy the accumulated temp value back to reduction array.
        if redtyp_is_scalar(typemap[var]):
            gufunc_txt += "    " + param_dict[arr] + "[0] = " + param_dict[var] + "\n"
            redargstartdim[arr] = 1
        else:
            # After the gufunc loops, copy the accumulated temp array back to reduction array with ":"
            gufunc_txt += (
                "    " + param_dict[arr] + "[:] = " + param_dict[var] + "[:]\n"
            )
            redargstartdim[arr] = 0
    gufunc_txt += "    return None\n"

    if config.DEBUG_ARRAY_OPT:
        print("gufunc_txt = ", type(gufunc_txt), "\n", gufunc_txt)
    # Force gufunc outline into existence.
    globls = {"np": np}
    locls = {}
    exec_(gufunc_txt, globls, locls)
    gufunc_func = locls[gufunc_name]

    if config.DEBUG_ARRAY_OPT:
        print("gufunc_func = ", type(gufunc_func), "\n", gufunc_func)
    # Get the IR for the gufunc outline.
    gufunc_ir = compiler.run_frontend(gufunc_func)

    if config.DEBUG_ARRAY_OPT:
        print("gufunc_ir dump ", type(gufunc_ir))
        gufunc_ir.dump()
        print("loop_body dump ", type(loop_body))
        _print_body(loop_body)

    # rename all variables in gufunc_ir afresh
    var_table = get_name_var_table(gufunc_ir.blocks)
    new_var_dict = {}
    reserved_names = [sentinel_name] + list(param_dict.values()) + legal_loop_indices
    for name, var in var_table.items():
        if not (name in reserved_names):
            new_var_dict[name] = mk_unique_var(name)
    replace_var_names(gufunc_ir.blocks, new_var_dict)
    if config.DEBUG_ARRAY_OPT:
        print("gufunc_ir dump after renaming ")
        gufunc_ir.dump()

    gufunc_param_types = [
        numba.types.npytypes.Array(index_var_typ, 1, "C")
    ] + param_types
    if config.DEBUG_ARRAY_OPT:
        print(
            "gufunc_param_types = ", type(gufunc_param_types), "\n", gufunc_param_types
        )

    gufunc_stub_last_label = max(gufunc_ir.blocks.keys()) + 1

    # Add gufunc stub last label to each parfor.loop_body label to prevent
    # label conflicts.
    loop_body = add_offset_to_labels(loop_body, gufunc_stub_last_label)
    # new label for splitting sentinel block
    new_label = max(loop_body.keys()) + 1

    # If enabled, add a print statement after every assignment.
    if config.DEBUG_ARRAY_OPT_RUNTIME:
        for label, block in loop_body.items():
            new_block = block.copy()
            new_block.clear()
            loc = block.loc
            scope = block.scope
            for inst in block.body:
                new_block.append(inst)
                # Append print after assignment
                if isinstance(inst, ir.Assign):
                    # Only apply to numbers
                    if typemap[inst.target.name] not in types.number_domain:
                        continue

                    # Make constant string
                    strval = "{} =".format(inst.target.name)
                    strconsttyp = types.StringLiteral(strval)

                    lhs = ir.Var(scope, mk_unique_var("str_const"), loc)
                    assign_lhs = ir.Assign(
                        value=ir.Const(value=strval, loc=loc), target=lhs, loc=loc
                    )
                    typemap[lhs.name] = strconsttyp
                    new_block.append(assign_lhs)

                    # Make print node
                    print_node = ir.Print(args=[lhs, inst.target], vararg=None, loc=loc)
                    new_block.append(print_node)
                    sig = numba.typing.signature(
                        types.none, typemap[lhs.name], typemap[inst.target.name]
                    )
                    lowerer.fndesc.calltypes[print_node] = sig
            loop_body[label] = new_block

    if config.DEBUG_ARRAY_OPT:
        print("parfor loop body")
        _print_body(loop_body)

    wrapped_blocks = wrap_loop_body(loop_body)
    hoisted, not_hoisted = hoist(parfor_params, loop_body, typemap, wrapped_blocks)
    start_block = gufunc_ir.blocks[min(gufunc_ir.blocks.keys())]
    start_block.body = start_block.body[:-1] + hoisted + [start_block.body[-1]]
    unwrap_loop_body(loop_body)

    # store hoisted into diagnostics
    diagnostics = lowerer.metadata["parfor_diagnostics"]
    diagnostics.hoist_info[parfor.id] = {"hoisted": hoisted, "not_hoisted": not_hoisted}

    if config.DEBUG_ARRAY_OPT:
        print("After hoisting")
        _print_body(loop_body)

    # Search all the block in the gufunc outline for the sentinel assignment.
    for label, block in gufunc_ir.blocks.items():
        for i, inst in enumerate(block.body):
            if isinstance(inst, ir.Assign) and inst.target.name == sentinel_name:
                # We found the sentinel assignment.
                loc = inst.loc
                scope = block.scope
                # split block across __sentinel__
                # A new block is allocated for the statements prior to the sentinel
                # but the new block maintains the current block label.
                prev_block = ir.Block(scope, loc)
                prev_block.body = block.body[:i]
                # The current block is used for statements after the sentinel.
                block.body = block.body[i + 1 :]
                # But the current block gets a new label.
                body_first_label = min(loop_body.keys())

                # The previous block jumps to the minimum labelled block of the
                # parfor body.
                prev_block.append(ir.Jump(body_first_label, loc))
                # Add all the parfor loop body blocks to the gufunc function's
                # IR.
                for l, b in loop_body.items():
                    gufunc_ir.blocks[l] = b
                body_last_label = max(loop_body.keys())
                gufunc_ir.blocks[new_label] = block
                gufunc_ir.blocks[label] = prev_block
                # Add a jump from the last parfor body block to the block containing
                # statements after the sentinel.
                gufunc_ir.blocks[body_last_label].append(ir.Jump(new_label, loc))
                break
        else:
            continue
        break

    if config.DEBUG_ARRAY_OPT:
        print("gufunc_ir last dump before renaming")
        gufunc_ir.dump()

    gufunc_ir.blocks = rename_labels(gufunc_ir.blocks)
    remove_dels(gufunc_ir.blocks)

    if config.DEBUG_ARRAY_OPT:
        print("gufunc_ir last dump")
        gufunc_ir.dump()
        print("flags", flags)
        print("typemap", typemap)

    old_alias = flags.noalias
    if not has_aliases:
        if config.DEBUG_ARRAY_OPT:
            print("No aliases found so adding noalias flag.")
        flags.noalias = True
    kernel_func = compiler.compile_ir(
        typingctx, targetctx, gufunc_ir, gufunc_param_types, types.none, flags, locals
    )

    flags.noalias = old_alias

    kernel_sig = signature(types.none, *gufunc_param_types)
    if config.DEBUG_ARRAY_OPT:
        print("kernel_sig = ", kernel_sig)

    return kernel_func, parfor_args, kernel_sig, redargstartdim, func_arg_types