<commit_before>from flask import Blueprint, request, redirect, g, abort, jsonify
from piipod.views import current_user, login_required, url_for, requires
from .forms import GroupForm, GroupSignupForm, ProcessWaitlistsForm, \
    ImportSignupsForm
from piipod.event.forms import EventForm
from piipod.models import Event, Group, Membership, GroupRole, GroupSetting,\
    Signup
from piipod.defaults import default_event_roles
from sqlalchemy.orm.exc import NoResultFound
import csv

group = Blueprint('group', __name__, url_prefix='/<string:group_url>')

@group.url_defaults
def add_group_id(endpoint, values):
    values.setdefault('group_url', getattr(g, 'group_url', None))

@group.url_value_preprocessor
def pull_group_id(endpoint, values):
    try:
        g.group_url = values.pop('group_url')
        g.group = Group.query.filter_by(url=g.group_url).one_or_none()
        if not g.group:
            abort(404)
        g.user = current_user()
        g.event_role = g.signup = None
        if g.user.is_authenticated:
            g.membership = Membership.query.filter_by(
                group_id=g.group.id,
                user_id=g.user.id,
                is_active=True).one_or_none()
            if g.membership:
                g.group_role = GroupRole.query.get(g.membership.role_id)
            else:
                g.group_role = None
        else:
            g.membership = g.group_role = None
    except NoResultFound:
        return 'No such group.'

def render_group(f, *args, **kwargs):
    """custom render for groups"""
    from piipod.views import render
    data = vars(g)
    data.update(kwargs)
    return render(f, *args, **data)

################
# PUBLIC PAGES #
################

@group.route('/')
def home():
    """group homepage"""
    return render_group('group/index.html')

@group.route('/events')
def events():
    """group events"""
    return render_group('group/events.html',
        page=int(request.args.get('page', 1)))

@group.route('/members')
def members():
    """group members"""
    return render_group('group/members.html')

@group.route('/signup', methods=['GET', 'POST'])
@login_required
def signup():
    """group signup"""
    message = ''
    form = GroupSignupForm(request.form)
    choose_role = g.group.setting('choose_role').is_active
    message = 'Thank you for your interest in %s! Just click "Join" to join.' % g.group.name
    whitelisted = []
    for block in g.group.setting('whitelist').value.split(','):
        data = block.split('(')
        if len(data) == 2:
            whitelisted.append((data[0].strip(), data[1].strip()[:-1]))
        else:
            whitelisted.append((data[0].strip(), ''))
    emails = [s.strip() for s, _ in whitelisted]
    if g.user.email in emails:
        title = [title for email, title in whitelisted if email == g.user.email][0]
        message = 'You\'ve been identified as "%s". Hello! Click "Confirm" below, to get started.' % title
    if choose_role:
        form.role_id.choices = [(r.id, r.name) for r in GroupRole.query.filter_by(
            group_id=g.group.id,
            is_active=True).all()]
    else:
        del form.role_id
    if g.user in g.group:
        return redirect(url_for('group.home', notif=5))
    if request.method == 'POST' and form.validate():
        if g.user.email in emails:
            role = {'role': title or 'Authorizer'}
        elif choose_role:
            role = {'role_id': request.form['role_id']}
        else:
            role = {'role': g.group.setting('role').value}
        membership = g.user.join(g.group, **role)
        return redirect(url_for('group.home'))
    form.group_id.default = g.group.id
    form.user_id.default = g.user.id
    form.process()
    return render_group('form.html',
        title='Signup for %s' % g.group.name,
        submit='Join',
        form=form,
        message=message,
        back=url_for('group.home'))

##############
# MANAGEMENT #
##############

@group.route('/edit', methods=['GET', 'POST'])
@login_required
def edit():
    """group edit"""
    form = GroupForm(request.form, obj=g.group)
    if request.method == 'POST' and form.validate():
        g.group.update(**request.form).save()
        return redirect(url_for('group.home'))
    return render_group('form.html',
        title='Edit Group',
        submit='Save',
        form=form)

@group.route('/settings', methods=['GET', 'POST'])
@login_required
def settings():
    """edit settings"""
    g.group.setting('whitelist')
    g.group.access_token
    if request.method == 'POST':
        id, value = request.form['id'], request.form['value']
        setting = GroupSetting.query.get(id)
        setting.value = value
        setting.save()
    settings = GroupSetting.query.filter_by(group_id=g.group.id).all()
    return render_group('group/settings.html', settings=settings, back=url_for('group.home'))

##########
# EVENTS #
##########

