Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Roulette Resimulation (Experiment July 2026)

Abstract

This is work in progress. We are debugging the report format.

Keywords:cosmologymachine learningsimulationroulette formalismgravitational lensing

This document will investigate inaccuracies in the standard evaluation document, Evaluation of machine learning for SIE. We hypothesised that image centring causes minor numerical inaccuracy, which differs between the original raytrace image and the roulette resimulation.

We build on CosmoSim Demo III Roulette Resimulation. The following modules are needed.

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import tomllib as tl

from CosmoSim.datagen import SimImage
import CosmoSim.Image as csimg
from CosmoSim import Parameters
from CosmoSim.roulettegen import Resim

import CosmoSim as cs
print( "CosmoSim version", cs.__version__ )
CosmoSim version 3.3.0

We will also need the following files, which will be loaded by the code:

Review of the SIE experiment data

We load and compare the testing results and the ground truth.

gt = pd.read_csv( "../sie-testing.csv", index_col="filename" )
pr = pd.read_csv( "pred-sie-testing.csv", index_col="filename" )
rawerrors = gt - pr
sse = (rawerrors**2).sum(axis=1)

From the dataset, we pick the three best and the three worst data points.

best = list(sse.nlargest(3).index)
worst = list(sse.nsmallest(3).index)
print( "Best:", best )
print( "Worst:", worst )
Best: ['image-012116.png', 'image-011292.png', 'image-012560.png']
Worst: ['image-010843.png', 'image-013675.png', 'image-011485.png']

Simulation from lens parameters

To simulate the images, we need to load the parameters from sie-dataset.csv. From this dataset we pick just the selected rows.

df = pd.read_csv( "../sie-dataset.csv", index_col="filename" )
df = df.loc[best+worst]
display( df.T )
Loading...

We will also need the simulator configuration from sie-dataset.toml.

with open( "../sie-dataset.toml", 'rb' ) as f:
            toml = tl.load(f)
param = Parameters( toml )

Now we can run the simulator.

param.setRow( df.iloc[0] )
param["simulator"]["centred"] = False
raysim0 = SimImage( param.copy(), verbose=0 )
ray0 = raysim0.getImage()
param["simulator"]["model"] = "Roulette" 
rousim0 = SimImage( param, verbose=0 )
rou0 = rousim0.getImage()
csimg.imageCompare( ray0, rou0, "Raytrace", "Roulette" ) 
<Figure size 1500x500 with 3 Axes>

Here we cannot see the discrepancy that we found in Evaluation of machine learning for SIE.

We can also get annotations on the images, like this:

param.setRow( df.iloc[0] )
param["simulator"]["centred"] = False
ray0a = raysim0.getAnnotated(convergenceRing=None,centrePoint=None)
param["simulator"]["model"] = "Roulette" 
rou0a = rousim0.getAnnotated(convergenceRing=None,centrePoint=None)
csimg.imageCompare( ray0a, rou0a, "Raytrace", "Roulette" ) 
<Figure size 1500x500 with 3 Axes>

The roulette simulation seems perfect within the convergence ring.

The full image set

We can repeat the simulation on all the selected imags.

for index, row in df.iterrows():
    param.setRow( row )
    param["simulator"]["centred"] = False
    ray = SimImage( param, verbose=0
        ).getAnnotated(centrePoint=None)
    param["simulator"]["model"] = "Roulette" 
    rou = SimImage( param, verbose=0
        ).getAnnotated(centrePoint=None)
    csimg.imageCompare( ray, rou, index, "Roulette", axiscross=True ) 
    plt.savefig( f"annot1-{index}" )
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>

These images show good match between raytrace and roulette, although it may be hard to judge when the visible image is small and close to the origin. Where the primary image is far from the origin, the match looks perfect. It may be somewhat easier to see without the annotations.

for index, row in df.iterrows():
    param.setRow( row )
    param["simulator"]["centred"] = False
    ray = SimImage( param, verbose=0
        ).getImage()
    param["simulator"]["model"] = "Roulette" 
    rou = SimImage( param, verbose=0
        ).getImage()
    csimg.imageCompare( ray, rou, index, "Roulette", axiscross=True ) 
    plt.savefig( f"resim1-{index}" )
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>

Resimulation

It will be interesting to check if resimulation confirms the result. First a simple check with a single data point.

row = raysim0.getData()
display( row )
filename image-012116.png source SersicSphere x 42.9933 y 10.8804 sigma 18.9116 sigma2 34.4593 theta 70.1152 luminosity 73.3291 n_sersic 3.4013 lensX 0 lensY 0 centreX 0 centreY 0 reletaX 42.9933 reletaY 10.8804 offsetX -0.000434 offsetY -78.230844 xiX 119.96727 xiY 9.62396 alpha[0][1] -76.974404 alpha[1][0] -0.189082 alpha[1][2] 0.186664 alpha[2][1] 0.002386 alpha[2][3] -0.002326 alpha[3][0] -0.000019 alpha[3][2] -0.000029 alpha[3][4] 0.000045 alpha[4][1] 0.000001 alpha[4][3] 0.000001 alpha[4][5] -0.000001 beta[0][1] 1.261843 beta[1][0] 0.0 beta[1][2] 0.030143 beta[2][1] -0.000274 beta[2][3] -0.00041 beta[3][0] 0.0 beta[3][2] 0.000006 beta[3][4] 0.00001 beta[4][1] -0.0 beta[4][3] -0.0 beta[4][5] -0.0 Name: image-012116.png, dtype: object
newcfg = { "simulator" : { "cropsize" : 256, "nterms" : 4, "centred" : False }
         , "source" : { "mode" : "SersicSphere" }
         } 
