<filename>code/syn.py
# syn.py
# <NAME> (with <NAME>)
# August 3, 2017
# python3

"""
Routines to generate a synthetic test election dataset for OpenAuditTool.py.

Calls data generation routines in syn1.py for elections "of type 1",
and calls routines in syn2.py for elections "of type 2".
"""


import numpy as np

import cli_syn
import OpenAuditTool

class Syn_Params(object):
    """ An object we can hang synthesis generation parameters off of. """

    pass


##############################################################################
## random choices

def geospace(start, stop, num=7):
    """
    Return a list of up to num distinct integer values,
    from start, start+1, ..., stop, inclusive, geometrically spread out.

    A bit like numpy.linspace, but geometrically spread
    out rather than linearly spread out, and only integers returned.
    >>> geospace(1, 64)
    [1, 2, 4, 8, 16, 32, 64]
    >>> geospace(0,1)
    [0, 1]
    >>> geospace(0,10)
    [0, 1, 2, 3, 5, 7, 10]
    >>> geospace(20, 10000)
    [20, 56, 159, 447, 1260, 3550, 10000]

    Twelve-tone equal temperament scale
    >>> geospace(1000, 2000, num=13)
    [1000, 1059, 1122, 1189, 1260, 1335, 1414, 1498, 1587, 1682, 1782, 1888, 2000]

    This should presumably be replaced by numpy.logspace !
    (although duplicates need to be removed...)
    """

    answer = {start, stop}
    start = max(start, 1)
    for i in range(1, num-1):
        answer.add(int(np.rint(start*(stop/start)**(i/(num-1)))))
    return sorted(answer)


def geospace_choice(e, syn, start, stop, num=7):
    """ 
    Return a random element from geospace(start, stop, num), 
    based on syn.RandomState.
    """

    elts = geospace(start, stop, num)
    return syn.RandomState.choice(elts)


def generate_segments(e, syn, low, high):
    """ 
    Return list of random segments (r, s) where low <= r < s <= high. 

    Number of segments returned is (high-low).

    Since r<s, does not return segments of the form (k, k).

    Intent is that cids are integers in range low <= cid <= high,
    and each segment yields a contest group covering cids r..s (inclusive).

    The segments "nest" -- given any two segments, either they
    are disjoint, or they are equal, or one contains the other.
    """

    assert low <= high
    L = []
    if low!=high:
        L.append((low, high))
        mid = syn.RandomState.choice(range(low, high))
        L.extend(generate_segments(e, syn, low, mid))
        L.extend(generate_segments(e, syn, mid+1, high))
    return L



if __name__=="__main__":

    e = OpenAuditTool.Election()
    args = cli_syn.parse_args()
    cli_syn.dispatch(e, args)


<filename>code/syn.py
# syn.py
# <NAME> (with <NAME>)
# August 3, 2017
# python3

"""
Routines to generate a synthetic test election dataset for OpenAuditTool.py.

Calls data generation routines in syn1.py for elections "of type 1",
and calls routines in syn2.py for elections "of type 2".
"""


import numpy as np

import cli_syn
import OpenAuditTool

class Syn_Params(object):
    """ An object we can hang synthesis generation parameters off of. """

    pass


##############################################################################
## random choices

def geospace(start, stop, num=7):
    """
    Return a list of up to num distinct integer values,
    from start, start+1, ..., stop, inclusive, geometrically spread out.

    A bit like numpy.linspace, but geometrically spread
    out rather than linearly spread out, and only integers returned.
    >>> geospace(1, 64)
    [1, 2, 4, 8, 16, 32, 64]
    >>> geospace(0,1)
    [0, 1]
    >>> geospace(0,10)
    [0, 1, 2, 3, 5, 7, 10]
    >>> geospace(20, 10000)
    [20, 56, 159, 447, 1260, 3550, 10000]

    Twelve-tone equal temperament scale
    >>> geospace(1000, 2000, num=13)
    [1000, 1059, 1122, 1189, 1260, 1335, 1414, 1498, 1587, 1682, 1782, 1888, 2000]

    This should presumably be replaced by numpy.logspace !
    (although duplicates need to be removed...)
    """

    answer = {start, stop}
    start = max(start, 1)
    for i in range(1, num-1):
        answer.add(int(np.rint(start*(stop/start)**(i/(num-1)))))
    return sorted(answer)


def geospace_choice(e, syn, start, stop, num=7):
    """ 
    Return a random element from geospace(start, stop, num), 
    based on syn.RandomState.
    """

    elts = geospace(start, stop, num)
    return syn.RandomState.choice(elts)


def generate_segments(e, syn, low, high):
    """ 
    Return list of random segments (r, s) where low <= r < s <= high. 

    Number of segments returned is (high-low).

    Since r<s, does not return segments of the form (k, k).

    Intent is that cids are integers in range low <= cid <= high,
    and each segment yields a contest group covering cids r..s (inclusive).

    The segments "nest" -- given any two segments, either they
    are disjoint, or they are equal, or one contains the other.
    """

    assert low <= high
    L = []
    if low!=high:
        L.append((low, high))
        mid = syn.RandomState.choice(range(low, high))
        L.extend(generate_segments(e, syn, low, mid))
        L.extend(generate_segments(e, syn, mid+1, high))
    return L



if __name__=="__main__":

    e = OpenAuditTool.Election()
    args = cli_syn.parse_args()
    cli_syn.dispatch(e, args)


# syn.py # <NAME> (with <NAME>) # August 3, 2017 # python3 Routines to generate a synthetic test election dataset for OpenAuditTool.py.  Calls data generation routines in syn1.py for elections "of type 1", and calls routines in syn2.py for elections "of type 2". An object we can hang synthesis generation parameters off of. ############################################################################## ## random choices Return a list of up to num distinct integer values,     from start, start+1, ..., stop, inclusive, geometrically spread out.      A bit like numpy.linspace, but geometrically spread     out rather than linearly spread out, and only integers returned.     >>> geospace(1, 64)     [1, 2, 4, 8, 16, 32, 64]     >>> geospace(0,1)     [0, 1]     >>> geospace(0,10)     [0, 1, 2, 3, 5, 7, 10]     >>> geospace(20, 10000)     [20, 56, 159, 447, 1260, 3550, 10000]      Twelve-tone equal temperament scale     >>> geospace(1000, 2000, num=13)     [1000, 1059, 1122, 1189, 1260, 1335, 1414, 1498, 1587, 1682, 1782, 1888, 2000]      This should presumably be replaced by numpy.logspace !     (although duplicates need to be removed...) Return a random element from geospace(start, stop, num),      based on syn.RandomState. Return list of random segments (r, s) where low <= r < s <= high.       Number of segments returned is (high-low).      Since r<s, does not return segments of the form (k, k).      Intent is that cids are integers in range low <= cid <= high,     and each segment yields a contest group covering cids r..s (inclusive).      The segments "nest" -- given any two segments, either they     are disjoint, or they are equal, or one contains the other.