@group.route('/e/', methods=['GET', 'POST'])
@requires('create_event')
@login_required
def create_event():
    """create event"""
    form = EventForm(request.form)
    if request.method == 'POST' and form.validate():
        event = Event.from_request().save().load_roles(
            default_event_roles[g.group.category]
        ).set_local('start', 'end').save()
        return redirect(url_for('event.home',
            event_id=g.user.signup(event, 'Owner').event_id)
    form.group_id.default = g.group.id
    form.process()
    return render_group('form.html',
        title='Create Event',
        submit='Create',
        form=form,
        back=url_for('group.events'))

@group.route('/process', methods=['GET', 'POST'])
@requires('create_event')
@login_required
def process():
    """process whitelists"""
    form = ProcessWaitlistsForm(request.form)
    if request.method == 'POST' and form.validate():
        pass
    return render_group('form.html',
        title='Process Waitlists',
        submit='Process',
        form=form,
        back=url_for('group.events'))

@group.route('/import/signups', methods=['GET', 'POST'])
@requires('create_event')
@login_required
def import_signups():
    """import signups"""
    form = ImportSignupsForm(request.form)
    if request.method == 'POST' and form.validate():
        signups = list(Signup.from_csv_string(
            request.form['csv'], request.form['override'] == 'y'))
        return render_group('group/import_signups.html',
            message='All %d signups created.' % len(signups),
            url=url_for('group.events'),
            action='Back',
            signups=signups)
    return render_group('form.html',
        title='Import Signups',
        submit='Import',
        form=form,
        back=url_for('group.events'))

################
# LOGIN/LOGOUT #
################

@group.route('/logout')
def logout():
    from piipod.public.views import logout
    return logout()

@group.route('/tokenlogin', methods=['POST'])
def token_login():
    from piipod.public.views import token_login
    return token_login()

@group.route('/whitelist/<string:access_token>')
@group.route('/whitelist/<string:group>/<string:access_token>')
def whitelist(access_token, group=None):
    """Endpoint for group whitelist of emails"""
    if access_token == g.group.access_token:
        terms = []
        for term in g.group.setting('whitelist').value.split(','):
            pieces = term.split('(')
            if len(pieces) == 2:
                terms.append({'email': pieces[0], 'position': pieces[1][:-1]})
            elif len(pieces) == 1 and pieces[0]:
                default_role = default_group_roles[g.group.category][-1]['name']
                terms.append({'email': pieces[0], 'position': default_role})
            else:
                continue
        return jsonify({'data': terms})
    abort(404)
<commit_msg>syntax<commit_after>from flask import Blueprint, request, redirect, g, abort, jsonify
from piipod.views import current_user, login_required, url_for, requires
from .forms import GroupForm, GroupSignupForm, ProcessWaitlistsForm, \
    ImportSignupsForm
from piipod.event.forms import EventForm
from piipod.models import Event, Group, Membership, GroupRole, GroupSetting,\
    Signup
from piipod.defaults import default_event_roles
from sqlalchemy.orm.exc import NoResultFound
import csv

group = Blueprint('group', __name__, url_prefix='/<string:group_url>')

@group.url_defaults
def add_group_id(endpoint, values):
    values.setdefault('group_url', getattr(g, 'group_url', None))

@group.url_value_preprocessor
def pull_group_id(endpoint, values):
    try:
        g.group_url = values.pop('group_url')
        g.group = Group.query.filter_by(url=g.group_url).one_or_none()
        if not g.group:
            abort(404)
        g.user = current_user()
        g.event_role = g.signup = None
        if g.user.is_authenticated:
            g.membership = Membership.query.filter_by(
                group_id=g.group.id,
                user_id=g.user.id,
                is_active=True).one_or_none()
            if g.membership:
                g.group_role = GroupRole.query.get(g.membership.role_id)
            else:
                g.group_role = None
        else:
            g.membership = g.group_role = None
    except NoResultFound:
        return 'No such group.'

def render_group(f, *args, **kwargs):
    """custom render for groups"""
    from piipod.views import render
    data = vars(g)
    data.update(kwargs)
    return render(f, *args, **data)

################
# PUBLIC PAGES #
################

@group.route('/')
def home():
    """group homepage"""
    return render_group('group/index.html')

@group.route('/events')
def events():
    """group events"""
    return render_group('group/events.html',
        page=int(request.args.get('page', 1)))

@group.route('/members')
def members():
    """group members"""
    return render_group('group/members.html')

@group.route('/signup', methods=['GET', 'POST'])
@login_required
def signup():
    """group signup"""
    message = ''
    form = GroupSignupForm(request.form)
    choose_role = g.group.setting('choose_role').is_active
    message = 'Thank you for your interest in %s! Just click "Join" to join.' % g.group.name
    whitelisted = []
    for block in g.group.setting('whitelist').value.split(','):
        data = block.split('(')
        if len(data) == 2:
            whitelisted.append((data[0].strip(), data[1].strip()[:-1]))
        else:
            whitelisted.append((data[0].strip(), ''))
    emails = [s.strip() for s, _ in whitelisted]
    if g.user.email in emails:
        title = [title for email, title in whitelisted if email == g.user.email][0]
        message = 'You\'ve been identified as "%s". Hello! Click "Confirm" below, to get started.' % title
    if choose_role:
        form.role_id.choices = [(r.id, r.name) for r in GroupRole.query.filter_by(
            group_id=g.group.id,
            is_active=True).all()]
    else:
        del form.role_id
    if g.user in g.group:
        return redirect(url_for('group.home', notif=5))
    if request.method == 'POST' and form.validate():
        if g.user.email in emails:
            role = {'role': title or 'Authorizer'}
        elif choose_role:
            role = {'role_id': request.form['role_id']}
        else:
            role = {'role': g.group.setting('role').value}
        membership = g.user.join(g.group, **role)
        return redirect(url_for('group.home'))
    form.group_id.default = g.group.id
    form.user_id.default = g.user.id
    form.process()
    return render_group('form.html',
        title='Signup for %s' % g.group.name,
        submit='Join',
        form=form,
        message=message,
        back=url_for('group.home'))

##############
# MANAGEMENT #
##############

@group.route('/edit', methods=['GET', 'POST'])
@login_required
def edit():
    """group edit"""
    form = GroupForm(request.form, obj=g.group)
    if request.method == 'POST' and form.validate():
        g.group.update(**request.form).save()
        return redirect(url_for('group.home'))
    return render_group('form.html',
        title='Edit Group',
        submit='Save',
        form=form)

@group.route('/settings', methods=['GET', 'POST'])
@login_required
def settings():
    """edit settings"""
    g.group.setting('whitelist')
    g.group.access_token
    if request.method == 'POST':
        id, value = request.form['id'], request.form['value']
        setting = GroupSetting.query.get(id)
        setting.value = value
        setting.save()
    settings = GroupSetting.query.filter_by(group_id=g.group.id).all()
    return render_group('group/settings.html', settings=settings, back=url_for('group.home'))

##########
# EVENTS #
##########

@group.route('/e/', methods=['GET', 'POST'])
@requires('create_event')
@login_required
def create_event():
    """create event"""
    form = EventForm(request.form)
    if request.method == 'POST' and form.validate():
        event = Event.from_request().save().load_roles(
            default_event_roles[g.group.category]
        ).set_local('start', 'end').save()
        return redirect(url_for('event.home',
            event_id=g.user.signup(event, 'Owner').event_id))
    form.group_id.default = g.group.id
    form.process()
    return render_group('form.html',
        title='Create Event',
        submit='Create',
        form=form,
        back=url_for('group.events'))

@group.route('/process', methods=['GET', 'POST'])
@requires('create_event')
@login_required
def process():
    """process whitelists"""
    form = ProcessWaitlistsForm(request.form)
    if request.method == 'POST' and form.validate():
        pass
    return render_group('form.html',
        title='Process Waitlists',
        submit='Process',
        form=form,
        back=url_for('group.events'))

@group.route('/import/signups', methods=['GET', 'POST'])
@requires('create_event')
@login_required
def import_signups():
    """import signups"""
    form = ImportSignupsForm(request.form)
    if request.method == 'POST' and form.validate():
        signups = list(Signup.from_csv_string(
            request.form['csv'], request.form['override'] == 'y'))
        return render_group('group/import_signups.html',
            message='All %d signups created.' % len(signups),
            url=url_for('group.events'),
            action='Back',
            signups=signups)
    return render_group('form.html',
        title='Import Signups',
        submit='Import',
        form=form,
        back=url_for('group.events'))

################
# LOGIN/LOGOUT #
################

@group.route('/logout')
def logout():
    from piipod.public.views import logout
    return logout()

@group.route('/tokenlogin', methods=['POST'])
def token_login():
    from piipod.public.views import token_login
    return token_login()

@group.route('/whitelist/<string:access_token>')
@group.route('/whitelist/<string:group>/<string:access_token>')
def whitelist(access_token, group=None):
    """Endpoint for group whitelist of emails"""
    if access_token == g.group.access_token:
        terms = []
        for term in g.group.setting('whitelist').value.split(','):
            pieces = term.split('(')
            if len(pieces) == 2:
                terms.append({'email': pieces[0], 'position': pieces[1][:-1]})
            elif len(pieces) == 1 and pieces[0]:
                default_role = default_group_roles[g.group.category][-1]['name']
                terms.append({'email': pieces[0], 'position': default_role})
            else:
                continue
        return jsonify({'data': terms})
    abort(404)
<|endoftext|>


<commit_before>#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# anytree documentation build configuration file, created by
# sphinx-quickstart on Fri Aug 26 19:57:13 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys
import os

name = "anytree"
version = "2.7.3"
author = 'c0fec0de'
description = "Python Tree Data"

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.coverage',
    'sphinx.ext.doctest',
    'sphinx.ext.viewcode',
    'sphinx.ext.napoleon',
    'sphinx.ext.intersphinx',
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = name

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = version
# The full version, including alpha/beta/rc tags.
release = version

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []

# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'sphinxdoc'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["static"]

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None

# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
#   'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
#   'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr'
#html_search_language = 'en'

# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
#html_search_options = {'type': 'default'}

# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#html_search_scorer = 'scorer.js'

# Output file base name for HTML help builder.
htmlhelp_basename = '%sdoc' % name

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    #'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    #'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    #'preamble': '',

    # Latex figure (float) alignment
    #'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, '%s.tex' % name, description,
     author, 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, name, description,
     [author], 1)
]

# If true, show URL addresses after external links.
#man_show_urls = False

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, name, description,
     author, name, description,
     'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
#texinfo_appendices = []

# If false, no module index is generated.
#texinfo_domain_indices = True

# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False

# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
    'python': ('http://docs.python.org/3', None),
}
intersphinx_cache_limit = 0

autodoc_default_flags = ['members', 'undoc-members', 'show-inheritance']
autoclass_content = 'both'
autodoc_member_order = 'bysource'
<commit_msg>Speed up build<commit_after>#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# anytree documentation build configuration file, created by
# sphinx-quickstart on Fri Aug 26 19:57:13 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys
import os

name = "anytree"
version = "2.7.3"
author = 'c0fec0de'
description = "Python Tree Data"

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.coverage',
    'sphinx.ext.doctest',
    'sphinx.ext.viewcode',
    'sphinx.ext.napoleon',
    'sphinx.ext.intersphinx',
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = name

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = version
# The full version, including alpha/beta/rc tags.
release = version

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []

# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'sphinxdoc'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["static"]

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None

# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
#   'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
#   'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr'
#html_search_language = 'en'

# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
#html_search_options = {'type': 'default'}

# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#html_search_scorer = 'scorer.js'

# Output file base name for HTML help builder.
htmlhelp_basename = '%sdoc' % name

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    #'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    #'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    #'preamble': '',

    # Latex figure (float) alignment
    #'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, '%s.tex' % name, description,
     author, 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, name, description,
     [author], 1)
]