rp = Parameters( newcfg )
resimImage = Resim(  row, rp, verbose=0 ).getImage()
csimg.imageCompare( resimImage, rou0, "Resimulation", "Original Roulette" )
csimg.imageCompare( resimImage, ray0, "Resimulation", "Original Raytrace" )
[getSource] src=SersicSphere, ltprf0=None, verbose=1
[SphericalSource] constructor done
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>

This looks perfect as far as the roulette formalism goes, as do the other selected images.

for index, row in df.iterrows():
    param.setRow( row )
    param["simulator"]["centred"] = False
    imsim = SimImage( param, verbose=0 )
    ray = imsim.getImage()
    rr = imsim.getData()
    rou = Resim( rr, rp, verbose=0 ).getImage()
    csimg.imageCompare( ray, rou, "Raytrace", "Roulette", axiscross=True ) 
    plt.savefig( f"resim2-{index}" )
[getSource] src=SersicSphere, ltprf0=None, verbose=1
[SphericalSource] constructor done
[getSource] src=SersicSphere, ltprf0=None, verbose=1
[SphericalSource] constructor done
[getSource] src=SersicSphere, ltprf0=None, verbose=1
[SphericalSource] constructor done
[getSource] src=SersicSphere, ltprf0=None, verbose=1
[SphericalSource] constructor done
[getSource] src=SersicSphere, ltprf0=None, verbose=1
[SphericalSource] constructor done
[getSource] src=SersicSphere, ltprf0=None, verbose=1
[SphericalSource] constructor done
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>

Note that we have simulated without centring.

print( "Centring:", param.get( "centred" ) )
Centring: False

Centred mode

Let us reset the simulator to use centred mode. Here we will see the numeric inaccuracy.

with open( "../sie-dataset.toml", 'rb' ) as f:
            toml = tl.load(f)
param = Parameters( toml )
for index, row in df.iterrows():
    param.setRow( row )
    imsim = SimImage( param, verbose=0 )
    ray = imsim.getImage()
    rr = imsim.getData()
    rou = Resim( rr, rp, verbose=0 ).getImage()
    csimg.imageCompare( ray, rou, index, "Roulette", axiscross=True ) 
    plt.savefig( f"centre-{index}" )
[getSource] src=SersicSphere, ltprf0=None, verbose=1
[SphericalSource] constructor done
[getSource] src=SersicSphere, ltprf0=None, verbose=1
[SphericalSource] constructor done
[getSource] src=SersicSphere, ltprf0=None, verbose=1
[SphericalSource] constructor done
[getSource] src=SersicSphere, ltprf0=None, verbose=1
[SphericalSource] constructor done
[getSource] src=SersicSphere, ltprf0=None, verbose=1
[SphericalSource] constructor done
[getSource] src=SersicSphere, ltprf0=None, verbose=1
[SphericalSource] constructor done
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>
<Figure size 1500x500 with 3 Axes>

This is also perfect.

Validation of the generated amplitudes

During the debugging phase, we also check the amplitudes generated here against the file generated by the batch run. Although this did not give any answers at the time, we give the code in case it may be useful in the future.

gt = pd.read_csv( "../sie-roulette.csv", index_col="filename" )
gt = gt.loc[best+worst]
display( gt )
Loading...

We did not store the amplitudes from getData when we did the simulation, so we need to create a DataFrame of these amplitudes now.

l = []
for index, row in df.iterrows():
    param.setRow( row )
    imsim = SimImage( param, verbose=0 )
    l.append( imsim.getData() )
gen = pd.DataFrame( l )
display( gen )
Loading...

The interesting step is now to compare gen to the ground truth gt.

diff = gen.drop("source",axis=1)-gt
display( diff )
Loading...

This looks like a good match, but to make sure we do not miss anything, we can look at the maximum absolute differences.

display( diff.abs().max() )
alpha[0][1] 4.440892e-16 alpha[1][0] 8.326673e-17 alpha[1][2] 6.938894e-17 alpha[2][1] 8.673617e-17 alpha[2][3] 8.066464e-17 alpha[3][0] 9.714451e-17 alpha[3][2] 9.714451e-17 alpha[3][4] 1.908196e-17 alpha[4][1] 6.938894e-17 alpha[4][3] 3.989864e-17 alpha[4][5] 9.801188e-17 beta[0][1] 2.220446e-16 beta[1][0] 0.000000e+00 beta[1][2] 8.326673e-17 beta[2][1] 6.938894e-17 beta[2][3] 9.540979e-17 beta[3][0] 0.000000e+00 beta[3][2] 9.714451e-17 beta[3][4] 7.632783e-17 beta[4][1] 6.863000e-17 beta[4][3] 9.324139e-17 beta[4][5] 8.586881e-17 centreX 5.551115e-17 centreY 0.000000e+00 filename NaN lensX 5.551115e-17 lensY 0.000000e+00 luminosity 0.000000e+00 n_sersic 0.000000e+00 offsetX 9.454243e-17 offsetY 5.551115e-17 reletaX 0.000000e+00 reletaY 8.326673e-17 sigma 0.000000e+00 sigma2 0.000000e+00 source NaN theta 0.000000e+00 x 0.000000e+00 xiX 0.000000e+00 xiY 3.552714e-15 y 0.000000e+00 dtype: float64

Right, so differences are around 10-14 and smaller.

Closure

This comparison shows good match between raytrace and roulette, except possibly for small images close to the origin. It also demonstrates that the new mode of operation in v3.3 is a necessary improvement.

Using roulette resimulation to validate machine learning predictions, we have to expect a numeric inaccuracy, due to the centring of the images subjected to the machine learning model. This is probably not a problem in practice, since observational images will show all sorts of inaccuracies in any event.