Expectation of global descriptors in 3D#

This example shows how to compute theoretical expectations of total volume (L3), surface area (L2), mean curvature measure (L1) and Euler characteristic (L0) of excursion sets as a function of the excursion’s threshold. Excrusions are define here as the subdomain of the domain where the Random Field is defined where the Random Field values are above a certain threshold.

Here the two measures are seen as function of the threshold value.

Monte Carlo results are confronted to the theory.

import spam.excursions
import spam.measurements
import matplotlib.pyplot as plt
import numpy

Correlated Random Field parameters#

First we set all the correlated Random Fields parameters. It is assumed that the distribution is Gaussian with zero mean and the covariance function is Gaussian too.

# set the variance
variance = 2.0
# standard deviation
std = numpy.sqrt(variance)
# correlation length
correlationLength = 0.5
# length of the domain
length = 10.0

# set the thresholds between -5 and 5
thresholds = numpy.linspace(-5, 5, 200)
thresholdsMC = numpy.linspace(-4, 4, 20)  # less for monte carlo

Compute the four theoretical expected measures#

The four measures of the excursion are computed and ploted for every thresholds

# spatial dimension
spatialDimension = 3
# the measure number 3
totalVolume = spam.excursions.expectedMesures(thresholds, 3, spatialDimension, std=std, lc=correlationLength, a=length)
# the measure number 2
totalSurface = 2.0 * spam.excursions.expectedMesures(thresholds, 2, spatialDimension, std=std, lc=correlationLength, a=length)
# the measure number 1
caliperDiameter = 0.5 * spam.excursions.expectedMesures(thresholds, 1, spatialDimension, std=std, lc=correlationLength, a=length)
# the measure number 0
eulerCharac = spam.excursions.expectedMesures(thresholds, 0, spatialDimension, std=std, lc=correlationLength, a=length)

plt.figure()
plt.xlabel("Threshold")
plt.title("Total volume")
plt.plot(thresholds, totalVolume, 'r')

plt.figure()
plt.xlabel("Threshold")
plt.title("Total surface area")
plt.plot(thresholds, totalSurface, 'r')

plt.figure()
plt.xlabel("Threshold")
plt.title("Mean caliper diametre")
plt.plot(thresholds, caliperDiameter, 'r')

plt.figure()
plt.xlabel("Threshold")
plt.title("Euler characteristic")
plt.plot(thresholds, eulerCharac, 'r')
  • Total volume
  • Total surface area
  • Mean caliper diametre
  • Euler characteristic
[<matplotlib.lines.Line2D object at 0x7f681b4e0850>]

Generate 5 realisations of the correlated Random Field#

In order to compare the theoretical values to Monte Carlo results, first 5 realisations of a correlated Random Field are generated.

# number of realisations
nRea = 5
nNodes = 100

# define the covariance
covarianceParameters = {'var': variance, 'len_scale': correlationLength}

# generate realisations
realisations = spam.excursions.simulateRandomField(
    lengths=length,
    nNodes=nNodes,
    covarianceParameters=covarianceParameters,
    dim=spatialDimension,
    nRea=nRea)
Generating Field_0000... 4.76 seconds
Generating Field_0001... 4.88 seconds
Generating Field_0002... 4.83 seconds
Generating Field_0003... 4.74 seconds
Generating Field_0004... 4.66 seconds

We now show, on the left, a slice a the first realisation and on the right the corresponding excrusion for 3 thresholds values: -2, 0 and 2 which corresponds to three different topologies.

plt.figure()
plt.title("Middle slice of the first realisation")
plt.imshow(realisations[0][int(nNodes / 2), :, :])

plt.figure()
plt.title("Middle slice of the excursion with a threshold of -2")
plt.imshow(realisations[0][int(nNodes / 2), :, :] > -2)

plt.figure()
plt.title("Middle slice of the excursion with a threshold of 0")
plt.imshow(realisations[0][int(nNodes / 2), :, :] > 0)

plt.figure()
plt.title("Middle slice of the excursion with a threshold of 2")
plt.imshow(realisations[0][int(nNodes / 2), :, :] > 2)
  • Middle slice of the first realisation
  • Middle slice of the excursion with a threshold of -2
  • Middle slice of the excursion with a threshold of 0
  • Middle slice of the excursion with a threshold of 2