# If true, show URL addresses after external links.
#man_show_urls = False

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, name, description,
     author, name, description,
     'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
#texinfo_appendices = []

# If false, no module index is generated.
#texinfo_domain_indices = True

# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False

# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
    'python': ('http://docs.python.org/3', None),
}
intersphinx_cache_limit = 1

autodoc_default_flags = ['members', 'undoc-members', 'show-inheritance']
autoclass_content = 'both'
autodoc_member_order = 'bysource'
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# Licensed under a 3-clause BSD style license - see LICENSE.rst
#
# Astropy documentation build configuration file.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this file.
#
# All configuration values have a default. Some values are defined in
# the global Astropy configuration which is loaded here before anything else.

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('..'))
# IMPORTANT: the above commented section was generated by sphinx-quickstart, but
# is *NOT* appropriate for astropy or Astropy affiliated packages. It is left
# commented out with this explanation to make it clear why this should not be
# done. If the sys.path entry above is added, when the astropy.sphinx.conf
# import occurs, it will import the *source* version of astropy instead of the
# version installed (if invoked as "make html" or directly with sphinx), or the
# version in the build directory.
# Thus, any C-extensions that are needed to build the documentation will *not*
# be accessible, and the documentation will not build correctly.
# See sphinx_astropy.conf for which values are set there.

from datetime import datetime
import os
import sys

import configparser
from pkg_resources import get_distribution

try:
    from sphinx_astropy.conf.v1 import *  # noqa
except ImportError:
    print('ERROR: the documentation requires the sphinx-astropy package to be installed')
    sys.exit(1)

plot_rcparams = {}
plot_rcparams['figure.figsize'] = (6, 6)
plot_rcparams['savefig.facecolor'] = 'none'
plot_rcparams['savefig.bbox'] = 'tight'
plot_rcparams['axes.labelsize'] = 'large'
plot_rcparams['figure.subplot.hspace'] = 0.5

plot_apply_rcparams = True
plot_html_show_source_link = False
plot_formats = ['png', 'svg', 'pdf']
# Don't use the default - which includes a numpy and matplotlib import
plot_pre_code = ""

# -- General configuration ----------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = '1.7'

# To perform a Sphinx version check that needs to be more specific than
# major.minor, call `check_sphinx_version("X.Y.Z")` here.
check_sphinx_version("1.2.1")  # noqa: F405

# The intersphinx_mapping in sphinx_astropy.sphinx refers to astropy for
# the benefit of other packages who want to refer to objects in the
# astropy core.  However, we don't want to cyclically reference astropy in its
# own build so we remove it here.
del intersphinx_mapping['astropy']  # noqa: F405

# add any custom intersphinx for astropy
intersphinx_mapping['pyerfa'] = ('https://pyerfa.readthedocs.io/en/stable/', None)  # noqa: F405
intersphinx_mapping['pytest'] = ('https://pytest.readthedocs.io/en/stable/', None)  # noqa: F405
intersphinx_mapping['ipython'] = ('https://ipython.readthedocs.io/en/stable/', None)  # noqa: F405
intersphinx_mapping['pandas'] = ('https://pandas.pydata.org/pandas-docs/stable/', None)  # noqa: F405, E501
intersphinx_mapping['sphinx_automodapi'] = ('https://sphinx-automodapi.readthedocs.io/en/stable/', None)  # noqa: F405, E501
intersphinx_mapping['packagetemplate'] = ('https://docs.astropy.org/projects/package-template/en/latest/', None)  # noqa: F405, E501
intersphinx_mapping['h5py'] = ('http://docs.h5py.org/en/stable/', None)  # noqa: F405

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns.append('_templates')  # noqa: F405
exclude_patterns.append('_pkgtemplate.rst')  # noqa: F405
exclude_patterns.append('**/*.inc.rst')  # .inc.rst mean *include* files, don't have sphinx process them  # noqa: F405, E501

# Add any paths that contain templates here, relative to this directory.
if 'templates_path' not in locals():  # in case parent conf.py defines it
    templates_path = []
templates_path.append('_templates')

# Grab minversion from setup.cfg
setup_cfg = configparser.ConfigParser()
setup_cfg.read(os.path.join(os.path.pardir, 'setup.cfg'))
__minimum_python_version__ = setup_cfg['options']['python_requires'].replace('>=', '')
project = u'Astropy'
astropy_dist = get_distribution(project)
astropy_req = astropy_dist.requires(extras=('all', ))

def minvers(name):
    if name == 'yaml':
        name = 'PyYAML'
    elif name == 'erfa':
        name = 'pyerfa'
    for cur_req in astropy_req:
        if cur_req.name == name:
            return cur_req.specs[0][1]
    raise ValueError(f"{name} not among requirements!")

# This is added to the end of RST files - a good place to put substitutions to
# be used globally.
rst_epilog += "\n".join(
    f".. |minimum_{name}_version| replace:: {minvers(name)}"
    for name in ('numpy', 'erfa', 'scipy', 'yaml', 'asdf', 'matplotlib', 'ipython')) + """
.. |minimum_python_version| replace:: {__minimum_python_version__}

.. Astropy
.. _`Astropy mailing list`: https://mail.python.org/mailman/listinfo/astropy
.. _`astropy-dev mailing list`: http://groups.google.com/group/astropy-dev
"""

# -- Project information ------------------------------------------------------

author = u'The Astropy Developers'
copyright = f'2011–{datetime.utcnow().year}, ' + author

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.

# The full version, including alpha/beta/rc tags.
release = astropy_dist.version
# The short X.Y version.
version = '.'.join(release.split('.')[:2])

# -- Options for the module index ---------------------------------------------

modindex_common_prefix = ['astropy.']

# -- Options for HTML output ---------------------------------------------------

# A NOTE ON HTML THEMES
#
# The global astropy configuration uses a custom theme,
# 'bootstrap-astropy', which is installed along with astropy. The
# theme has options for controlling the text of the logo in the upper
# left corner. This is how you would specify the options in order to
# override the theme defaults (The following options *are* the
# defaults, so we do not actually need to set them here.)

# html_theme_options = {
#    'logotext1': 'astro',  # white,  semi-bold
#    'logotext2': 'py',     # orange, light
#    'logotext3': ':docs'   # white,  light
#    }

# A different theme can be used, or other parts of this theme can be
# modified, by overriding some of the variables set in the global
# configuration. The variables set in the global configuration are
# listed below, commented out.

# Add any paths that contain custom themes here, relative to this directory.
# To use a different custom theme, add the directory containing the theme.
# html_theme_path = []

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes. To override the custom theme, set this to the
# name of a builtin theme or the name of a custom theme in html_theme_path.
# html_theme = None

# Custom sidebar templates, maps document names to template names.
# html_sidebars = {}

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
# html_favicon = ''

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
# html_last_updated_fmt = ''

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
html_title = f'{project} v{release}'

# Output file base name for HTML help builder.
htmlhelp_basename = project + 'doc'

# A dictionary of values to pass into the template engine’s context for all pages.
html_context = {
    'to_be_indexed': ['stable', 'latest']
}

# -- Options for LaTeX output --------------------------------------------------

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [('index', project + '.tex', project + u' Documentation',
                    author, 'manual')]

latex_logo = '_static/astropy_logo.pdf'

# -- Options for manual page output --------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [('index', project.lower(), project + u' Documentation',
              [author], 1)]

# Setting this URL is requited by sphinx-astropy
github_issues_url = 'https://github.com/astropy/astropy/issues/'
edit_on_github_branch = 'master'

# Enable nitpicky mode - which ensures that all references in the docs
# resolve.

nitpicky = True
# This is not used. See docs/nitpick-exceptions file for the actual listing.
nitpick_ignore = []

for line in open('nitpick-exceptions'):
    if line.strip() == "" or line.startswith("#"):
        continue
    dtype, target = line.split(None, 1)
    target = target.strip()
    nitpick_ignore.append((dtype, target))

# -- Options for the Sphinx gallery -------------------------------------------

