<commit_before>"""
.. module:: skrf.time
========================================
time (:mod:`skrf.time`)
========================================

Time domain functions

Methods
------------

.. autosummary::
   :toctree: generated/

   time_gate
   detect_span
   find_n_peaks
   indexes

"""

from .util import  find_nearest_index

from scipy.ndimage.filters import convolve1d
from scipy import  signal
import numpy as npy
import numpy as np # so i dont have to change indexes (from peakutils)
from numpy import fft

def indexes(y, thres=0.3, min_dist=1):
    """Peak detection routine.

    Finds the numeric index of the peaks in *y* by taking its first order difference. By using
    *thres* and *min_dist* parameters, it is possible to reduce the number of
    detected peaks. *y* must be signed.

    Parameters
    ----------
    y : ndarray (signed)
        1D amplitude data to search for peaks.
    thres : float between [0., 1.]
        Normalized threshold. Only the peaks with amplitude higher than the
        threshold will be detected.
    min_dist : int
        Minimum distance between each detected peak. The peak with the highest
        amplitude is preferred to satisfy this constraint.

    Returns
    -------
    ndarray
        Array containing the numeric indexes of the peaks that were detected

    Notes
    --------
    This function was taken from peakutils-1.1.0
    http://pythonhosted.org/PeakUtils/index.html

    """
    #This function  was taken from peakutils, and is covered
    # by the MIT license, included below:

    #The MIT License (MIT)

    #Copyright (c) 2014 Lucas Hermann Negri

    #Permission is hereby granted, free of charge, to any person obtaining a copy
    #of this software and associated documentation files (the "Software"), to deal
    #in the Software without restriction, including without limitation the rights
    #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    #copies of the Software, and to permit persons to whom the Software is
    #furnished to do so, subject to the following conditions:

    #The above copyright notice and this permission notice shall be included in
    #all copies or substantial portions of the Software.

    #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    #THE SOFTWARE.

    if isinstance(y, np.ndarray) and np.issubdtype(y.dtype, np.unsignedinteger):
        raise ValueError("y must be signed")

    thres = thres * (np.max(y) - np.min(y)) + np.min(y)
    min_dist = int(min_dist)

    # compute first order difference
    dy = np.diff(y)

    # propagate left and right values successively to fill all plateau pixels (0-value)
    zeros,=np.where(dy == 0)

    # check if the singal is totally flat
    if len(zeros) == len(y) - 1:
        return np.array([])

    while len(zeros):
        # add pixels 2 by 2 to propagate left and right value onto the zero-value pixel
        zerosr = np.hstack([dy[1:], 0.])
        zerosl = np.hstack([0., dy[:-1]])

        # replace 0 with right value if non zero
        dy[zeros]=zerosr[zeros]
        zeros,=np.where(dy == 0)

        # replace 0 with left value if non zero
        dy[zeros]=zerosl[zeros]
        zeros,=np.where(dy == 0)

    # find the peaks by using the first order difference
    peaks = np.where((np.hstack([dy, 0.]) < 0.)
                     & (np.hstack([0., dy]) > 0.)
                     & (y > thres))[0]

    # handle multiple peaks, respecting the minimum distance
    if peaks.size > 1 and min_dist > 1:
        highest = peaks[np.argsort(y[peaks])][::-1]
        rem = np.ones(y.size, dtype=bool)
        rem[peaks] = False

        for peak in highest:
            if not rem[peak]:
                sl = slice(max(0, peak - min_dist), peak + min_dist + 1)
                rem[sl] = True
                rem[peak] = False

        peaks = np.arange(y.size)[~rem]

    return peaks

def find_n_peaks(x,n, thres=.9, **kw):
    '''
    Find a given number of peaks in a signal
    '''

    for dummy  in range(10):

        idx = indexes(x, **kw)
        if len(idx) < n:
            thres*=.5

        else:
            peak_vals = sorted(x[idx], reverse=True)[:n]
            peak_idxs =[x.tolist().index(k) for k in peak_vals]

            return peak_idxs
    raise ValueError('Couldnt find %i peaks'%n)

def detect_span(ntwk):
    '''
    detect the correct time-span between two largest peaks
    '''
    x = ntwk.s_time_db.flatten()
    p1,p2 = find_n_peaks(x,n=2)
    #distance to nearest neighbor peak
    span = abs(ntwk.frequency.t_ns[p1]-ntwk.frequency.t_ns[p2])
    return span

def time_gate(ntwk, start=None, stop=None, center=None, span=None,
              mode='bandpass', window=('kaiser', 6),media=None,
              boundary='reflect',return_all=False):
    '''
    Time-gate one-port s-parameters.

    The gate can be defined with start/stop times, or by
    center/span. all times are in units of nanoseconds. common
    windows are:
     * ('kaiser', 6)
     * 6 # integers are interpreted as kaiser beta-values
     * 'hamming'
     * 'boxcar'  # a straight up rect

    If no parameters are passed this will try to auto-gate the largest
    peak.

    Parameters
    ------------
    start : number, or None
        start of time gate, (ns).
    stop : number, or None
        stop of time gate (ns).
    center : number, or None
        center of time gate, (ns). If None, and span is given,
        the gate will be centered on the peak.
    span : number, or None
        span of time gate, (ns).  If None span will be half of the
        distance to the second tallest peak
    mode: ['bandpass','bandstop']
        mode of gate
    boundary:  {'reflect', 'constant', 'nearest', 'mirror', 'wrap'},
        passed to `scipy.ndimage.filters.convolve1d`
    window : string, float, or tuple
        passed to `window` arg of `scipy.signal.get_window`

    Notes
    ------
    You cant gate things that are 'behind' strong reflections. This
    is due to the multiple reflections that occur.

    If `center!=0`, then the ntwk's time response is shifted
    to t=0, gated, then shifted back to where it was. This is
    done in frequency domain using `ntwk.delay()`. If the media being
    gated is dispersive (ie waveguide), then the gate `span` will be
    span at t=0, which is different.

    If you need to time-gate an N-port network, then you should
    gate each s-parameter independently.

    Returns
    --------
    ntwk : Network
        copy of ntwk with time-gated s-parameters

    .. warning::
        Depending on sharpness of the gate, the  band edges may be
        inaccurate, due to properties of FFT. We do not re-normalize
        anything.

    '''
    if ntwk.nports >1:
        raise ValueError('Time-gating only works on one-ports. try taking ntwk.s11 or ntwk.s21 first')

    if start is not None and stop is not None:
        start *= 1e-9
        stop *= 1e-9
        span = abs(stop-start)
        center = (stop+start)/2.

    else:
        if center is None:
            # they didnt provide center, so find the peak
            n = ntwk.s_time_mag.argmax()
            center = ntwk.frequency.t_ns[n]

        if span is None:
            span = detect_span(ntwk)

        center *= 1e-9
        span *= 1e-9
        start = center - span / 2.
        stop = center + span / 2.

    # find start/stop gate indices
    t = ntwk.frequency.t
    start_idx = find_nearest_index(t, start)
    stop_idx = find_nearest_index(t, stop)

    # create window
    window_width = abs(stop_idx - start_idx)
    window = signal.get_window(window, window_width)

    # create the gate by padding the window with zeros
    gate = npy.r_[npy.zeros(start_idx),
                           window,
                           npy.zeros(len(t) - stop_idx)]

    #FFT the gate, so we have it's frequency response, aka kernel
    kernel=fft.ifftshift(fft.fft(fft.fftshift(gate, axes=0), axis=0))
    kernel =abs(kernel).flatten() # take mag and flatten
    kernel=kernel/sum(kernel) # normalize kernel

    out = ntwk.copy()

    # conditionally delay ntwk, to center at t=0, this is
    # equivalent to gating at center.  (this is probably very inefficient)
    if center!=0:
        out = out.delay(-center*1e9, 'ns',port=0,media=media)

    # waste of code to handle convolve1d suck
    re = out.s_re[:,0,0]
    im = out.s_im[:,0,0]
    s = convolve1d(re,kernel, mode=boundary)+\
     1j*convolve1d(im,kernel, mode=boundary)
    out.s[:,0,0] = s
    # conditionally  un-delay ntwk
    if center!=0:
        out = out.delay(center*1e9, 'ns',port=0,media=media)

    if mode == 'bandstop':
        out = ntwk-out
    elif mode=='bandpass':
        pass
    else:
        raise ValueError('mode should be \'bandpass\' or \'bandstop\'')

    if return_all:
        # compute the gate ntwk and add delay
        gate_ntwk = out.s11.copy()
        gate_ntwk.s = kernel
        gate_ntwk= gate_ntwk.delay(center*1e9, 'ns', media=media)

        return {'gated_ntwk':out,
                'gate':gate_ntwk}
    else:
        return out

<commit_msg>Fix Sphinx documentation for time.py<commit_after>"""
.. module:: skrf.time
========================================
time (:mod:`skrf.time`)
========================================

Time domain functions

.. autosummary::
   :toctree: generated/

   time_gate
   detect_span
   find_n_peaks
   indexes

"""

from .util import  find_nearest_index

from scipy.ndimage.filters import convolve1d
from scipy import  signal
import numpy as npy
import numpy as np # so i dont have to change indexes (from peakutils)
from numpy import fft

