class_docstr: Combines `LinearOperators` into a blockwise lower-triangular matrix.
  
  This operator is initialized with a nested list of linear operators, which
  are combined into a new `LinearOperator` whose underlying matrix
  representation is square and has each operator on or below the main diagonal,
  and zero's elsewhere. Each element of the outer list is a list of
  `LinearOperators` corresponding to a row-partition of the blockwise structure.
  The number of `LinearOperator`s in row-partion `i` must be equal to `i`.
  
  For example, a blockwise `3 x 3` `LinearOperatorBlockLowerTriangular` is
  initialized with the list `[[op_00], [op_10, op_11], [op_20, op_21, op_22]]`,
  where the `op_ij`, `i < 3, j <= i`, are `LinearOperator` instances. The
  `LinearOperatorBlockLowerTriangular` behaves as the following blockwise
  matrix, where `0` represents appropriately-sized [batch] matrices of zeros:
  
  ```none
  [[op_00,     0,     0],
   [op_10, op_11,     0],
   [op_20, op_21, op_22]]
  ```
  
  Each `op_jj` on the diagonal is required to represent a square matrix, and
  hence will have shape `batch_shape_j + [M_j, M_j]`. `LinearOperator`s in row
  `j` of the blockwise structure must have `range_dimension` equal to that of
  `op_jj`, and `LinearOperators` in column `j` must have `domain_dimension`
  equal to that of `op_jj`.
  
  If each `op_jj` on the diagonal has shape `batch_shape_j + [M_j, M_j]`, then
  the combined operator has shape `broadcast_batch_shape + [sum M_j, sum M_j]`,
  where `broadcast_batch_shape` is the mutual broadcast of `batch_shape_j`,
  `j = 0, 1, ..., J`, assuming the intermediate batch shapes broadcast.
  Even if the combined shape is well defined, the combined operator's
  methods may fail due to lack of broadcasting ability in the defining
  operators' methods.
  
  For example, to create a 4 x 4 linear operator combined of three 2 x 2
  operators:
  >>> operator_0 = tf.linalg.LinearOperatorFullMatrix([[1., 2.], [3., 4.]])
  >>> operator_1 = tf.linalg.LinearOperatorFullMatrix([[1., 0.], [0., 1.]])
  >>> operator_2 = tf.linalg.LinearOperatorLowerTriangular([[5., 6.], [7., 8]])
  >>> operator = LinearOperatorBlockLowerTriangular(
  ...   [[operator_0], [operator_1, operator_2]])
  
  >>> operator.to_dense()
  <tf.Tensor: shape=(4, 4), dtype=float32, numpy=
  array([[1., 2., 0., 0.],
         [3., 4., 0., 0.],
         [1., 0., 5., 0.],
         [0., 1., 7., 8.]], dtype=float32)>
  
  >>> operator.shape
  TensorShape([4, 4])
  
  >>> operator.log_abs_determinant()
  <tf.Tensor: shape=(), dtype=float32, numpy=4.3820267>
  
  >>> x0 = [[1., 6.], [-3., 4.]]
  >>> x1 = [[0., 2.], [4., 0.]]
  >>> x = tf.concat([x0, x1], 0)  # Shape [2, 4] Tensor
  >>> operator.matmul(x)
  <tf.Tensor: shape=(4, 2), dtype=float32, numpy=
  array([[-5., 14.],
         [-9., 34.],
         [ 1., 16.],
         [29., 18.]], dtype=float32)>
  
  The above `matmul` is equivalent to:
  >>> tf.concat([operator_0.matmul(x0),
  ...   operator_1.matmul(x0) + operator_2.matmul(x1)], axis=0)
  <tf.Tensor: shape=(4, 2), dtype=float32, numpy=
  array([[-5., 14.],
         [-9., 34.],
         [ 1., 16.],
         [29., 18.]], dtype=float32)>
  
  #### Shape compatibility
  
  This operator acts on [batch] matrix with compatible shape.
  `x` is a batch matrix with compatible shape for `matmul` and `solve` if
  
  ```
  operator.shape = [B1,...,Bb] + [M, N],  with b >= 0
  x.shape =        [B1,...,Bb] + [N, R],  with R >= 0.
  ```
  
  For example:
  
  Create a [2, 3] batch of 4 x 4 linear operators:
  >>> matrix_44 = tf.random.normal(shape=[2, 3, 4, 4])
  >>> operator_44 = tf.linalg.LinearOperatorFullMatrix(matrix_44)
  
  Create a [1, 3] batch of 5 x 4 linear operators:
  >>> matrix_54 = tf.random.normal(shape=[1, 3, 5, 4])
  >>> operator_54 = tf.linalg.LinearOperatorFullMatrix(matrix_54)
  
  Create a [1, 3] batch of 5 x 5 linear operators:
  >>> matrix_55 = tf.random.normal(shape=[1, 3, 5, 5])
  >>> operator_55 = tf.linalg.LinearOperatorFullMatrix(matrix_55)
  
  Combine to create a [2, 3] batch of 9 x 9 operators:
  >>> operator_99 = LinearOperatorBlockLowerTriangular(
  ...   [[operator_44], [operator_54, operator_55]])
  >>> operator_99.shape
  TensorShape([2, 3, 9, 9])
  
  Create a shape [2, 1, 9] batch of vectors and apply the operator to it.
  >>> x = tf.random.normal(shape=[2, 1, 9])
  >>> y = operator_99.matvec(x)
  >>> y.shape
  TensorShape([2, 3, 9])
  
  Create a blockwise list of vectors and apply the operator to it. A blockwise
  list is returned.
  >>> x4 = tf.random.normal(shape=[2, 1, 4])
  >>> x5 = tf.random.normal(shape=[2, 3, 5])
  >>> y_blockwise = operator_99.matvec([x4, x5])
  >>> y_blockwise[0].shape
  TensorShape([2, 3, 4])
  >>> y_blockwise[1].shape
  TensorShape([2, 3, 5])
  
  #### Performance
  
  Suppose `operator` is a `LinearOperatorBlockLowerTriangular` consisting of `D`
  row-partitions and `D` column-partitions, such that the total number of
  operators is `N = D * (D + 1) // 2`.
  
  * `operator.matmul` has complexity equal to the sum of the `matmul`
    complexities of the individual operators.
  * `operator.solve` has complexity equal to the sum of the `solve` complexities
    of the operators on the diagonal and the `matmul` complexities of the
    operators off the diagonal.
  * `operator.determinant` has complexity equal to the sum of the `determinant`
    complexities of the operators on the diagonal.
  
  #### Matrix property hints
  
  This `LinearOperator` is initialized with boolean flags of the form `is_X`,
  for `X = non_singular, self_adjoint, positive_definite, square`.
  These have the following meaning:
  
  * If `is_X == True`, callers should expect the operator to have the
    property `X`.  This is a promise that should be fulfilled, but is *not* a
    runtime assert.  For example, finite floating point precision may result
    in these promises being violated.
  * If `is_X == False`, callers should expect the operator to not have `X`.
  * If `is_X == None` (the default), callers should have no expectation either
    way.