try:
    import warnings
    import sphinx_gallery  # noqa: F401
    extensions += ["sphinx_gallery.gen_gallery"]  # noqa: F405

    sphinx_gallery_conf = {
        'backreferences_dir': 'generated/modules',  # path to store the module using example template  # noqa: E501
        'filename_pattern': '^((?!skip_).)*$',  # execute all examples except those that start with "skip_"  # noqa: E501
        'examples_dirs': f'..{os.sep}examples',  # path to the examples scripts
        'gallery_dirs': 'generated/examples',  # path to save gallery generated examples
        'reference_url': {
            'astropy': None,
            'matplotlib': 'https://matplotlib.org/',
            'numpy': 'https://numpy.org/doc/stable/',
        },
        'abort_on_example_error': True
    }

    # Filter out backend-related warnings as described in
    # https://github.com/sphinx-gallery/sphinx-gallery/pull/564
    warnings.filterwarnings("ignore", category=UserWarning,
                            message='Matplotlib is currently using agg, which is a'
                                    ' non-GUI backend, so cannot show the figure.')

except ImportError:
    def setup(app):
        msg = ('The sphinx_gallery extension is not installed, so the '
               'gallery will not be built.  You will probably see '
               'additional warnings about undefined references due '
               'to this.')
        try:
            app.warn(msg)
        except AttributeError:
            # Sphinx 1.6+
            from sphinx.util import logging
            logger = logging.getLogger(__name__)
            logger.warning(msg)

# -- Options for linkcheck output -------------------------------------------
linkcheck_retry = 5
linkcheck_ignore = ['https://journals.aas.org/manuscript-preparation/',
                    'https://maia.usno.navy.mil/',
                    'https://www.usno.navy.mil/USNO/time/gps/usno-gps-time-transfer',
                    'https://aa.usno.navy.mil/publications/docs/Circular_179.php',
                    'http://data.astropy.org',
                    r'https://github\.com/astropy/astropy/(?:issues|pull)/\d+']
linkcheck_timeout = 180
linkcheck_anchors = False

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
html_extra_path = ['robots.txt']
<commit_msg>Fix string formatting for minimum_python_version<commit_after># -*- coding: utf-8 -*-
# Licensed under a 3-clause BSD style license - see LICENSE.rst
#
# Astropy documentation build configuration file.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this file.
#
# All configuration values have a default. Some values are defined in
# the global Astropy configuration which is loaded here before anything else.

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('..'))
# IMPORTANT: the above commented section was generated by sphinx-quickstart, but
# is *NOT* appropriate for astropy or Astropy affiliated packages. It is left
# commented out with this explanation to make it clear why this should not be
# done. If the sys.path entry above is added, when the astropy.sphinx.conf
# import occurs, it will import the *source* version of astropy instead of the
# version installed (if invoked as "make html" or directly with sphinx), or the
# version in the build directory.
# Thus, any C-extensions that are needed to build the documentation will *not*
# be accessible, and the documentation will not build correctly.
# See sphinx_astropy.conf for which values are set there.

from datetime import datetime
import os
import sys

import configparser
from pkg_resources import get_distribution

try:
    from sphinx_astropy.conf.v1 import *  # noqa
except ImportError:
    print('ERROR: the documentation requires the sphinx-astropy package to be installed')
    sys.exit(1)

plot_rcparams = {}
plot_rcparams['figure.figsize'] = (6, 6)
plot_rcparams['savefig.facecolor'] = 'none'
plot_rcparams['savefig.bbox'] = 'tight'
plot_rcparams['axes.labelsize'] = 'large'
plot_rcparams['figure.subplot.hspace'] = 0.5

plot_apply_rcparams = True
plot_html_show_source_link = False
plot_formats = ['png', 'svg', 'pdf']
# Don't use the default - which includes a numpy and matplotlib import
plot_pre_code = ""

# -- General configuration ----------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = '1.7'

# To perform a Sphinx version check that needs to be more specific than
# major.minor, call `check_sphinx_version("X.Y.Z")` here.
check_sphinx_version("1.2.1")  # noqa: F405

# The intersphinx_mapping in sphinx_astropy.sphinx refers to astropy for
# the benefit of other packages who want to refer to objects in the
# astropy core.  However, we don't want to cyclically reference astropy in its
# own build so we remove it here.
del intersphinx_mapping['astropy']  # noqa: F405

# add any custom intersphinx for astropy
intersphinx_mapping['pyerfa'] = ('https://pyerfa.readthedocs.io/en/stable/', None)  # noqa: F405
intersphinx_mapping['pytest'] = ('https://pytest.readthedocs.io/en/stable/', None)  # noqa: F405
intersphinx_mapping['ipython'] = ('https://ipython.readthedocs.io/en/stable/', None)  # noqa: F405
intersphinx_mapping['pandas'] = ('https://pandas.pydata.org/pandas-docs/stable/', None)  # noqa: F405, E501
intersphinx_mapping['sphinx_automodapi'] = ('https://sphinx-automodapi.readthedocs.io/en/stable/', None)  # noqa: F405, E501
intersphinx_mapping['packagetemplate'] = ('https://docs.astropy.org/projects/package-template/en/latest/', None)  # noqa: F405, E501
intersphinx_mapping['h5py'] = ('http://docs.h5py.org/en/stable/', None)  # noqa: F405

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns.append('_templates')  # noqa: F405
exclude_patterns.append('_pkgtemplate.rst')  # noqa: F405
exclude_patterns.append('**/*.inc.rst')  # .inc.rst mean *include* files, don't have sphinx process them  # noqa: F405, E501

# Add any paths that contain templates here, relative to this directory.
if 'templates_path' not in locals():  # in case parent conf.py defines it
    templates_path = []
templates_path.append('_templates')

# Grab minversion from setup.cfg
setup_cfg = configparser.ConfigParser()
setup_cfg.read(os.path.join(os.path.pardir, 'setup.cfg'))
__minimum_python_version__ = setup_cfg['options']['python_requires'].replace('>=', '')
project = u'Astropy'
astropy_dist = get_distribution(project)
astropy_req = astropy_dist.requires(extras=('all', ))

def minvers(name):
    if name == 'yaml':
        name = 'PyYAML'
    elif name == 'erfa':
        name = 'pyerfa'
    for cur_req in astropy_req:
        if cur_req.name == name:
            return cur_req.specs[0][1]
    raise ValueError(f"{name} not among requirements!")

# This is added to the end of RST files - a good place to put substitutions to
# be used globally.
rst_epilog += "\n".join(
    f".. |minimum_{name}_version| replace:: {minvers(name)}"
    for name in ('numpy', 'erfa', 'scipy', 'yaml', 'asdf', 'matplotlib', 'ipython')) + f"""
.. |minimum_python_version| replace:: {__minimum_python_version__}

.. Astropy
.. _`Astropy mailing list`: https://mail.python.org/mailman/listinfo/astropy
.. _`astropy-dev mailing list`: http://groups.google.com/group/astropy-dev
"""

# -- Project information ------------------------------------------------------

author = u'The Astropy Developers'
copyright = f'2011–{datetime.utcnow().year}, ' + author

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.

# The full version, including alpha/beta/rc tags.
release = astropy_dist.version
# The short X.Y version.
version = '.'.join(release.split('.')[:2])

# -- Options for the module index ---------------------------------------------

modindex_common_prefix = ['astropy.']

# -- Options for HTML output ---------------------------------------------------

# A NOTE ON HTML THEMES
#
# The global astropy configuration uses a custom theme,
# 'bootstrap-astropy', which is installed along with astropy. The
# theme has options for controlling the text of the logo in the upper
# left corner. This is how you would specify the options in order to
# override the theme defaults (The following options *are* the
# defaults, so we do not actually need to set them here.)

# html_theme_options = {
#    'logotext1': 'astro',  # white,  semi-bold
#    'logotext2': 'py',     # orange, light
#    'logotext3': ':docs'   # white,  light
#    }

# A different theme can be used, or other parts of this theme can be
# modified, by overriding some of the variables set in the global
# configuration. The variables set in the global configuration are
# listed below, commented out.

# Add any paths that contain custom themes here, relative to this directory.
# To use a different custom theme, add the directory containing the theme.
# html_theme_path = []

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes. To override the custom theme, set this to the
# name of a builtin theme or the name of a custom theme in html_theme_path.
# html_theme = None

