| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739 | <!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" /><meta name="generator" content="pdoc 0.5.2" /><title>run_simulated_annealing API documentation</title><meta name="description" content="Simulated annealing algorithm used to match the simulation of theAntennae to the observations by comparing binarized images." /><link href='https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css' rel='stylesheet'><link href='https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/8.0.0/sanitize.min.css' rel='stylesheet'><link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel="stylesheet"><style>.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{font-weight:bold}#index h4 + ul{margin-bottom:.6em}#index .two-column{column-count:2}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.name small{font-weight:normal}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase;cursor:pointer}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}.admonition{padding:.1em .5em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style><style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style><style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style></head><body><main><article id="content"><header><h1 class="title"><code>run_simulated_annealing</code> module</h1></header><section id="section-intro"><p>Simulated annealing algorithm used to match the simulation of theAntennae to the observations by comparing binarized images.</p><details class="source"><summary>Source code</summary><pre><code class="python">"""Simulated annealing algorithm used to match the simulation of theAntennae to the observations by comparing binarized images."""import numpy as npimport scipyimport picklefrom scipy import ndimagefrom fast_histogram import histogram2dfrom scipy.signal import convolve2dfrom numba import jitfrom matplotlib.image import imreadimport datetimefrom simulation import Simulation, GalaxyT = .25 # Initial tempreatureSTEPS = 0DECAY = .998 # Exponential decay factordef simToBitmap(sim, theta, phi, view, scale, x, y, galaxy):    """Obtain a bitmap of one galaxy as viewed from a given direction.       The binning has been chosen so that the scale and the offset (x,y)       are expected to be approximately 1 and (0, 0) respectively.        Parameters:            sim (Simulation): Simulation to project.            theta (float): polar angle of viewing direction.            phi (float): azimuthal angle of viewing direction.            view (float): rotation angle along viewing direction.            scale (float): scaling factor.            x (float): x offset            y (float): y offset            galaxy (int): galaxy to plot. Either 0, or 1. They are assumed                    to have the same number of particles.    """    # Obtain components in new x',y' plane    r_vec = (sim.project(theta, phi, view) - [[x+.12,y+1.3]]) * scale    # Select a single galaxy. We match them separately in the algorithm.    if galaxy==0: r_vec = r_vec[2:len(r_vec)//2-1] #omit central masses    if galaxy==1: r_vec = r_vec[len(r_vec)//2-1:]    # Use a fast histogram, much faster than numpy ()    H = histogram2d(r_vec[:,0], r_vec[:,1],         range=[[-5,5], [-5,5]], bins=(50, 50))    im = np.zeros((50, 50))    H = convolve2d(H, np.ones((2,2)), mode='same') # Smooth the image    im[H>=1] = 1 # Binary the image        return im@jit(nopython=True) # Numba annotationdef bitmapScoreAlgo(im1, im2):    """Computes the f1 score betwen two binarized images    Parameters        im1 (arr): nxm ground truth image        im2 (arr): nxm candidate image        Returns        f1 score    """    TP = np.sum((im1==1.0) & (im2==1.0))    TN = np.sum((im1==0.0) & (im2==0.0))    FP = np.sum((im1==0.0) & (im2==1.0))    FN = np.sum((im1==1.0) & (im2==0.0))    if TP==0: return 0    precision = TP/(TP+FP)     recall = TP/(TP+FN)    return 2*precision*recall / (precision + recall)# The matching algorithm attempts to improve the match by shifting the# image by one pixel in each direction. If none improve the score the# f1-score of said local maximum is retuned. To make this highly efficient# (as this is run millions of time) we explicitely write functions to # shift an image by 1 pixel in each direction, as these can then be compiled# using Numba (jit annotation) to low-level code.# Performance is crucial here and must sadly be prioritized over conciseness@jit(nopython=True)def shiftBottom(im, im2):    """Shifts an image by one pixel downwards.        Parameters:        im (arr): the nxm image to shift by one pixel.        im2 (arr): an nxm array where the new image will be placed.    Returns:        A reference to im2    """    im2[1:] = im[:-1]    im2[0] = 0    return im2@jit(nopython=True)def shiftTop(im, im2):    """Shifts an image by one pixel upwards."""    im2[:-1] = im[1:]    im2[-1] = 0    return im2@jit(nopython=True)def shiftLeft(im, im2):    """Shifts an image by one pixel to the left."""    im2[:,1:] = im[:,:-1]    im2[:,0] = 0    return im2@jit(nopython=True)def shiftRight(im, im2):    """Shifts an image by one pixel to the right."""    im2[:,:-1] = im[:,1:]    im2[:,-1] = 0    return im2@jitdef bitmapScore(im1, im2, _prev=None, _bestscore=None, _zeros=None):    """Computes the bitmap score between two images. This is the f1-score       but we allow the algorithm to attempt to improve the score by       shifting the images. The algorithm is implemented recursively.    Parameters:        im1 (array): The ground truth nxm image.        im2 (array): The candidate nxm imgae.        _prev: Used internally for recursion.        _bestscore: Used internally for recursion.        _zeros: Used internally for recursion.        Returns:        f1-score for the two images.    """    # When the function is called externally, initialize an array of zeros    # and compute the score for no shifting. The zeros array is used for    # performance to only create a new array once.    if _bestscore is None:        _bestscore = bitmapScoreAlgo(im1, im2)        _zeros = np.zeros_like(im2)    # Attempt to improve the score by shifting the image in a direction    # Keeping track of _prev allows to not 'go back' and undo and attempt    # to undo a shift needlessly.     if _prev is not 0: # try left        shifted = shiftLeft(im2, _zeros)        score = bitmapScoreAlgo(im1, shifted)        if score > _bestscore: return bitmapScore(im1, shifted,             _prev=1, _bestscore=score, _zeros=_zeros)    if _prev is not 1: # try right        shifted = shiftRight(im2, _zeros)        score = bitmapScoreAlgo(im1, shifted)        if score > _bestscore: return bitmapScore(im1, shifted,             _prev=0, _bestscore=score, _zeros=_zeros)    if _prev is not 2: # try top        shifted = shiftTop(im2, _zeros)         score = bitmapScoreAlgo(im1, shifted)        if score > _bestscore: return bitmapScore(im1, shifted,             _prev=3, _bestscore=score, _zeros=_zeros)    if _prev is not 3: # try bottom        shifted = shiftBottom(im2, _zeros)        score = bitmapScoreAlgo(im1, shifted)        if score > _bestscore: return bitmapScore(im1, shifted,             _prev=2, _bestscore=score, _zeros=_zeros)    # Return _bestscore if shifting did not improve (local maximum).    return _bestscoredef attemptSimulation(theta1, phi1, theta2, phi2, rmin=1, e=.5, R=2,         disk1=.75, disk2=.65, mu=1, plot=False, steps=2000):    """Runs a simulation with the given parameters and compares it     to observations of the antennae to return a score out of 2.    Parameters:        theta1 (float): polar angle for the spin of the first galaxy.        phi1 (float): azimuthal angle for the spin of the first galaxy.        theta2 (float): polar angle for the spin of the second galaxy.        phi2 (float): azimuthal angle for the spin of the second galaxy.        rmin (float): closest distance of approach of orbit.        e (float): eccentricity of the orbit.        R (float): initial separation        disk1 (float): radius of the uniform disk of the first galaxy        disk2 (float): radius of the uniform disk of the first galaxy        mu (float): ratio of masses of the two galaxies        plot (float): if true the simulation will be saved to data/annealing/        steps (float): number of times the f1 score will be computed, along            random viewing directions per 100 timesteps.    Returns:        f1-score: score obtained by the simulation.     """    # Initialize the simulation    sim = Simulation(dt=1E-3, soft=0.1, verbose=False, CONFIG=None, method='bruteForceNumbaOptimized')    galaxy1 = Galaxy(orientation = (theta1, phi1), centralMass=2/(1+mu),        sim=sim, bulge={'model':'plummer', 'particles':0, 'totalMass':0, 'l':0},        disk={'model':'uniform', 'particles':2000, 'l':disk1, 'totalMass':0},        halo={'model':'NFW', 'particles':0, 'rs':1, 'totalMass':0})    galaxy2 = Galaxy(orientation = (theta2, phi2), centralMass=2*mu/(1+mu),        sim=sim, bulge={'model':'plummer', 'particles':0, 'totalMass':0, 'l':0},        disk={'model':'uniform', 'particles':2000, 'l':disk2, 'totalMass':0},        halo={'model':'NFW', 'particles':0, 'rs':1, 'totalMass':0})    sim.setOrbit(galaxy1, galaxy2, e=e, R0=R, rmin=rmin) # define the orbit    # Run the simulation manually    # Initialize the parameters consistently with the velocities    sim.rprev_vec = sim.r_vec - sim.v_vec * sim.dt    # Keep track of the best score    bestScore = 0    # and its corresponding binarized image and parameters    bestImage, bestParams = 0, 0    hasReachedPericenter = False    # Run until tmax = 25    for i in range(25001):        sim.step()        if i%100==0: # Every $\Delta t = 0.1$            # Check if we are close to pericenter            centers = sim.r_vec[sim.type[:,0] == 'center']            if np.linalg.norm(centers[0] - centers[1]) < 1.3*rmin:                 hasReachedPericenter = True            # Do not evaluate the f1-score until pericenter.            if not hasReachedPericenter: continue            # Check multiple (steps) viewing directions at random            localBestScore = 0            localBestImage, localBestParams = 0, 0            for j in range(steps):                # The viewing directions are isotropically distributed                theta = np.arccos(np.random.uniform(-1, 1))                phi = np.random.uniform(0, 2*np.pi)                # Rotation along line of sight                view = np.random.uniform(0, 2*np.pi)                scale = np.random.uniform(0.6, 1.0)                x = np.random.uniform(-1.0, 1.0) # Offsets                y = np.random.uniform(-1.0, 1.0)                # Get images for each galaxy and compute their score separately                im1 = simToBitmap(sim, theta, phi, view, scale, x, y, galaxy=0)                im2 = simToBitmap(sim, theta, phi, view, scale, x, y, galaxy=1)                score = bitmapScore(GT1, im1) + bitmapScore(GT2, im2)                if score > localBestScore:                     localBestScore = score                    localBestImage = [im1,im2]                    localBestParams = [i, theta, phi, view, scale, x, y]            if bestScore < localBestScore:                 bestScore = localBestScore                bestImage = localBestImage                bestParams = localBestParams            if plot:                sim.save('annealing', type='2D')        print('Best score for this attempt', bestScore)    print('using viewing parameters', bestParams)        return bestScore################################################################################################################################################################# Generate a (50, 50) bitmap for each galaxy# They are stored globally in GT1 and GT2 (Ground Truth)im = imread('literature/figs/g1c.tif')im = np.mean(im, axis=-1)im = scipy.misc.imresize(im, (50,50))GT1 = np.zeros((50, 50))GT1[im > 50] = 1im = imread('literature/figs/g2c.tif')im = np.mean(im, axis=-1)im = scipy.misc.imresize(im, (50,50))GT2 = np.zeros((50, 50))# Define the limits and relate scale of the variations in each parameter# In the same order as attemptSimulation# phi1, theta1, phi2, theta2, rmin (fixed), e, R (fixed), disk1, disk2LIMITS = np.array([[np.pi, 2 * np.pi], [-np.pi, np.pi],                   [0, np.pi], [-np.pi, np.pi],                   [1,1], [.5,1.0], [2.2,2.2], [.5,.8], [.5,.8]])VARIATIONS = np.array([.08, .15, .08, .15, 0, .01, 0, .01, .01])# Choose a random starting point and evaluate itbestparams = [np.random.uniform(*l) for l in LIMITS]log = []bestscore = attemptSimulation(*bestparams, steps=500)print('Starting with score', bestscore, 'with parameters', bestparams)log.append([bestscore, bestparams, True])for i in range(STEPS):    T = T * DECAY #exponential decay    # Perturb the parameters    params = bestparams + np.random.normal(scale=VARIATIONS) * 2 * T / 0.04    # Allow the angles from $-\pi$ to $\pi$ to wrap around    for j in [1,3]:        params[j] = np.mod(params[j] - LIMITS[j][0], LIMITS[j][1] - LIMITS[j][0])        params[j] += LIMITS[j][0]    # Clip parameters outside their allowed range    params = np.clip(params, LIMITS[:, 0], LIMITS[:, 1])    # Evaluate the score for this attempt, use more steps as i progresses    # so as to reduce the noise in the evaluation of the score    score = attemptSimulation(*params, steps=500 + i)    # Perform simulated annealing with a typical exponential rule    if score > bestscore or np.exp(-(bestscore-score)/T) > np.random.rand():        # Flip to this new point        print('NEW BEST ____', i, T, score, params)        bestscore = score        bestparams = params        log.append([score, params, True])    else: # Remain in the old point        log.append([score, params, False])    # Save the progress for future plotting    pickle.dump(log, open('data/logs/logb.pickle', "wb" ) )   </code></pre></details></section><section></section><section></section><section><h2 class="section-title" id="header-functions">Functions</h2><dl><dt id="run_simulated_annealing.attemptSimulation"><code class="name flex"><span>def <span class="ident">attemptSimulation</span></span>(<span>theta1, phi1, theta2, phi2, rmin=1, e=0.5, R=2, disk1=0.75, disk2=0.65, mu=1, plot=False, steps=2000)</span></code></dt><dd><section class="desc"><p>Runs a simulation with the given parameters and compares itto observations of the antennae to return a score out of 2.</p><h2 id="parameters">Parameters</h2><dl><dt><strong><code>theta1</code></strong> : <code>float</code></dt><dd>polar angle for the spin of the first galaxy.</dd><dt><strong><code>phi1</code></strong> : <code>float</code></dt><dd>azimuthal angle for the spin of the first galaxy.</dd><dt><strong><code>theta2</code></strong> : <code>float</code></dt><dd>polar angle for the spin of the second galaxy.</dd><dt><strong><code>phi2</code></strong> : <code>float</code></dt><dd>azimuthal angle for the spin of the second galaxy.</dd><dt><strong><code>rmin</code></strong> : <code>float</code></dt><dd>closest distance of approach of orbit.</dd><dt><strong><code>e</code></strong> : <code>float</code></dt><dd>eccentricity of the orbit.</dd><dt><strong><code>R</code></strong> : <code>float</code></dt><dd>initial separation</dd><dt><strong><code>disk1</code></strong> : <code>float</code></dt><dd>radius of the uniform disk of the first galaxy</dd><dt><strong><code>disk2</code></strong> : <code>float</code></dt><dd>radius of the uniform disk of the first galaxy</dd><dt><strong><code>mu</code></strong> : <code>float</code></dt><dd>ratio of masses of the two galaxies</dd><dt><strong><code>plot</code></strong> : <code>float</code></dt><dd>if true the simulation will be saved to data/annealing/</dd><dt><strong><code>steps</code></strong> : <code>float</code></dt><dd>number of times the f1 score will be computed, alongrandom viewing directions per 100 timesteps.</dd></dl><h2 id="returns">Returns</h2><p>f1-score: score obtained by the simulation.</p></section><details class="source"><summary>Source code</summary><pre><code class="python">def attemptSimulation(theta1, phi1, theta2, phi2, rmin=1, e=.5, R=2,         disk1=.75, disk2=.65, mu=1, plot=False, steps=2000):    """Runs a simulation with the given parameters and compares it     to observations of the antennae to return a score out of 2.    Parameters:        theta1 (float): polar angle for the spin of the first galaxy.        phi1 (float): azimuthal angle for the spin of the first galaxy.        theta2 (float): polar angle for the spin of the second galaxy.        phi2 (float): azimuthal angle for the spin of the second galaxy.        rmin (float): closest distance of approach of orbit.        e (float): eccentricity of the orbit.        R (float): initial separation        disk1 (float): radius of the uniform disk of the first galaxy        disk2 (float): radius of the uniform disk of the first galaxy        mu (float): ratio of masses of the two galaxies        plot (float): if true the simulation will be saved to data/annealing/        steps (float): number of times the f1 score will be computed, along            random viewing directions per 100 timesteps.    Returns:        f1-score: score obtained by the simulation.     """    # Initialize the simulation    sim = Simulation(dt=1E-3, soft=0.1, verbose=False, CONFIG=None, method='bruteForceNumbaOptimized')    galaxy1 = Galaxy(orientation = (theta1, phi1), centralMass=2/(1+mu),        sim=sim, bulge={'model':'plummer', 'particles':0, 'totalMass':0, 'l':0},        disk={'model':'uniform', 'particles':2000, 'l':disk1, 'totalMass':0},        halo={'model':'NFW', 'particles':0, 'rs':1, 'totalMass':0})    galaxy2 = Galaxy(orientation = (theta2, phi2), centralMass=2*mu/(1+mu),        sim=sim, bulge={'model':'plummer', 'particles':0, 'totalMass':0, 'l':0},        disk={'model':'uniform', 'particles':2000, 'l':disk2, 'totalMass':0},        halo={'model':'NFW', 'particles':0, 'rs':1, 'totalMass':0})    sim.setOrbit(galaxy1, galaxy2, e=e, R0=R, rmin=rmin) # define the orbit    # Run the simulation manually    # Initialize the parameters consistently with the velocities    sim.rprev_vec = sim.r_vec - sim.v_vec * sim.dt    # Keep track of the best score    bestScore = 0    # and its corresponding binarized image and parameters    bestImage, bestParams = 0, 0    hasReachedPericenter = False    # Run until tmax = 25    for i in range(25001):        sim.step()        if i%100==0: # Every $\Delta t = 0.1$            # Check if we are close to pericenter            centers = sim.r_vec[sim.type[:,0] == 'center']            if np.linalg.norm(centers[0] - centers[1]) < 1.3*rmin:                 hasReachedPericenter = True            # Do not evaluate the f1-score until pericenter.            if not hasReachedPericenter: continue            # Check multiple (steps) viewing directions at random            localBestScore = 0            localBestImage, localBestParams = 0, 0            for j in range(steps):                # The viewing directions are isotropically distributed                theta = np.arccos(np.random.uniform(-1, 1))                phi = np.random.uniform(0, 2*np.pi)                # Rotation along line of sight                view = np.random.uniform(0, 2*np.pi)                scale = np.random.uniform(0.6, 1.0)                x = np.random.uniform(-1.0, 1.0) # Offsets                y = np.random.uniform(-1.0, 1.0)                # Get images for each galaxy and compute their score separately                im1 = simToBitmap(sim, theta, phi, view, scale, x, y, galaxy=0)                im2 = simToBitmap(sim, theta, phi, view, scale, x, y, galaxy=1)                score = bitmapScore(GT1, im1) + bitmapScore(GT2, im2)                if score > localBestScore:                     localBestScore = score                    localBestImage = [im1,im2]                    localBestParams = [i, theta, phi, view, scale, x, y]            if bestScore < localBestScore:                 bestScore = localBestScore                bestImage = localBestImage                bestParams = localBestParams            if plot:                sim.save('annealing', type='2D')        print('Best score for this attempt', bestScore)    print('using viewing parameters', bestParams)        return bestScore</code></pre></details></dd><dt id="run_simulated_annealing.bitmapScore"><code class="name flex"><span>def <span class="ident">bitmapScore</span></span>(<span>im1, im2)</span></code></dt><dd><section class="desc"><p>Computes the bitmap score between two images. This is the f1-scorebut we allow the algorithm to attempt to improve the score byshifting the images. The algorithm is implemented recursively.</p><h2 id="parameters">Parameters</h2><dl><dt><strong><code>im1</code></strong> : <code>array</code></dt><dd>The ground truth nxm image.</dd><dt><strong><code>im2</code></strong> : <code>array</code></dt><dd>The candidate nxm imgae.</dd><dt><strong><code>_prev</code></strong></dt><dd>Used internally for recursion.</dd><dt><strong><code>_bestscore</code></strong></dt><dd>Used internally for recursion.</dd><dt><strong><code>_zeros</code></strong></dt><dd>Used internally for recursion.</dd></dl><h2 id="returns">Returns</h2><p>f1-score for the two images.</p></section><details class="source"><summary>Source code</summary><pre><code class="python">@jitdef bitmapScore(im1, im2, _prev=None, _bestscore=None, _zeros=None):    """Computes the bitmap score between two images. This is the f1-score       but we allow the algorithm to attempt to improve the score by       shifting the images. The algorithm is implemented recursively.    Parameters:        im1 (array): The ground truth nxm image.        im2 (array): The candidate nxm imgae.        _prev: Used internally for recursion.        _bestscore: Used internally for recursion.        _zeros: Used internally for recursion.        Returns:        f1-score for the two images.    """    # When the function is called externally, initialize an array of zeros    # and compute the score for no shifting. The zeros array is used for    # performance to only create a new array once.    if _bestscore is None:        _bestscore = bitmapScoreAlgo(im1, im2)        _zeros = np.zeros_like(im2)    # Attempt to improve the score by shifting the image in a direction    # Keeping track of _prev allows to not 'go back' and undo and attempt    # to undo a shift needlessly.     if _prev is not 0: # try left        shifted = shiftLeft(im2, _zeros)        score = bitmapScoreAlgo(im1, shifted)        if score > _bestscore: return bitmapScore(im1, shifted,             _prev=1, _bestscore=score, _zeros=_zeros)    if _prev is not 1: # try right        shifted = shiftRight(im2, _zeros)        score = bitmapScoreAlgo(im1, shifted)        if score > _bestscore: return bitmapScore(im1, shifted,             _prev=0, _bestscore=score, _zeros=_zeros)    if _prev is not 2: # try top        shifted = shiftTop(im2, _zeros)         score = bitmapScoreAlgo(im1, shifted)        if score > _bestscore: return bitmapScore(im1, shifted,             _prev=3, _bestscore=score, _zeros=_zeros)    if _prev is not 3: # try bottom        shifted = shiftBottom(im2, _zeros)        score = bitmapScoreAlgo(im1, shifted)        if score > _bestscore: return bitmapScore(im1, shifted,             _prev=2, _bestscore=score, _zeros=_zeros)    # Return _bestscore if shifting did not improve (local maximum).    return _bestscore</code></pre></details></dd><dt id="run_simulated_annealing.bitmapScoreAlgo"><code class="name flex"><span>def <span class="ident">bitmapScoreAlgo</span></span>(<span>im1, im2)</span></code></dt><dd><section class="desc"><p>Computes the f1 score betwen two binarized images</p><dl><dt><strong><code>Parameters</code></strong></dt><dd>im1 (arr): nxm ground truth imageim2 (arr): nxm candidate image</dd><dt><strong><code>Returns</code></strong></dt><dd>f1 score</dd></dl></section><details class="source"><summary>Source code</summary><pre><code class="python">@jit(nopython=True) # Numba annotationdef bitmapScoreAlgo(im1, im2):    """Computes the f1 score betwen two binarized images    Parameters        im1 (arr): nxm ground truth image        im2 (arr): nxm candidate image        Returns        f1 score    """    TP = np.sum((im1==1.0) & (im2==1.0))    TN = np.sum((im1==0.0) & (im2==0.0))    FP = np.sum((im1==0.0) & (im2==1.0))    FN = np.sum((im1==1.0) & (im2==0.0))    if TP==0: return 0    precision = TP/(TP+FP)     recall = TP/(TP+FN)    return 2*precision*recall / (precision + recall)</code></pre></details></dd><dt id="run_simulated_annealing.shiftBottom"><code class="name flex"><span>def <span class="ident">shiftBottom</span></span>(<span>im, im2)</span></code></dt><dd><section class="desc"><p>Shifts an image by one pixel downwards.</p><h2 id="parameters">Parameters</h2><dl><dt><strong><code>im</code></strong> : <code>arr</code></dt><dd>the nxm image to shift by one pixel.</dd><dt><strong><code>im2</code></strong> : <code>arr</code></dt><dd>an nxm array where the new image will be placed.</dd></dl><h2 id="returns">Returns</h2><p>A reference to im2</p></section><details class="source"><summary>Source code</summary><pre><code class="python">@jit(nopython=True)def shiftBottom(im, im2):    """Shifts an image by one pixel downwards.        Parameters:        im (arr): the nxm image to shift by one pixel.        im2 (arr): an nxm array where the new image will be placed.    Returns:        A reference to im2    """    im2[1:] = im[:-1]    im2[0] = 0    return im2</code></pre></details></dd><dt id="run_simulated_annealing.shiftLeft"><code class="name flex"><span>def <span class="ident">shiftLeft</span></span>(<span>im, im2)</span></code></dt><dd><section class="desc"><p>Shifts an image by one pixel to the left.</p></section><details class="source"><summary>Source code</summary><pre><code class="python">@jit(nopython=True)def shiftLeft(im, im2):    """Shifts an image by one pixel to the left."""    im2[:,1:] = im[:,:-1]    im2[:,0] = 0    return im2</code></pre></details></dd><dt id="run_simulated_annealing.shiftRight"><code class="name flex"><span>def <span class="ident">shiftRight</span></span>(<span>im, im2)</span></code></dt><dd><section class="desc"><p>Shifts an image by one pixel to the right.</p></section><details class="source"><summary>Source code</summary><pre><code class="python">@jit(nopython=True)def shiftRight(im, im2):    """Shifts an image by one pixel to the right."""    im2[:,:-1] = im[:,1:]    im2[:,-1] = 0    return im2</code></pre></details></dd><dt id="run_simulated_annealing.shiftTop"><code class="name flex"><span>def <span class="ident">shiftTop</span></span>(<span>im, im2)</span></code></dt><dd><section class="desc"><p>Shifts an image by one pixel upwards.</p></section><details class="source"><summary>Source code</summary><pre><code class="python">@jit(nopython=True)def shiftTop(im, im2):    """Shifts an image by one pixel upwards."""    im2[:-1] = im[1:]    im2[-1] = 0    return im2</code></pre></details></dd><dt id="run_simulated_annealing.simToBitmap"><code class="name flex"><span>def <span class="ident">simToBitmap</span></span>(<span>sim, theta, phi, view, scale, x, y, galaxy)</span></code></dt><dd><section class="desc"><p>Obtain a bitmap of one galaxy as viewed from a given direction.The binning has been chosen so that the scale and the offset (x,y)are expected to be approximately 1 and (0, 0) respectively.</p><p>Parameters:sim (Simulation): Simulation to project.theta (float): polar angle of viewing direction.phi (float): azimuthal angle of viewing direction.view (float): rotation angle along viewing direction.scale (float): scaling factor.x (float): x offsety (float): y offsetgalaxy (int): galaxy to plot. Either 0, or 1. They are assumed<br>to have the same number of particles.</p></section><details class="source"><summary>Source code</summary><pre><code class="python">def simToBitmap(sim, theta, phi, view, scale, x, y, galaxy):    """Obtain a bitmap of one galaxy as viewed from a given direction.       The binning has been chosen so that the scale and the offset (x,y)       are expected to be approximately 1 and (0, 0) respectively.        Parameters:            sim (Simulation): Simulation to project.            theta (float): polar angle of viewing direction.            phi (float): azimuthal angle of viewing direction.            view (float): rotation angle along viewing direction.            scale (float): scaling factor.            x (float): x offset            y (float): y offset            galaxy (int): galaxy to plot. Either 0, or 1. They are assumed                    to have the same number of particles.    """    # Obtain components in new x',y' plane    r_vec = (sim.project(theta, phi, view) - [[x+.12,y+1.3]]) * scale    # Select a single galaxy. We match them separately in the algorithm.    if galaxy==0: r_vec = r_vec[2:len(r_vec)//2-1] #omit central masses    if galaxy==1: r_vec = r_vec[len(r_vec)//2-1:]    # Use a fast histogram, much faster than numpy ()    H = histogram2d(r_vec[:,0], r_vec[:,1],         range=[[-5,5], [-5,5]], bins=(50, 50))    im = np.zeros((50, 50))    H = convolve2d(H, np.ones((2,2)), mode='same') # Smooth the image    im[H>=1] = 1 # Binary the image        return im</code></pre></details></dd></dl></section><section></section></article><nav id="sidebar"><h1>Index</h1><div class="toc"><ul></ul></div><ul id="index"><li><h3><a href="#header-functions">Functions</a></h3><ul class="two-column"><li><code><a title="run_simulated_annealing.attemptSimulation" href="#run_simulated_annealing.attemptSimulation">attemptSimulation</a></code></li><li><code><a title="run_simulated_annealing.bitmapScore" href="#run_simulated_annealing.bitmapScore">bitmapScore</a></code></li><li><code><a title="run_simulated_annealing.bitmapScoreAlgo" href="#run_simulated_annealing.bitmapScoreAlgo">bitmapScoreAlgo</a></code></li><li><code><a title="run_simulated_annealing.shiftBottom" href="#run_simulated_annealing.shiftBottom">shiftBottom</a></code></li><li><code><a title="run_simulated_annealing.shiftLeft" href="#run_simulated_annealing.shiftLeft">shiftLeft</a></code></li><li><code><a title="run_simulated_annealing.shiftRight" href="#run_simulated_annealing.shiftRight">shiftRight</a></code></li><li><code><a title="run_simulated_annealing.shiftTop" href="#run_simulated_annealing.shiftTop">shiftTop</a></code></li><li><code><a title="run_simulated_annealing.simToBitmap" href="#run_simulated_annealing.simToBitmap">simToBitmap</a></code></li></ul></li></ul></nav></main><footer id="footer"><p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.5.2</a>.</p></footer><script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script><script>hljs.initHighlightingOnLoad()</script></body></html>
 |