class_docstr_tok: ["Combines","`","LinearOperators","`","into","a","blockwise","lower-triangular","matrix",".","This","operator","is","initialized","with","a","nested","list","of","linear","operators",",","which","are","combined","into","a","new","`","LinearOperator","`","whose","underlying","matrix","representation","is","square","and","has","each","operator","on","or","below","the","main","diagonal",",","and","zero","'s","elsewhere",".","Each","element","of","the","outer","list","is","a","list","of","`","LinearOperators","`","corresponding","to","a","row-partition","of","the","blockwise","structure",".","The","number","of","`","LinearOperator","`","s","in","row-partion","`","i","`","must","be","equal","to","`","i","`",".","For","example",",","a","blockwise","`","3","x","3","`","`","LinearOperatorBlockLowerTriangular","`","is","initialized","with","the","list","`","[","[","op_00","]",",","[","op_10",",","op_11","]",",","[","op_20",",","op_21",",","op_22","]","]","`",",","where","the","`","op_ij","`",",","`","i","<","3",",","j","<","=","i","`",",","are","`","LinearOperator","`","instances",".","The","`","LinearOperatorBlockLowerTriangular","`","behaves","as","the","following","blockwise","matrix",",","where","`","0","`","represents","appropriately-sized","[","batch","]","matrices","of","zeros",":","``","`","none","[","[","op_00",",","0",",","0","]",",","[","op_10",",","op_11",",","0","]",",","[","op_20",",","op_21",",","op_22","]","]","``","`","Each","`","op_jj","`","on","the","diagonal","is","required","to","represent","a","square","matrix",",","and","hence","will","have","shape","`","batch_shape_j","+","[","M_j",",","M_j","]","`",".","`","LinearOperator","`","s","in","row","`","j","`","of","the","blockwise","structure","must","have","`","range_dimension","`","equal","to","that","of","`","op_jj","`",",","and","`","LinearOperators","`","in","column","`","j","`","must","have","`","domain_dimension","`","equal","to","that","of","`","op_jj","`",".","If","each","`","op_jj","`","on","the","diagonal","has","shape","`","batch_shape_j","+","[","M_j",",","M_j","]","`",",","then","the","combined","operator","has","shape","`","broadcast_batch_shape","+","[","sum","M_j",",","sum","M_j","]","`",",","where","`","broadcast_batch_shape","`","is","the","mutual","broadcast","of","`","batch_shape_j","`",",","`","j","=","0",",","1",",","...",",","J","`",",","assuming","the","intermediate","batch","shapes","broadcast",".","Even","if","the","combined","shape","is","well","defined",",","the","combined","operator's","methods","may","fail","due","to","lack","of","broadcasting","ability","in","the","defining","operators","'","methods",".","For","example",",","to","create","a","4","x","4","linear","operator","combined","of","three","2","x","2","operators",":",">",">",">","operator_0","=","tf.linalg.LinearOperatorFullMatrix","(","[","[","1.",",","2",".","]",",","[","3.",",","4",".","]","]",")",">",">",">","operator_1","=","tf.linalg.LinearOperatorFullMatrix","(","[","[","1.",",","0",".","]",",","[","0.",",","1",".","]","]",")",">",">",">","operator_2","=","tf.linalg.LinearOperatorLowerTriangular","(","[","[","5.",",","6",".","]",",","[","7.",",","8","]","]",")",">",">",">","operator","=","LinearOperatorBlockLowerTriangular","(","...","[","[","operator_0","]",",","[","operator_1",",","operator_2","]","]",")",">",">",">","operator.to_dense","(",")","<","tf.Tensor",":","shape=","(","4",",","4",")",",","dtype=float32",",","numpy=","array","(","[","[","1.",",","2.",",","0.",",","0",".","]",",","[","3.",",","4.",",","0.",",","0",".","]",",","[","1.",",","0.",",","5.",",","0",".","]",",","[","0.",",","1.",",","7.",",","8",".","]","]",",","dtype=float32",")",">",">",">",">","operator.shape","TensorShape","(","[","4",",","4","]",")",">",">",">","operator.log_abs_determinant","(",")","<","tf.Tensor",":","shape=","(",")",",","dtype=float32",",","numpy=4.3820267",">",">",">",">","x0","=","[","[","1.",",","6",".","]",",","[","-3.",",","4",".","]","]",">",">",">","x1","=","[","[","0.",",","2",".","]",",","[","4.",",","0",".","]","]",">",">",">","x","=","tf.concat","(","[","x0",",","x1","]",",","0",")","#","Shape","[","2",",","4","]","Tensor",">",">",">","operator.matmul","(","x",")","<","tf.Tensor",":","shape=","(","4",",","2",")",",","dtype=float32",",","numpy=","array","(","[","[","-5.",",","14",".","]",",","[","-9.",",","34",".","]",",","[","1.",",","16",".","]",",","[","29.",",","18",".","]","]",",","dtype=float32",")",">","The","above","`","matmul","`","is","equivalent","to",":",">",">",">","tf.concat","(","[","operator_0.matmul","(","x0",")",",","...","operator_1.matmul","(","x0",")","+","operator_2.matmul","(","x1",")","]",",","axis=0",")","<","tf.Tensor",":","shape=","(","4",",","2",")",",","dtype=float32",",","numpy=","array","(","[","[","-5.",",","14",".","]",",","[","-9.",",","34",".","]",",","[","1.",",","16",".","]",",","[","29.",",","18",".","]","]",",","dtype=float32",")",">","#","#","#","#","Shape","compatibility","This","operator","acts","on","[","batch","]","matrix","with","compatible","shape",".","`","x","`","is","a","batch","matrix","with","compatible","shape","for","`","matmul","`","and","`","solve","`","if","``","`","operator.shape","=","[","B1",",","...",",","Bb","]","+","[","M",",","N","]",",","with","b",">","=","0","x.shape","=","[","B1",",","...",",","Bb","]","+","[","N",",","R","]",",","with","R",">","=","0",".","``","`","For","example",":","Create","a","[","2",",","3","]","batch","of","4","x","4","linear","operators",":",">",">",">","matrix_44","=","tf.random.normal","(","shape=","[","2",",","3",",","4",",","4","]",")",">",">",">","operator_44","=","tf.linalg.LinearOperatorFullMatrix","(","matrix_44",")","Create","a","[","1",",","3","]","batch","of","5","x","4","linear","operators",":",">",">",">","matrix_54","=","tf.random.normal","(","shape=","[","1",",","3",",","5",",","4","]",")",">",">",">","operator_54","=","tf.linalg.LinearOperatorFullMatrix","(","matrix_54",")","Create","a","[","1",",","3","]","batch","of","5","x","5","linear","operators",":",">",">",">","matrix_55","=","tf.random.normal","(","shape=","[","1",",","3",",","5",",","5","]",")",">",">",">","operator_55","=","tf.linalg.LinearOperatorFullMatrix","(","matrix_55",")","Combine","to","create","a","[","2",",","3","]","batch","of","9","x","9","operators",":",">",">",">","operator_99","=","LinearOperatorBlockLowerTriangular","(","...","[","[","operator_44","]",",","[","operator_54",",","operator_55","]","]",")",">",">",">","operator_99.shape","TensorShape","(","[","2",",","3",",","9",",","9","]",")","Create","a","shape","[","2",",","1",",","9","]","batch","of","vectors","and","apply","the","operator","to","it",".",">",">",">","x","=","tf.random.normal","(","shape=","[","2",",","1",",","9","]",")",">",">",">","y","=","operator_99.matvec","(","x",")",">",">",">","y.shape","TensorShape","(","[","2",",","3",",","9","]",")","Create","a","blockwise","list","of","vectors","and","apply","the","operator","to","it",".","A","blockwise","list","is","returned",".",">",">",">","x4","=","tf.random.normal","(","shape=","[","2",",","1",",","4","]",")",">",">",">","x5","=","tf.random.normal","(","shape=","[","2",",","3",",","5","]",")",">",">",">","y_blockwise","=","operator_99.matvec","(","[","x4",",","x5","]",")",">",">",">","y_blockwise","[","0","]",".shape","TensorShape","(","[","2",",","3",",","4","]",")",">",">",">","y_blockwise","[","1","]",".shape","TensorShape","(","[","2",",","3",",","5","]",")","#","#","#","#","Performance","Suppose","`","operator","`","is","a","`","LinearOperatorBlockLowerTriangular","`","consisting","of","`","D","`","row-partitions","and","`","D","`","column-partitions",",","such","that","the","total","number","of","operators","is","`","N","=","D","*","(","D","+","1",")","\/\/","2","`",".","*","`","operator.matmul","`","has","complexity","equal","to","the","sum","of","the","`","matmul","`","complexities","of","the","individual","operators",".","*","`","operator.solve","`","has","complexity","equal","to","the","sum","of","the","`","solve","`","complexities","of","the","operators","on","the","diagonal","and","the","`","matmul","`","complexities","of","the","operators","off","the","diagonal",".","*","`","operator.determinant","`","has","complexity","equal","to","the","sum","of","the","`","determinant","`","complexities","of","the","operators","on","the","diagonal",".","#","#","#","#","Matrix","property","hints","This","`","LinearOperator","`","is","initialized","with","boolean","flags","of","the","form","`","is_X","`",",","for","`","X","=","non_singular",",","self_adjoint",",","positive_definite",",","square","`",".","These","have","the","following","meaning",":","*","If","`","is_X","==","True","`",",","callers","should","expect","the","operator","to","have","the","property","`","X","`",".","This","is","a","promise","that","should","be","fulfilled",",","but","is","*","not","*","a","runtime","assert",".","For","example",",","finite","floating","point","precision","may","result","in","these","promises","being","violated",".","*","If","`","is_X","==","False","`",",","callers","should","expect","the","operator","to","not","have","`","X","`",".","*","If","`","is_X","==","None","`","(","the","default",")",",","callers","should","have","no","expectation","either","way","."]
docstr: Solve (exact or approx) `R` (batch) systems of equations: `A X = rhs`.
  
  The returned `Tensor` will be close to an exact solution if `A` is well
  conditioned. Otherwise closeness will vary. See class docstring for details.
  
  Given the blockwise `n + 1`-by-`n + 1` linear operator:
  
  op = [[A_00     0  ...     0  ...    0],
        [A_10  A_11  ...     0  ...    0],
        ...
        [A_k0  A_k1  ...  A_kk  ...    0],
        ...
        [A_n0  A_n1  ...  A_nk  ... A_nn]]
  
  we find `x = op.solve(y)` by observing that
  
  `y_k = A_k0.matmul(x_0) + A_k1.matmul(x_1) + ... + A_kk.matmul(x_k)`
  
  and therefore
  
  `x_k = A_kk.solve(y_k -
                    A_k0.matmul(x_0) - ... - A_k(k-1).matmul(x_(k-1)))`
  
  where `x_k` and `y_k` are the `k`th blocks obtained by decomposing `x`
  and `y` along their appropriate axes.
  
  We first solve `x_0 = A_00.solve(y_0)`. Proceeding inductively, we solve
  for `x_k`, `k = 1..n`, given `x_0..x_(k-1)`.
  
  The adjoint case is solved similarly, beginning with
  `x_n = A_nn.solve(y_n, adjoint=True)` and proceeding backwards.
  
  Examples:
  
  ```python
  # Make an operator acting like batch matrix A.  Assume A.shape = [..., M, N]
  operator = LinearOperator(...)
  operator.shape = [..., M, N]
  
  # Solve R > 0 linear systems for every member of the batch.
  RHS = ... # shape [..., M, R]
  
  X = operator.solve(RHS)
  # X[..., :, r] is the solution to the r'th linear system
  # sum_j A[..., :, j] X[..., j, r] = RHS[..., :, r]
  
  operator.matmul(X)
  ==> RHS
  ```
  
  Args:
    rhs: `Tensor` with same `dtype` as this operator and compatible shape,
      or a list of `Tensor`s. `Tensor`s are treated like a [batch] matrices
      meaning for every set of leading dimensions, the last two dimensions
      defines a matrix.
      See class docstring for definition of compatibility.
    adjoint: Python `bool`.  If `True`, solve the system involving the adjoint
      of this `LinearOperator`:  `A^H X = rhs`.
    adjoint_arg:  Python `bool`.  If `True`, solve `A X = rhs^H` where `rhs^H`
      is the hermitian transpose (transposition and complex conjugation).
    name:  A name scope to use for ops added by this method.
  
  Returns:
    `Tensor` with shape `[...,N, R]` and same `dtype` as `rhs`.
  
  Raises:
    NotImplementedError:  If `self.is_non_singular` or `is_square` is False.