<matplotlib.image.AxesImage object at 0x7f681947aa10>

Compute the four averaged measures (actually three)#

For every thresholds, three average measures (L0, L2 and L3) over all the realisations are compute and compared to the theoretical values.

# save average for every thresholds
totalVolumeMC = numpy.zeros_like(thresholdsMC)
totalSurfaceMC = numpy.zeros_like(thresholdsMC)
caliperDiameterMC = numpy.zeros_like(thresholdsMC)
eulerCharacMC = numpy.zeros_like(thresholdsMC)

# compute the aspect ratio
ar = length / float(nNodes)

# loop over the thresholds
for i, t in enumerate(thresholdsMC):
    print(f"Threshold: {i + 1}/{len(thresholdsMC)}: {t:.2f}")
    # loop over the realisations
    for r in realisations:
        totalVolumeMC[i] += spam.measurements.volume((r > t), aspectRatio=(ar, ar, ar)) / float(nRea)
        totalSurfaceMC[i] += spam.measurements.surfaceArea(r, level=t, aspectRatio=(ar, ar, ar)) / float(nRea)
        # commented because requires too much ressources
        # caliperDiameterMC[i] += spam.measurements.totalCurvature(r, level=t, aspectRatio=(ar, ar, ar))/(float(nRea)*2.0*numpy.pi)
        eulerCharacMC[i] += spam.measurements.eulerCharacteristic((r > t)) / float(nRea)
    print(f"\t volume  = {totalVolumeMC[i]:.2f} \t (err = {abs(totalVolume[i] - totalVolumeMC[i]) / abs(totalVolume[i]):.2f})")
    print(f"\t surface = {totalSurfaceMC[i]:.2f} \t (err = {abs(totalSurface[i] - totalSurfaceMC[i]) / abs(totalSurface[i]):.2f})")
    print(f"\t caliper = {caliperDiameterMC[i]:.2f} \t (err = {abs(caliperDiameter[i] - caliperDiameterMC[i]) / abs(caliperDiameter[i]):.2f})")
    print(f"\t euler   = {eulerCharacMC[i]:.2f} \t (err = {abs(eulerCharac[i] - eulerCharacMC[i]) / abs(eulerCharac[i]):.2f})")
Threshold: 1/20: -4.00
         volume  = 997.84        (err = 0.00)
         surface = 624.96        (err = 0.04)
         caliper = 0.00          (err = 1.00)
         euler   = 57.20         (err = 3.50)
Threshold: 2/20: -3.58
         volume  = 994.73        (err = 0.01)
         surface = 655.74        (err = 0.09)
         caliper = 0.00          (err = 1.00)
         euler   = 101.80        (err = 6.29)
Threshold: 3/20: -3.16
         volume  = 988.03        (err = 0.01)
         surface = 718.01        (err = 0.19)
         caliper = 0.00          (err = 1.00)
         euler   = 158.20        (err = 9.31)
Threshold: 4/20: -2.74
         volume  = 974.83        (err = 0.02)
         surface = 827.20        (err = 0.37)
         caliper = 0.00          (err = 1.00)
         euler   = 200.80        (err = 10.93)
Threshold: 5/20: -2.32
         volume  = 951.13        (err = 0.05)
         surface = 998.07        (err = 0.65)
         caliper = 0.00          (err = 1.00)
         euler   = 186.00        (err = 9.08)
Threshold: 6/20: -1.89
         volume  = 912.16        (err = 0.09)
         surface = 1231.50       (err = 1.03)
         caliper = 0.00          (err = 1.00)
         euler   = 106.60        (err = 4.28)
Threshold: 7/20: -1.47
         volume  = 853.74        (err = 0.15)
         surface = 1507.40       (err = 1.48)
         caliper = 0.00          (err = 1.00)
         euler   = -51.80        (err = 3.34)
Threshold: 8/20: -1.05
         volume  = 773.51        (err = 0.23)
         surface = 1782.83       (err = 1.93)
         caliper = 0.00          (err = 1.00)
         euler   = -269.80       (err = 12.18)