# Custom sidebar templates, maps document names to template names.
# html_sidebars = {}

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
# html_favicon = ''

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
# html_last_updated_fmt = ''

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
html_title = f'{project} v{release}'

# Output file base name for HTML help builder.
htmlhelp_basename = project + 'doc'

# A dictionary of values to pass into the template engine’s context for all pages.
html_context = {
    'to_be_indexed': ['stable', 'latest']
}

# -- Options for LaTeX output --------------------------------------------------

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [('index', project + '.tex', project + u' Documentation',
                    author, 'manual')]

latex_logo = '_static/astropy_logo.pdf'

# -- Options for manual page output --------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [('index', project.lower(), project + u' Documentation',
              [author], 1)]

# Setting this URL is requited by sphinx-astropy
github_issues_url = 'https://github.com/astropy/astropy/issues/'
edit_on_github_branch = 'master'

# Enable nitpicky mode - which ensures that all references in the docs
# resolve.

nitpicky = True
# This is not used. See docs/nitpick-exceptions file for the actual listing.
nitpick_ignore = []

for line in open('nitpick-exceptions'):
    if line.strip() == "" or line.startswith("#"):
        continue
    dtype, target = line.split(None, 1)
    target = target.strip()
    nitpick_ignore.append((dtype, target))

# -- Options for the Sphinx gallery -------------------------------------------

try:
    import warnings
    import sphinx_gallery  # noqa: F401
    extensions += ["sphinx_gallery.gen_gallery"]  # noqa: F405

    sphinx_gallery_conf = {
        'backreferences_dir': 'generated/modules',  # path to store the module using example template  # noqa: E501
        'filename_pattern': '^((?!skip_).)*$',  # execute all examples except those that start with "skip_"  # noqa: E501
        'examples_dirs': f'..{os.sep}examples',  # path to the examples scripts
        'gallery_dirs': 'generated/examples',  # path to save gallery generated examples
        'reference_url': {
            'astropy': None,
            'matplotlib': 'https://matplotlib.org/',
            'numpy': 'https://numpy.org/doc/stable/',
        },
        'abort_on_example_error': True
    }

    # Filter out backend-related warnings as described in
    # https://github.com/sphinx-gallery/sphinx-gallery/pull/564
    warnings.filterwarnings("ignore", category=UserWarning,
                            message='Matplotlib is currently using agg, which is a'
                                    ' non-GUI backend, so cannot show the figure.')

except ImportError:
    def setup(app):
        msg = ('The sphinx_gallery extension is not installed, so the '
               'gallery will not be built.  You will probably see '
               'additional warnings about undefined references due '
               'to this.')
        try:
            app.warn(msg)
        except AttributeError:
            # Sphinx 1.6+
            from sphinx.util import logging
            logger = logging.getLogger(__name__)
            logger.warning(msg)

# -- Options for linkcheck output -------------------------------------------
linkcheck_retry = 5
linkcheck_ignore = ['https://journals.aas.org/manuscript-preparation/',
                    'https://maia.usno.navy.mil/',
                    'https://www.usno.navy.mil/USNO/time/gps/usno-gps-time-transfer',
                    'https://aa.usno.navy.mil/publications/docs/Circular_179.php',
                    'http://data.astropy.org',
                    r'https://github\.com/astropy/astropy/(?:issues|pull)/\d+']
linkcheck_timeout = 180
linkcheck_anchors = False

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
html_extra_path = ['robots.txt']
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# Teak: Unity documentation build configuration file, created by
# sphinx-quickstart on Tue Aug 29 19:04:36 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import sys
# sys.path.insert(0, os.path.abspath('.'))

import os, subprocess, re, sys, errno
from functools import cmp_to_key

read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True'

import importlib
import sys

sys.path.append('.')

docs_common = importlib.import_module('teak-docs-common')

# if read_the_docs_build:
#     subprocess.call('cd .. ; doxygen', shell=True)

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.

# sys.path.append(os.path.abspath('./ext'))
extensions = [ 'sphinx.ext.intersphinx' ]

# Intersphinx
intersphinx_mapping = docs_common.intersphinx_mapping(globals())

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'Teak for Unity'
copyright = u'Teak.io, Inc.'
author = u'Teak'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# x.y.z[-short-sha]
git_version = subprocess.check_output(['git', 'describe', '--tags', '--always'])
# The short X.Y version.
version = str(git_version.split(b'-')[0], 'utf-8')
# The full version, including alpha/beta/rc tags.
release = str(git_version, 'utf-8')

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'versions',
    'ios/index.rst', 'ios/versions', 'android/index.rst', 'android/versions']

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
import sphinx_rtd_theme
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# This is required for the alabaster theme
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
html_sidebars = {
    '**': [
        'about.html',
        'navigation.html',
        'relations.html',  # needs 'show_related': True theme option to display
        'searchbox.html',
        'donate.html',
    ]
}

# -- Options for HTMLHelp output ------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = 'TeakUnitydoc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    #
    # 'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    #
    # 'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    #
    # 'preamble': '',

    # Latex figure (float) alignment
    #
    # 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, 'TeakUnity.tex', u'Teak for Unity Documentation',
     u'Teak', 'manual'),
]

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'teakunity', u'Teak for Unity Documentation',
     [author], 1)
]

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, 'TeakUnity', u'Teak for Unity Documentation',
     author, 'TeakUnity', 'One line description of project.',
     'Miscellaneous'),
]

# -- Sidebar --------------------------------------------------------------

docs_common.generate_sidebar(globals(), 'unity', './_sidebar.rst.inc')

####
# Global include
with open('global.irst', 'r') as f:
    rst_prolog = f.read()

#####
# Setup
def cmp_versions(a, b):
    v = re.compile('^(\d+)\.(\d+)\.(\d+)')
    av = list(list(map(lambda x: map(int, x), v.findall(a)))[0])
    bv = list(list(map(lambda x: map(int, x), v.findall(b)))[0])

    if av[0] > bv[0]: return 1
    elif av[0] < bv[0]: return -1
    elif av[1] > bv[1]: return 1
    elif av[1] < bv[1]: return -1
    elif av[2] > bv[2]: return 1
    elif av[2] < bv[2]: return -1
    return 0

def ensure_versions():
    ios = set(os.listdir('ios/versions'))
    android = set(os.listdir('android/versions'))
    sdk = set(os.listdir('versions'))

    if len(ios.difference(android)) != 0:
        raise Exception('Android versions missing: %s' % ios.difference(android))
    if len(android.difference(ios)) != 0:
        raise Exception('iOS versions missing: %s' % android.difference(ios))
    if len(android.difference(sdk)):
        raise Exception('Versions missing: %s' % android.difference(sdk))

def mkdir_p(path):
    try:
        os.makedirs(path)
    except OSError as exc:  # Python >2.5
        if exc.errno == errno.EEXIST and os.path.isdir(path):
            pass
        else:
            raise

def gen_version(v):
    version = os.path.splitext(v)[0]

    contents = '.. _unity-%s:\n\n' % version.replace('.', '-')
    with open('versions/%s.rst' % version, 'r') as f:
        contents += f.read().replace(version, '{0} - `Download <http://sdks.teakcdn.com/unity/Teak-{1}.unitypackage>`_'.format(version, version))

    contents += '\n.. _android-%s:\n\nAndroid\n^^^^^^^\n' % version.replace('.', '-')
    with open('android/versions/%s.rst' % version, 'r') as f:
        data = f.read().splitlines(True)
        contents += ''.join(map(lambda ln: '    %s' % ln, data[2:]))

    contents += '\n.. _ios-%s:\n\niOS\n^^^\n' % version.replace('.', '-')
    with open('ios/versions/%s.rst' % version, 'r') as f:
        data = f.read().splitlines(True)
        contents += ''.join(map(lambda ln: '    %s' % ln, data[2:]))

    return contents