def indexes(y, thres=0.3, min_dist=1):
    """
    Peak detection routine.

    Finds the numeric index of the peaks in *y* by taking its first order difference. By using
    *thres* and *min_dist* parameters, it is possible to reduce the number of
    detected peaks. *y* must be signed.

    Parameters
    ----------
    y : ndarray (signed)
        1D amplitude data to search for peaks.
    thres : float between [0., 1.]
        Normalized threshold. Only the peaks with amplitude higher than the
        threshold will be detected.
    min_dist : int
        Minimum distance between each detected peak. The peak with the highest
        amplitude is preferred to satisfy this constraint.

    Returns
    -------
    ndarray
        Array containing the numeric indexes of the peaks that were detected

    Notes
    --------
    This function was taken from peakutils-1.1.0
    http://pythonhosted.org/PeakUtils/index.html

    """
    #This function  was taken from peakutils, and is covered
    # by the MIT license, included below:

    #The MIT License (MIT)

    #Copyright (c) 2014 Lucas Hermann Negri

    #Permission is hereby granted, free of charge, to any person obtaining a copy
    #of this software and associated documentation files (the "Software"), to deal
    #in the Software without restriction, including without limitation the rights
    #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    #copies of the Software, and to permit persons to whom the Software is
    #furnished to do so, subject to the following conditions:

    #The above copyright notice and this permission notice shall be included in
    #all copies or substantial portions of the Software.

    #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    #THE SOFTWARE.

    if isinstance(y, np.ndarray) and np.issubdtype(y.dtype, np.unsignedinteger):
        raise ValueError("y must be signed")

    thres = thres * (np.max(y) - np.min(y)) + np.min(y)
    min_dist = int(min_dist)

    # compute first order difference
    dy = np.diff(y)

    # propagate left and right values successively to fill all plateau pixels (0-value)
    zeros,=np.where(dy == 0)

    # check if the singal is totally flat
    if len(zeros) == len(y) - 1:
        return np.array([])

    while len(zeros):
        # add pixels 2 by 2 to propagate left and right value onto the zero-value pixel
        zerosr = np.hstack([dy[1:], 0.])
        zerosl = np.hstack([0., dy[:-1]])

        # replace 0 with right value if non zero
        dy[zeros]=zerosr[zeros]
        zeros,=np.where(dy == 0)

        # replace 0 with left value if non zero
        dy[zeros]=zerosl[zeros]
        zeros,=np.where(dy == 0)

    # find the peaks by using the first order difference
    peaks = np.where((np.hstack([dy, 0.]) < 0.)
                     & (np.hstack([0., dy]) > 0.)
                     & (y > thres))[0]

    # handle multiple peaks, respecting the minimum distance
    if peaks.size > 1 and min_dist > 1:
        highest = peaks[np.argsort(y[peaks])][::-1]
        rem = np.ones(y.size, dtype=bool)
        rem[peaks] = False

        for peak in highest:
            if not rem[peak]:
                sl = slice(max(0, peak - min_dist), peak + min_dist + 1)
                rem[sl] = True
                rem[peak] = False

        peaks = np.arange(y.size)[~rem]

    return peaks

def find_n_peaks(x,n, thres=.9, **kw):
    '''
    Find a given number of peaks in a signal
    '''

    for dummy  in range(10):

        idx = indexes(x, **kw)
        if len(idx) < n:
            thres*=.5

        else:
            peak_vals = sorted(x[idx], reverse=True)[:n]
            peak_idxs =[x.tolist().index(k) for k in peak_vals]

            return peak_idxs
    raise ValueError('Couldnt find %i peaks'%n)

def detect_span(ntwk):
    '''
    detect the correct time-span between two largest peaks
    '''
    x = ntwk.s_time_db.flatten()
    p1,p2 = find_n_peaks(x,n=2)
    #distance to nearest neighbor peak
    span = abs(ntwk.frequency.t_ns[p1]-ntwk.frequency.t_ns[p2])
    return span

def time_gate(ntwk, start=None, stop=None, center=None, span=None,
              mode='bandpass', window=('kaiser', 6),media=None,
              boundary='reflect',return_all=False):
    '''
    Time-gate one-port s-parameters.

    The gate can be defined with start/stop times, or by
    center/span. all times are in units of nanoseconds. common
    windows are:
     * ('kaiser', 6)
     * 6 # integers are interpreted as kaiser beta-values
     * 'hamming'
     * 'boxcar'  # a straight up rect

    If no parameters are passed this will try to auto-gate the largest
    peak.

    Parameters
    ------------
    start : number, or None
        start of time gate, (ns).
    stop : number, or None
        stop of time gate (ns).
    center : number, or None
        center of time gate, (ns). If None, and span is given,
        the gate will be centered on the peak.
    span : number, or None
        span of time gate, (ns).  If None span will be half of the
        distance to the second tallest peak
    mode: ['bandpass','bandstop']
        mode of gate
    boundary:  {'reflect', 'constant', 'nearest', 'mirror', 'wrap'},
        passed to `scipy.ndimage.filters.convolve1d`
    window : string, float, or tuple
        passed to `window` arg of `scipy.signal.get_window`

    Notes
    ------
    You cant gate things that are 'behind' strong reflections. This
    is due to the multiple reflections that occur.

    If `center!=0`, then the ntwk's time response is shifted
    to t=0, gated, then shifted back to where it was. This is
    done in frequency domain using `ntwk.delay()`. If the media being
    gated is dispersive (ie waveguide), then the gate `span` will be
    span at t=0, which is different.

    If you need to time-gate an N-port network, then you should
    gate each s-parameter independently.

    Returns
    --------
    ntwk : Network
        copy of ntwk with time-gated s-parameters

    .. warning::
        Depending on sharpness of the gate, the  band edges may be
        inaccurate, due to properties of FFT. We do not re-normalize
        anything.

    '''
    if ntwk.nports >1:
        raise ValueError('Time-gating only works on one-ports. try taking ntwk.s11 or ntwk.s21 first')

    if start is not None and stop is not None:
        start *= 1e-9
        stop *= 1e-9
        span = abs(stop-start)
        center = (stop+start)/2.

    else:
        if center is None:
            # they didnt provide center, so find the peak
            n = ntwk.s_time_mag.argmax()
            center = ntwk.frequency.t_ns[n]

        if span is None:
            span = detect_span(ntwk)

        center *= 1e-9
        span *= 1e-9
        start = center - span / 2.
        stop = center + span / 2.

    # find start/stop gate indices
    t = ntwk.frequency.t
    start_idx = find_nearest_index(t, start)
    stop_idx = find_nearest_index(t, stop)

    # create window
    window_width = abs(stop_idx - start_idx)
    window = signal.get_window(window, window_width)

    # create the gate by padding the window with zeros
    gate = npy.r_[npy.zeros(start_idx),
                           window,
                           npy.zeros(len(t) - stop_idx)]

    #FFT the gate, so we have it's frequency response, aka kernel
    kernel=fft.ifftshift(fft.fft(fft.fftshift(gate, axes=0), axis=0))
    kernel =abs(kernel).flatten() # take mag and flatten
    kernel=kernel/sum(kernel) # normalize kernel

    out = ntwk.copy()

    # conditionally delay ntwk, to center at t=0, this is
    # equivalent to gating at center.  (this is probably very inefficient)
    if center!=0:
        out = out.delay(-center*1e9, 'ns',port=0,media=media)

    # waste of code to handle convolve1d suck
    re = out.s_re[:,0,0]
    im = out.s_im[:,0,0]
    s = convolve1d(re,kernel, mode=boundary)+\
     1j*convolve1d(im,kernel, mode=boundary)
    out.s[:,0,0] = s
    # conditionally  un-delay ntwk
    if center!=0:
        out = out.delay(center*1e9, 'ns',port=0,media=media)

    if mode == 'bandstop':
        out = ntwk-out
    elif mode=='bandpass':
        pass
    else:
        raise ValueError('mode should be \'bandpass\' or \'bandstop\'')

    if return_all:
        # compute the gate ntwk and add delay
        gate_ntwk = out.s11.copy()
        gate_ntwk.s = kernel
        gate_ntwk= gate_ntwk.delay(center*1e9, 'ns', media=media)

        return {'gated_ntwk':out,
                'gate':gate_ntwk}
    else:
        return out

<|endoftext|>


<commit_before>##===-----------------------------------------------------------------------------*- Python -*-===##
##                          _
##                         | |
##                       __| | __ ___      ___ ___
##                      / _` |/ _` \ \ /\ / / '_  |
##                     | (_| | (_| |\ V  V /| | | |
##                      \__,_|\__,_| \_/\_/ |_| |_| - Compiler Toolchain
##
##
##  This file is distributed under the MIT License (MIT).
##  See LICENSE.txt for details.
##
##===------------------------------------------------------------------------------------------===##

import os
import re
import shutil
import subprocess
import sys

from glob import glob
from distutils.version import LooseVersion
from setuptools import setup, find_packages, Extension
from setuptools.command.build_ext import build_ext
from setuptools.command.build_py import build_py
from setuptools.command.develop import develop

