| 786 | } |
| 787 | |
| 788 | MX MXNode::get_binary(casadi_int op, const MX& y) const { |
| 789 | // If-else-zero nodes are always simplified at top level to avoid NaN propagation |
| 790 | if (y.op() == OP_IF_ELSE_ZERO) { |
| 791 | if (op == OP_MUL) { |
| 792 | // (Rule 1.) x * if_else_zero(c, y), simplified to if_else_zero(c, x * y) |
| 793 | // Background: x is often a partial derivative and may evaluate to INF or NAN. |
| 794 | // The simplification ensures that the zero seed corresponding to an inactive branch does |
| 795 | // not give rise to any NaN contribution to the derivative due to NaN * 0 == NaN. |
| 796 | return if_else_zero(y.dep(0), shared_from_this<MX>() * y.dep(1)); |
| 797 | } else if (op == OP_ADD && this->op() == OP_IF_ELSE_ZERO && MX::is_equal(dep(0), y.dep(0))) { |
| 798 | // (Rule 2.) if_else_zero(c, x) + if_else_zero(c, y) is simplified to if_else_zero(c, x + y) |
| 799 | // Background: During the backward propagation, seeds are added together. Without this rule, |
| 800 | // the addition node can prevent rule (1.) from working in subsequent steps. |
| 801 | return if_else_zero(y.dep(0), dep(1) + y.dep(1)); |
| 802 | } |
| 803 | } else if (this->op() == OP_IF_ELSE_ZERO && op == OP_MUL) { |
| 804 | // Same as Rule 1. above, but with factors swapped. For symmetry. |
| 805 | return if_else_zero(dep(0), dep(1) * y); |
| 806 | } |
| 807 | // Create binary node |
| 808 | if (sparsity().is_scalar(false)) { |
| 809 | if (nnz()==0) { |
| 810 | if (operation_checker<F0XChecker>(op)) return MX::zeros(Sparsity(y.size())); |
| 811 | return to_matrix(MX(0)->_get_binary(op, y, true, false), y.sparsity()); |
| 812 | } else { |
| 813 | return to_matrix(_get_binary(op, y, true, false), y.sparsity()); |
| 814 | } |
| 815 | } else if (y.is_scalar()) { |
| 816 | if (y.nnz()==0) { |
| 817 | if (operation_checker<FX0Checker>(op)) return MX::zeros(Sparsity(size())); |
| 818 | return to_matrix(_get_binary(op, MX(0), false, true), sparsity()); |
| 819 | } else { |
| 820 | return to_matrix(_get_binary(op, y, false, true), sparsity()); |
| 821 | } |
| 822 | } else { |
| 823 | casadi_assert(sparsity().size() == y.sparsity().size(), "Dimension mismatch."); |
| 824 | if (sparsity()==y.sparsity()) { |
| 825 | // Matching sparsities |
| 826 | return _get_binary(op, y, false, false); |
| 827 | } else { |
| 828 | // Get the sparsity pattern of the result |
| 829 | // (ignoring structural zeros giving rise to nonzero result) |
| 830 | const Sparsity& x_sp = sparsity(); |
| 831 | const Sparsity& y_sp = y.sparsity(); |
| 832 | Sparsity r_sp = x_sp.combine(y_sp, operation_checker<F0XChecker>(op), |
| 833 | operation_checker<FX0Checker>(op)); |
| 834 | |
| 835 | // Project the arguments to this sparsity |
| 836 | MX xx = project(shared_from_this<MX>(), r_sp); |
| 837 | MX yy = project(y, r_sp); |
| 838 | return xx->_get_binary(op, yy, false, false); |
| 839 | } |
| 840 | } |
| 841 | } |
| 842 | |
| 843 | MX MXNode::_get_binary(casadi_int op, const MX& y, bool scX, bool scY) const { |
| 844 | casadi_assert_dev(sparsity()==y.sparsity() || scX || scY); |
no test coverage detected