Infer the model parameters.

def infer(args):
    """
    Infer the model parameters.
    """

    expected_output_files = ["inferred.pkl"]
    if args.plotting:
        expected_output_files.extend([each.format(args.plot_format) \
            for each in "chain.{}", "corner.{}", "acceptance-fractions.{}",
            "autocorrelation.{}"])

    # Optimise them first.
    model, data, metadata, optimised_theta = optimise(args,
        expected_output_files=expected_output_files, __return_result=True)

    # Get the inference parameters from the model configuration.
    kwargs = model._configuration.get("infer", {})
    [kwargs.pop(k, None) \
        for k in ("debug", "full_output", "initial_proposal", "data")]

    try:
        theta, chains, lnprobability, acceptance_fractions, sampler, info_dict \
            = model.infer(data, initial_proposal=optimised_theta, 
                full_output=True, debug=args.debug, 
                __keep_convolution_functions=True,
                __show_progress_bar=True, **kwargs)

    except:
        logger.exception("Failed to infer model parameters")
        raise

    metadata["inferred"] = {
        "theta": theta,
        "chi_sq": info_dict["chi_sq"],
        "dof": info_dict["dof"],
        "r_chi_sq": info_dict["chi_sq"]/info_dict["dof"]
    }

    logger.info("Inferred parameters are:")
    _announce_theta(theta)
    
    # Write the results to file.
    _write_output(_prefix(args, "inferred.pkl"), metadata)

    # Write the chains, etc to disk.
    if args.save_chain_files:
        filename = _prefix(args, "chains.pkl")
        with open(filename, "wb") as f:
            pickle.dump(
                (chains, lnprobability, acceptance_fractions, info_dict), f, -1)
        logger.info("Saved chains to {}".format(filename))

    # Make plots.
    if args.plotting:
        burn = info_dict["burn"]

        # Any truth values to plot?
        truths = model._configuration.get("truths", None)
        if truths:
            truths = [truths.get(p, np.nan) for p in model.parameters]

        # Labels?
        labels = model._configuration.get("labels", {})
        labels = [labels.get(p, p) for p in info_dict["parameters"]]
        
        # Acceptance fractions.
        fig = sick.plot.acceptance_fractions(acceptance_fractions,
            burn=burn)
        _ = _prefix(args, "acceptance-fractions.{}".format(args.plot_format))
        fig.savefig(_)
        logger.info("Saved acceptance fractions figure to {}".format(_))

        # Autocorrelation.
        fig = sick.plot.normalised_autocorrelation_function(chains, burn=burn)
        _ = _prefix(args, "auto-correlation.{}".format(args.plot_format))
        fig.savefig(_)
        logger.info("Saved auto-correlation figure to {}".format(_))

        # Chains.
        fig = sick.plot.chains(chains, labels=labels, burn=burn,
            truths=truths)
        _ = _prefix(args, "chains.{}".format(args.plot_format))
        fig.savefig(_)
        logger.info("Saved chains figure to {}".format(_))

        # Corner plots (astrophysical + all).
        N = len(model.grid_points.dtype.names)
        fig = sick.plot.corner(chains[:, burn:, :N].reshape(-1, N),
            labels=labels, truths=truths[:N] if truths else None)
        _ = _prefix(args, "corner.{}".format(args.plot_format))
        fig.savefig(_)
        logger.info("Saved corner plot (astrophysical parameters) to {}"\
            .format(_))

        if len(model.parameters) > N:
            fig = sick.plot.corner(chains[:, burn:, :].reshape(-1, len(theta)),
                labels=labels, truths=truths)
            _ = _prefix(args, "corner-all.{}".format(args.plot_format))
            fig.savefig(_)
            logger.info("Saved corner plot (all parameters) to {}".format(_))

        # Projections.
        # Note here we need to scale the chains back to redshift so the data
        # are generated properly.
        fig = sick.plot.projection(data, model, 
            chains=chains[:, burn:, :]/info_dict["scales"],
            parameters=theta.keys())
        _ = _prefix(args, "projection.{}".format(args.plot_format))
        fig.savefig(_)
        logger.info("Saved projection plot to {}".format(_))

    model._destroy_convolution_functions()

    return None