def setup(app):
    # Ensure all versions of iOS/Android/X have entries
    sys.stderr.write("Ensuring all native docs have changelogs\n")
    ensure_versions()

    # Make array of versions
    versions = []
    for version in sorted(os.listdir('versions'), key = cmp_to_key(cmp_versions), reverse=True):
        sys.stderr.write("Adding version %s\n" % version)
        versions.append(gen_version(version))

    changelog = """Changelog
=========

%s
""" % '\n'.join(versions)

    if os.path.isfile('changelog.rst'):
        with open('changelog.rst', 'r') as f:
            data = f.read()
    else:
        data = ''

    if data != changelog:
        sys.stderr.write("Writing new changelog.rst\n")
        with open('changelog.rst', 'w') as f:
            f.write(changelog)
<commit_msg>Maybe fix changelog anchors<commit_after># -*- coding: utf-8 -*-
#
# Teak: Unity documentation build configuration file, created by
# sphinx-quickstart on Tue Aug 29 19:04:36 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import sys
# sys.path.insert(0, os.path.abspath('.'))

import os, subprocess, re, sys, errno
from functools import cmp_to_key

read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True'

import importlib
import sys

sys.path.append('.')

docs_common = importlib.import_module('teak-docs-common')

# if read_the_docs_build:
#     subprocess.call('cd .. ; doxygen', shell=True)

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.

# sys.path.append(os.path.abspath('./ext'))
extensions = [ 'sphinx.ext.intersphinx' ]

# Intersphinx
intersphinx_mapping = docs_common.intersphinx_mapping(globals())

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'Teak for Unity'
copyright = u'Teak.io, Inc.'
author = u'Teak'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# x.y.z[-short-sha]
git_version = subprocess.check_output(['git', 'describe', '--tags', '--always'])
# The short X.Y version.
version = str(git_version.split(b'-')[0], 'utf-8')
# The full version, including alpha/beta/rc tags.
release = str(git_version, 'utf-8')

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'versions',
    'ios/index.rst', 'ios/versions', 'android/index.rst', 'android/versions']

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
import sphinx_rtd_theme
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# This is required for the alabaster theme
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
html_sidebars = {
    '**': [
        'about.html',
        'navigation.html',
        'relations.html',  # needs 'show_related': True theme option to display
        'searchbox.html',
        'donate.html',
    ]
}

# -- Options for HTMLHelp output ------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = 'TeakUnitydoc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    #
    # 'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    #
    # 'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    #
    # 'preamble': '',

    # Latex figure (float) alignment
    #
    # 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, 'TeakUnity.tex', u'Teak for Unity Documentation',
     u'Teak', 'manual'),
]

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'teakunity', u'Teak for Unity Documentation',
     [author], 1)
]

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, 'TeakUnity', u'Teak for Unity Documentation',
     author, 'TeakUnity', 'One line description of project.',
     'Miscellaneous'),
]

# -- Sidebar --------------------------------------------------------------

docs_common.generate_sidebar(globals(), 'unity', './_sidebar.rst.inc')

####
# Global include
with open('global.irst', 'r') as f:
    rst_prolog = f.read()

#####
# Setup
def cmp_versions(a, b):
    v = re.compile('^(\d+)\.(\d+)\.(\d+)')
    av = list(list(map(lambda x: map(int, x), v.findall(a)))[0])
    bv = list(list(map(lambda x: map(int, x), v.findall(b)))[0])

    if av[0] > bv[0]: return 1
    elif av[0] < bv[0]: return -1
    elif av[1] > bv[1]: return 1
    elif av[1] < bv[1]: return -1
    elif av[2] > bv[2]: return 1
    elif av[2] < bv[2]: return -1
    return 0

def ensure_versions():
    ios = set(os.listdir('ios/versions'))
    android = set(os.listdir('android/versions'))
    sdk = set(os.listdir('versions'))

    if len(ios.difference(android)) != 0:
        raise Exception('Android versions missing: %s' % ios.difference(android))
    if len(android.difference(ios)) != 0:
        raise Exception('iOS versions missing: %s' % android.difference(ios))
    if len(android.difference(sdk)):
        raise Exception('Versions missing: %s' % android.difference(sdk))

def mkdir_p(path):
    try:
        os.makedirs(path)
    except OSError as exc:  # Python >2.5
        if exc.errno == errno.EEXIST and os.path.isdir(path):
            pass
        else:
            raise

def gen_version(v):
    version = os.path.splitext(v)[0]

    contents = '.. _unity-%s:\n\n' % version.replace('.', '-')
    with open('versions/%s.rst' % version, 'r') as f:
        data = f.read().splitlines(True)
        contents += data[0]
        contents += data[1]
        contents += "\n"
        contents += '`Download <http://sdks.teakcdn.com/unity/Teak-{1}.unitypackage>`_'.format(version, version)
        contents += "\n\n"
        contents += ''.join(data[2:])

    contents += '\n.. _android-%s:\n\nAndroid\n^^^^^^^\n' % version.replace('.', '-')
    with open('android/versions/%s.rst' % version, 'r') as f:
        data = f.read().splitlines(True)
        contents += ''.join(map(lambda ln: '    %s' % ln, data[2:]))

    contents += '\n.. _ios-%s:\n\niOS\n^^^\n' % version.replace('.', '-')
    with open('ios/versions/%s.rst' % version, 'r') as f:
        data = f.read().splitlines(True)
        contents += ''.join(map(lambda ln: '    %s' % ln, data[2:]))

    return contents

def setup(app):
    # Ensure all versions of iOS/Android/X have entries
    sys.stderr.write("Ensuring all native docs have changelogs\n")
    ensure_versions()

    # Make array of versions
    versions = []
    for version in sorted(os.listdir('versions'), key = cmp_to_key(cmp_versions), reverse=True):
        sys.stderr.write("Adding version %s\n" % version)
        versions.append(gen_version(version))

    changelog = """Changelog
=========

%s
""" % '\n'.join(versions)

    if os.path.isfile('changelog.rst'):
        with open('changelog.rst', 'r') as f:
            data = f.read()
    else:
        data = ''

    if data != changelog:
        sys.stderr.write("Writing new changelog.rst\n")
        with open('changelog.rst', 'w') as f:
            f.write(changelog)
<|endoftext|>


<commit_before>#
# Functions to calculate various MCMC diagnostics
#
# This file is part of PINTS (https://github.com/pints-team/pints/) which is
# released under the BSD 3-clause license. See accompanying LICENSE.md for
# copyright notice and full license details.
#
import numpy as np

def autocorrelation(x):
    """
    Calculate autocorrelation for a vector x using a spectrum density
    calculation.
    """
    x = (x - np.mean(x)) / (np.std(x) * np.sqrt(len(x)))
    result = np.correlate(x, x, mode='full')
    return result[int(result.size / 2):]

def autocorrelate_negative(autocorrelation):
    """
    Finds last positive autocorrelation, T.
    """
    T = 1
    for a in autocorrelation:
        if a < 0:
            return T - 1
        T += 1
    return T

def ess_single_param(x):
    """
    Calculates ESS for a single parameter.
    """
    rho = autocorrelation(x)
    T = autocorrelate_negative(rho)
    n = len(x)
    ess = n / (1 + 2 * np.sum(rho[0:T]))
    return ess

def effective_sample_size(samples):
    """
    Calculates ESS for a matrix of samples.
    """
    try:
        n_samples, n_params = samples.shape
    except (ValueError, IndexError):
        raise ValueError('Samples must be given as a 2d array.')
    if n_samples < 2:
        raise ValueError('At least two samples must be given.')

    return [ess_single_param(samples[:, i]) for i in range(0, n_params)]

def _within(chains):
    r"""
    Calculates mean within-chain variance.

    The mean within chain variance :math:`W` of :math:`m` chains of length
    :math:`n` is defined as

    .. math::
        W = \frac{1}{m}\sum _{j=1}^{m}s_j^2\quad \text{where}\quad
        s_j^2=\frac{1}{n-1}\sum _{i=1}^n(\psi _{ij} - \bar{\psi} _j)^2.

    Here, :math:`\psi _{ij}` is the :math:`j`th sample of the :math:`i`th
    chain and :math:`\bar{\psi _j}=\sum _{i=1}^{n}\psi _{ij}/n` is the within
    chain mean of the parameter :math:`\psi`.

    Parameters
    ----------
    chains {np.ndarray, shape=(m, n)}
        A numpy array with the :math:`n` samples for `:math:`m` chains.
    """
    # Compute unbiased within-chain variance estimate
    within_chain_var = np.var(chains, axis=1, ddof=1)

    # Compute mean-within chain variance
    w = np.mean(within_chain_var)

    return w

