A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
two-ray-to-three-gpp-ch-calibration.py
Go to the documentation of this file.
1#!/usr/bin/env python
2
3"""
4Companion TwoRaySpectrumPropagationLossModel calibration script
5
6Copyright (c) 2022 SIGNET Lab, Department of Information Engineering,
7University of Padova
8
9This program is free software: you can redistribute it and/or modify it under
10the terms of the GNU General Public License as published by the Free Software
11Foundation, either version 3 of the License, or (at your option) any later
12version.
13
14This program is distributed in the hope that it will be useful, but WITHOUT
15ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License along with
19this program. If not, see <http://www.gnu.org/licenses/>.
20"""
21
22import pandas as pd
23from matplotlib import pyplot as plt
24import seaborn as sns
25import numpy as np
26from pathlib import Path
27from itertools import product
28from tqdm import tqdm
29import joblib
30import contextlib
31import argparse as argp
32
33
34# Command line arguments
35parser = argp.ArgumentParser(formatter_class=argp.ArgumentDefaultsHelpFormatter)
36parser.add_argument("--num_search_grid_params", default=30,
37 help="Number of values for each parameter of the search grids")
38parser.add_argument("--num_refinements", default=1,
39 help="Number of refinement local search runs to be carried out")
40parser.add_argument("--ref_data_fname", default="two-ray-to-three-gpp-splm-calibration.csv",
41 help="Filename of the fit reference data, obtained from ns-3")
42parser.add_argument("--fit_out_fname", default="two-ray-splm-fitted-params.txt",
43 help="Filename of the fit results")
44parser.add_argument("--c_plus_plus_out_fname", default="two-ray-cplusplus-fitted-params.txt",
45 help="Filename of the fit results, encoded as a C++ data structure to be imported in ns-3")
46parser.add_argument("--figs_folder", default="FiguresTwoRayThreeGppChCalibration/",
47 help="Output folder for the fit results figures")
48parser.add_argument("--epsilon", default=1e-7,
49 help="Tolerance value for the preliminary tests")
50parser.add_argument("--preliminary_fit_test", default=True,
51 help="Whether to run preliminary tests which check the correctness of the script functions")
52parser.add_argument("--fit_ftr_to_threegpp", default=True,
53 help="Whether to run the calibration with respect to the 3GPP reference channel gains")
54parser.add_argument("--output_ns3_table", default=True,
55 help="Whether to output the code for importing the calibration results in ns-3")
56parser.add_argument("--plot_fit_results", default=False,
57 help="Whether to plot a comparison of the reference data ECDFs vs the fitted FTR distributions")
58
59args = parser.parse_args()
60# Number of values for each parameter of the search grids
61num_search_grid_params = int(args.num_search_grid_params)
62# Number of refinement local search runs to be carried out
63num_refinements = int(args.num_refinements)
64# Filename of the fit reference data, obtained from ns-3
65ref_data_fname = args.ref_data_fname
66# Filename of the fit results
67fit_out_fname = args.fit_out_fname
68# Filename of the fit results, encoded as a C++ data structure to be imported in ns-3
69c_plus_plus_out_fname = args.c_plus_plus_out_fname
70# Output folder for the fit results figures
71figs_folder = args.figs_folder
72# Tolerance value for the preliminary tests
73epsilon = float(args.epsilon)
74# Whether to run preliminary tests which check the correctness of the script functions
75preliminary_fit_test = bool(args.preliminary_fit_test)
76# Whether to run the calibration with respect to the 3GPP reference channel gains
77fit_ftr_to_threegpp = bool(args.fit_ftr_to_threegpp)
78# Whether to output the code for importing the calibration results in ns-3
79output_ns3_table = bool(args.output_ns3_table)
80# Whether to plot a comparison of the reference data ECDFs vs the fitted FTR distributions
81plot_fit_results = bool(args.plot_fit_results)
82
83
84@contextlib.contextmanager
85def tqdm_joblib(tqdm_object):
86 """
87 Context manager to patch joblib to report into tqdm progress bar given as argument.
88 Taken from: https://stackoverflow.com/questions/24983493/tracking-progress-of-joblib-parallel-execution
89
90 """
91 class TqdmBatchCompletionCallback(joblib.parallel.BatchCompletionCallBack):
92 def __call__(self, *args, **kwargs):
93 tqdm_object.update(n=self.batch_size)
94 return super().__call__(*args, **kwargs)
95
96 old_batch_callback = joblib.parallel.BatchCompletionCallBack
97 joblib.parallel.BatchCompletionCallBack = TqdmBatchCompletionCallback
98 try:
99 yield tqdm_object
100 finally:
101 joblib.parallel.BatchCompletionCallBack = old_batch_callback
102 tqdm_object.close()
103
104
105
107
115
116 def __init__(self, m: float, sigma: float, k: float, delta: float):
117 '''! The initializer.
118 @param self: the object pointer
119 @param m: Parameter m for the Gamma variable. Used both as the shape and rate parameters.
120 @param sigma: Parameter sigma. Used as the variance of the amplitudes of the normal diffuse components.
121 @param k: Parameter K. Expresses ratio between dominant specular components and diffuse components.
122 @param delta: Parameter delta [0, 1]. Expresses how similar the amplitudes of the two dominant specular components are.
123 '''
124
125 self.m = m
126 self.sigma = sigma
127 self.k = k
128 self.delta = delta
129
130 def __init__(self):
131 '''! The initializer with default values.
132 @param self: the object pointer
133 '''
134
135 self.m = 1
136 self.sigma = 1.0
137 self.k = 0.0
138 self.delta = 0.0
139
140 def __str__(self):
141 '''! The initializer with default values.
142 @param self: the object pointer
143 @returns A string reporting the value of each of the FTR fading model parameters
144 '''
145
146 return f'm: {self.m}, sigma: {self.sigma}, k: {self.k}, delta: {self.delta}'
147
148
149def get_ftr_ecdf(params: FtrParams, n_samples: int, db=False):
150 '''! Returns the ECDF for the FTR fading model, for a given parameter grid.
151 @param params: The FTR parameters grid.
152 @param n_samples: The number of samples of the output ECDF
153 @param db: Whether to return the ECDF with the gain expressed in dB
154 @returns The ECDF for the FTR fading model
155 '''
156
157 assert (params.delta >= 0 and params.delta <= 1.0)
158
159 # Compute the specular components amplitudes from the FTR parameters
160 cmn_sqrt_term = np.sqrt(1 - params.delta**2)
161 v1 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 - cmn_sqrt_term))
162 v2 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 + cmn_sqrt_term))
163
164 assert (abs((v1**2 + v2**2)/(2*params.sigma) - params.k) < 1e-5)
165 if params.k > 0:
166 assert (abs((2*v1*v2)/(v1**2 + v2**2) - params.delta) < 1e-4)
167 else:
168 assert (v1 == v2 == params.k)
169
170 sqrt_gamma = np.sqrt(np.random.gamma(
171 shape=params.m, scale=1/params.m, size=n_samples))
172
173 # Sample the random phases of the specular components, which are uniformly distributed in [0, 2*PI]
174 phi1 = np.random.uniform(low=0, high=1.0, size=n_samples)
175 phi2 = np.random.uniform(low=0, high=1.0, size=n_samples)
176
177 # Sample the normal-distributed real and imaginary parts of the diffuse components
178 x = np.random.normal(scale=np.sqrt(params.sigma), size=n_samples)
179 y = np.random.normal(scale=np.sqrt(params.sigma), size=n_samples)
180
181 # Compute the channel response by combining the above terms
182 compl_phi1 = np.vectorize(complex)(np.cos(phi1), np.sin(phi1))
183 compl_phi2 = np.vectorize(complex)(np.cos(phi2), np.sin(phi2))
184 compl_xy = np.vectorize(complex)(x, y)
185 h = np.multiply(sqrt_gamma, compl_phi1) * v1 + \
186 np.multiply(sqrt_gamma, compl_phi2) * v2 + compl_xy
187
188 # Compute the squared norms
189 power = np.square(np.absolute(h))
190
191 if db:
192 power = 10*np.log10(power)
193
194 return np.sort(power)
195
196
197def compute_ftr_mean(params: FtrParams):
198 '''! Computes the mean of the FTR fading model, given a specific set of parameters.
199 @param params: The FTR fading model parameters.
200 '''
201
202 cmn_sqrt_term = np.sqrt(1 - params.delta**2)
203 v1 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 - cmn_sqrt_term))
204 v2 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 + cmn_sqrt_term))
205
206 mean = v1**2 + v2**2 + 2*params.sigma
207
208 return mean
209
210
211def compute_ftr_th_mean(params: FtrParams):
212 '''! Computes the mean of the FTR fading model using the formula reported in the corresponding paper,
213 given a specific set of parameters.
214 @param params: The FTR fading model parameters.
215 '''
216
217 return 2 * params.sigma * (1 + params.k)
218
219
220def compute_anderson_darling_measure(ref_ecdf: list, target_ecdf: list) -> float:
221 '''! Computes the Anderson-Darling measure for the specified reference and targets distributions.
222 In particular, the Anderson-Darling measure is defined as:
223 \f$A^2 = -N -S\f$, where \f$S = \sum_{i=1}^N \frac{2i - 1}{N} \left[ ln F(Y_i) + ln F(Y_{N + 1 - i}) \right]\f$.
224
225 See https://www.itl.nist.gov/div898/handbook/eda/section3/eda35e.htm for further details.
226
227 @param ref_ecdf: The reference ECDF.
228 @param target_ecdf: The target ECDF we wish to match the reference distribution to.
229 @returns The Anderson-Darling measure for the specified reference and targets distributions.
230 '''
231
232 assert (len(ref_ecdf) == len(target_ecdf))
233
234 n = len(ref_ecdf)
235 mult_factors = np.linspace(start=1, stop=n, num=n)*2 + 1
236 ecdf_values = compute_ecdf_value(ref_ecdf, target_ecdf)
237
238 # First and last elements of the ECDF may lead to NaNs
239 with np.errstate(divide='ignore'):
240 log_a_plus_b = np.log(ecdf_values) + np.log(1 - np.flip(ecdf_values))
241
242 valid_idxs = np.isfinite(log_a_plus_b)
243 A_sq = - np.dot(mult_factors[valid_idxs], log_a_plus_b[valid_idxs])
244
245 return A_sq
246
247
248def compute_ecdf_value(ecdf: list, data_points: float) -> np.ndarray:
249 '''! Given an ECDF and data points belonging to its domain, returns their associated EDCF value.
250 @param ecdf: The ECDF, represented as a sorted list of samples.
251 @param data_points: A list of data points belonging to the same domain as the samples.
252 @returns The ECDF value of the domain points of the specified ECDF
253 '''
254
255 ecdf_values = []
256 for point in data_points:
257 idx = np.searchsorted(ecdf, point) / len(ecdf)
258 ecdf_values.append(idx)
259
260 return np.asarray(ecdf_values)
261
262
263def get_sigma_from_k(k: float) -> float:
264 '''! Computes the value for the FTR parameter sigma, given k, yielding a unit-mean fading process.
265 @param k: The K parameter of the FTR fading model, which represents the ratio of the average power
266 of the dominant components to the power of the remaining diffuse multipath.
267 @returns The value for the FTR parameter sigma, given k, yielding a unit-mean fading process.
268 '''
269
270 return 1 / (2 + 2 * k)
271
272
273def fit_ftr_to_reference(ref_data: pd.DataFrame, ref_params_combo: tuple, num_params: int, num_refinements: int) -> str:
274 '''! Estimate the FTR parameters yielding the closest ECDF to the reference one.
275
276 Uses a global search to estimate the FTR parameters yielding the best fit to the reference ECDF.
277 Then, the search is refined by repeating the procedure in the neighborhood of the parameters
278 identified with the global search. Such a neighborhood is determined as the interval whose center
279 is the previous iteration best value, and the lower and upper bounds are the first lower and upper
280 values which were previously considered, respectively.
281
282 @param ref_data: The reference data, represented as a DataFrame of samples.
283 @param ref_params_combo: The specific combination of simulation parameters corresponding
284 to the reference ECDF
285 @param num_params: The number of values of each parameter in the global and local search grids.
286 @param num_refinements: The number of local refinement search to be carried out after the global search.
287
288 @returns An estimate of the FTR parameters yielding the closest ECDF to the reference one.
289 '''
290
291 # Retrieve the reference ECDF
292 ref_ecdf = ref_data.query(
293 'scen == @ref_params_combo[0] and cond == @ref_params_combo[1] and fc == @ref_params_combo[2]')
294
295 # Perform the fit
296 n_samples = len(ref_ecdf)
297 best_params = FtrParams()
298 best_ad = np.inf
299
300 # The m and K parameters can range in ]0, +inf[
301 m_and_k_ub = 4
302 m_and_k_lb = 0.001
303 m_and_k_step = (m_and_k_ub - m_and_k_lb) / n_samples
304
305 # The delta parameter can range in [0, 1]
306 delta_step = 1/n_samples
307
308 # Define the coarse grid
309 coarse_search_grid = {
310 # m must be in [0, +inf]
311 'm': np.power(np.ones(num_params)*10, np.linspace(start=m_and_k_lb, stop=m_and_k_ub, endpoint=True, num=num_params)),
312 # k must be in [0, +inf]
313 'k': np.power(np.ones(num_params)*10, np.linspace(start=m_and_k_lb, stop=m_and_k_ub, endpoint=True, num=num_params)),
314 # delta must be in [0, 1]
315 'delta': np.linspace(start=0.0, stop=1.0, endpoint=True, num=num_params)
316 # sigma determined from k, due to the unit-mean constraint
317 }
318
319 for element in product(*coarse_search_grid.values()):
320
321 # Create FTR params object
322 params = FtrParams()
323 params.m = element[0]
324 params.k = element[1]
325 params.delta = element[2]
326 params.sigma = get_sigma_from_k(params.k)
327
328 # Retrieve the corresponding FTR ECDF
329 ftr_ecdf = get_ftr_ecdf(params, n_samples, db=True)
330 ad_meas = compute_anderson_darling_measure(ref_ecdf, ftr_ecdf)
331
332 if (ad_meas < best_ad):
333 best_params = params
334 best_ad = ad_meas
335
336 for _ in range(num_refinements):
337 # Refine search in the neighborhood of the previously identified params
338 finer_search_grid = {
339 'm': np.power(np.ones(num_params)*10,
340 np.linspace(start=max(0, np.log10(best_params.m) - m_and_k_step),
341 stop=np.log10(best_params.m) +
342 m_and_k_step,
343 endpoint=True, num=num_params)),
344 'k': np.power(np.ones(num_params)*10,
345 np.linspace(start=max(0, np.log10(best_params.k) - m_and_k_step),
346 stop=np.log10(best_params.k) +
347 m_and_k_step,
348 endpoint=True, num=num_params)),
349 'delta': np.linspace(start=max(0, best_params.delta - delta_step),
350 stop=min(1, best_params.delta + delta_step),
351 endpoint=True, num=num_params)
352 # sigma determined from k, due to the unit-mean constraint
353 }
354
355 m_and_k_step = (np.log10(best_params.m) + m_and_k_step -
356 max(0, np.log10(best_params.m) - m_and_k_step)) / n_samples
357 delta_step = (min(1, best_params.delta + 1/num_params) -
358 max(0, best_params.delta - 1/num_params)) / n_samples
359
360 for element in product(*finer_search_grid.values()):
361
362 # Create FTR params object
363 params = FtrParams()
364 params.m = element[0]
365 params.k = element[1]
366 params.delta = element[2]
367 params.sigma = get_sigma_from_k(params.k)
368
369 # Retrieve the corresponding FTR ECDF
370 ftr_ecdf = get_ftr_ecdf(params, n_samples, db=True)
371 ad_meas = compute_anderson_darling_measure(ref_ecdf, ftr_ecdf)
372
373 if (ad_meas < best_ad):
374 best_params = params
375 best_ad = ad_meas
376
377 out_str = f"{ref_params_combo[0]}\t{ref_params_combo[1]}\t{ref_params_combo[2]}" + \
378 f" \t{best_params.sigma}\t{best_params.k}\t{best_params.delta}\t{best_params.m}\n"
379
380 return out_str
381
382
383def append_ftr_params_to_cpp_string(text: str, params: FtrParams) -> str:
384 text += f'TwoRaySpectrumPropagationLossModel::FtrParams({np.format_float_scientific(params.m)}, {np.format_float_scientific(params.sigma)}, \
385 {np.format_float_scientific(params.k)}, {np.format_float_scientific(params.delta)})'
386
387 return text
388
389
390def print_cplusplus_map_from_fit_results(fit: pd.DataFrame, out_fname: str):
391 """
392 Prints to a file the results of the FTR fit, as C++ code.
393
394 Args:
395 fit (pd.DataFrame): A Pandas Dataframe holding the results of the FTR fit.
396 out_fname (str): The name of the file to print the C++ code to.
397 """
398
399 out_str = '{'
400
401 for scen in set(fit['scen']):
402 out_str += f'{{\"{scen}\",\n{{'
403
404 for cond in set(fit['cond']):
405 out_str += f'{{ChannelCondition::LosConditionValue::{cond}, \n'
406
407 # Print vector of carrier frequencies
408 freqs = np.sort(list(set(fit['fc'])))
409 out_str += "{{"
410 for fc in freqs:
411 out_str += f'{float(fc)}, '
412 out_str = out_str[0:-2]
413 out_str += '},\n{'
414
415 # Load corresponding fit results
416 for fc in freqs:
417 fit_line = fit.query(
418 'scen == @scen and cond == @cond and fc == @fc')
419 assert(fit_line.reset_index().shape[0] == 1)
420
421 params = FtrParams()
422 params.m = fit_line.iloc[0]['m']
423 params.k = fit_line.iloc[0]['k']
424 params.delta = fit_line.iloc[0]['delta']
425 params.sigma = fit_line.iloc[0]['sigma']
426
427 # Print vector of corresponding FTR parameters
428 out_str = append_ftr_params_to_cpp_string(out_str, params)
429 out_str += ', '
430
431 out_str = out_str[0:-2]
432 out_str += '}'
433 out_str += '}},\n'
434
435 out_str = out_str[0:-2]
436 out_str += '}},\n'
437
438 out_str = out_str[0:-2]
439 out_str += '}\n'
440
441 with open(out_fname, "w", encoding="utf-8") as f:
442 f.write(out_str)
443
444
445if __name__ == '__main__':
446
447
450
451 # Load reference data obtained from the ns-3 TR 38.901 implementation
452 df = pd.read_csv(ref_data_fname, sep='\t')
453 # Linear gain --> gain in dB
454 df['gain'] = 10*np.log10(df['gain'])
455
456 # Retrieve the possible parameters configurations
457 scenarios = set(df['scen'])
458 is_los = set(df['cond'])
459 frequencies = np.sort(list(set(df['fc'])))
460
461
464
465 if preliminary_fit_test:
466
467 params = FtrParams()
468 get_ftr_ecdf(params, 100)
469
470 # Make sure the mean is indeed independent from the delta parameter
471 mean_list = []
472 params = FtrParams()
473 for delta in np.linspace(start=0, stop=1, num=100):
474 params.delta = delta
475 mean_list.append(compute_ftr_mean(params))
476
477 avg_mean = np.mean(mean_list)
478 assert np.all(np.abs(mean_list - avg_mean) < epsilon)
479
480 # Make sure that we are indeed generating parameters yielding unit-mean
481 mean_list.clear()
482 mean_th_list = []
483 params = FtrParams()
484 for k in np.linspace(start=1, stop=500, num=50):
486 params.sigma = sigma
487 params.k = k
488 mean_list.append(compute_ftr_mean(params))
489 mean_th_list.append(compute_ftr_th_mean(params))
490
491 assert np.all(np.abs(mean_list - np.float64(1.0)) < epsilon)
492 assert np.all(np.abs(mean_th_list - np.float64(1.0)) < epsilon)
493
494 if fit_ftr_to_threegpp:
495
496 # Parallel search for the different simulation parameters combination
497 with tqdm_joblib(tqdm(desc="Fitting FTR to the 3GPP fading model", total=(len(scenarios) * len(is_los) * len(frequencies)))) as progress_bar:
498 res = joblib.Parallel(n_jobs=10)(
499 joblib.delayed(fit_ftr_to_reference)(df, params_comb, num_search_grid_params, num_refinements) for params_comb in product(scenarios, is_los, frequencies))
500
501 with open(fit_out_fname, "w", encoding="utf-8") as f:
502 f.write("scen\tcond\tfc\tsigma\tk\tdelta\tm\n")
503 for line in res:
504 f.write(line)
505
506 if output_ns3_table:
507
508 # Load the fit results
509 fit = pd.read_csv(fit_out_fname, delimiter='\t')
510
511 # Output the C++ data structure
512 print_cplusplus_map_from_fit_results(fit, c_plus_plus_out_fname)
513
514 if plot_fit_results:
515
516 # Set Seaborn defaults and setup output folder
517 sns.set(rc={'figure.figsize': (7, 5)})
518 sns.set_theme()
519 sns.set_style('darkgrid')
520
521 fit = pd.read_csv(fit_out_fname, delimiter='\t')
522 # Create folder if it does not exist
523 Path(figs_folder).mkdir(parents=True, exist_ok=True)
524 ad_measures = []
525
526 for params_comb in product(scenarios, is_los, frequencies):
527
528 data_query = 'scen == @params_comb[0] and cond == @params_comb[1] and fc == @params_comb[2]'
529
530 # Load corresponding reference data
531 ref_data = df.query(data_query)
532
533 # Create FTR params object
534 fit_line = fit.query(data_query)
535 assert(fit_line.reset_index().shape[0] == 1)
536 params = FtrParams()
537 params.m = fit_line.iloc[0]['m']
538 params.k = fit_line.iloc[0]['k']
539 params.delta = fit_line.iloc[0]['delta']
540 params.sigma = fit_line.iloc[0]['sigma']
541
542 # Retrieve the corresponding FTR ECDF
543 ftr_ecdf = get_ftr_ecdf(params, len(ref_data), db=True)
544
545 # Compute the AD measure
547 np.sort(ref_data['gain']), ftr_ecdf)
548 ad_measures.append(np.sqrt(ad_meas))
549
550 sns.ecdfplot(data=ref_data, x='gain',
551 label='38.901 reference model')
552 sns.ecdfplot(
553 ftr_ecdf, label=f'Fitted FTR, sqrt(AD)={round(np.sqrt(ad_meas), 2)}')
554 plt.xlabel(
555 'End-to-end channel gain due to small scale fading [dB]')
556 plt.legend()
557 plt.savefig(
558 f'{figs_folder}{params_comb[0]}_{params_comb[1]}_{params_comb[2]/1e9}GHz_fit.png', dpi=500, bbox_inches='tight')
559 plt.clf()
560
561 # Plot ECDF of the scaled and normalized AD measures
562 sns.ecdfplot(ad_measures, label='AD measures')
563 plt.xlabel('Anderson-Darling goodness-of-fit')
564 plt.legend()
565 plt.savefig(f'{figs_folder}AD_measures.png',
566 dpi=500, bbox_inches='tight')
567 plt.clf()
#define min(a, b)
Definition: 80211b.c:41
#define max(a, b)
Definition: 80211b.c:42
def __str__(self)
The initializer with default values.
def __init__(self, float m, float sigma, float k, float delta)
The initializer.
str append_ftr_params_to_cpp_string(str text, FtrParams params)
float compute_anderson_darling_measure(list ref_ecdf, list target_ecdf)
Computes the Anderson-Darling measure for the specified reference and targets distributions.
def compute_ftr_th_mean(FtrParams params)
Computes the mean of the FTR fading model using the formula reported in the corresponding paper,...
str fit_ftr_to_reference(pd.DataFrame ref_data, tuple ref_params_combo, int num_params, int num_refinements)
Estimate the FTR parameters yielding the closest ECDF to the reference one.
def get_ftr_ecdf(FtrParams params, int n_samples, db=False)
Returns the ECDF for the FTR fading model, for a given parameter grid.
def print_cplusplus_map_from_fit_results(pd.DataFrame fit, str out_fname)
float get_sigma_from_k(float k)
Computes the value for the FTR parameter sigma, given k, yielding a unit-mean fading process.
def compute_ftr_mean(FtrParams params)
Computes the mean of the FTR fading model, given a specific set of parameters.
np.ndarray compute_ecdf_value(list ecdf, float data_points)
Given an ECDF and data points belonging to its domain, returns their associated EDCF value.
#define list