docstr_tok: ["Solve","(","exact","or","approx",")","`","R","`","(","batch",")","systems","of","equations",":","`","A","X","=","rhs","`",".","The","returned","`","Tensor","`","will","be","close","to","an","exact","solution","if","`","A","`","is","well","conditioned",".","Otherwise","closeness","will","vary",".","See","class","docstring","for","details",".","Given","the","blockwise","`","n","+","1","`","-by-","`","n","+","1","`","linear","operator",":","op","=","[","[","A_00","0","...","0","...","0","]",",","[","A_10","A_11","...","0","...","0","]",",","...","[","A_k0","A_k1","...","A_kk","...","0","]",",","...","[","A_n0","A_n1","...","A_nk","...","A_nn","]","]","we","find","`","x","=","op.solve","(","y",")","`","by","observing","that","`","y_k","=","A_k0.matmul","(","x_0",")","+","A_k1.matmul","(","x_1",")","+","...","+","A_kk.matmul","(","x_k",")","`","and","therefore","`","x_k","=","A_kk.solve","(","y_k","-","A_k0.matmul","(","x_0",")","-","...","-","A_k","(","k-1",")",".matmul","(","x_","(","k-1",")",")",")","`","where","`","x_k","`","and","`","y_k","`","are","the","`","k","`","th","blocks","obtained","by","decomposing","`","x","`","and","`","y","`","along","their","appropriate","axes",".","We","first","solve","`","x_0","=","A_00.solve","(","y_0",")","`",".","Proceeding","inductively",",","we","solve","for","`","x_k","`",",","`","k","=","1","..","n","`",",","given","`","x_0","..","x_","(","k-1",")","`",".","The","adjoint","case","is","solved","similarly",",","beginning","with","`","x_n","=","A_nn.solve","(","y_n",",","adjoint=True",")","`","and","proceeding","backwards",".","Examples",":","``","`","python","#","Make","an","operator","acting","like","batch","matrix","A",".","Assume","A.shape","=","[","...",",","M",",","N","]","operator","=","LinearOperator","(","...",")","operator.shape","=","[","...",",","M",",","N","]","#","Solve","R",">","0","linear","systems","for","every","member","of","the","batch",".","RHS","=","...","#","shape","[","...",",","M",",","R","]","X","=","operator.solve","(","RHS",")","#","X","[","...",",",":",",","r","]","is","the","solution","to","the","r'th","linear","system","#","sum_j","A","[","...",",",":",",","j","]","X","[","...",",","j",",","r","]","=","RHS","[","...",",",":",",","r","]","operator.matmul","(","X",")","==",">","RHS","``","`","Args",":","rhs",":","`","Tensor","`","with","same","`","dtype","`","as","this","operator","and","compatible","shape",",","or","a","list","of","`","Tensor","`","s",".","`","Tensor","`","s","are","treated","like","a","[","batch","]","matrices","meaning","for","every","set","of","leading","dimensions",",","the","last","two","dimensions","defines","a","matrix",".","See","class","docstring","for","definition","of","compatibility",".","adjoint",":","Python","`","bool","`",".","If","`","True","`",",","solve","the","system","involving","the","adjoint","of","this","`","LinearOperator","`",":","`","A^H","X","=","rhs","`",".","adjoint_arg",":","Python","`","bool","`",".","If","`","True","`",",","solve","`","A","X","=","rhs^H","`","where","`","rhs^H","`","is","the","hermitian","transpose","(","transposition","and","complex","conjugation",")",".","name",":","A","name","scope","to","use","for","ops","added","by","this","method",".","Returns",":","`","Tensor","`","with","shape","`","[","...",",","N",",","R","]","`","and","same","`","dtype","`","as","`","rhs","`",".","Raises",":","NotImplementedError",":","If","`","self.is_non_singular","`","or","`","is_square","`","is","False","."]
code: def solve(self, rhs, adjoint=False, adjoint_arg=False, name="solve"):
      """Solve (exact or approx) `R` (batch) systems of equations: `A X = rhs`.
  
      The returned `Tensor` will be close to an exact solution if `A` is well
      conditioned. Otherwise closeness will vary. See class docstring for details.
  
      Given the blockwise `n + 1`-by-`n + 1` linear operator:
  
      op = [[A_00     0  ...     0  ...    0],
            [A_10  A_11  ...     0  ...    0],
            ...
            [A_k0  A_k1  ...  A_kk  ...    0],
            ...
            [A_n0  A_n1  ...  A_nk  ... A_nn]]
  
      we find `x = op.solve(y)` by observing that
  
      `y_k = A_k0.matmul(x_0) + A_k1.matmul(x_1) + ... + A_kk.matmul(x_k)`
  
      and therefore
  
      `x_k = A_kk.solve(y_k -
                        A_k0.matmul(x_0) - ... - A_k(k-1).matmul(x_(k-1)))`
  
      where `x_k` and `y_k` are the `k`th blocks obtained by decomposing `x`
      and `y` along their appropriate axes.
  
      We first solve `x_0 = A_00.solve(y_0)`. Proceeding inductively, we solve
      for `x_k`, `k = 1..n`, given `x_0..x_(k-1)`.
  
      The adjoint case is solved similarly, beginning with
      `x_n = A_nn.solve(y_n, adjoint=True)` and proceeding backwards.
  
      Examples:
  
      ```python
      # Make an operator acting like batch matrix A.  Assume A.shape = [..., M, N]
      operator = LinearOperator(...)
      operator.shape = [..., M, N]
  
      # Solve R > 0 linear systems for every member of the batch.
      RHS = ... # shape [..., M, R]
  
      X = operator.solve(RHS)
      # X[..., :, r] is the solution to the r'th linear system
      # sum_j A[..., :, j] X[..., j, r] = RHS[..., :, r]
  
      operator.matmul(X)
      ==> RHS
      ```
  
      Args:
        rhs: `Tensor` with same `dtype` as this operator and compatible shape,
          or a list of `Tensor`s. `Tensor`s are treated like a [batch] matrices
          meaning for every set of leading dimensions, the last two dimensions
          defines a matrix.
          See class docstring for definition of compatibility.
        adjoint: Python `bool`.  If `True`, solve the system involving the adjoint
          of this `LinearOperator`:  `A^H X = rhs`.
        adjoint_arg:  Python `bool`.  If `True`, solve `A X = rhs^H` where `rhs^H`
          is the hermitian transpose (transposition and complex conjugation).
        name:  A name scope to use for ops added by this method.
  
      Returns:
        `Tensor` with shape `[...,N, R]` and same `dtype` as `rhs`.
  
      Raises:
        NotImplementedError:  If `self.is_non_singular` or `is_square` is False.
      """
      if self.is_non_singular is False:
          raise NotImplementedError(
              "Exact solve not implemented for an operator that is expected to "
              "be singular."
          )
      if self.is_square is False:
          raise NotImplementedError(
              "Exact solve not implemented for an operator that is expected to "
              "not be square."
          )
      if isinstance(rhs, linear_operator.LinearOperator):
          left_operator = self.adjoint() if adjoint else self
          right_operator = rhs.adjoint() if adjoint_arg else rhs
  
          if (
              right_operator.range_dimension is not None
              and left_operator.domain_dimension is not None
              and right_operator.range_dimension
              != left_operator.domain_dimension
          ):
              raise ValueError(
                  "Operators are incompatible. Expected `rhs` to have dimension"
                  " {} but got {}.".format(
                      left_operator.domain_dimension,
                      right_operator.range_dimension,
                  )
              )
          with self._name_scope(name):  # pylint: disable=not-callable
              return self._linop_solve(left_operator, right_operator)
  
      with self._name_scope(name):  # pylint: disable=not-callable
          block_dimensions = (
              self._block_domain_dimensions()
              if adjoint
              else self._block_range_dimensions()
          )
          arg_dim = -1 if adjoint_arg else -2
          blockwise_arg = linear_operator_util.arg_is_blockwise(
              block_dimensions, rhs, arg_dim
          )
          if blockwise_arg:
              for i, block in enumerate(rhs):
                  if not isinstance(
                      block, linear_operator.LinearOperator
                  ):
                      block = tensor_conversion.convert_to_tensor_v2_with_dispatch(
                          block
                      )
                      self._check_input_dtype(block)
                      block_dimensions[i].assert_is_compatible_with(
                          block.shape[arg_dim]
                      )
                      rhs[i] = block
              if adjoint_arg:
                  split_rhs = [linalg.adjoint(y) for y in rhs]
              else:
                  split_rhs = rhs
  
          else:
              rhs = (
                  tensor_conversion.convert_to_tensor_v2_with_dispatch(
                      rhs, name="rhs"
                  )
              )
              self._check_input_dtype(rhs)
              op_dimension = (
                  self.domain_dimension
                  if adjoint
                  else self.range_dimension
              )
              op_dimension.assert_is_compatible_with(rhs.shape[arg_dim])
  
              rhs = linalg.adjoint(rhs) if adjoint_arg else rhs
              split_rhs = linear_operator_util.split_arg_into_blocks(
                  self._block_domain_dimensions(),
                  self._block_domain_dimension_tensors,
                  rhs,
                  axis=-2,
              )
  
          solution_list = []
          if adjoint:
              # For an adjoint blockwise lower-triangular linear operator, the system
              # must be solved bottom to top. Iterate backwards over rows of the
              # adjoint (i.e. columns of the non-adjoint operator).
              for index in reversed(range(len(self.operators))):
                  y = split_rhs[index]
                  # Iterate top to bottom over the operators in the off-diagonal portion
                  # of the column-partition (i.e. row-partition of the adjoint), apply
                  # the operator to the respective block of the solution found in
                  # previous iterations, and subtract the result from the `rhs` block.
                  # For example,let `A`, `B`, and `D` be the linear operators in the top
                  # row-partition of the adjoint of
                  # `LinearOperatorBlockLowerTriangular([[A], [B, C], [D, E, F]])`,
                  # and `x_1` and `x_2` be blocks of the solution found in previous
                  # iterations of the outer loop. The following loop (when `index == 0`)
                  # expresses
                  # `Ax_0 + Bx_1 + Dx_2 = y_0` as `Ax_0 = y_0*`, where
                  # `y_0* = y_0 - Bx_1 - Dx_2`.
                  for j in reversed(
                      range(index + 1, len(self.operators))
                  ):
                      y = y - self.operators[j][index].matmul(
                          solution_list[len(self.operators) - 1 - j],
                          adjoint=adjoint,
                      )
                  # Continuing the example above, solve `Ax_0 = y_0*` for `x_0`.
                  solution_list.append(
                      self._diagonal_operators[index].solve(
                          y, adjoint=adjoint
                      )
                  )
              solution_list.reverse()
          else:
              # Iterate top to bottom over the row-partitions.
              for row, y in zip(self.operators, split_rhs):
                  # Iterate left to right over the operators in the off-diagonal portion
                  # of the row-partition, apply the operator to the block of the
                  # solution found in previous iterations, and subtract the result from
                  # the `rhs` block. For example, let `D`, `E`, and `F` be the linear
                  # operators in the bottom row-partition of
                  # `LinearOperatorBlockLowerTriangular([[A], [B, C], [D, E, F]])` and
                  # `x_0` and `x_1` be blocks of the solution found in previous
                  # iterations of the outer loop. The following loop
                  # (when `index == 2`), expresses
                  # `Dx_0 + Ex_1 + Fx_2 = y_2` as `Fx_2 = y_2*`, where
                  # `y_2* = y_2 - D_x0 - Ex_1`.
                  for i, operator in enumerate(row[:-1]):
                      y = y - operator.matmul(
                          solution_list[i], adjoint=adjoint
                      )
                  # Continuing the example above, solve `Fx_2 = y_2*` for `x_2`.
                  solution_list.append(
                      row[-1].solve(y, adjoint=adjoint)
                  )
  
          if blockwise_arg:
              return solution_list
  
          solution_list = (
              linear_operator_util.broadcast_matrix_batch_dims(
                  solution_list
              )
          )
          return array_ops.concat(solution_list, axis=-2)
  
imports: from tensorflow.python.framework import common_shapes
  from tensorflow.python.framework import dtypes
  from tensorflow.python.framework import ops
  from tensorflow.python.framework import tensor_conversion
  from tensorflow.python.framework import tensor_shape
  from tensorflow.python.ops import array_ops
  from tensorflow.python.ops import array_ops_stack
  from tensorflow.python.ops import check_ops
  from tensorflow.python.ops import control_flow_ops
  from tensorflow.python.ops import math_ops
  from tensorflow.python.ops.linalg import linalg_impl
  from tensorflow.python.ops.linalg import linear_operator
  from tensorflow.python.ops.linalg import linear_operator_addition
  from tensorflow.python.ops.linalg import linear_operator_full_matrix
  from tensorflow.python.ops.linalg import linear_operator_identity
  from tensorflow.python.ops.linalg import linear_operator_util
  from tensorflow.python.util import nest
  from tensorflow.python.util.tf_export import tf_export
label_desc: Use image node_id 20 for calling the LinearOperatorBlockLowerTriangular obj's underlying member method code with example usage: obj.solve(rhs, adjoint, adjoint_arg, name) and returns: array_ops, self, solution_list