def _between(chains):
    r"""
    Calculates mean between-chain variance.

    The mean between-chain variance :math:`W` of :math:`m` chains of length
    :math:`n` is defined as

    .. math::
        B = \frac{n'}{m'-1}\sum _{j=1}^{m'}(\bar{\psi} _j - \bar{\psi})^2,

    where :math:`\psi _{ij}` is the :math:`j`th sample of the :math:`i`th
    chain, :math:`\bar{\psi _j}=\sum _{i=1}^{n'}\psi _{ij}/n'` is the within
    chain mean of the parameter :math:`\psi`, and
    :math:`\bar{\psi } = \sum _{j=1}^{m}\bar{\psi} _{j}/m` is the between
    chain mean of the within chain means.

    Parameters
    ----------
    chains {np.ndarray, shape=(m, n)}
        A numpy array with the :math:`n` samples for `:math:`m` chains.
    """
    # Get number of samples
    n = chains.shape[1]

    # Compute within-chain mean
    within_chain_means = np.mean(chains, axis=1)

    # Compute variance across chains of within-chain means
    between_chain_var = np.var(within_chain_means, ddof=1)

    # Weight variance with number of samples per chain
    b = n * between_chain_var

    return b

def reorder(param_number, chains):
    """
    Reorders chains for a given parameter into a more useful format for
    calculating rhat.
    """
    # split chains in two
    a_len = int(chains.shape[1] / 2)
    chains_first = [chain[:a_len, :] for chain in chains]
    chains_second = [chain[a_len:, :] for chain in chains]
    chains = chains_first + chains_second
    num_chains = len(chains)
    samples = [chains[i][:, param_number] for i in range(0, num_chains)]
    return samples

def reorder_all_params(chains):
    """
    Reorders chains for all parameters into a more useful format for
    calculating rhat.
    """
    num_params = chains[0].shape[1]
    samples_all = [reorder(i, chains) for i in range(0, num_params)]
    return samples_all

def rhat(chains, warm_up=0.0):
    r"""
    Returns the convergence measure :math:`\hat{R}` for the approximate
    posterior of a model parameter according to [1]_.

    Parameters
    ----------
    chains {np.ndarray, shape=(m, n)}
        A numpy array with the :math:`n` samples for `:math:`m` chains.
    warm_up {float}
        First portion of each chain that will not be used for the
        computation of :math:`\hat{R}`.

    Background
    ----------

    :math:`\hat{R}` diagnoses convergence by checking mixing and stationarity
    of :math:`m` chains (at least two, :math:`m\geq 2`). To diminish the
    influence of starting values, the first portion of each chain can be
    excluded from the computation. Subsequently, the truncated
    chains are split in half, resulting in a total number of :math:`m'=2m`
    chains of length :math:`n'=(1-\text{warm_up})n/2`. The mean of the
    variances within and between the resulting chains are computed, :math:`W`
    and :math:`B` respectively. Based on those variances an estimator of the
    marginal posterior variance is constructed

    .. math::
        \widehat{\text{var}}^+ = \frac{n'-1}{n'}W + \frac{1}{n'}B,

    The estimator overestimates the variance of the marginal posterior
    if the chains are not well mixed and stationary, but is unbiased if the
    original chains equal the target distribution. At the same time, the mean
    within variance :math:`W` underestimates the marginal posterior variance
    for finite :math:`n`, but converges to the true variance for
    :math:`n\rightarrow \infty`. By comparing :math:`\widehat{\text{var}}^+`
    and :math:`W` the mixing and stationarity of the chains can be quantified

    .. math::
        \hat{R} = \sqrt{\frac{\widehat{\text{var}}^+}{W}}.

    For well mixed and stationary chains :math:`\hat{R}` will be close to one.

    The mean within :math:`W` and mean between :math:`B` variance of the
    :math:`m'=2m` chains of length :math:`n'=(1-\text{warm_up})n/2` are defined
    as

    .. math::
        W = \frac{1}{m'}\sum _{j=1}^{m'}s_j^2\quad \text{where}\quad
        s_j^2=\frac{1}{n'-1}\sum _{i=1}^{n'}(\psi _{ij} - \bar{\psi} _j)^2,

    .. math::
        B = \frac{n'}{m'-1}\sum _{j=1}^{m'}(\bar{\psi} _j - \bar{\psi})^2.

    Here, :math:`\psi _{ij}` is the :math:`j`th sample of the :math:`i`th
    chain, :math:`\bar{\psi _j}=\sum _{i=1}^{n'}\psi _{ij}/n'` is the within
    chain mean of the parameter :math:`\psi` and
    :math:`\bar{\psi } = \sum _{j=1}^{m'}\bar{\psi} _{j}/m'` is the between
    chain mean of the within chain means.

    References
    ----------
    ..  [1] "Bayesian data analysis", 3rd edition, Gelman et al., 2014.
    """
    if chains.ndim != 2:
        raise ValueError(
            'Dimension of chains is %d. Method computes R^hat for one '
            'parameter and therefore only accepts 2 dimensional arrays.')
    if chains.shape[0] < 2:
        raise ValueError(
            'Number of chains is %d. Method needs at least 2 chains.' %
            chains.shape[0])
    if warm_up > 1:
        raise ValueError(
            'warum_up is set to %f. warm_up only takes values in [0,1].' %
            warm_up)

    # Get number of samples
    n = chains.shape[1]

    # Exclude warm up
    chains = chains[:, int(n * warm_up):]
    n = chains.shape[1]

    # Split chains in half
    n = n // 2  # new length of chains
    if n < 2:
        raise ValueError(
            'Number of samples per chain after warm-up and chain splitting is '
            '%d. Method needs at least 2 samples per chain.' % n)
    chains = np.vstack([chains[:n], chains[-n:]])

    # Compute mean within-chain variance
    w = _within(chains)

    # Compute mean between-chain variance
    b = _between(chains)

    # Compute R^hat
    try:
        rhat = np.sqrt((w + (b - w)) / (w * n))
    except ZeroDivisionError:
        raise ValueError(
            'Vanishing mean within-chain variance.')

    return rhat

def rhat_all_params(chains):
    r"""
    Calculates :math:`\hat{R} = \sqrt{(W(n - 1) / n + (1 / n) B) / W}` as per
    [1]_ for all parameters. It does this after splitting each chain into two.

    References
    ----------
    ..  [1] "Bayesian data analysis", 3rd edition, Gelman et al., 2014.
    """
    samples_all = reorder_all_params(chains)
    rhat_all = list(map(lambda x: rhat(x), samples_all))
    return rhat_all
<commit_msg>refactored rhat<commit_after>#
# Functions to calculate various MCMC diagnostics
#
# This file is part of PINTS (https://github.com/pints-team/pints/) which is
# released under the BSD 3-clause license. See accompanying LICENSE.md for
# copyright notice and full license details.
#
import numpy as np

def autocorrelation(x):
    """
    Calculate autocorrelation for a vector x using a spectrum density
    calculation.
    """
    x = (x - np.mean(x)) / (np.std(x) * np.sqrt(len(x)))
    result = np.correlate(x, x, mode='full')
    return result[int(result.size / 2):]

def autocorrelate_negative(autocorrelation):
    """
    Finds last positive autocorrelation, T.
    """
    T = 1
    for a in autocorrelation:
        if a < 0:
            return T - 1
        T += 1
    return T

def ess_single_param(x):
    """
    Calculates ESS for a single parameter.
    """
    rho = autocorrelation(x)
    T = autocorrelate_negative(rho)
    n = len(x)
    ess = n / (1 + 2 * np.sum(rho[0:T]))
    return ess

def effective_sample_size(samples):
    """
    Calculates ESS for a matrix of samples.
    """
    try:
        n_samples, n_params = samples.shape
    except (ValueError, IndexError):
        raise ValueError('Samples must be given as a 2d array.')
    if n_samples < 2:
        raise ValueError('At least two samples must be given.')

    return [ess_single_param(samples[:, i]) for i in range(0, n_params)]

