Computes a Householder reflection of the form: H = I - tau v v.T. such that H . ( x1 ) = ( x1 ) ( x2 ) = ( x2 ) ( ... ) = ( ... ) ( xk ) = ( beta ) ( ... ) ( 0 ) ( ... ) ( 0 ) Unlike the usual formulation, we allow the caller to supply 'k' rather than only providing the relevant part of 'x' to maintain XLA's static shape invariant. In addition, the implementation supports batchin
| 73 | // TODO(phawkins): LAPACK's xLARFG implementation has code for handling |
| 74 | // overflows in the norm/beta calculations. Perhaps do the same here. |
| 75 | Status House(XlaOp x, XlaOp k, absl::Span<const int64> batch_dims, |
| 76 | const int64 m, XlaOp* v, XlaOp* tau, XlaOp* beta) { |
| 77 | XlaBuilder* const builder = x.builder(); |
| 78 | TF_ASSIGN_OR_RETURN(Shape x_shape, builder->GetShape(x)); |
| 79 | const PrimitiveType type = x_shape.element_type(); |
| 80 | |
| 81 | std::vector<int64> batch_dim_ids(batch_dims.size()); |
| 82 | std::iota(batch_dim_ids.begin(), batch_dim_ids.end(), 0); |
| 83 | const int64 minor_dim = batch_dims.size(); |
| 84 | |
| 85 | XlaOp zero = ScalarLike(x, 0.0); |
| 86 | XlaOp one = ScalarLike(x, 1.0); |
| 87 | |
| 88 | // alpha = x[k] |
| 89 | XlaOp alpha = Reshape(DynamicSliceInMinorDims(x, {k}, {1}), batch_dims); |
| 90 | |
| 91 | // Compute x[k+1:] (padded with zeros in elements 0..k) |
| 92 | XlaOp iota = Iota(builder, S32, m); |
| 93 | XlaOp x_after_k = Mul(x, ConvertElementType(Gt(iota, k), type), |
| 94 | /*broadcast_dimensions=*/{minor_dim}); |
| 95 | |
| 96 | // sigma = np.dot(x[k+1:], x[k+1:]) |
| 97 | auto sigma = Reduce(x_after_k * x_after_k, zero, |
| 98 | CreateScalarAddComputation(type, builder), {minor_dim}); |
| 99 | // mu = np.sqrt(x[k]*x[k] + sigma) |
| 100 | auto mu = Sqrt(Square(alpha) + sigma); |
| 101 | |
| 102 | auto sigma_is_zero = Eq(sigma, zero); |
| 103 | |
| 104 | *beta = Select(sigma_is_zero, alpha, Select(Lt(alpha, zero), one, -one) * mu); |
| 105 | *tau = Select(sigma_is_zero, Broadcast(zero, batch_dims), |
| 106 | (*beta - alpha) / *beta); |
| 107 | auto divisor = |
| 108 | Select(sigma_is_zero, Broadcast(one, batch_dims), alpha - *beta); |
| 109 | |
| 110 | auto e_k = Broadcast(ConvertElementType(Eq(iota, k), type), |
| 111 | std::vector<int64>(batch_dims.size(), 1)); |
| 112 | |
| 113 | // Form v as [0, 0, ..., 1] ++ x[k+1:] / divisor |
| 114 | // If sigma is zero, x[k+1:] is zero, so use any non-zero divisor. |
| 115 | *v = e_k + Div(x_after_k, divisor, /*broadcast_dimensions=*/batch_dim_ids); |
| 116 | return Status::OK(); |
| 117 | } |
| 118 | |
| 119 | // Householder QR decomposition. Algorithm 5.2.1 from Golub and Van |
| 120 | // Loan "Matrix Computations", 4th Edition. This is an unblocked implementation |
no test coverage detected