# Based on:
#   https://github.com/navdeep-G/setup.py
NAME = "dawn4py"
DESCRIPTION = "High-level DSL toolchain for geophysical fluid dynamics models."
URL = "https://github.com/MeteoSwiss-APN/dawn"
EMAIL = "gridtools@cscs.com"
AUTHOR = "MeteoSwiss / ETH Zurich / Vulcan"

# Note that DAWN_DIR below may be the version pip copies over before building
DAWN_DIR = os.path.dirname(__file__)
DAWN4PY_DIR = os.path.join(DAWN_DIR, "src", "dawn4py")

BUILD_JOBS = os.cpu_count()

# Select protobuf version
with open(os.path.join(DAWN_DIR, "cmake", "FetchProtobuf.cmake"), "r") as f:
    text = f.read()
    m = re.search(r".*\/protocolbuffers\/protobuf\/archive\/v(?P<version>.*)(?=\.tar)+", text)
    protobuf_version = m.group("version")

# Dependencies
REQUIRED = ["attrs>=19", "black>=19.3b0", f"protobuf>={protobuf_version}", "pytest>=4.3.0"]
EXTRAS = {"dev": ["pytest"]}

# Get the Dawn version string
with open(os.path.join(DAWN_DIR, "version.txt"), mode="r") as f:
    VERSION = f.read().strip("\n")

# Add the main Dawn version file to the dawn4py package
shutil.copyfile(os.path.join(DAWN_DIR, "version.txt"), os.path.join(DAWN4PY_DIR, "version.txt"))

# Copy additional C++ headers for the generated code

for srcdir in ["driver-includes", "interface"]:
  target_path = os.path.join(DAWN4PY_DIR, "_external_src", srcdir)
  if os.path.exists(target_path):
    shutil.rmtree(target_path)
  shutil.copytree(
      os.path.join(DAWN_DIR, "src", srcdir), target_path,
  )

# Based on:
#   https://www.benjack.io/2018/02/02/python-cpp-revisited.html
#   https://gist.github.com/hovren/5b62175731433c741d07ee6f482e2936
class CMakeExtension(Extension):
    def __init__(self, name):
        Extension.__init__(self, name, sources=[])

class CMakeBuild(build_ext):
    def run(self):
        assert all(isinstance(ext, CMakeExtension) for ext in self.extensions)

        module_dir = os.getenv("DAWN4PY_MODULE_DIR", default=os.path.join(DAWN_DIR, "src"))

        # Dest dir is here
        dest_dir = os.path.join(DAWN_DIR, "src") if self.inplace else self.build_lib

        irs_in_module = glob(
            os.path.join(module_dir, "dawn4py", "serialization") + "/**/*_pb2.py", recursive=True,
        )
        has_irs_in_module = len(irs_in_module) > 0

        exts_in_module = [
            os.path.join(module_dir, self.get_ext_filename(ext.name)) for ext in self.extensions
        ]
        has_exts_in_module = all(os.path.exists(e) for e in exts_in_module)

        # Check if the extensions and python protobuf files exist in build_dir
        if has_irs_in_module and has_exts_in_module:
            # Copy irs over to dest_dir
            for proto in irs_in_module:
                rel_path = proto.replace(module_dir + "/", "")
                self.copy_file(proto, os.path.join(dest_dir, rel_path))

            # Copy extension over to dest_dir
            for extension in exts_in_module:
                rel_path = extension.replace(module_dir + "/", "")
                self.copy_file(extension, os.path.join(dest_dir, rel_path))

        else:
            # Otherwise, build extension, copying protos over in the process

            # Activate ccache
            # Taken from: https://github.com/h5py/h5py/pull/1382
            # This allows ccache to recognise the files when pip builds in a temp
            # directory. It speeds up repeatedly running tests through tox with
            # ccache configured (CC="ccache gcc"). It should have no effect if
            # ccache is not in use.
            os.environ["CCACHE_BASEDIR"] = DAWN_DIR
            os.environ["CCACHE_NOHASHDIR"] = "1"

            cmake_executable = self.validate_cmake_install(self.extensions)
            self.compile_extension(self.build_temp, cmake=cmake_executable)

    def compile_extension(self, build_dir, cmake="cmake"):
        if os.path.exists(build_dir):
            shutil.rmtree(build_dir, ignore_errors=False)
        cmake_args = os.getenv("CMAKE_ARGS", default="").split(" ") or []

        os.makedirs(build_dir, exist_ok=True)

        # Run CMake configure
        print("-" * 10, "Running CMake prepare", "-" * 40)
        cmake_args += [
            "-DPYTHON_EXECUTABLE=" + sys.executable,
            "-DBUILD_TESTING=OFF",
            "-DDAWN_REQUIRE_PYTHON=ON",
        ]
        if not self.inplace:
            cmake_args.append("-DDAWN4PY_MODULE_DIR=" + self.build_lib)
        self.spawn([cmake, "-S", os.path.abspath(DAWN_DIR), "-B", build_dir] + cmake_args)

        # Run CMake build
        print("-" * 10, "Building extensions", "-" * 40)
        cfg = "Debug" if self.debug else "Release"
        build_args = ["--config", cfg, "-j", str(BUILD_JOBS)]
        self.spawn([cmake, "--build", build_dir, "--target", "python"] + build_args)

    @staticmethod
    def validate_cmake_install(extensions):
        """Return a cmake executable or \"cmake\" after checking version.
        Raises an exception if cmake is not found."""
        # Check if a recent version of CMake is present
        cmake_executable = os.getenv("CMAKE_EXECUTABLE", default="cmake")
        try:
            out = subprocess.check_output([cmake_executable, "--version"])
        except OSError:
            raise RuntimeError(
                "CMake must be installed to build the following extensions: "
                + ", ".join(e.name for e in extensions)
            )

        cmake_version = LooseVersion(re.search(r"version\s*([\d.]+)", out.decode()).group(1))
        if cmake_version < "3.13.0":
            raise RuntimeError("CMake >= 3.13.0 is required")

        return cmake_executable

