Commit a1c7ad32 authored by valentin.emiya's avatar valentin.emiya
Browse files

documentation completed

parent 5ee7f1e1
......@@ -9,6 +9,8 @@ tests:
- pip3 install 'scipy==1.4.1' -U
- pip3 install 'matplotlib==3.1.2' -U
- pip3 install --no-deps .
- pip3 freeze
- conda list
- python3 tffpy/tests/ci_config.py
- pytest-3
......
......@@ -272,6 +272,7 @@ texinfo_no_detailmenu = False
intersphinx_mapping = {
'numpy': ('https://docs.scipy.org/doc/numpy/', None),
'scipy': ('https://docs.scipy.org/doc/scipy/reference/', None),
'pandas': ('https://pandas.pydata.org/docs/', None),
'skpomade': ('http://valentin.emiya.pages.lis-lab.fr/skpomade/', None),
'yafe': ('http://skmad-suite.pages.lis-lab.fr/yafe/', None),
'ltfatpy': ('http://dev.pages.lis-lab.fr/ltfatpy/', None),
......
......@@ -57,5 +57,6 @@ tffpy\.experiments\.exp_solve_tff module
.. automodule:: tffpy.experiments.exp_solve_tff
:members:
:special-members: __call__
:undoc-members:
:show-inheritance:
......@@ -29,7 +29,7 @@ def get_dataset():
-------
dataset : dict
dataset['wideband'] (resp. dataset['localized']) is a dictionary
containing the :py:class:`Path` object for all the wideband
containing the :py:class:`~pathlib.Path` object for all the wideband
(resp. localized) sounds.
"""
dataset = dict()
......@@ -94,7 +94,7 @@ def get_mix(loc_source, wideband_src, crop=None,
closing_first : bool
If True, morphological closings are applied first, followed by
openings. If False, the reverse way is used.
fig_dir : str or Path
fig_dir : None or str or Path
If not None, folder where figures are stored. If None, figures are
not plotted.
prefix : str
......
# -*- coding: utf-8 -*-
"""
Class `SolveTffExperiment` uses the :class:`yafe.base.Experiment` experiment
framework to handle the main time-frequency fading experiment: It includes
loading the data, generating the problems, applying solvers, and exploiting
results.
See the `documentation <http://skmad-suite.pages.lis-lab.fr/yafe/>`_ of
package :py:mod:`yafe` for the technical details.
.. moduleauthor:: Valentin Emiya
"""
......@@ -8,6 +15,7 @@ import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import pandas as pd
from pathlib import Path
from yafe import Experiment
from madarrays import Waveform
......@@ -20,6 +28,21 @@ from tffpy.utils import \
class SolveTffExperiment(Experiment):
"""
The main experiment to solve time-frequency fading problems with a
number of sounds mixtures and solvers.
Parameters
----------
force_reset : bool
If true, reset the experiment by erasing all previous results
in order to run it from scratch. If False, the existing results are
kept in order to proceed with the existing experiment.
suffix : str
Suffix that is appended to the name of the experiment, useful to
save results in a specific folder.
"""
def __init__(self, force_reset=False, suffix=''):
Experiment.__init__(self,
name='SolveTffExperiment' + suffix,
......@@ -36,10 +59,36 @@ class SolveTffExperiment(Experiment):
@property
def n_tasks(self):
"""
Number of tasks
Returns
-------
int
"""
return len(list((self.xp_path / 'tasks').glob('*')))
@staticmethod
def get_experiment(setting='full', force_reset=False):
"""
Get the experiment instance with default values in order to handle it.
Parameters
----------
setting : {'full', 'light'}
If 'full', the default values are set to run the full
experiment. If 'light', the default values are set to have a
very light experiment with few tasks, running fast, for test
purposes.
force_reset : bool
If true, reset the experiment by erasing all previous results
in order to run it from scratch. If False, the existing results are
kept in order to proceed with the existing experiment.
Returns
-------
SolveTffExperiment
"""
assert setting in ('full', 'light')
dataset = get_dataset()
......@@ -76,7 +125,25 @@ class SolveTffExperiment(Experiment):
exp.generate_tasks()
return exp
def export_task_params(self):
def export_task_params(self, csv_path=None):
"""
Export task parameters to a csv file and to a
:class:`pandas.DataFrame` object.
Parameters
----------
csv_path : str or Path
Name of the csv file to be written. If None, file is
located in the experiment folder with name 'task_params.csv'.
Returns
-------
pandas.DataFrame
"""
if csv_path is None:
csv_path = self.xp_path / 'task_params.csv'
else:
csv_path = Path(csv_path)
task_list = []
for i_task in range(self.n_tasks):
task = self.get_task_data_by_id(idt=i_task)
......@@ -84,17 +151,39 @@ class SolveTffExperiment(Experiment):
for k in task['task_params']
for kk in task['task_params'][k]})
df = pd.DataFrame(task_list)
csv_path = self.xp_path / 'task_params.csv'
df.to_csv(csv_path)
print('Task params exported to', csv_path)
return df
def generate_tasks(self):
"""
Generate tasks and export params to a csv file
See :py:meth:`yafe.Experiment.generate_tasks`
"""
Experiment.generate_tasks(self)
self.export_task_params()
def get_misc_file(self, task_params=None, idt=None):
"""
Get file with some additional task results.
This has been set up in order to pass additional data in a way that
could not be handled by the :py:mod:`yafe` framework.
Parameters
----------
task_params : dict
Task parameters.
idt : int
Task identifier. Either `task_params` or `idt` should be given
in order to specify the task.
Returns
-------
Path
File containing additional task results.
"""
if task_params is not None:
task = self.get_task_data_by_params(
data_params=task_params['data_params'],
......@@ -107,6 +196,9 @@ class SolveTffExperiment(Experiment):
return path_task / 'misc.npz'
def plot_results(self):
"""
Plot and save results of the experiment
"""
self.fig_dir.mkdir(parents=True, exist_ok=True)
print('Figures saved in {}'.format(self.fig_dir))
results = self.load_results(array_type='xarray')
......@@ -285,6 +377,16 @@ class SolveTffExperiment(Experiment):
return label
def plot_task(self, idt, fontsize=16):
"""
Plot and save figures for a specific task
Parameters
----------
idt : int
Task identifier
fontsize : int
Fontsize to be used in Figures.
"""
matplotlib.rcParams.update({'font.size': fontsize})
fig_dir = self.xp_path / 'figures' / 'tasks' / '{:06}'.format(idt)
fig_dir.mkdir(parents=True, exist_ok=True)
......@@ -468,14 +570,72 @@ class SolveTffExperiment(Experiment):
def get_data(loc_source, wideband_src):
"""
Prepare the input data information for the :py:class:`SolveTffExperiment`
experiment.
This function is only embedding its input in a dictionary
Parameters
----------
loc_source : Path
File for the source localized in time-frequency (perturbation)
wideband_src : Path
File for the source of interest.
Returns
-------
dict
Dictionary to be given when calling the problemm (
see :py:meth:`Problem.__call__`), with keys `'loc_source'` and
`wideband_src`.
"""
return dict(loc_source=loc_source, wideband_src=wideband_src)
class Problem:
def __init__(self, win_choice, or_mask,
n_iter_closing, n_iter_opening, closing_first,
delta_mix_db, delta_loc_db, wb_to_loc_ratio_db, crop,
fig_dir):
"""
Problem generation for the :py:class:`SolveTffExperiment` experiment.
Parameters
----------
crop : int or None
If not None, a cropped, centered portion of the sound will be
extracted with the specified length, in samples.
win_choice : str
String of the form 'name len' where 'name' is a window name and
'len' is a window length, e.g. 'hann 512', 'gauss 256.
delta_mix_db : float
Coefficient energy ratio, in dB, between the wideband source and the
localized source in the mixture in order to select coefficients in
the mask.
delta_loc_db : float
Dynamic range, in dB, for the localized source in order to select
coefficients in the mask.
wb_to_loc_ratio_db : float
Wideband source to localized source energy ratio to be adjusted in
the mix.
or_mask : bool
If True, the mask is build by taking the union of the two masks
obtained using thresholds `delta_mix_db` and `delta_loc_db`. If
False, the intersection is taken.
n_iter_closing : int
Number of successive morphological closings with radius 1 (a.k.a.
radius of one single closing)
n_iter_opening : int
Number of successive morphological openings with radius 1 (a.k.a.
radius of one single opening)
closing_first : bool
If True, morphological closings are applied first, followed by
openings. If False, the reverse way is used.
fig_dir : None or str or Path
If not None, folder where figures are stored. If None, figures are
not plotted.
"""
def __init__(self, crop, win_choice,
delta_mix_db, delta_loc_db, wb_to_loc_ratio_db, or_mask,
n_iter_closing, n_iter_opening, closing_first, fig_dir):
win_type, win_len_str = win_choice.split(sep=' ')
win_dur = int(win_len_str) / 8000
self.win_dur = win_dur
......@@ -497,6 +657,26 @@ class Problem:
self.fig_dir = fig_dir
def __call__(self, loc_source, wideband_src):
"""
Generate the problem from input data.
Parameters
----------
loc_source : Path
File for the source localized in time-frequency (perturbation)
wideband_src : Path
File for the source of interest.
Returns
-------
problem_data : dict
Dictionary to be given to a solver, with keys `'x_mix'` (mix
signal), `mask` (time-frequency mask), `dgt_params` (DGT
parameters) and `signal_params` (signal parameters).
solution_data : dict
Dictionary containing problem solutions, with keys `'x_loc'` (
localized signal ) and `x_wb` (wideband signal).
"""
x_mix, dgt_params, signal_params, mask, x_loc, x_wb = \
get_mix(loc_source=loc_source,
wideband_src=wideband_src,
......@@ -521,12 +701,78 @@ class Problem:
class Solver:
"""
Solver for the :py:class:`SolveTffExperiment` experiment.
This solver is computing
* the `TFF-1` of `TFF-P` solution (depending on parameter `tol_subregions`)
using a :py:class:`~tffpy.tf_fading.GabMulTff` instance
* the `Interp` solution using function
:py:func:`~tffpy.interpolation_solver.solve_by_interpolation`
Parameters
----------
tol_subregions : None or float
Tolerance to split the mask into sub-regions in
:py:class:`~tffpy.tf_fading.GabMulTff`.
tolerance_arrf : float
Tolerance for the randomized EVD in
:py:class:`~tffpy.tf_fading.GabMulTff`, see method
:py:meth:`~tffpy.tf_fading.GabMulTff.compute_decomposition`.
proba_arrf : float
Probability of error for the randomized EVD in
:py:class:`~tffpy.tf_fading.GabMulTff`, see method
:py:meth:`~tffpy.tf_fading.GabMulTff.compute_decomposition`.
"""
def __init__(self, tol_subregions, tolerance_arrf, proba_arrf):
self.tol_subregions = tol_subregions
self.tolerance_arrf = tolerance_arrf
self.proba_arrf = proba_arrf
def __call__(self, x_mix, mask, dgt_params, signal_params):
"""
Apply the solver to estimate solutions from the problem data.
The output dictionary is composed of data with keys:
* `'x_tff'`: solution estimated by :py:class:`~tffpy.tf_fading.GabMulTff`
* `'x_zero'`: solution when applying the Gabor
multiplier (i.e., :math:`\lambda=1`)
* `'x_interp'`: solution from function
:py:func:`~tffpy.interpolation_solver.solve_by_interpolation`
* `'gmtff'`: `GabMulTff` instance
* `'t_lambda_tff'`: running times to estimate hyperparameter in method
:py:meth:`~tffpy.tf_fading.GabMulTff.compute_lambda`
* `'t_arrf'`: running times to compute range approximation in method
:py:meth:`~tffpy.tf_fading.GabMulTff.compute_decomposition`
* `'t_evdn'`: running times to compute EVD in method
:py:meth:`~tffpy.tf_fading.GabMulTff.compute_decomposition`
* `'t_uh_x'`: running times to compute additional matrix products in
method :py:meth:`~tffpy.tf_fading.GabMulTff.compute_decomposition`
* `'t_subreg'`: running times to split mask into sub-regions in class
:py:class:`~tffpy.tf_fading.GabMulTff`
* `'lambda_tff'`: estimated values for hyper-parameters
:math:`\lambda_i` estimated by
:py:meth:`~tffpy.tf_fading.GabMulTff`.compute_lambda`
Parameters
----------
x_mix : nd-array
Mix signal
mask : nd-array
Time-frequency mask
dgt_params : dict
DGT parameters
signal_params : dict
Signal parameters
Returns
-------
dict
The estimated solution and additional information
"""
gmtff = GabMulTff(x_mix=x_mix, mask=mask, dgt_params=dgt_params,
signal_params=signal_params,
tol_subregions=self.tol_subregions)
......@@ -550,6 +796,31 @@ class Solver:
def perf_measures(task_params, source_data, problem_data,
solution_data, solved_data, exp=None):
"""
Performance measure, including computation of oracle solutions
Parameters
----------
task_params : dict
Task parameters
source_data : dict
Input data
problem_data : dict
Problem data
solution_data : dict
Solver output
solved_data : dict
True solution data
exp : SolveTffExperiment
The experiment
Returns
-------
dict
All data useful for result analysis including SDR and Itakura-Saito
performance, running times, hyperparameter values, mask size and
number of sub-regions.
"""
x_tff = solved_data['x_tff']
x_zero = solved_data['x_zero']
gmtff = solved_data['gmtff']
......@@ -607,6 +878,9 @@ def perf_measures(task_params, source_data, problem_data,
def create_and_run_light_experiment():
"""
Create a light experiment and run it
"""
exp = SolveTffExperiment.get_experiment(setting='light', force_reset=True)
print('*' * 80)
print('Created experiment')
......
# -*- coding: utf-8 -*-
"""
Class :class:`GabMulTff` is the main object to solve a time-frequency fading
problem.
.. moduleauthor:: Valentin Emiya
"""
......@@ -38,7 +40,7 @@ class GabMulTff:
tol_subregions : None or float
If None, the mask is considered as a single region. If float,
tolerance to split the mask into sub-regions using
:py:func:`tffpy.create_subregions.create_subregions`.
:py:func:`~tffpy.create_subregions.create_subregions`.
fig_dir : str or Path
If not None, folder where figures are stored. If None, figures are
not plotted.
......@@ -95,16 +97,19 @@ class GabMulTff:
followed by :py:func:`skpomade.factorization_construction.evd_nystrom`.
The rank of each decomposition is estimated using parameters
`tolerance_arrf` and `proba_arrf`.
Running times are stored in attributes `t_arrf`, `t_evdn` and `t_uh_x`.
Running times to compute the range approximation, the
EVD itself and the additional matrix products for subsequent
computations are stored in attributes `t_arrf`, `t_evdn` and
`t_uh_x`, respectively.
Parameters
----------
tolerance_arrf : float
Tolerance for
:py:func:`skpomade.range_approximation.adaptive_randomized_range_finder`
:py:func:`~skpomade.range_approximation.adaptive_randomized_range_finder`
proba_arrf : float
Probability of error for
:py:func:`skpomade.range_approximation.adaptive_randomized_range_finder`
:py:func:`~skpomade.range_approximation.adaptive_randomized_range_finder`
"""
for i in range(self.n_areas):
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment