1import math
2import warnings
3
4import numpy
5
6import chainer
7from chainer.backends import cuda
8from chainer import function_node
9from chainer import utils
10from chainer.utils import type_check
11
12
13_erfc_cpu = None
14
15
16class Erfc(function_node.FunctionNode):
17
18    @property
19    def label(self):
20        return 'erfc'
21
22    def check_type_forward(self, in_types):
23        type_check._argname(in_types, ('x',))
24        type_check.expect(in_types[0].dtype.kind == 'f')
25
26    def forward_cpu(self, x):
27        global _erfc_cpu
28        if _erfc_cpu is None:
29            try:
30                from scipy import special
31                _erfc_cpu = special.erfc
32            except ImportError:
33                warnings.warn(
34                    'SciPy is not available. Forward computation of erfc in'
35                    ' CPU can be slow without SciPy.',
36                    chainer.warnings.PerformanceWarning)
37                _erfc_cpu = numpy.vectorize(math.erfc)
38        self.retain_inputs((0,))
39        return utils.force_array(_erfc_cpu(x[0]), dtype=x[0].dtype),
40
41    def forward_gpu(self, x):
42        self.retain_inputs((0,))
43        return cuda.elementwise(
44            'T x', 'T y',
45            'y = erfc(x)',
46            'elementwise_erfc',
47        )(x[0]),
48
49    def backward(self, indexes, gy):
50        x = self.get_retained_inputs()[0]
51        return -2 / numpy.pi ** 0.5 * chainer.functions.exp(-x ** 2) * gy[0],
52
53
54def erfc(x):
55    """Elementwise complementary error function.
56
57    .. note::
58       Forward computation in CPU can be slow if
59       `SciPy <https://www.scipy.org/>`_ is not available.
60
61    Args:
62        x (:class:`~chainer.Variable` or :ref:`ndarray`): Input variable.
63
64    Returns:
65        ~chainer.Variable: Output variable.
66    """
67    return Erfc().apply((x,))[0]
68