netket.utils.is_probably_holomorphic(apply_fun, parameters, samples, model_state=None)[source]#

Check if a function \(\psi\) is likely to be holomorphic almost everywhere.

The check is done by verifying if the function satisfies on the provided samples the Cauchy-Riemann equations. In particular, assuming that the function is complex-valued \(\psi(\theta, x) = \psi_r(\theta, x) + i \psi_i(\theta, x)\) where \(\psi_r\) and \(\psi_i\) are it’s real and imaginary part, and the parameters can also be split in real and imaginary part according to \(\theta = \theta_r + i\theta_i\), the conditions real

\[\frac{\partial \psi_r(\theta, x)}{\partial \theta_r} = \frac{\partial \psi_i(\theta, x)}{\partial \theta_i} \,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\, \frac{\partial \psi_r(\theta, x)}{\partial \theta_i} = -\frac{\partial \psi_i(\theta, x)}{\partial \theta_r}\]

Those conditions are always not verified if the parameters are real (\(\theta_i=0\)), in which case we automatically return False.

We verify the Cauchy-Riemann equations on the provided samples, so you should provide a sufficiently large number of points to be relatively sure if your function is holomorphic, because there are some functions that are locally non holomorphic, however that is rare and in general you can trust the result of this function.


If you are working with NetKet, you are most likely pretty good at math. In that case, instead of relying on the computer to tell you if the function is holomorphic or not you can do it by yourself! It’s quite easy: the Cauchy-Riemann conditions can be rewritten using Wirtinger derivatives (where we treat the complex conjugate variables \(\theta\) and \(\theta^\star\) as linearly independent) as

\[\frac{\partial \psi(\theta, x)]}{\partial \theta^\star} = 0 .\]

Practically, this can be read as if the function :math:`psi` depends on :math:`theta^star` it will not be holomorphic. Therefore, a very easy way to spot non-holomorphic functions is to verify if any operation among the following appear in the function:

  • complex conjugation

  • absolute value

  • real or imaginary part


In most Machine-Learning applications we are only interested in holomorphicity almost-everywhere, therefore if the function is not holomorphic on one particular point, such as \(\theta=0\), :math:`theta=1`or similar this will in general not matter because that is a set of dimension zero that we will never cross.

This is similar to the way the derivative of locally non-differentiable functions like the modulus is obtained: we assume that we never enter those regions.

  • apply_fun (Callable[[Any, Union[ndarray, Array]], Union[ndarray, Array]]) – The forward pass of the variational function. This should be a Callable accepting two inputs, the first being the parameters as a dictionary with at least a key params, and possibly other keys given by the model_state. The second argument to apply_fun will be the samples.

  • parameters (Any) – a pytree of parameters to be passed as the first argument to the apply_fun. We will check if the ansatz is holomoprhic with respect to the derivatives of those parameters.

  • samples (Union[ndarray, Array]) – a set of samples.

  • model_state (Optional[Any]) – optional dictionary of state parameters of the model.

Return type: