# Source code for neurokit2.complexity.fractal_petrosian

import numpy as np
import pandas as pd

from .utils_complexity_symbolize import complexity_symbolize

[docs]
def fractal_petrosian(signal, symbolize="C", show=False):
"""**Petrosian fractal dimension (PFD)**

Petrosian (1995) proposed a fast method to estimate the fractal dimension by converting the
signal into a binary sequence from which the fractal dimension is estimated. Several variations
of the algorithm exist (e.g., "A", "B", "C" or "D"), primarily differing in the
way the discrete (symbolic) sequence is created (see func:complexity_symbolize for details).
The most common method ("C", by default) binarizes the signal by the sign of consecutive
differences.

.. math::

\\frac{log(N)}{log(N) + log(\\frac{N}{N+0.4N_{\\delta}})}

Most of these methods assume that the signal is periodic (without a linear trend). Linear
detrending might be useful to eliminate linear trends (see :func:.signal_detrend).

--------
information_mutual, entropy_svd

Parameters
----------
signal : Union[list, np.array, pd.Series]
The signal (i.e., a time series) in the form of a vector of values.
symbolize : str
Method to convert a continuous signal input into a symbolic (discrete) signal. By default,
assigns 0 and 1 to values below and above the mean. Can be None to skip the process (in
case the input is already discrete). See :func:complexity_symbolize for details.
show : bool
If True, will show the discrete the signal.

Returns
-------
pfd : float
The petrosian fractal dimension (PFD).
info : dict
A dictionary containing additional information regarding the parameters used
to compute PFD.

Examples
----------
.. ipython:: python

import neurokit2 as nk

signal = nk.signal_simulate(duration=2, frequency=[5, 12])

@savefig p_fractal_petrosian1.png scale=100%
pfd, info = nk.fractal_petrosian(signal, symbolize = "C", show=True)
@suppress
plt.close()

.. ipython:: python

pfd
info

References
----------
* Esteller, R., Vachtsevanos, G., Echauz, J., & Litt, B. (2001). A comparison of waveform
fractal dimension algorithms. IEEE Transactions on Circuits and Systems I: Fundamental Theory
and Applications, 48(2), 177-183.
* Petrosian, A. (1995, June). Kolmogorov complexity of finite sequences and recognition of
different preictal EEG patterns. In Proceedings eighth IEEE symposium on computer-based
medical systems (pp. 212-217). IEEE.
* Kumar, D. K., Arjunan, S. P., & Aliahmad, B. (2017). Fractals: applications in biological
Signalling and image processing. CRC Press.
* Goh, C., Hamadicharef, B., Henderson, G., & Ifeachor, E. (2005, June). Comparison of fractal
dimension algorithms for the computation of EEG biomarkers for dementia. In 2nd International
Conference on Computational Intelligence in Medicine and Healthcare (CIMED2005).

"""
# Sanity checks
if isinstance(signal, (np.ndarray, pd.DataFrame)) and signal.ndim > 1:
raise ValueError(
"Multidimensional inputs (e.g., matrices or multichannel data) are not supported yet."
)

# Binarize the sequence
symbolic = complexity_symbolize(signal, method=symbolize, show=show)

# if isinstance(method, str) and method.lower() in ["d", "r"]:
#     # These methods are already based on the consecutive differences
#     n_inversions = symbolic.sum()
# else:
#     # Note: np.diff(symbolic).sum() wouldn't work in case there's a seq like [0, -1, 1]
#     n_inversions = (symbolic[1:] != symbolic[:-1]).sum()
n_inversions = (symbolic[1:] != symbolic[:-1]).sum()

n = len(symbolic)
pfd = np.log10(n) / (np.log10(n) + np.log10(n / (n + 0.4 * n_inversions)))
return pfd, {"Symbolization": symbolize}