setup(
    name=NAME,
    version=VERSION,
    author=AUTHOR,
    author_email=EMAIL,
    url=URL,
    description=DESCRIPTION,
    include_package_data=True,
    license="MIT",
    packages=find_packages("src"),
    package_dir={"": "src"},
    ext_modules=[CMakeExtension("dawn4py._dawn4py")],
    cmdclass={"build_ext": CMakeBuild},
    install_requires=REQUIRED,
    extras_require=EXTRAS,
    zip_safe=False,
)
<commit_msg>Control build threads using BUILD_JOBS env var (#974)<commit_after>##===-----------------------------------------------------------------------------*- Python -*-===##
##                          _
##                         | |
##                       __| | __ ___      ___ ___
##                      / _` |/ _` \ \ /\ / / '_  |
##                     | (_| | (_| |\ V  V /| | | |
##                      \__,_|\__,_| \_/\_/ |_| |_| - Compiler Toolchain
##
##
##  This file is distributed under the MIT License (MIT).
##  See LICENSE.txt for details.
##
##===------------------------------------------------------------------------------------------===##

import os
import re
import shutil
import subprocess
import sys

from glob import glob
from distutils.version import LooseVersion
from setuptools import setup, find_packages, Extension
from setuptools.command.build_ext import build_ext
from setuptools.command.build_py import build_py
from setuptools.command.develop import develop

# Based on:
#   https://github.com/navdeep-G/setup.py
NAME = "dawn4py"
DESCRIPTION = "High-level DSL toolchain for geophysical fluid dynamics models."
URL = "https://github.com/MeteoSwiss-APN/dawn"
EMAIL = "gridtools@cscs.com"
AUTHOR = "MeteoSwiss / ETH Zurich / Vulcan"

# Note that DAWN_DIR below may be the version pip copies over before building
DAWN_DIR = os.path.dirname(__file__)
DAWN4PY_DIR = os.path.join(DAWN_DIR, "src", "dawn4py")

BUILD_JOBS = os.getenv("BUILD_JOBS", default=str(os.cpu_count()))

# Select protobuf version
with open(os.path.join(DAWN_DIR, "cmake", "FetchProtobuf.cmake"), "r") as f:
    text = f.read()
    m = re.search(r".*\/protocolbuffers\/protobuf\/archive\/v(?P<version>.*)(?=\.tar)+", text)
    protobuf_version = m.group("version")

# Dependencies
REQUIRED = ["attrs>=19", "black>=19.3b0", f"protobuf>={protobuf_version}", "pytest>=4.3.0"]
EXTRAS = {"dev": ["pytest"]}

# Get the Dawn version string
with open(os.path.join(DAWN_DIR, "version.txt"), mode="r") as f:
    VERSION = f.read().strip("\n")

# Add the main Dawn version file to the dawn4py package
shutil.copyfile(os.path.join(DAWN_DIR, "version.txt"), os.path.join(DAWN4PY_DIR, "version.txt"))

# Copy additional C++ headers for the generated code

for srcdir in ["driver-includes", "interface"]:
  target_path = os.path.join(DAWN4PY_DIR, "_external_src", srcdir)
  if os.path.exists(target_path):
    shutil.rmtree(target_path)
  shutil.copytree(
      os.path.join(DAWN_DIR, "src", srcdir), target_path,
  )

# Based on:
#   https://www.benjack.io/2018/02/02/python-cpp-revisited.html
#   https://gist.github.com/hovren/5b62175731433c741d07ee6f482e2936
class CMakeExtension(Extension):
    def __init__(self, name):
        Extension.__init__(self, name, sources=[])

class CMakeBuild(build_ext):
    def run(self):
        assert all(isinstance(ext, CMakeExtension) for ext in self.extensions)

        module_dir = os.getenv("DAWN4PY_MODULE_DIR", default=os.path.join(DAWN_DIR, "src"))

        # Dest dir is here
        dest_dir = os.path.join(DAWN_DIR, "src") if self.inplace else self.build_lib

        irs_in_module = glob(
            os.path.join(module_dir, "dawn4py", "serialization") + "/**/*_pb2.py", recursive=True,
        )
        has_irs_in_module = len(irs_in_module) > 0

        exts_in_module = [
            os.path.join(module_dir, self.get_ext_filename(ext.name)) for ext in self.extensions
        ]
        has_exts_in_module = all(os.path.exists(e) for e in exts_in_module)

        # Check if the extensions and python protobuf files exist in build_dir
        if has_irs_in_module and has_exts_in_module:
            # Copy irs over to dest_dir
            for proto in irs_in_module:
                rel_path = proto.replace(module_dir + "/", "")
                self.copy_file(proto, os.path.join(dest_dir, rel_path))

            # Copy extension over to dest_dir
            for extension in exts_in_module:
                rel_path = extension.replace(module_dir + "/", "")
                self.copy_file(extension, os.path.join(dest_dir, rel_path))

        else:
            # Otherwise, build extension, copying protos over in the process

            # Activate ccache
            # Taken from: https://github.com/h5py/h5py/pull/1382
            # This allows ccache to recognise the files when pip builds in a temp
            # directory. It speeds up repeatedly running tests through tox with
            # ccache configured (CC="ccache gcc"). It should have no effect if
            # ccache is not in use.
            os.environ["CCACHE_BASEDIR"] = DAWN_DIR
            os.environ["CCACHE_NOHASHDIR"] = "1"

            cmake_executable = self.validate_cmake_install(self.extensions)
            self.compile_extension(self.build_temp, cmake=cmake_executable)

    def compile_extension(self, build_dir, cmake="cmake"):
        if os.path.exists(build_dir):
            shutil.rmtree(build_dir, ignore_errors=False)
        cmake_args = os.getenv("CMAKE_ARGS", default="").split(" ") or []

        os.makedirs(build_dir, exist_ok=True)

        # Run CMake configure
        print("-" * 10, "Running CMake prepare", "-" * 40)
        cmake_args += [
            "-DPYTHON_EXECUTABLE=" + sys.executable,
            "-DBUILD_TESTING=OFF",
            "-DDAWN_REQUIRE_PYTHON=ON",
        ]
        if not self.inplace:
            cmake_args.append("-DDAWN4PY_MODULE_DIR=" + self.build_lib)
        self.spawn([cmake, "-S", os.path.abspath(DAWN_DIR), "-B", build_dir] + cmake_args)

        # Run CMake build
        print("-" * 10, "Building extensions", "-" * 40)
        cfg = "Debug" if self.debug else "Release"
        build_args = ["--config", cfg, "-j", BUILD_JOBS]
        self.spawn([cmake, "--build", build_dir, "--target", "python"] + build_args)

    @staticmethod
    def validate_cmake_install(extensions):
        """Return a cmake executable or \"cmake\" after checking version.
        Raises an exception if cmake is not found."""
        # Check if a recent version of CMake is present
        cmake_executable = os.getenv("CMAKE_EXECUTABLE", default="cmake")
        try:
            out = subprocess.check_output([cmake_executable, "--version"])
        except OSError:
            raise RuntimeError(
                "CMake must be installed to build the following extensions: "
                + ", ".join(e.name for e in extensions)
            )

        cmake_version = LooseVersion(re.search(r"version\s*([\d.]+)", out.decode()).group(1))
        if cmake_version < "3.13.0":
            raise RuntimeError("CMake >= 3.13.0 is required")

        return cmake_executable

setup(
    name=NAME,
    version=VERSION,
    author=AUTHOR,
    author_email=EMAIL,
    url=URL,
    description=DESCRIPTION,
    include_package_data=True,
    license="MIT",
    packages=find_packages("src"),
    package_dir={"": "src"},
    ext_modules=[CMakeExtension("dawn4py._dawn4py")],
    cmdclass={"build_ext": CMakeBuild},
    install_requires=REQUIRED,
    extras_require=EXTRAS,
    zip_safe=False,
)
<|endoftext|>


<commit_before>import tensorflow as tf

flags = tf.app.flags
FLAGS = flags.FLAGS

flags.DEFINE_integer('runs', 10, 'total runs')
flags.DEFINE_integer('total', 100000, 'total timesteps')
flags.DEFINE_integer('train', 1000, 'training timesteps between testing')
flags.DEFINE_string('data', '.', 'dir contains outputs of DDPG, NAF and ICNN')
flags.DEFINE_float('min', 0, 'y axis min')
flags.DEFINE_float('max', 1, 'y axis max')

folders = [
    [FLAGS.data + '/DDPG/%d' % i for i in xrange(FLAGS.runs)],
    [FLAGS.data + '/NAF/%d' % i for i in xrange(FLAGS.runs)],
    [FLAGS.data + '/ICNN/%d' % i for i in xrange(FLAGS.runs)],
]
names = [
    'DDPG',
    'NAF',
    'ICNN',
]
colors = [
    'red',
    'blue',
    'green'
]

import matplotlib.pyplot as plt
import numpy as np

plt.style.use('bmh')
fig, ax = plt.subplots(1, 1, figsize=(6, 5))
plt.xlabel('Timestep')
plt.ylabel('Reward')

import os
def get_data(folders, n=100, k=100):
    X = [i * k for i in xrange(n)]
    Y = []
    for folder in folders:
        with open(os.path.join(folder, 'log.txt')) as f:
            lines = f.readlines()
        lines = map(lambda x: map(float, x.split()), lines)
        try:
            x = np.asarray(lines)[:, 0].flatten()
            y = np.asarray(lines)[:, 1].flatten()
        except:
            print folder
            continue
        z = np.ones(n) * -1e8
        z[0] = y[0]
        for u, v in zip(x[1:], y[1:]):
            idx = int((u - 1) / k) + 1
            if idx >= n:
                break
            z[idx] = v
        for i in xrange(n):
            if z[i] < -1e7:
                z[i] = z[i - 1]

        Y.append(z)

    Y = np.asarray(Y)
    Ysdom = 1.96 * np.std(Y, axis=0) / np.sqrt(Y.shape[0])
    Ymean = np.mean(Y, axis=0)

    return X, Ymean - Ysdom, Ymean + Ysdom, Ymean

if __name__ == '__main__':
    lines = []
    for name, item, color in zip(names, folders, colors):
        X, Ymin, Ymax, Ymean = get_data(item, FLAGS.total / FLAGS.train, FLAGS.train)
        line, = plt.plot(X, Ymean, label=name, color=color)
        lines.append(line)
        plt.fill_between(X, Ymin, Ymax, alpha=0.1, color=color)
    plt.ylim([FLAGS.min, FLAGS.max])
    plt.legend(handles=lines, loc=2)
    plt.savefig(FLAGS.data + '/result.pdf')
<commit_msg>Update the plotting script to be more automatic and to use linear interpolation.<commit_after>#!/usr/bin/env python3

import argparse
import os
import numpy as np

import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt
plt.style.use('bmh')

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('data', type=str, help='dir contains outputs of DDPG, NAF and ICNN')
    parser.add_argument('--xmax', type=float)
    parser.add_argument('--ymin', type=float)
    parser.add_argument('--ymax', type=float)
    args = parser.parse_args()

    names = ['DDPG', 'NAF', 'ICNN']
    colors = ['red', 'blue', 'green']

    fig, ax = plt.subplots(1, 1, figsize=(6, 5))
    plt.xlabel('Timestep')
    plt.ylabel('Reward')

    lines = []
    for name, color in zip(names, colors):
        dirpath = os.path.join(args.data, name)
        if os.path.isdir(dirpath):
            X, Ymin, Ymax, Ymean = get_data(dirpath)
            line, = plt.plot(X, Ymean, label=name, color=color)
            lines.append(line)
            plt.fill_between(X, Ymin, Ymax, alpha=0.1, color=color)
    # plt.ylim([FLAGS.min, FLAGS.max])
    # TODO
    plt.legend(handles=lines, loc=2)
    fname = os.path.join(args.data, 'result.pdf')
    plt.savefig(fname)
    print('Created {}'.format(fname))

def get_data(dirpath):
    minX, maxX = None, None
    X, Y = [], []
    for d in os.listdir(dirpath):
        logName = os.path.join(dirpath, d, 'log.txt')
        if os.path.exists(logName):
            data = np.loadtxt(logName)
            x = data[:,0].ravel()
            y = data[:,1].ravel()
        else:
            print("Log file not found for: {}".format(os.path.join(dirpath, d)))
            continue

        minX = np.min(x) if not minX else min(np.min(x), minX)
        maxX = np.max(x) if not maxX else max(np.max(x), maxX)
        X.append(x)
        Y.append(y)

    xs = np.linspace(minX, maxX, 3000)

    interpY = []
    for x,y in zip(X,Y):
        interpY.append(np.interp(xs, x, y))

    Y = np.asarray(interpY)
    Ysdom = 1.96 * np.std(Y, axis=0) / np.sqrt(Y.shape[0])
    Ymean = np.mean(Y, axis=0)

    return xs, Ymean - Ysdom, Ymean + Ysdom, Ymean

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before># Copyright 2017 Calico LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# =========================================================================
"""SeqNN trainer"""
import time

import numpy as np
import tensorflow as tf
from tensorflow.python.ops import math_ops
from tensorflow.python.framework import ops
from tensorflow.python.framework import dtypes

from basenji import layers
from basenji import metrics

class Trainer:
  def __init__(self, params, train_data, eval_data, out_dir):
    self.params = params
    self.train_data = train_data
    self.eval_data = eval_data
    self.out_dir = out_dir
    self.compiled = False

    # loss
    self.loss = self.params.get('loss','poisson')
    if loss_name.lower() == 'mse':
      self.loss_fn = tf.keras.losses.MSE()
    else:
      self.loss_fn = tf.keras.losses.Poisson()

    # optimizer
    self.make_optimizer()

    # early stopping
    self.patience = self.params.get('patience', 20)

    # compute batches/epoch
    self.train_epoch_batches = train_data.batches_per_epoch()
    self.eval_epoch_batches = eval_data.batches_per_epoch()
    self.train_epochs = self.params.get('train_epochs', 1000)

  def compile(self, model):
    num_targets = model.output_shape[-1]
    model.compile(loss=self.loss_fn,
                  optimizer=self.optimizer,
                  metrics=[metrics.PearsonR(num_targets), metrics.R2(num_targets)])
    self.compiled = True

  def fit(self, model):
    if not self.compiled:
      self.compile(model)

    callbacks = [
      tf.keras.callbacks.EarlyStopping(patience=self.patience, monitor='val_loss', verbose=1),
      tf.keras.callbacks.TensorBoard(self.out_dir),
      tf.keras.callbacks.ModelCheckpoint('%s/model_check.h5'%self.out_dir),
      tf.keras.callbacks.ModelCheckpoint('%s/model_best.h5'%self.out_dir, save_best_only=True, monitor='val_loss', verbose=1)]

    model.fit(
      self.train_data.dataset,
      epochs=self.train_epochs,
      steps_per_epoch=self.train_epoch_batches,
      callbacks=callbacks,
      validation_data=self.eval_data.dataset,
      validation_steps=self.eval_epoch_batches)

  def fit_tape(self, model):
    if not self.compiled:
      self.compile(model)

    # metrics
    num_targets = model.output_shape[-1]
    train_loss = tf.keras.metrics.Poisson()
    train_r = metrics.PearsonR(num_targets)
    valid_r = metrics.PearsonR(num_targets)

    # @tf.function
    def train_step(x, y):
      with tf.GradientTape() as tape:
        pred = model(x, training=True)
        loss = self.loss_fn(y, pred)
      train_loss(y, pred)
      train_r(y, pred)
      gradients = tape.gradient(loss, model.trainable_variables)
      self.optimizer.apply_gradients(zip(gradients, model.trainable_variables))

    # improvement variables
    valid_best = np.inf
    valid_ei = 0
    unimproved = 0

    # training loop
    for ei in range(self.train_epochs):
      if unimproved > self.patience:
        break
      else:
        # train
        t0 = time.time()
        si = 0
        for x, y in self.train_data.dataset:
          train_step(x, y)
          si += 1
          if si >= self.train_epoch_batches:
            break

        # print training accuracy
        train_loss_epoch = train_loss.result().numpy()
        train_r_epoch = train_r.result().numpy()
        print('Epoch %d - %ds - train_loss: %.4f - train_r: %.4f' % (ei, (time.time()-t0), train_loss_epoch, train_r_epoch), end='')

        # checkpoint
        model.save('%s/model_check.h5'%self.out_dir)

        # print validation accuracy
        valid_loss, valid_pr, valid_r2 = model.evaluate(self.eval_data.dataset, verbose=0)
        print(' - valid_loss: %.4f - valid_r: %.4f - valid_r2: %.4f' % (valid_loss, valid_pr, valid_r2), end='')

        # check best
        if valid_loss < valid_best:
          print(' - BEST!', end='')
          unimproved = 0
          valid_ei = ei
          valid_best = valid_loss
          model.save('%s/model_best.h5'%self.out_dir)
        else:
          unimproved += 1
        print('', flush=True)

        # reset metrics
        train_loss.reset_states()
        train_r.reset_states()

  def make_optimizer(self):
    # schedule (currently OFF)
    initial_learning_rate = self.params.get('learning_rate', 0.01)
    if False:
      lr_schedule = keras.optimizers.schedules.ExponentialDecay(
        initial_learning_rate,
        decay_steps=self.params.get('decay_steps', 100000),
        decay_rate=self.params.get('decay_rate', 0.96),
        staircase=True)
    else:
      lr_schedule = initial_learning_rate

    clip_norm = self.params.get('clipnorm', None)

    # optimizer
    optimizer_type = self.params.get('optimizer', 'sgd').lower()
    if optimizer_type == 'adam':
      self.optimizer = tf.keras.optimizers.Adam(
          lr=lr_schedule,
          beta_1=self.params.get('adam_beta1',0.9),
          beta_2=self.params.get('adam_beta2',0.999),
          clipnorm=clip_norm)

    elif optimizer_type in ['sgd', 'momentum']:
      self.optimizer = tf.keras.optimizers.SGD(
          lr=lr_schedule,
          momentum=self.params.get('momentum', 0.99),
          clipnorm=clip_norm)

    else:
      print('Cannot recognize optimization algorithm %s' % optimizer_type)
      exit(1)

class EarlyStoppingBest(tf.keras.callbacks.EarlyStopping):
  """Adds printing "best" in verbose mode."""
  def __init__(self, **kwargs):
    super(EarlyStoppingBest, self).__init__(**kwargs)

  def on_epoch_end(self, epoch, logs=None):
    current = self.get_monitor_value(logs)
    if current is None:
      return
    if self.monitor_op(current - self.min_delta, self.best):
      if self.verbose > 0:
        print(' - best!', end='')
      self.best = current
      self.wait = 0
      if self.restore_best_weights:
        self.best_weights = self.model.get_weights()
    else:
      self.wait += 1
      if self.wait >= self.patience:
        self.stopped_epoch = epoch
        self.model.stop_training = True
        if self.restore_best_weights:
          if self.verbose > 0:
            print('Restoring model weights from the end of the best epoch.')
          self.model.set_weights(self.best_weights)
<commit_msg>tf.function<commit_after># Copyright 2017 Calico LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# =========================================================================
"""SeqNN trainer"""
import time

import numpy as np
import tensorflow as tf
from tensorflow.python.ops import math_ops
from tensorflow.python.framework import ops
from tensorflow.python.framework import dtypes

from basenji import layers
from basenji import metrics

class Trainer:
  def __init__(self, params, train_data, eval_data, out_dir):
    self.params = params
    self.train_data = train_data
    self.eval_data = eval_data
    self.out_dir = out_dir
    self.compiled = False

    # loss
    self.loss = self.params.get('loss','poisson')
    if self.loss.lower() == 'mse':
      self.loss_fn = tf.keras.losses.MSE
    else:
      self.loss_fn = tf.keras.losses.Poisson()

    # optimizer
    self.make_optimizer()

    # early stopping
    self.patience = self.params.get('patience', 20)

    # compute batches/epoch
    self.train_epoch_batches = train_data.batches_per_epoch()
    self.eval_epoch_batches = eval_data.batches_per_epoch()
    self.train_epochs = self.params.get('train_epochs', 1000)

  def compile(self, model):
    num_targets = model.output_shape[-1]
    model.compile(loss=self.loss_fn,
                  optimizer=self.optimizer,
                  metrics=[metrics.PearsonR(num_targets), metrics.R2(num_targets)])
    self.compiled = True

  def fit(self, model):
    if not self.compiled:
      self.compile(model)

    callbacks = [
      tf.keras.callbacks.EarlyStopping(patience=self.patience, monitor='val_loss', verbose=1),
      tf.keras.callbacks.TensorBoard(self.out_dir),
      tf.keras.callbacks.ModelCheckpoint('%s/model_check.h5'%self.out_dir),
      tf.keras.callbacks.ModelCheckpoint('%s/model_best.h5'%self.out_dir, save_best_only=True, monitor='val_loss', verbose=1)]

    model.fit(
      self.train_data.dataset,
      epochs=self.train_epochs,
      steps_per_epoch=self.train_epoch_batches,
      callbacks=callbacks,
      validation_data=self.eval_data.dataset,
      validation_steps=self.eval_epoch_batches)

  def fit_tape(self, model):
    if not self.compiled:
      self.compile(model)

    # metrics
    num_targets = model.output_shape[-1]
    train_r = metrics.PearsonR(num_targets)
    valid_r = metrics.PearsonR(num_targets)
    if self.loss.lower() == 'mse':
      train_loss = tf.keras.metrics.MSE()
    else:
      train_loss = tf.keras.metrics.Poisson()

    @tf.function
    def train_step(x, y):
      with tf.GradientTape() as tape:
        pred = model(x, training=tf.constant(True))
        loss = self.loss_fn(y, pred)
      train_loss(y, pred)
      train_r(y, pred)
      gradients = tape.gradient(loss, model.trainable_variables)
      self.optimizer.apply_gradients(zip(gradients, model.trainable_variables))

    # improvement variables
    valid_best = np.inf
    valid_ei = 0
    unimproved = 0

    # training loop
    for ei in range(self.train_epochs):
      if unimproved > self.patience:
        break
      else:
        # train
        t0 = time.time()
        si = 0
        for x, y in self.train_data.dataset:
          train_step(x, y)
          si += 1
          if si >= self.train_epoch_batches:
            break

        # print training accuracy
        train_loss_epoch = train_loss.result().numpy()
        train_r_epoch = train_r.result().numpy()
        print('Epoch %d - %ds - train_loss: %.4f - train_r: %.4f' % (ei, (time.time()-t0), train_loss_epoch, train_r_epoch), end='')

        # checkpoint
        model.save('%s/model_check.h5'%self.out_dir)

        # print validation accuracy
        valid_loss, valid_pr, valid_r2 = model.evaluate(self.eval_data.dataset, verbose=0)
        print(' - valid_loss: %.4f - valid_r: %.4f - valid_r2: %.4f' % (valid_loss, valid_pr, valid_r2), end='')

        # check best
        if valid_loss < valid_best:
          print(' - best!', end='')
          unimproved = 0
          valid_ei = ei
          valid_best = valid_loss
          model.save('%s/model_best.h5'%self.out_dir)
        else:
          unimproved += 1
        print('', flush=True)

        # reset metrics
        train_loss.reset_states()
        train_r.reset_states()

  def make_optimizer(self):
    # schedule (currently OFF)
    initial_learning_rate = self.params.get('learning_rate', 0.01)
    if False:
      lr_schedule = keras.optimizers.schedules.ExponentialDecay(
        initial_learning_rate,
        decay_steps=self.params.get('decay_steps', 100000),
        decay_rate=self.params.get('decay_rate', 0.96),
        staircase=True)
    else:
      lr_schedule = initial_learning_rate

    clip_norm = self.params.get('clipnorm', None)

    # optimizer
    optimizer_type = self.params.get('optimizer', 'sgd').lower()
    if optimizer_type == 'adam':
      self.optimizer = tf.keras.optimizers.Adam(
          lr=lr_schedule,
          beta_1=self.params.get('adam_beta1',0.9),
          beta_2=self.params.get('adam_beta2',0.999),
          clipnorm=clip_norm)

    elif optimizer_type in ['sgd', 'momentum']:
      self.optimizer = tf.keras.optimizers.SGD(
          lr=lr_schedule,
          momentum=self.params.get('momentum', 0.99),
          clipnorm=clip_norm)

    else:
      print('Cannot recognize optimization algorithm %s' % optimizer_type)
      exit(1)

class EarlyStoppingBest(tf.keras.callbacks.EarlyStopping):
  """Adds printing "best" in verbose mode."""
  def __init__(self, **kwargs):
    super(EarlyStoppingBest, self).__init__(**kwargs)

  def on_epoch_end(self, epoch, logs=None):
    current = self.get_monitor_value(logs)
    if current is None:
      return
    if self.monitor_op(current - self.min_delta, self.best):
      if self.verbose > 0:
        print(' - best!', end='')
      self.best = current
      self.wait = 0
      if self.restore_best_weights:
        self.best_weights = self.model.get_weights()
    else:
      self.wait += 1
      if self.wait >= self.patience:
        self.stopped_epoch = epoch
        self.model.stop_training = True
        if self.restore_best_weights:
          if self.verbose > 0:
            print('Restoring model weights from the end of the best epoch.')
          self.model.set_weights(self.best_weights)
<|endoftext|>


<commit_before>print "todo next"
<commit_msg>finish day29: palindrome validate.<commit_after># -*- coding:utf-8 -*-
"""
valid palindrome
"""

def is_palindrome(str_):
    l = len(str_)
    i, j = 0, l - 1
    while i < l and j >= 0:
        char_i = str_[i]
        char_j = str_[j]
        if not char_i.isalpha():
            i += 1
            continue
        if not char_j.isalpha():
            j -= 1
            continue
        if char_i.lower() != char_j.lower():
            return False
        else:
            i += 1
            j -= 1

    return True

if __name__ == '__main__':
    result = is_palindrome("A man, a plan, a canal: Panama")
    print(result)
<|endoftext|>


<commit_before>#! /usr/bin/env python2.7

from BasicFunctions import leading_zeros
from RoleSymmetricGame import Game, SampleGame, PayoffData
from GameIO import io_parser

from itertools import combinations
from numpy.random import uniform as U, normal
from numpy import array, arange

def uniform_zero_sum(S):
	roles = ["row", "column"]
	players = {r:1 for r in roles}
	strategies = {"row":["r" + leading_zeros(i,S) for i in range(S)], \
			"column":["c" + leading_zeros(i,S) for i in range(S)]}
	g = Game(roles, players, strategies)
	for prof in g.allProfiles():
		row_strat = prof["row"].keys()[0]
		row_val = U(-1,1)
		col_strat = prof["column"].keys()[0]
		p = {"row":[PayoffData(row_strat, 1, row_val)], \
				"column":[PayoffData(col_strat, 1, -row_val)]}
		g.addProfile(p)
	return g

def uniform_symmetric(N, S):
	roles = ["All"]
	players = {"All":N}
	strategies = {"All":["s" + leading_zeros(i,S) for i in range(S)]}
	g = Game(roles, players, strategies)
	for prof in g.allProfiles():
		payoffs = []
		for strat, count in prof["All"].items():
			payoffs.append(PayoffData(strat, count, U(-1,1)))
		g.addProfile({"All":payoffs})
	return g

def congestion(N, facilities, required):
	roles = ["All"]
	players = {"All":N}
	strategies = {'+'.join(["f"+str(f) for f in strat]):strat for strat in \
			combinations(range(facilities), required)}
	facility_values = [array([U(facilities), U(-required), U(-1)]) for i in \
			range(facilities)]
	g = Game(roles, players, {"All":strategies.keys()})
	for prof in g.allProfiles():
		payoffs = []
		useage = [0]*facilities
		for strat, count in prof["All"].items():
			for facility in strategies[strat]:
				useage[facility] += count
		for strat, count in prof["All"].items():
			payoffs.append(PayoffData(strat, count, congestion_payoff(useage, \
					facility_values, strategies[strat])))
		g.addProfile({"All":payoffs})
	return g

def congestion_payoff(useage, facility_values, facility_set):
	value = 0
	for f in facility_set:
		value += sum(useage[f]**arange(3) * facility_values[f])
	return value

def normal_noise(game, stdev, samples):
	sg = SampleGame(game.roles, game.players, game.strategies)
	for prof in game.knownProfiles():
		sg.addProfile({r:[PayoffData(s, prof[r][s], normal(0, stdev, samples)) \
				for s in prof[r]] for r in game.roles})
	return sg

from GameIO import to_JSON_str
from argparse import ArgumentParser
import sys

def parse_args():
	parser = io_parser(description="Generate random games.")
	parser.add_argument("type", choices=["uZS", "uSym", "CG"], help= \
			"Type of random game to generate. uZS = uniform zero sum. " +\
			"uSym = uniform symmetric. CG = congestion game.")
	parser.add_argument("count", type=int, help="Number of random games " +\
			"to create.")
	parser.add_argument("game_args", nargs="*", help="Additional arguments " +\
			"for game generator function.")
	assert "-input" not in sys.argv, "no input JSON required"
	sys.argv = sys.argv[:2] + ["-input", None] + sys.argv[2:]
	return parser.parse_args()

def main():
	args = parse_args()
	game_args = map(int, args.game_args)
	if args.type == "uZS":
		game_func = uniform_zero_sum
		assert len(game_args) == 1, "one game_arg specifies strategy count"
	elif args.type == "uSym":
		game_func = uniform_symmetric
		assert len(game_args) == 2, "game_args specify player and strategy "+\
				"counts"
		game_args = map(int, args.game_args[:2])
	elif args.type == "CG":
		game_func = congestion
		assert len(game_args) == 3, "game_args specify player, facility, and"+\
				" required facility counts"
	games = [game_func(*game_args) for i in range(args.count)]
	if len(games) == 1:
		print to_JSON_str(games[0])
	else:
		print to_JSON_str(games)

if __name__ == "__main__":
	main()
<commit_msg>RandomGames can now write [base_game, noisy_game] pairs<commit_after>#! /usr/bin/env python2.7

from BasicFunctions import leading_zeros
from RoleSymmetricGame import Game, SampleGame, PayoffData
from GameIO import io_parser

from itertools import combinations
from numpy.random import uniform as U, normal
from numpy import array, arange

def uniform_zero_sum(S):
	roles = ["row", "column"]
	players = {r:1 for r in roles}
	strategies = {"row":["r" + leading_zeros(i,S) for i in range(S)], \
			"column":["c" + leading_zeros(i,S) for i in range(S)]}
	g = Game(roles, players, strategies)
	for prof in g.allProfiles():
		row_strat = prof["row"].keys()[0]
		row_val = U(-1,1)
		col_strat = prof["column"].keys()[0]
		p = {"row":[PayoffData(row_strat, 1, row_val)], \
				"column":[PayoffData(col_strat, 1, -row_val)]}
		g.addProfile(p)
	return g

def uniform_symmetric(N, S):
	roles = ["All"]
	players = {"All":N}
	strategies = {"All":["s" + leading_zeros(i,S) for i in range(S)]}
	g = Game(roles, players, strategies)
	for prof in g.allProfiles():
		payoffs = []
		for strat, count in prof["All"].items():
			payoffs.append(PayoffData(strat, count, U(-1,1)))
		g.addProfile({"All":payoffs})
	return g

def congestion(N, facilities, required):
	roles = ["All"]
	players = {"All":N}
	strategies = {'+'.join(["f"+str(f) for f in strat]):strat for strat in \
			combinations(range(facilities), required)}
	facility_values = [array([U(facilities), U(-required), U(-1)]) for i in \
			range(facilities)]
	g = Game(roles, players, {"All":strategies.keys()})
	for prof in g.allProfiles():
		payoffs = []
		useage = [0]*facilities
		for strat, count in prof["All"].items():
			for facility in strategies[strat]:
				useage[facility] += count
		for strat, count in prof["All"].items():
			payoffs.append(PayoffData(strat, count, congestion_payoff(useage, \
					facility_values, strategies[strat])))
		g.addProfile({"All":payoffs})
	return g

def congestion_payoff(useage, facility_values, facility_set):
	value = 0
	for f in facility_set:
		value += sum(useage[f]**arange(3) * facility_values[f])
	return value

def normal_noise(game, stdev, samples):
	sg = SampleGame(game.roles, game.players, game.strategies)
	for prof in game.knownProfiles():
		sg.addProfile({r:[PayoffData(s, prof[r][s], normal(0, stdev, samples)) \
				for s in prof[r]] for r in game.roles})
	return sg

from GameIO import to_JSON_str
from argparse import ArgumentParser
import sys

def parse_args():
	parser = io_parser(description="Generate random games.")
	parser.add_argument("type", choices=["uZS", "uSym", "CG"], help= \
			"Type of random game to generate. uZS = uniform zero sum. " +\
			"uSym = uniform symmetric. CG = congestion game.")
	parser.add_argument("count", type=int, help="Number of random games " +\
			"to create.")
	parser.add_argument("-samples", type=int, default=1, help="Number of " +\
			"noisy samples to give for each profile. -stdev must also be " +\
			"specified.")
	parser.add_argument("-stdev", type=float, default=0, help="Standard " +\
			"deviation of normal noise added to each sample. -samples must " +\
			"also be specified.")
	parser.add_argument("game_args", nargs="*", help="Additional arguments " +\
			"for game generator function.")
	assert "-input" not in sys.argv, "no input JSON required"
	sys.argv = sys.argv[:3] + ["-input", None] + sys.argv[3:]
	return parser.parse_args()

def main():
	args = parse_args()
	game_args = map(int, args.game_args)
	if args.type == "uZS":
		game_func = uniform_zero_sum
		assert len(game_args) == 1, "one game_arg specifies strategy count"
	elif args.type == "uSym":
		game_func = uniform_symmetric
		assert len(game_args) == 2, "game_args specify player and strategy "+\
				"counts"
		game_args = map(int, args.game_args[:2])
	elif args.type == "CG":
		game_func = congestion
		assert len(game_args) == 3, "game_args specify player, facility, and"+\
				" required facility counts"
	games = [game_func(*game_args) for i in range(args.count)]
	if args.samples > 1 and args.stdev > 0:
		noisy = map(lambda g: normal_noise(g, args.stdev, args.samples), games)
		games = zip(games, noisy)
	if len(games) == 1:
		print to_JSON_str(games[0])
	else:
		print to_JSON_str(games)

if __name__ == "__main__":
	main()
<|endoftext|>


<commit_before>

"""
tutorial4.py
by E. Dennison
"""
from ggame import App, RectangleAsset, ImageAsset, Sprite, LineStyle, Color, Frame
import random

SCREEN_WIDTH = 1250
SCREEN_HEIGHT = 700

lvl = int(input("What is your level? "))
#randomizing the spawn point of the asteroid
nums=lvl
if nums>=5:
    numbs=nums+1
    nums=5

saved=[]

while nums >=1:
    h=1
    while h==1:
        b=random.randint(1,1000)
        c=random.randint(1,700)
        d=random.randint(1,1000)
        e=random.randint(1,700)
        if b<=1250 and d<=1250 and e<=5 and c<=5:
            h=0
            f=(b,d)
            g=(c,e)
            h=list(zip(f,g))
            saved.append((h))

        elif b<=5 and d<=5 and e<=700 and c<=700:
            h=0
            f=(b,d)
            g=(c,e)
            h=list(zip(f,g))
            saved.append((h))

        else:
            h=1
    nums=nums-1

print(saved)
saved2=[]

while numbs >=5:
    h=1
    while h==1:
        b=random.randint(1,1000)
        c=random.randint(1,700)
        d=random.randint(1,1000)
        e=random.randint(1,700)
        if b<=1250 and d<=1250 and e<=5 and c<=5:
            h=0
            f=(b,d)
            g=(c,e)
            h=list(zip(f,g))
            saved2.append((h))

        elif b<=5 and d<=5 and e<=700 and c<=700:
            h=0
            f=(b,d)
            g=(c,e)
            h=list(zip(f,g))
            saved2.append((h))

        else:
            h=1
    numbs=numbs-1

print(saved2)
<commit_msg>Updated from Brython Server: 4/29/2016 2:39:54 PM<commit_after>

"""
tutorial4.py
by E. Dennison
"""
from ggame import App, RectangleAsset, ImageAsset, Sprite, LineStyle, Color, Frame
import random

SCREEN_WIDTH = 1250
SCREEN_HEIGHT = 700

lvl = int(input("What is your level? "))
#randomizing the spawn point of the asteroid
nums=lvl
if nums>=5:
    numbs=nums+1
    nums=5
else:
    numbs=0

saved=[]

while nums >=1:
    h=1
    while h==1:
        b=random.randint(1,1000)
        c=random.randint(1,700)
        d=random.randint(1,1000)
        e=random.randint(1,700)
        if b<=1250 and d<=1250 and e<=5 and c<=5:
            h=0
            f=(b,d)
            g=(c,e)
            h=list(zip(f,g))
            saved.append((h))

        elif b<=5 and d<=5 and e<=700 and c<=700:
            h=0
            f=(b,d)
            g=(c,e)
            h=list(zip(f,g))
            saved.append((h))

        else:
            h=1
    nums=nums-1

print(saved)
saved2=[]

while numbs >=5:
    h=1
    while h==1:
        b=random.randint(1,1000)
        c=random.randint(1,700)
        d=random.randint(1,1000)
        e=random.randint(1,700)
        if b<=1250 and d<=1250 and e<=5 and c<=5:
            h=0
            f=(b,d)
            g=(c,e)
            h=list(zip(f,g))
            saved2.append((h))

        elif b<=5 and d<=5 and e<=700 and c<=700:
            h=0
            f=(b,d)
            g=(c,e)
            h=list(zip(f,g))
            saved2.append((h))

        else:
            h=1
    numbs=numbs-1

print(saved2)
<|endoftext|>


<commit_before>import Tests.unitTests.test_OfflineValidation as test_OfflineValidation
import Tests.unitTests.test_MiseqParser as test_MiseqParser
import Tests.unitTests.test_ApiCalls as test_ApiCalls
import Tests.unitTests.test_IridaUploaderMain as test_IridaUploaderMain

import Tests.integrationTests.test_ApiCalls_integration as apiCalls_integration

import unittest
import platform
import argparse
import sys
from os import system, path, listdir, getcwd

"""
For running all tests or commenting out particular tests suites to only run
selected tests.
"""

def load_integration_tests(suite_list):

    if platform.system() == "Linux":
        print "Loading integration tests"
        api_integ_ts = apiCalls_integration.load_test_suite()
        suite_list.append(api_integ_ts)

def load_unit_tests(suite_list):

    print "Loading unit tests"

    iu_main_ts = test_IridaUploaderMain.load_test_suite()
    suite_list.append(iu_main_ts)

    api_ts = test_ApiCalls.load_test_suite()
    suite_list.append(api_ts)

    miseq_ts = test_MiseqParser.load_test_suite()
    suite_list.append(miseq_ts)

    off_valid_ts = test_OfflineValidation.load_test_suite()
    suite_list.append(off_valid_ts)

def run_verify_PEP8():

    if platform.system() == "Linux" and "scripts" in listdir(getcwd()):
        print "Running PEP8 verification"
        res = system("./scripts/verifyPEP8.sh")

    return res

if __name__ == "__main__":

    suite_list = []
    setup_handler = None
    exit_with_failure = False

    parser = argparse.ArgumentParser()
    parser.add_argument("--integration", action="store_true",
                        help="Run integration tests (can take a long time)")
    args = parser.parse_args()

    if args.integration:
        load_integration_tests(suite_list)

        print "Starting setup"
        setup_handler = apiCalls_integration.start_setup()

    load_unit_tests(suite_list)

    full_suite = unittest.TestSuite(suite_list)
    runner = unittest.TextTestRunner()
    test_result = runner.run(full_suite)

    if setup_handler is not None:
        setup_handler.stop_irida()

    pep8_result = run_verify_PEP8()

    if len(test_result.failures)>0 or len(test_result.errors)>0:
        exit_with_failure = True

    if pep8_result == 0:
        print "No PEP8 errors"
    else:
        exit_with_failure = True

    if exit_with_failure:
        sys.exit(1)
<commit_msg>no changes. trigger gitlab-ci<commit_after>import Tests.unitTests.test_OfflineValidation as test_OfflineValidation
import Tests.unitTests.test_MiseqParser as test_MiseqParser
import Tests.unitTests.test_ApiCalls as test_ApiCalls
import Tests.unitTests.test_IridaUploaderMain as test_IridaUploaderMain

import Tests.integrationTests.test_ApiCalls_integration as apiCalls_integration

import unittest
import platform
import argparse
import sys
from os import system, path, listdir, getcwd

"""
For running all tests or commenting out particular tests suites to only run
selected tests.
"""

def load_integration_tests(suite_list):

    if platform.system() == "Linux":
        print "Loading integration tests"
        api_integ_ts = apiCalls_integration.load_test_suite()
        suite_list.append(api_integ_ts)

def load_unit_tests(suite_list):

    print "Loading unit tests"

    iu_main_ts = test_IridaUploaderMain.load_test_suite()
    suite_list.append(iu_main_ts)

    api_ts = test_ApiCalls.load_test_suite()
    suite_list.append(api_ts)

    miseq_ts = test_MiseqParser.load_test_suite()
    suite_list.append(miseq_ts)

    off_valid_ts = test_OfflineValidation.load_test_suite()
    suite_list.append(off_valid_ts)

def run_verify_PEP8():

    if platform.system() == "Linux" and "scripts" in listdir(getcwd()):
        print "Running PEP8 verification"
        res = system("./scripts/verifyPEP8.sh")

    return res

if __name__ == "__main__":

    suite_list = []
    setup_handler = None
    exit_with_failure = False

    parser = argparse.ArgumentParser()
    parser.add_argument("--integration", action="store_true",
                        help="Run integration tests (can take a long time)")
    args = parser.parse_args()

    if args.integration:
        load_integration_tests(suite_list)

        print "Starting setup"
        setup_handler = apiCalls_integration.start_setup()

    load_unit_tests(suite_list)

    full_suite = unittest.TestSuite(suite_list)
    runner = unittest.TextTestRunner()
    test_result = runner.run(full_suite)

    if setup_handler is not None:
        setup_handler.stop_irida()

    pep8_result = run_verify_PEP8()

    if len(test_result.failures)>0 or len(test_result.errors)>0:
        exit_with_failure = True

    if pep8_result == 0:
        print "No PEP8 errors"
    else:
        exit_with_failure = True

    if exit_with_failure:
        sys.exit(1)
<|endoftext|>


<commit_before>#!/usr/bin/env python
import sys
import os
import logging
import argparse
import urllib2
import time
from selenium import webdriver

__version__ = '0.1.1'

Logger = logging.getLogger('slide2pdf')

TEMP_CAPTURE_DIR = '.slide2pdf'

def find_phantomjs_path():
    candidate_path = (
        # local node-modules path
        os.path.join(os.getcwd(), 'node_modules/phantomjs/bin/phantomjs'),
        # nodebrew current version path
        os.path.join(os.environ['HOME'], '.nodebrew/current/bin/phantomjs'),
    )
    for path in candidate_path:
        if os.path.exists(path):
            return path
    return None

def count_slide_from_dom(body):
    # FIXME: Too bad know-how
    import re
    return len(re.split('<\/slide>', body)) - 1

parser = argparse.ArgumentParser()
parser.add_argument('path', help='Slide endpoint file path', type=str)
parser.add_argument('-o', '--output', help='Output slide file path', type=str, default='./slide.pdf')

def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    args = parser.parse_args(argv)
    args.path = os.path.abspath(args.path)
    args.output = os.path.abspath(args.output)

    root_dir = os.getcwd()
    cache_dir = os.path.join(root_dir, TEMP_CAPTURE_DIR)
    if not os.path.exists(cache_dir):
        os.makedirs(cache_dir)
    elif not os.path.isdir(cache_dir):
        # TODO: Modify custom exception?
        raise Exception('{} is not directory.'.format(cache_dir))

    # Capture
    phantomjs_path = find_phantomjs_path()
    Logger.debug(phantomjs_path)
    driver = webdriver.PhantomJS(phantomjs_path)
    driver.set_window_size(960, 720)

    resp_ = urllib2.urlopen('file://' + args.path)
    slides = count_slide_from_dom(resp_.read())
    Logger.debug('{} slides'.format(slides))

    slide_captures = []
    for slide_idx in range(1, slides):
        url_ = 'file://' + args.path + '#' + str(slide_idx)
        FILENAME = os.path.join(cache_dir, "screen_{}.png".format(slide_idx))
        Logger.debug(url_)
        Logger.debug(FILENAME)

        # Open Web Browser & Resize 720P
        driver.get(url_)
        driver.refresh()
        time.sleep(2)

        # Get Screen Shot
        driver.save_screenshot(FILENAME)

        slide_captures.append(FILENAME)

    # https://github.com/SeleniumHQ/selenium/issues/767
    import signal
    driver.service.process.send_signal(signal.SIGTERM)
    driver.quit()

    # Merge
    pdf_path = os.path.join(os.getcwd(), 'slide.pdf')

    from reportlab.lib.pagesizes import A4, landscape
    from reportlab.pdfgen import canvas

    slide_size = landscape(A4)
    pdf = canvas.Canvas(pdf_path, pagesize=slide_size)
    idx = 0
    for slide in slide_captures:
        pdf.drawImage(slide, 0, 0, slide_size[0], slide_size[1])
        pdf.showPage()
        idx += 1
    pdf.save()

if __name__ == '__main__':
    main()
<commit_msg>Search phantomjs from all PATH.<commit_after>#!/usr/bin/env python
import sys
import os
import logging
import argparse
import urllib2
import time
from selenium import webdriver

__version__ = '0.1.1'

Logger = logging.getLogger('slide2pdf')

TEMP_CAPTURE_DIR = '.slide2pdf'

def find_phantomjs_path():
    """Find path of PhantomJS

    :returns: Path of PhantomJS (If it is not found, return None)
    :rtype: str or None
    """
    candidate_path = [d+'/phantomjs' for d in os.getenv('PATH', '').split(':')]
    for path in candidate_path:
        if os.path.exists(path):
            return path
    return None

def count_slide_from_dom(body):
    # FIXME: Too bad know-how
    import re
    return len(re.split('<\/slide>', body)) - 1

parser = argparse.ArgumentParser()
parser.add_argument('path', help='Slide endpoint file path', type=str)
parser.add_argument('-o', '--output', help='Output slide file path', type=str, default='./slide.pdf')

def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    args = parser.parse_args(argv)
    args.path = os.path.abspath(args.path)
    args.output = os.path.abspath(args.output)

    root_dir = os.getcwd()
    cache_dir = os.path.join(root_dir, TEMP_CAPTURE_DIR)
    if not os.path.exists(cache_dir):
        os.makedirs(cache_dir)
    elif not os.path.isdir(cache_dir):
        # TODO: Modify custom exception?
        raise Exception('{} is not directory.'.format(cache_dir))

    # Capture
    phantomjs_path = find_phantomjs_path()
    Logger.debug(phantomjs_path)
    driver = webdriver.PhantomJS(phantomjs_path)
    driver.set_window_size(960, 720)

    resp_ = urllib2.urlopen('file://' + args.path)
    slides = count_slide_from_dom(resp_.read())
    Logger.debug('{} slides'.format(slides))

    slide_captures = []
    for slide_idx in range(1, slides):
        url_ = 'file://' + args.path + '#' + str(slide_idx)
        FILENAME = os.path.join(cache_dir, "screen_{}.png".format(slide_idx))
        Logger.debug(url_)
        Logger.debug(FILENAME)

        # Open Web Browser & Resize 720P
        driver.get(url_)
        driver.refresh()
        time.sleep(2)

        # Get Screen Shot
        driver.save_screenshot(FILENAME)

        slide_captures.append(FILENAME)

    # https://github.com/SeleniumHQ/selenium/issues/767
    import signal
    driver.service.process.send_signal(signal.SIGTERM)
    driver.quit()

    # Merge
    pdf_path = os.path.join(os.getcwd(), 'slide.pdf')

    from reportlab.lib.pagesizes import A4, landscape
    from reportlab.pdfgen import canvas

    slide_size = landscape(A4)
    pdf = canvas.Canvas(pdf_path, pagesize=slide_size)
    idx = 0
    for slide in slide_captures:
        pdf.drawImage(slide, 0, 0, slide_size[0], slide_size[1])
        pdf.showPage()
        idx += 1
    pdf.save()

if __name__ == '__main__':
    main()
<|endoftext|>