Threshold: 9/20: -0.63
         volume  = 672.88        (err = 0.33)
         surface = 1995.42       (err = 2.28)
         caliper = 0.00          (err = 1.00)
         euler   = -471.60       (err = 18.90)
Threshold: 10/20: -0.21
         volume  = 557.66        (err = 0.44)
         surface = 2085.36       (err = 2.42)
         caliper = 0.00          (err = 1.00)
         euler   = -572.80       (err = 20.95)
Threshold: 11/20: 0.21
         volume  = 437.13        (err = 0.56)
         surface = 2011.41       (err = 2.29)
         caliper = 0.00          (err = 1.00)
         euler   = -530.00       (err = 17.96)
Threshold: 12/20: 0.63
         volume  = 322.31        (err = 0.68)
         surface = 1775.77       (err = 1.90)
         caliper = 0.00          (err = 1.00)
         euler   = -320.60       (err = 10.44)
Threshold: 13/20: 1.05
         volume  = 222.80        (err = 0.78)
         surface = 1436.87       (err = 1.34)
         caliper = 0.00          (err = 1.00)
         euler   = -62.60        (err = 2.70)
Threshold: 14/20: 1.47
         volume  = 143.76        (err = 0.86)
         surface = 1062.92       (err = 0.73)
         caliper = 0.00          (err = 1.00)
         euler   = 169.40        (err = 3.24)
Threshold: 15/20: 1.89
         volume  = 86.62         (err = 0.91)
         surface = 722.33        (err = 0.17)
         caliper = 0.00          (err = 1.00)
         euler   = 294.40        (err = 5.81)
Threshold: 16/20: 2.32
         volume  = 48.59         (err = 0.95)
         surface = 450.13        (err = 0.27)
         caliper = 0.00          (err = 1.00)
         euler   = 321.60        (err = 5.88)
Threshold: 17/20: 2.74
         volume  = 25.32         (err = 0.97)
         surface = 257.54        (err = 0.59)
         caliper = 0.00          (err = 1.00)
         euler   = 284.40        (err = 4.64)
Threshold: 18/20: 3.16
         volume  = 12.29         (err = 0.99)
         surface = 135.26        (err = 0.78)
         caliper = 0.00          (err = 1.00)
         euler   = 204.20        (err = 2.76)
Threshold: 19/20: 3.58
         volume  = 5.47          (err = 0.99)
         surface = 64.76         (err = 0.90)
         caliper = 0.00          (err = 1.00)
         euler   = 134.60        (err = 1.30)
Threshold: 20/20: 4.00
         volume  = 2.25          (err = 1.00)
         surface = 28.40         (err = 0.95)
         caliper = 0.00          (err = 1.00)
         euler   = 74.00         (err = 0.18)

We can now plot the theory and Monte Carlo measures over the thresholds

# plot volume
plt.figure()
plt.xlabel("Threshold")
plt.title("Total volume")
plt.plot(thresholds, totalVolume, 'r', label='Theory')
plt.plot(thresholdsMC, totalVolumeMC, '*b', label='Monte Carlo')
plt.legend()

# plot surface
plt.figure()
plt.xlabel("Threshold")
plt.title("Total surface area")
plt.plot(thresholds, totalSurface, 'r', label='Theory')
plt.plot(thresholdsMC, totalSurfaceMC, '*b', label='Monte Carlo')
plt.legend()

# plot caliper diametre
plt.figure()
plt.plot(thresholds, caliperDiameter, 'r')
plt.plot(thresholdsMC, caliperDiameterMC, '*b', label='Monte Carlo')
plt.xlabel("Threshold")
plt.title("Mean caliper diametre")
plt.legend()

# plot Euler characteristic
plt.figure()
plt.xlabel("Threshold")
plt.title("Euler characteristic")
plt.plot(thresholds, eulerCharac, 'r', label='Theory')
plt.plot(thresholdsMC, eulerCharacMC, '*b', label='Monte Carlo')
plt.legend()
plt.show()
  • Total volume
  • Total surface area
  • Mean caliper diametre
  • Euler characteristic

Total running time of the script: (0 minutes 37.260 seconds)

Gallery generated by Sphinx-Gallery