def _within(chains):
    r"""
    Calculates mean within-chain variance.

    The mean within chain variance :math:`W` of :math:`m` chains of length
    :math:`n` is defined as

    .. math::
        W = \frac{1}{m}\sum _{j=1}^{m}s_j^2\quad \text{where}\quad
        s_j^2=\frac{1}{n-1}\sum _{i=1}^n(\psi _{ij} - \bar{\psi} _j)^2.

    Here, :math:`\psi _{ij}` is the :math:`j`th sample of the :math:`i`th
    chain and :math:`\bar{\psi _j}=\sum _{i=1}^{n}\psi _{ij}/n` is the within
    chain mean of the parameter :math:`\psi`.

    Parameters
    ----------
    chains {np.ndarray, shape=(m, n)}
        A numpy array with the :math:`n` samples for `:math:`m` chains.
    """
    # Compute unbiased within-chain variance estimate
    within_chain_var = np.var(chains, axis=1, ddof=1)

    # Compute mean-within chain variance
    w = np.mean(within_chain_var, axis=0)

    return w

def _between(chains):
    r"""
    Calculates mean between-chain variance.

    The mean between-chain variance :math:`W` of :math:`m` chains of length
    :math:`n` is defined as

    .. math::
        B = \frac{n'}{m'-1}\sum _{j=1}^{m'}(\bar{\psi} _j - \bar{\psi})^2,

    where :math:`\psi _{ij}` is the :math:`j`th sample of the :math:`i`th
    chain, :math:`\bar{\psi _j}=\sum _{i=1}^{n'}\psi _{ij}/n'` is the within
    chain mean of the parameter :math:`\psi`, and
    :math:`\bar{\psi } = \sum _{j=1}^{m}\bar{\psi} _{j}/m` is the between
    chain mean of the within chain means.

    Parameters
    ----------
    chains {np.ndarray, shape=(m, n)}
        A numpy array with the :math:`n` samples for `:math:`m` chains.
    """
    # Get number of samples
    n = chains.shape[1]

    # Compute within-chain mean
    within_chain_means = np.mean(chains, axis=1)

    # Compute variance across chains of within-chain means
    between_chain_var = np.var(within_chain_means, axis=0, ddof=1)

    # Weight variance with number of samples per chain
    b = n * between_chain_var

    return b

def reorder(param_number, chains):
    """
    Reorders chains for a given parameter into a more useful format for
    calculating rhat.
    """
    # split chains in two
    a_len = int(chains.shape[1] / 2)
    chains_first = [chain[:a_len, :] for chain in chains]
    chains_second = [chain[a_len:, :] for chain in chains]
    chains = chains_first + chains_second
    num_chains = len(chains)
    samples = [chains[i][:, param_number] for i in range(0, num_chains)]
    return samples

def reorder_all_params(chains):
    """
    Reorders chains for all parameters into a more useful format for
    calculating rhat.
    """
    num_params = chains[0].shape[1]
    samples_all = [reorder(i, chains) for i in range(0, num_params)]
    return samples_all

def rhat(chains, warm_up=0.0):
    r"""
    Returns the convergence measure :math:`\hat{R}` for the approximate
    posterior according to [1]_.

    Parameters
    ----------
    chains {np.ndarray, shape=(m, n), optional: shape=(m, n, p)}
        A numpy array with the :math:`n` samples for `:math:`m` chains.
        Optionally the :math:`\hat{R}` for :math:`p` parameters can be computed
        by passing a numpy array with :math:`m` chains of length :math:`n`
        for :math:`p` parameters.
    warm_up {float}
        First portion of each chain that will not be used for the
        computation of :math:`\hat{R}`.

    Returns
    -------
    rhat {float or np.ndarray, shape=(p,)}
        :math:`\hat{R}` of the posteriors for each parameter.

    Background
    ----------

    :math:`\hat{R}` diagnoses convergence by checking mixing and stationarity
    of :math:`m` chains (at least two, :math:`m\geq 2`). To diminish the
    influence of starting values, the first portion of each chain can be
    excluded from the computation. Subsequently, the truncated
    chains are split in half, resulting in a total number of :math:`m'=2m`
    chains of length :math:`n'=(1-\text{warm_up})n/2`. The mean of the
    variances within and between the resulting chains are computed, :math:`W`
    and :math:`B` respectively. Based on those variances an estimator of the
    marginal posterior variance is constructed

    .. math::
        \widehat{\text{var}}^+ = \frac{n'-1}{n'}W + \frac{1}{n'}B,

    The estimator overestimates the variance of the marginal posterior
    if the chains are not well mixed and stationary, but is unbiased if the
    original chains equal the target distribution. At the same time, the mean
    within variance :math:`W` underestimates the marginal posterior variance
    for finite :math:`n`, but converges to the true variance for
    :math:`n\rightarrow \infty`. By comparing :math:`\widehat{\text{var}}^+`
    and :math:`W` the mixing and stationarity of the chains can be quantified

    .. math::
        \hat{R} = \sqrt{\frac{\widehat{\text{var}}^+}{W}}.

    For well mixed and stationary chains :math:`\hat{R}` will be close to one.

    The mean within :math:`W` and mean between :math:`B` variance of the
    :math:`m'=2m` chains of length :math:`n'=(1-\text{warm_up})n/2` are defined
    as

    .. math::
        W = \frac{1}{m'}\sum _{j=1}^{m'}s_j^2\quad \text{where}\quad
        s_j^2=\frac{1}{n'-1}\sum _{i=1}^{n'}(\psi _{ij} - \bar{\psi} _j)^2,

    .. math::
        B = \frac{n'}{m'-1}\sum _{j=1}^{m'}(\bar{\psi} _j - \bar{\psi})^2.

    Here, :math:`\psi _{ij}` is the :math:`j`th sample of the :math:`i`th
    chain, :math:`\bar{\psi _j}=\sum _{i=1}^{n'}\psi _{ij}/n'` is the within
    chain mean of the parameter :math:`\psi` and
    :math:`\bar{\psi } = \sum _{j=1}^{m'}\bar{\psi} _{j}/m'` is the between
    chain mean of the within chain means.

    References
    ----------
    ..  [1] "Bayesian data analysis", 3rd edition, Gelman et al., 2014.
    """
    if not (chains.ndim == 2 or chains.ndim == 3):
        raise ValueError(
            'Dimension of chains is %d. ' % chains.ndim
            + 'Method computes R^hat for one '
            ' or multiple parameters and therefore only accepts 2 or 3 '
            'dimensional arrays.')
    if chains.shape[0] < 2:
        raise ValueError(
            'Number of chains is %d. Method needs at least 2 chains.' %
            chains.shape[0])
    if warm_up > 1:
        raise ValueError(
            'warum_up is set to %f. warm_up only takes values in [0,1].' %
            warm_up)

    # Get number of samples
    n = chains.shape[1]

    # Exclude warm-up
    chains = chains[:, int(n * warm_up):]
    n = chains.shape[1]

    # Split chains in half
    n = n // 2  # new length of chains
    if n < 2:
        raise ValueError(
            'Number of samples per chain after warm-up and chain splitting is '
            '%d. Method needs at least 2 samples per chain.' % n)
    chains = np.vstack([chains[:, :n], chains[:, -n:]])

    # Compute mean within-chain variance
    w = _within(chains)

    # Compute mean between-chain variance
    b = _between(chains)

    # Compute R^hat
    try:
        rhat = np.sqrt((w + (b - w)) / (w * n))
    except ZeroDivisionError:
        raise ValueError(
            'Vanishing mean within-chain variance.')

    return rhat

def rhat_all_params(chains, warm_up):
    r"""
    Calculates :math:`\hat{R} = \sqrt{(W(n - 1) / n + (1 / n) B) / W}` as per
    [1]_ for all parameters. It does this after splitting each chain into two.

    References
    ----------
    ..  [1] "Bayesian data analysis", 3rd edition, Gelman et al., 2014.
    """
    samples_all = reorder_all_params(chains)
    rhat_all = list(map(lambda x: rhat(x), samples_all))
    return rhat_all
<|endoftext|>