1import math 2 3import numpy 4import six 5 6import chainer 7from chainer import backend 8from chainer.backends import cuda 9from chainer.backends import intel64 10from chainer import function_node 11import chainer.functions 12from chainer.functions.math import floor as _floor 13from chainer import utils 14from chainer.utils import type_check 15from chainer import variable 16 17 18def _convert_value_to_string(value): 19 if isinstance(value, variable.Variable): 20 value = value.data 21 22 if numpy.isscalar(value): 23 if value < 0: 24 return '({})'.format(value) 25 else: 26 return str(value) 27 28 array_types = chainer.get_array_types() 29 if isinstance(value, array_types): 30 return 'constant array' 31 else: 32 raise ValueError( 33 'Value must be a Variable, scalar, {} or {}. Actual: {}'.format( 34 ', '.join([str(at) for at in array_types[:-1]]), 35 array_types[-1], type(value))) 36 37 38def _preprocess_const(x, value): 39 return x.dtype.type(value) 40 41 42def _chainerx_preprocess_const(x, value, label): 43 # Allow mixing of numpy/cupy array and chainerx array as long as 44 # conversion without copy is possible. 45 if isinstance(value, (numpy.ndarray, cuda.ndarray)): 46 # TODO(niboshi): force zero-copy 47 return backend.to_chx(value) 48 49 if isinstance(value, (six.integer_types, float)): 50 return value 51 if isinstance(value, numpy.generic): 52 return value.item() 53 if isinstance(value, variable.Variable): 54 value = variable.as_array(value) 55 utils._check_arrays_forward_compatible((x, value), label) 56 return value 57 58 59def _preprocess_rhs(x, value): 60 if isinstance(value, chainer.Variable): 61 return value 62 63 if not (numpy.isscalar(value) 64 or isinstance(value, chainer.get_array_types())): 65 raise TypeError( 66 'Value must be a scalar, `numpy.ndarray`, `cupy.ndarray` ' 67 'or a `Variable`.\nActual: {}'.format(type(value))) 68 69 return value.astype(x.dtype, copy=False) 70 71 72class Neg(function_node.FunctionNode): 73 74 is_elementwise = True 75 76 @property 77 def label(self): 78 return '__neg__' 79 80 def check_type_forward(self, in_types): 81 type_check._argname(in_types, ('x',)) 82 83 def forward_chainerx(self, x): 84 return -x[0], 85 86 def forward(self, x): 87 self.retain_inputs(()) 88 return utils.force_array(-x[0]), 89 90 def backward(self, indexes, gy): 91 return -gy[0], 92 93 94def neg(self): # -x 95 """Element-wise negation. 96 97 Returns: 98 ~chainer.Variable: Output variable. 99 """ 100 return Neg().apply((self,))[0] 101 102 103class Absolute(function_node.FunctionNode): 104 105 is_elementwise = True 106 107 @property 108 def label(self): 109 return '|_|' 110 111 def check_type_forward(self, in_types): 112 type_check._argname(in_types, ('x',)) 113 type_check.expect(in_types[0].dtype.kind == 'f') 114 115 def forward(self, x): 116 self.retain_inputs((0,)) 117 return utils.force_array(abs(x[0])), 118 119 def backward(self, indexes, grad_outputs): 120 x = self.get_retained_inputs()[0] 121 return AbsoluteGrad(x.data).apply(grad_outputs) 122 123 124class AbsoluteGrad(function_node.FunctionNode): 125 126 is_elementwise = True 127 128 def __init__(self, x): 129 super(AbsoluteGrad, self).__init__() 130 self.x = x 131 132 def check_type_forward(self, in_types): 133 type_check._argname(in_types, ('gy',)) 134 type_check.expect(in_types[0].dtype.kind == 'f') 135 136 def forward_cpu(self, inputs): 137 return utils.force_array(numpy.sign(self.x) * inputs[0]), 138 139 def forward_gpu(self, inputs): 140 gx0 = cuda.elementwise( 141 'T x0, T gy', 'T gx0', 142 'gx0 = ((x0 > 0) - (x0 < 0)) * gy', 143 'abs_bwd')(self.x, inputs[0]) 144 return gx0, 145 146 def backward(self, indexes, grad_outputs): 147 return AbsoluteGrad(self.x).apply(grad_outputs) 148 149 150def absolute(self): 151 """Element-wise absolute. 152 153 Returns: 154 ~chainer.Variable: Output variable. 155 """ 156 return Absolute().apply((self,))[0] 157 158 159class Add(function_node.FunctionNode): 160 161 is_elementwise = True 162 163 @property 164 def label(self): 165 return '_ + _' 166 167 def check_type_forward(self, in_types): 168 type_check._argname(in_types, ('lhs', 'rhs')) 169 type_check.expect( 170 in_types[0].dtype == in_types[1].dtype, 171 ) 172 type_check.expect_broadcast_shapes( 173 in_types[0].shape, in_types[1].shape) 174 175 def forward_chainerx(self, x): 176 return x[0] + x[1], 177 178 def forward(self, x): 179 # may broadcast 180 y = utils.force_array(x[0] + x[1]) 181 return y, 182 183 def backward(self, indexes, gy): 184 return tuple(chainer.functions.sum_to(gy[0], self.inputs[i].shape) 185 for i in indexes) 186 187 188class AddConstant(function_node.FunctionNode): 189 190 is_elementwise = True 191 192 def __init__(self, value): 193 self.value = value 194 195 @property 196 def label(self): 197 return '_ + %s' % _convert_value_to_string(self.value) 198 199 def check_type_forward(self, in_types): 200 type_check._argname(in_types, ('x',)) 201 type_check.expect(in_types.size() == 1) 202 203 def forward_chainerx(self, x): 204 value = _chainerx_preprocess_const(x[0], self.value, 'add') 205 return x[0] + value, 206 207 def forward(self, x): 208 value = _preprocess_const(x[0], self.value) 209 return utils.force_array(x[0] + value), 210 211 def backward(self, indexes, gy): 212 x_node, = self.inputs 213 return gy 214 215 216class MultiAdd(function_node.FunctionNode): 217 218 is_elementwise = True 219 220 def check_type_forward(self, in_types): 221 for i, in_type in enumerate(in_types): 222 type_check._argname((in_type,), ('x{}'.format(i),)) 223 type_check.expect(in_types[0].dtype == in_type.dtype) 224 225 def forward(self, xs): 226 self.len = len(xs) 227 if len(xs) == 1: 228 return xs 229 if (intel64.should_use_ideep('>=auto') 230 and intel64.inputs_all_ready(xs) 231 and all(x.shape == xs[0].shape for x in xs[1:])): 232 y = intel64.ideep.multi_add(xs) 233 else: 234 # The output should be a new array. Add the first 2 arrays 235 # and get the result y. Then add the rest arrays to y. 236 y = xs[0] + xs[1] 237 for x in xs[2:]: 238 if x.shape == y.shape: 239 y += x 240 else: 241 y = x + y 242 243 return utils.force_array(y), 244 245 def backward(self, indexes, gy): 246 return tuple(chainer.functions.sum_to(gy[0], x_node.shape) 247 for x_node in self.inputs) 248 249 250# TODO(hvy): Implement multi-add with chainerx.ndarrays. 251def add(*xs): # lhs + rhs or add more than 2 variables 252 """Element-wise addition. 253 254 Returns: 255 ~chainer.Variable: Output variable. 256 """ 257 if len(xs) == 2: 258 lhs, rhs = xs 259 if numpy.isscalar(rhs): 260 return AddConstant(rhs).apply((lhs,))[0] 261 rhs = _preprocess_rhs(lhs, rhs) 262 return Add().apply((lhs, rhs))[0] 263 else: 264 return MultiAdd().apply(xs)[0] 265 266 267class Sub(function_node.FunctionNode): 268 269 is_elementwise = True 270 271 @property 272 def label(self): 273 return '_ - _' 274 275 def check_type_forward(self, in_types): 276 type_check._argname(in_types, ('lhs', 'rhs')) 277 type_check.expect(in_types[0].dtype == in_types[1].dtype) 278 type_check.expect_broadcast_shapes( 279 in_types[0].shape, in_types[1].shape) 280 281 def forward_chainerx(self, x): 282 return x[0] - x[1], 283 284 def forward(self, x): 285 # may broadcast 286 return utils.force_array(x[0] - x[1]), 287 288 def backward(self, indexes, gy): 289 x1, x2 = self.inputs 290 g, = gy 291 return ( 292 chainer.functions.sum_to(g, x1.shape) if 0 in indexes else None, 293 -chainer.functions.sum_to(g, x2.shape) if 1 in indexes else None, 294 ) 295 296 297def sub(self, rhs): # lhs - rhs 298 """Element-wise subtraction. 299 300 Returns: 301 ~chainer.Variable: Output variable. 302 """ 303 if numpy.isscalar(rhs): 304 return AddConstant(-rhs).apply((self,))[0] 305 rhs = _preprocess_rhs(self, rhs) 306 return Sub().apply((self, rhs))[0] 307 308 309class SubFromConstant(function_node.FunctionNode): 310 311 is_elementwise = True 312 313 def __init__(self, value): 314 self.value = value 315 316 @property 317 def label(self): 318 return '%s - _' % _convert_value_to_string(self.value) 319 320 def check_type_forward(self, in_types): 321 type_check._argname(in_types, ('x',)) 322 323 def forward(self, x): 324 value = _preprocess_const(x[0], self.value) 325 return utils.force_array(value - x[0]), 326 327 def backward(self, indexes, gy): 328 g, = gy 329 return -g, 330 331 332def rsub(self, rhs): # rhs - lhs 333 """Element-wise subtraction. 334 335 Returns: 336 ~chainer.Variable: Output variable. 337 """ 338 if numpy.isscalar(rhs): 339 return SubFromConstant(rhs).apply((self,))[0] 340 rhs = _preprocess_rhs(self, rhs) 341 return Sub().apply((rhs, self))[0] 342 343 344class Mul(function_node.FunctionNode): 345 346 is_elementwise = True 347 348 @property 349 def label(self): 350 return '_ * _' 351 352 def check_type_forward(self, in_types): 353 type_check._argname(in_types, ('lhs', 'rhs')) 354 type_check.expect( 355 in_types[0].dtype.kind == 'f', 356 in_types[0].dtype == in_types[1].dtype, 357 ) 358 type_check.expect_broadcast_shapes( 359 in_types[0].shape, in_types[1].shape) 360 361 def forward_chainerx(self, x): 362 return x[0] * x[1], 363 364 def forward(self, x): 365 self.retain_inputs((0, 1)) 366 # may broadcast 367 return utils.force_array(x[0] * x[1]), 368 369 def backward(self, indexes, gy): 370 xs = self.get_retained_inputs() 371 return tuple( 372 chainer.functions.sum_to(gy[0] * xs[1 - i], xs[i].shape) 373 for i in indexes 374 ) 375 376 377class MulConstant(function_node.FunctionNode): 378 379 is_elementwise = True 380 381 def __init__(self, value): 382 self.value = value 383 384 @property 385 def label(self): 386 return '_ * %s' % _convert_value_to_string(self.value) 387 388 def check_type_forward(self, in_types): 389 type_check._argname(in_types, ('x',)) 390 391 def forward_chainerx(self, x): 392 value = _chainerx_preprocess_const(x[0], self.value, 'mul') 393 return x[0] * value, 394 395 def forward(self, x): 396 value = _preprocess_const(x[0], self.value) 397 return utils.force_array(value * x[0]), 398 399 def backward(self, indexes, gy): 400 g, = gy 401 return self.value * g, 402 403 404def mul(self, rhs): # lhs * rhs 405 """Element-wise multiplication. 406 407 Returns: 408 ~chainer.Variable: Output variable. 409 """ 410 if numpy.isscalar(rhs): 411 return MulConstant(rhs).apply((self,))[0] 412 rhs = _preprocess_rhs(self, rhs) 413 return Mul().apply((self, rhs))[0] 414 415 416class Div(function_node.FunctionNode): 417 418 is_elementwise = True 419 420 @property 421 def label(self): 422 return '_ / _' 423 424 def check_type_forward(self, in_types): 425 type_check._argname(in_types, ('lhs', 'rhs')) 426 type_check.expect( 427 in_types[0].dtype.kind == 'f', 428 in_types[0].dtype == in_types[1].dtype, 429 ) 430 type_check.expect_broadcast_shapes( 431 in_types[0].shape, in_types[1].shape) 432 433 def forward_chainerx(self, x): 434 return x[0] / x[1], 435 436 def forward(self, x): 437 self.retain_inputs((0, 1)) 438 # may broadcast 439 return utils.force_array(x[0] / x[1]), 440 441 def backward(self, indexes, grad_outputs): 442 x0, x1 = self.get_retained_inputs() 443 is_grad_elementwise = x0.shape == x1.shape 444 divgrad = DivGrad(is_grad_elementwise) 445 return divgrad.apply((x0, x1, grad_outputs[0])) 446 447 448class DivGrad(function_node.FunctionNode): 449 450 def __init__(self, is_elementwise): 451 self.is_elementwise = is_elementwise 452 453 def forward_cpu(self, inputs): 454 self.retain_inputs((0, 1, 2)) 455 x0, x1, gy = inputs 456 gx0 = utils.force_array(gy / x1) 457 gx1 = utils.force_array(-gx0 * x0 / x1) 458 return utils.sum_to(gx0, x0.shape), utils.sum_to(gx1, x1.shape) 459 460 def forward_gpu(self, inputs): 461 self.retain_inputs((0, 1, 2)) 462 x0, x1, gy = inputs 463 gx0, gx1 = cuda.elementwise( 464 'T x0, T x1, T gy', 465 'T gx0, T gx1', 466 ''' 467 gx0 = gy / x1; 468 gx1 = -gx0 * x0 / x1; 469 ''', 'div_bwd')(x0, x1, gy) 470 return utils.sum_to(gx0, x0.shape), utils.sum_to(gx1, x1.shape) 471 472 def backward(self, indexes, grad_outputs): 473 x0, x1, gy = self.get_retained_inputs() 474 ggx0, ggx1 = grad_outputs 475 476 ret = [] 477 x1_square = x1 * x1 478 479 if 0 in indexes: 480 if ggx1 is None: 481 ret.append(None) 482 else: 483 gx0 = -ggx1 * gy / x1_square 484 ret.append(chainer.functions.sum_to(gx0, x0.shape)) 485 486 if 1 in indexes: 487 gx1 = None if ggx0 is None else -ggx0 * gy / x1_square 488 gx1_1 = (None if ggx1 is None else 489 ggx1 * 2 * gy * x0 / (x1_square * x1)) 490 if gx1 is None: 491 gx1 = gx1_1 492 elif gx1_1 is not None: 493 gx1 += gx1_1 494 ret.append(None if gx1 is None else 495 chainer.functions.sum_to(gx1, x1.shape)) 496 497 if 2 in indexes: 498 ggy = None if ggx0 is None else ggx0 / x1 499 ggy_1 = None if ggx1 is None else ggx1 * x0 / x1_square 500 if ggy is None: 501 ggy = -ggy_1 502 elif ggy_1 is not None: 503 ggy -= ggy_1 504 ret.append(ggy) 505 506 return ret 507 508 509def div(self, rhs): # lhs / rhs 510 """Element-wise division 511 512 Returns: 513 ~chainer.Variable: Output variable. 514 """ 515 if numpy.isscalar(rhs): 516 return MulConstant(1. / rhs).apply((self,))[0] 517 rhs = _preprocess_rhs(self, rhs) 518 return Div().apply((self, rhs))[0] 519 520 521# TODO(sonots): Support chainerx 522class DivFromConstant(function_node.FunctionNode): 523 524 is_elementwise = True 525 526 def __init__(self, value): 527 self.value = value 528 529 @property 530 def label(self): 531 return '%s / _' % _convert_value_to_string(self.value) 532 533 def check_type_forward(self, in_types): 534 type_check._argname(in_types, ('x',)) 535 type_check.expect(in_types[0].dtype.kind == 'f') 536 537 def forward(self, x): 538 self.retain_inputs((0,)) 539 value = _preprocess_const(x[0], self.value) 540 return utils.force_array(value / x[0]), 541 542 def backward(self, indexes, grad_outputs): 543 x = self.get_retained_inputs() 544 return DivFromConstantGrad(self.value).apply((x[0], grad_outputs[0])) 545 546 547class DivFromConstantGrad(function_node.FunctionNode): 548 549 def __init__(self, value): 550 super(DivFromConstantGrad, self).__init__() 551 self.value = value 552 553 def forward_cpu(self, inputs): 554 self.retain_inputs((0, 1)) 555 x, gy = inputs 556 value = _preprocess_const(x, self.value) 557 return utils.force_array(-value * gy / (x ** 2)), 558 559 def forward_gpu(self, inputs): 560 self.retain_inputs((0, 1)) 561 x, gy = inputs 562 # TODO(beam2d): Make it not use the input 563 value = _preprocess_const(x, self.value) 564 return cuda.elementwise('T x, T gy, T value', 'T gx', 565 'gx = -value * gy / (x * x)', 566 'div_from_const_bwd')(x, gy, value), 567 568 def backward(self, indexes, grad_outputs): 569 x, gy = self.get_retained_inputs() 570 value = _preprocess_const(x.data, self.value) 571 ret = [] 572 if 0 in indexes: 573 ret.append(grad_outputs[0] * 2 * value * gy / (x ** 3)) 574 if 1 in indexes: 575 ret.append(grad_outputs[0] * -value / (x ** 2)) 576 return ret 577 578 579def rdiv(self, rhs): # rhs / lhs 580 """Element-wise division. 581 582 Returns: 583 ~chainer.Variable: Output variable. 584 """ 585 if numpy.isscalar(rhs): 586 return DivFromConstant(rhs).apply((self,))[0] 587 rhs = _preprocess_rhs(self, rhs) 588 return Div().apply((rhs, self))[0] 589 590 591def floordiv(self, rhs): # lhs // rhs 592 """Element-wise floor division. 593 594 Returns: 595 ~chainer.Variable: Output variable. 596 """ 597 598 return _floor.floor(div(self, rhs)) 599 600 601def rfloordiv(self, rhs): # rhs // lhs 602 """Element-wise floor division. 603 604 Returns: 605 ~chainer.Variable: Output variable. 606 """ 607 608 return _floor.floor(rdiv(self, rhs)) 609 610 611class PowVarVar(function_node.FunctionNode): 612 613 is_elementwise = True 614 615 @property 616 def label(self): 617 return '_ ** _' 618 619 def check_type_forward(self, in_types): 620 type_check._argname(in_types, ('lhs', 'rhs')) 621 type_check.expect( 622 in_types[0].dtype.kind == 'f', 623 in_types[0].dtype == in_types[1].dtype, 624 ) 625 type_check.expect_broadcast_shapes( 626 in_types[0].shape, in_types[1].shape) 627 628 def forward(self, x): 629 self.retain_inputs((0, 1)) 630 # may broadcast 631 self.y = x[0] ** x[1] 632 return utils.force_array(self.y), 633 634 def backward(self, indexes, gy): 635 x0, x1 = self.get_retained_inputs() 636 is_grad_elementwise = x0.shape == x1.shape 637 return PowVarVarGrad( 638 is_grad_elementwise, self.y).apply((x0, x1, gy[0])) 639 640 641class PowVarVarGrad(function_node.FunctionNode): 642 643 def __init__(self, is_elementwise, y): 644 self.is_elementwise = is_elementwise 645 self.y = y 646 647 def check_type_forward(self, in_types): 648 type_check._argname(in_types, ('lhs', 'rhs', 'gy')) 649 type_check.expect( 650 in_types[0].dtype.kind == 'f', 651 in_types[0].dtype == in_types[1].dtype, 652 in_types[0].dtype == in_types[2].dtype, 653 ) 654 655 def forward_cpu(self, inputs): 656 self.retain_inputs((0, 1, 2)) 657 x0, x1, gy = inputs 658 659 one = x1.dtype.type(1) 660 gx0 = utils.sum_to( 661 utils.force_array(x1 * (x0 ** (x1 - one)) * gy), x0.shape) 662 gx1 = utils.sum_to( 663 utils.force_array(numpy.log(x0) * self.y * gy), x1.shape) 664 return gx0, gx1 665 666 def forward_gpu(self, inputs): 667 self.retain_inputs((0, 1, 2)) 668 x0, x1, gy = inputs 669 670 gx0, gx1 = cuda.elementwise( 671 'T x0, T x1, T gy, T y', 'T gx0, T gx1', 672 ''' 673 gx0 = x1 * pow(x0, x1 - 1) * gy; 674 gx1 = log(x0) * y * gy; 675 ''', 'pow_var_var_bwd')(x0, x1, gy, self.y) 676 677 gx0 = utils.sum_to(gx0, x0.shape) 678 gx1 = utils.sum_to(gx1, x1.shape) 679 680 return gx0, gx1 681 682 def backward(self, indexes, ggx): 683 x0, x1, gy = self.get_retained_inputs() 684 ggx0, ggx1 = ggx 685 686 log_x0 = chainer.functions.log(x0) 687 pow_x0_x1 = x0 ** x1 688 pow_x0_x1_1 = x0 ** (x1 - 1) 689 pow_x0_x1_2 = x0 ** (x1 - 2) 690 691 ret = [] 692 if 0 in indexes: 693 gx0_0 = (0 if ggx0 is None else 694 ggx0 * x1 * (x1 - 1) * pow_x0_x1_2) 695 gx0_1 = (0 if ggx1 is None else 696 ggx1 * pow_x0_x1_1 * (log_x0 * x1 + 1)) 697 gx0 = (gx0_0 + gx0_1) * gy 698 ret.append(chainer.functions.sum_to(gx0, x0.shape)) 699 if 1 in indexes: 700 gx1_0 = (0 if ggx0 is None else 701 ggx0 * pow_x0_x1_1 * (log_x0 * x1 + 1)) 702 gx1_1 = (0 if ggx1 is None else 703 ggx1 * log_x0 * log_x0 * pow_x0_x1) 704 gx1 = (gx1_0 + gx1_1) * gy 705 ret.append(chainer.functions.sum_to(gx1, x1.shape)) 706 if 2 in indexes: 707 ggy_0 = 0 if ggx0 is None else ggx0 * x1 * pow_x0_x1_1 708 ggy_1 = 0 if ggx1 is None else ggx1 * log_x0 * pow_x0_x1 709 ggy = ggy_0 + ggy_1 710 ret.append(ggy) 711 return ret 712 713 714class PowVarConst(function_node.FunctionNode): 715 716 is_elementwise = True 717 718 def __init__(self, value): 719 self.value = value 720 721 @property 722 def label(self): 723 return '_ ** %s' % _convert_value_to_string(self.value) 724 725 def check_type_forward(self, in_types): 726 type_check._argname(in_types, ('x',)) 727 type_check.expect(in_types[0].dtype.kind == 'f') 728 729 def forward(self, x): 730 self.retain_inputs((0,)) 731 y = x[0] ** _preprocess_const(x[0], self.value) 732 return utils.force_array(y, x[0].dtype), 733 734 def backward(self, indexes, gy): 735 inputs = self.get_retained_inputs() 736 return PowVarConstGrad(self.value).apply((inputs[0], gy[0])) 737 738 739class PowVarConstGrad(function_node.FunctionNode): 740 741 is_elementwise = True 742 743 def __init__(self, value): 744 self.value = value 745 self.val = self.val_1 = None 746 747 def check_type_forward(self, in_types): 748 type_check._argname(in_types, ('x', 'gy')) 749 type_check.expect( 750 in_types[0].dtype.kind == 'f', 751 in_types[0].dtype == in_types[1].dtype, 752 in_types[0].shape == in_types[1].shape 753 ) 754 755 def forward_cpu(self, inputs): 756 self.retain_inputs((0, 1)) 757 x, gy = inputs 758 759 self.val_1 = _preprocess_const(x, self.value - 1) 760 gx = utils.force_type(x.dtype, self.value) * (x ** self.val_1) * gy 761 gx = utils.force_array(gx) 762 return gx, 763 764 def forward_gpu(self, inputs): 765 self.retain_inputs((0, 1)) 766 x, gy = inputs 767 768 self.val = _preprocess_const(x, self.value) 769 gx = cuda.elementwise( 770 'T x, T gy, T value', 'T gx', 771 'gx = value * pow(x, value - 1) * gy', 772 'pow_var_const_bwd')(x, gy, self.val) 773 return gx, 774 775 def backward(self, indexes, ggx): 776 x, gy = self.get_retained_inputs() 777 778 if self.val is None: 779 self.val = _preprocess_const(x.data, self.value) 780 if self.val_1 is None: 781 self.val_1 = _preprocess_const(x.data, self.value - 1) 782 val_2 = _preprocess_const(x.data, self.value - 2) 783 784 ret = [] 785 if 0 in indexes: 786 ret.append(ggx[0] * self.val * gy * self.val_1 * x ** val_2) 787 if 1 in indexes: 788 ret.append(ggx[0] * self.val * x ** self.val_1) 789 return ret 790 791 792def pow(self, rhs): # lhs ** rhs 793 """Element-wise power function. 794 795 Returns: 796 ~chainer.Variable: Output variable. 797 """ 798 799 if numpy.isscalar(rhs): 800 return PowVarConst(rhs).apply((self,))[0] 801 rhs = _preprocess_rhs(self, rhs) 802 return PowVarVar().apply((self, rhs))[0] 803 804 805class PowConstVar(function_node.FunctionNode): 806 807 is_elementwise = True 808 809 def __init__(self, value): 810 self.value = value 811 812 @property 813 def label(self): 814 return '%s ** _' % _convert_value_to_string(self.value) 815 816 def check_type_forward(self, in_types): 817 type_check._argname(in_types, ('x',)) 818 type_check.expect(in_types[0].dtype.kind == 'f') 819 820 def forward(self, x): 821 self.retain_outputs((0,)) 822 value = _preprocess_const(x[0], self.value) 823 y = value ** x[0] 824 return utils.force_array(y), 825 826 def backward(self, indexes, gy): 827 outputs = self.get_retained_outputs() 828 return PowConstVarGrad(self.value).apply((outputs[0], gy[0])) 829 830 831class PowConstVarGrad(function_node.FunctionNode): 832 833 is_elementwise = True 834 835 def __init__(self, value): 836 self.value = value 837 self.log_value = math.log(value) 838 839 def check_type_forward(self, in_types): 840 type_check._argname(in_types, ('y', 'gy')) 841 type_check.expect( 842 in_types[0].dtype.kind == 'f', 843 in_types[0].dtype == in_types[1].dtype, 844 in_types[0].shape == in_types[1].shape 845 ) 846 847 def forward_cpu(self, inputs): 848 self.retain_inputs((0, 1)) 849 y, gy = inputs 850 851 gx = utils.force_array(y.dtype.type(self.log_value) * y * gy) 852 return gx, 853 854 def forward_gpu(self, inputs): 855 self.retain_inputs((0, 1)) 856 y, gy = inputs 857 858 value = _preprocess_const(y, self.value) 859 gx = cuda.elementwise( 860 'T y, T gy, T value', 'T gx', 861 'gx = log(value) * y * gy', 862 'pow_const_var_bwd')(y, gy, value) 863 return gx, 864 865 def backward(self, indexes, ggx): 866 y, gy = self.get_retained_inputs() 867 868 gygy = y.dtype.type(self.log_value) * ggx[0] 869 870 ret = [] 871 if 0 in indexes: 872 ret.append(gygy * gy) 873 if 1 in indexes: 874 ret.append(gygy * y) 875 return ret 876 877 878def rpow(self, rhs): # rhs ** lhs 879 """Element-wise power function. 880 881 Returns: 882 ~chainer.Variable: Output variable. 883 """ 884 885 if numpy.isscalar(rhs): 886 return PowConstVar(rhs).apply((self,))[0] 887 rhs = _preprocess_rhs(self, rhs) 888 return PowVarVar().apply((rhs, self))[0] 889 890 891def matmul(self, rhs): # lhs @ rhs 892 """Matrix multiplication. 893 894 Returns: 895 ~chainer.Variable: Output variable. 896 """ 897 898 rhs = _preprocess_rhs(self, rhs) 899 return chainer.functions.matmul(self, rhs) 900 901 902def rmatmul(self, rhs): # rhs @ lhs 903 """Matrix multiplication. 904 905 Returns: 906 ~chainer.Variable: Output variable. 907 """ 908 909 rhs = _preprocess_rhs(self, rhs) 910 return chainer.functions.matmul(rhs, self) 911 912 913def install_variable_arithmetics(): 914 variable.Variable.__neg__ = neg 915 variable.Variable.__abs__ = absolute 916 variable.Variable.__add__ = add 917 variable.Variable.__radd__ = add 918 variable.Variable.__sub__ = sub 919 variable.Variable.__rsub__ = rsub 920 variable.Variable.__mul__ = mul 921 variable.Variable.__rmul__ = mul 922 variable.Variable.__div__ = div 923 variable.Variable.__truediv__ = div 924 variable.Variable.__rdiv__ = rdiv 925 variable.Variable.__rtruediv__ = rdiv 926 variable.Variable.__floordiv__ = floordiv 927 variable.Variable.__rfloordiv__ = rfloordiv 928 variable.Variable.__pow__ = pow 929 variable.Variable.__rpow__ = rpow 930 variable.Variable.__matmul__ = matmul 931 variable.Variable.__rmatmul__ = rmatmul 932