def test_lu_solve_flexible_signature_with_vectors(self, arrays):
        m_ss = arrays[0]
        v_s, v_b = hn.core_only(*arrays[1:], dims=1)
        hy.assume(len(v_s) != len(v_b))
        hy.assume(hn.all_well_behaved(m_ss))

        # with self.subTest('lu_solve'):
        _, x_f, i_p = gfl.solve_lu(m_ss, v_s)
        self.assertArrayShape(gfl.lu_solve(x_f, i_p, v_s), m_ss.shape[:-1])
        with self.assertRaisesRegex(*utn.core_dim_err):
            gfl.lu_solve(x_f, i_p, v_b)
        # with self.subTest('rlu_solve'):
        self.assertArrayShape(gfl.rlu_solve(v_s, x_f, i_p), m_ss.shape[:-1])
        with self.assertRaisesRegex(*utn.core_dim_err):
            gfl.rlu_solve(v_b, x_f, i_p)
    def test_rlu_solve_flexible_signature_with_vectors(self, arrays):
        m_ss, m_sb = arrays[:-2]
        v_s, v_b = hn.core_only(*arrays[-2:], dims=1)
        hy.assume(hn.nonsquare(m_sb))
        hy.assume(hn.all_well_behaved(m_ss))
        off_b, y_one = utn.make_off_by_one(m_ss, m_sb)

        # with self.subTest('rlu_solve'):
        _, x_f, i_p = gfl.rsolve_lu(v_s, m_ss)
        self.assertArrayShape(gfl.rlu_solve(v_s, x_f, i_p), m_ss.shape[:-1])
        with self.assertRaisesRegex(*utn.core_dim_err):
            gfl.rlu_solve(v_b, x_f, i_p)
        with self.assertRaisesRegex(*utn.core_dim_err):
            # This would succeed/broadcast error if interpreted as vM:
            gfl.rlu_solve(m_sb[y_one], x_f[off_b], i_p[off_b])
        self.assertArrayShape(gfl.lu_solve(x_f, i_p, v_s), m_ss.shape[:-1])
        with self.assertRaisesRegex(*utn.core_dim_err):
            gfl.lu_solve(x_f, i_p, v_b)
    def test_rlu_solve_returns_expected_shapes(self, arrays):
        m_ss, m_sb, m_bb, m_bs = arrays
        hy.assume(hn.nonsquare(m_sb))
        hy.assume(hn.all_well_behaved(m_bb))

        _, x_f, i_p = gfl.rsolve_lu(m_sb, m_bb)
        expect = utn.array_return_shape('(a,b),(b,b)->(a,b)', m_sb, x_f)
        self.assertArrayShape(gfl.rlu_solve(m_sb, x_f, i_p), expect)
        expect = utn.array_return_shape('(a,a),(a,b)->(a,b)', x_f, m_bs)
        self.assertArrayShape(gfl.lu_solve(x_f, i_p, m_bs), expect)
        with self.assertRaisesRegex(*utn.core_dim_err):
            gfl.rlu_solve(m_ss, x_f, i_p)
        with self.assertRaisesRegex(*utn.core_dim_err):
            gfl.lu_solve(x_f, i_p, m_sb)
        _, x_f, i_p = gfl.rsolve_lu(m_sb, m_bb)
        with self.assertRaisesRegex(*utn.broadcast_err):
            gfl.rlu_solve(*utn.make_bad_broadcast(m_sb, x_f), i_p)
        x_f, i_p = unbroadcast_factors(m_bb, x_f, i_p)
        expect = utn.array_return_shape('(a,a),(a,b)->(a,b)', m_bb, m_bs)
        self.assertArrayShape(gfl.lu_solve(x_f, i_p, m_bs), expect)
    def test_rsolve_lu_returns_expected_values(self, arrays):
        m_ss, m_sb, m_bs = arrays
        hy.assume(hn.all_well_behaved(m_ss))
        cond = np.linalg.cond(m_ss).max()

        x0_bs = gfl.rsolve(m_bs, m_ss)
        x_bs, x_f, i_p = gfl.rsolve_lu(m_bs, m_ss)
        # with self.subTest('rsolve0'):
        self.assertArrayAllClose(x_bs, x0_bs, cond=cond)
        xx_bs = gfl.rlu_solve(m_bs, x_f, i_p)
        # with self.subTest('rsolve(rlu)'):
        self.assertArrayAllClose(xx_bs, x0_bs, cond=cond)
        x_sb = gfl.lu_solve(x_f, i_p, m_sb)
        # with self.subTest('solve(rlu)'):
        self.assertArrayAllClose(m_ss @ x_sb, m_sb, cond=cond)