File: factorization.m

package info (click to toggle)
suitesparse 1%3A5.12.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 176,720 kB
  • sloc: ansic: 1,193,914; cpp: 31,704; makefile: 6,638; fortran: 1,927; java: 1,826; csh: 765; ruby: 725; sh: 529; python: 333; perl: 225; sed: 164; awk: 35
file content (650 lines) | stat: -rw-r--r-- 26,728 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
classdef factorization
%FACTORIZATION a generic matrix factorization object
% Normally, this object is created via the F=factorize(A) function.  Users
% do not need to use this method directly.
%
% This is an abstract class that is specialized into 13 different kinds of
% matrix factorizations:
%
%   factorization_chol_dense    dense Cholesky      A = R'*R
%   factorization_lu_dense      dense LU            A(p,:) = L*U
%   factorization_qr_dense      dense QR of A       A = Q*R
%   factorization_qrt_dense     dense QR of A'      A' = Q*R
%   factorization_ldl_dense     dense LDL           A(p,p) = L*D*L'
%   factorization_cod_dense     dense COD           A = U*R*V'
%
%   factorization_chol_sparse   sparse Cholesky     P'*A*P = L*L'
%   factorization_lu_sparse     sparse LU           P*(R\A)*Q = L*U
%   factorization_qr_sparse     sparse QR of A      (A*P)'*(A*P) = R'*R
%   factorization_qrt_sparse    sparse QR of A'     (P*A)*(P*A)' = R'*R
%   factorization_ldl_sparse    sparse LDL          P'*A*P = L*D*L'
%   factorization_cod_sparse    sparse COD          A = U*R*V'
%
%   factorization_svd           SVD                 A = U*S*V'
%
% The abstract class provides the following functions.  In the descriptions,
% F is a factorization.  The arguments b, y, and z may be factorizations or
% matrices.  The output x is normally matrix unless it can be represented as a
% scaled factorization.  For example, G=F\2 and G=inverse(F)*2 both return a
% factorization G.  Below, s is always a scalar, and C is always a matrix.
%
%   These methods return a matrix x, unless one argument is a scalar (in which
%   case they return a scaled factorization object):
%   x = mldivide (F, b)     x = A \ b
%   x = mrdivide (b, F)     x = b / A
%   x = mtimes (y, z)       y * z
%
%   These methods always return a factorization:
%   F = uplus (F)           +F
%   F = uminus (F)          -F
%   F = inverse (F)         representation of inv(A), without computing it
%   F = ctranspose (F)      F'
%
%   These built-in methods return a scalar:
%   s = isreal (F)
%   s = isempty (F)
%   s = isscalar (F)
%   s = issingle (F)
%   s = isnumeric (F)
%   s = isfloat (F)
%   s = isvector (F)
%   s = issparse (F)
%   s = isfield (F,f)
%   s = isa (F, s)
%   s = condest (F)
%
%   This method returns the estimated rank from the factorization.
%   s = rankest (F)
%
%   These methods support access to the contents of a factorization object
%   e = end (F, k, n)
%   [m,n] = size (F, k)
%   S = double (F)
%   C = subsref (F, ij)
%   S = struct (F)
%   disp (F)
%
% The factorization_chol_dense object also provides cholupdate, which acts
% just like the builtin cholupdate.
%
% The factorization_svd object provides:
%
%   c = cond (F,p)      the p-norm condition number.  p=2 is the default.
%                       cond(F,2) takes no time to compute, since it was
%                       computed when the SVD factorization was found.
%   a = norm (F,p)      the p-norm.  see the cond(F,p) discussion above.
%   r = rank (F)        returns the rank of A, precomputed by the SVD.
%   Z = null (F)        orthonormal basis for the null space of A
%   Q = orth (F)        orthonormal basis for the range of A
%   C = pinv (F)        the pseudo-inverse, V'*(S\V).
%   [U,S,V] = svd (F)   SVD of A or pinv(A), regular, economy, or rank-sized
%
% See also mldivide, lu, chol, ldl, qr, svd.

% Copyright 2011-2012, Timothy A. Davis, http://www.suitesparse.com

    properties (SetAccess = protected)
        % The abstract class holds a QR, LU, Cholesky factorization:
        A = [ ] ;           % a copy of the input matrix
        Factors = [ ] ;
        is_inverse = false ;% F represents the factorization of A or inv(A)
        is_ctrans = false ; % F represents the factorization of A or A'
        kind = '' ;         % a string stating what kind of factorization F is
        alpha = 1 ;         % F represents the factorization of A or alpha*A.
        A_rank = [ ] ;      % rank of A, from SVD, or estimate from COD
        A_cond = [ ] ;      % 2-norm condition number of A, from SVD
        A_condest = [ ] ;   % quick and dirty estimate of the condition number
        % If F is inverted, alpha doesn't change.  For example:
        %   F = alpha*factorize(A) ; % F = alpha*A, in factorized form.
        %   G = inverse(F) ;         % G = inv(alpha*A)
        %   H = beta*G               % H = beta*inv(alpha*A) 
        %                                = inv((alpha/beta)*A)
        % So to update alpha via scaling, beta*F, the new scale factor beta
        % is multiplied with F.alpha if F.is_inverse is false.  Otherwise,
        % F.alpha is divided by beta to get the new scale factor.
    end

    methods (Abstract)
        x = mldivide_subclass (F, b) ;
        x = mrdivide_subclass (b, F) ;
        e = error_check (F) ;
    end

    methods

        %-----------------------------------------------------------------------
        % mldivide and mrdivide: return a scaled factorization or a matrix
        %-----------------------------------------------------------------------

        % Let b be a double scalar, F a non-scalar factorization, and g a scalar
        % factorization.  Then these operations return scaled factorization
        % objects (unless flatten is true, in which case a matrix is returned):
        %
        %   F\b = inverse (F) * b           |   F/b = F / b
        %   F\g = inverse (F) * double (g)  |   F/g = F / double (g)
        %   b\F = F / b                     |   b/F = b * inverse (F)
        %   g\F = F / double (g)            |   g/F = double (g) * inverse (F)
        %
        % Otherwise mldivide & mrdivide always return a matrix as their result.

        function x = mldivide (y, z, flatten)
            %MLDIVIDE x = y\z where either y or z or both are factorizations.
            flatten = (nargin > 2 && flatten) ;
            if (isobject (y) && isscalar (z) && ~flatten)
                % x = y\z where y is an object and z is scalar (perhaps object).
                % result is a scaled factorization object x.
                x = scale_factor (inverse (y), ~(y.is_inverse), double (z)) ;
            elseif (isscalar (y) && isobject (z) && ~flatten)
                % x = y\z where y is scalar (perhaps object) and z is an object.
                % result is a scaled factorization object x.
                x = scale_factor (z, ~(z.is_inverse), double (y)) ;
            else
                % result x will be a matrix.  b is coerced to be a matrix.
                [F, b, first_arg_is_F] = getargs (y, z) ;
                if (~first_arg_is_F)
                    % x = b\F where F is a factorization.
                    error_if_inverse (F, 1) ;
                    x = b \ F.A ;            % use builtin backslash
                elseif (F.is_ctrans)
                    % x = F\b where F represents (alpha*A)' or inv(alpha*A)'
                    if (F.is_inverse)
                        % x = inv(alpha*A)'\b = (A'*b)*alpha'
                        x = (F.A'*b) * F.alpha' ;
                    else
                        % x = (alpha*A)'\b = (b'/A)' / alpha'
                        x = mrdivide_subclass (b', F)' / F.alpha' ;
                    end
                else
                    % x = F\b where F represents (alpha*A) or inv(alpha*A)
                    if (F.is_inverse)
                        % x = inv(alpha*A)\b = (A*b)*alpha
                        x = (F.A*b) * F.alpha ;
                    else
                        % x = (alpha*A)\b = (A\b) / alpha
                        x = mldivide_subclass (F, b) / F.alpha ;
                    end
                end
            end
        end

        function x = mrdivide (y, z, flatten)
            %MRDIVIDE x = y/z where either y or z or both are factorizations.
            flatten = (nargin > 2 && flatten) ;
            if (isobject (y) && isscalar (z) && ~flatten)
                % x = y/z where y is an object and z is scalar (perhaps object).
                % result is a scaled factorization object x.
                x = scale_factor (y, ~(y.is_inverse), double (z)) ;
            elseif (isscalar (y) && isobject (z) && ~flatten)
                % x = y/z where y is scalar (perhaps object) and z is an object.
                % result is a scaled factorization object x.
                x = scale_factor (inverse (z), ~(z.is_inverse), double (y)) ;
            else
                % result x will be a matrix.  b is coerced to be a matrix.
                [F, b, first_arg_is_F] = getargs (y, z) ;
                if (first_arg_is_F)
                    % x = F/b where F is a factorization object
                    error_if_inverse (F, 2)
                    x = F.A / b ;            % use builtin slash
                elseif (F.is_ctrans)
                    % x = b/F where F represents (alpha*A)' or inv(alpha*A)'
                    if (F.is_inverse)
                        % x = b/inv(alpha*A)' = (b*A')*alpha'
                        x = (b*F.A') * F.alpha' ;
                    else
                        % x = b/(alpha*A)' = (A\b')' / alpha'
                        x = mldivide_subclass (F, b')' / F.alpha' ;
                    end
                else
                    % x = b/F where F represents (alpha*A) or inv(alpha*A)
                    if (F.is_inverse)
                        % x = b/inv(alpha*A) = (b*A)*alpha
                        x = (b*F.A) * F.alpha ;
                    else
                        % x = b/(alpha*A) = (b/A) / alpha
                        x = mrdivide_subclass (b, F) / F.alpha ;
                    end
                end
            end
        end

        %-----------------------------------------------------------------------
        % mtimes: a simple and clean wrapper for mldivide and mrdivide
        %-----------------------------------------------------------------------

        function x = mtimes (y, z)
            %MTIMES x=y*z where y or z is a factorization object (or both).
            % Since inverse(F) is so cheap, and does the right thing inside
            % mldivide and mrdivide, this is just a simple wrapper.
            if (isobject (y))
                % A*b               becomes inverse(A)\b
                % inverse(A)*b      becomes A\b 
                % A'*b              becomes inverse(A)'\b
                % inverse(A)'*b     becomes A'\b
                x = mldivide (inverse (y), z) ;
            else
                % b*A               becomes b/inverse(A)
                % b*inverse(A)      becomes b/A
                % b*A'              becomes b/inverse(A)'
                % b*inverse(A)'     becomes b/A'
                % y is a scalar or matrix, z must be an object
                x = mrdivide (y, inverse (z)) ;
            end
        end

        %-----------------------------------------------------------------------
        % uplus, uminus, ctranspose, inverse: always return a factorization
        %-----------------------------------------------------------------------

        function F = uplus (F)
            %UPLUS +F
        end

        function F = uminus (F)
            %UMINUS -F
            F.alpha = -(F.alpha) ;
        end

        function F = inverse (F)
            %INVERSE "inverts" F by flagging it as factorization of inv(A)
            F.is_inverse = ~(F.is_inverse) ;
        end

        function F = ctranspose (F)
            %CTRANSPOSE "transposes" F by flagging it as factorization of A'
            F.is_ctrans = ~(F.is_ctrans) ;
        end

        %-----------------------------------------------------------------------
        % is* methods that return a scalar
        %-----------------------------------------------------------------------

        function s = isreal (F)
            %ISREAL for F=factorize(A): same as isreal(A)
            s = isreal (F.A) ;
        end

        function s = isempty (F)
            %ISEMPTY for F=factorize(A): same as isempty(A)
            s = any (size (F.A) == 0) ;
        end

        function s = isscalar (F)
            %ISSCALAR for F=factorize(A): same as isscalar(A)
            s = isscalar (F.A)  ;
        end

        function s = issingle (F)                                           %#ok
            %ISSINGLE for F=factorize(A) is always false
            s = false ;
        end

        function s = isnumeric (F)                                          %#ok
            %ISNUMERIC for F=factorize(A) is always true
            s = true ;
        end

        function s = isfloat (F)                                            %#ok
            %ISFLOAT for F=factorize(A) is always true
            s = true ;
        end

        function s = isvector (F)
            %ISVECTOR for F=factorize(A): same as isvector(A)
            s = isvector (F.A) ;
        end

        function s = issparse (F)
            %ISSPARSE for F=factorize(A): same as issparse(A)
            s = issparse (F.A) ;
        end

        function s = isfield (F, f)                                         %#ok
            %ISFIELD isfield(F,f) is true if F.f exists, false otherwise
            s = (ischar (f) && (strcmp (f, 'A') ...
                || strcmp (f, 'Factors') || strcmp (f, 'kind') ...
                || strcmp (f, 'is_inverse') || strcmp (f, 'is_ctrans') ...
                || strcmp (f, 'alpha') || strcmp (f, 'A_rank') ...
                || strcmp (f, 'A_cond') || strcmp (f, 'A_condest'))) ;
        end

        function s = isa (F, s)
            %ISA for F=factorize(A): 'double', 'numeric', 'float' are true.
            % For other types, the builtin isa does the right thing.
            s = strcmp (s, 'double') || strcmp (s, 'numeric') ||  ...
                strcmp (s, 'float') || builtin ('isa', F, s) ;
        end

        %-----------------------------------------------------------------------
        % condest, rankest
        %-----------------------------------------------------------------------

        function C = abs (F)
            %ABS abs(F) returns abs(A) or abs(inverse(A)), as appropriate.  The
            % ONLY reason abs is included here is to support the builtin
            % normest1 for small matrices (n <= 4).  Computing abs(inverse(A))
            % explicitly computes the inverse of A, so use with caution.
            C = abs (double (F)) ;
        end

        function s = condest (F)
            %CONDEST 1-norm condition number for square matrices.
            % Does not require another factorization of A, so it's very fast.
            % Does NOT explicitly compute the inverse of A.  Instead, if F
            % represents an inverse, F*x inside normest1 does the right thing,
            % and does A\b using the factorization F.
            A = F.A ;                                                       %#ok
            [m, n] = size (A) ;                                             %#ok
            if (m ~= n)
                error ('MATLAB:condest:NonSquareMatrix', ...
                       'Matrix must be square.') ;
            end
            if (n == 0)
                s = 0 ;
            elseif (F.is_inverse)
                % F already represents the factorization of the inverse of A
                s = F.alpha * norm (A,1) * normest1 (F) ;                   %#ok
            else
                % Note that the inverse is NOT explicitly computed.
                s = F.alpha * norm (A,1) * normest1 (inverse (F)) ;         %#ok
            end
        end

        function r = rankest (F)
            %RANKEST returns the estimated rank of A.
            % It is a very rough estimate if Cholesky, LU, QR, or LDL succeeded
            % (in which A is assumed to have full rank).  COD finds a more
            % accurate estimate, and SVD finds the exact rank.
            r = F.A_rank ;
        end

        %-----------------------------------------------------------------------
        % end, size
        %-----------------------------------------------------------------------

        function e = end (F, k, n)
            %END returns index of last item for use in subsref
            if (n == 1)
                e = numel (F.A) ;   % # of elements, for linear indexing
            else
                e = size (F, k) ;   % # of rows or columns in A or pinv(A)
            end
        end

        function [m, n] = size (F, k)
            %SIZE returns the size of the matrix F.A in the factorization F
            if (F.is_inverse ~= F.is_ctrans)
                % swap the dimensions to match pinv(A)
                if (nargout > 1)
                    [n, m] = size (F.A) ;
                else
                    m = size (F.A) ;
                    m = m ([2 1]) ;
                end
            else
                if (nargout > 1)
                    [m, n] = size (F.A) ;
                else
                    m = size (F.A) ;
                end
            end
            if (nargin > 1)
                m = m (k) ;
            end
        end

        %-----------------------------------------------------------------------
        % double: a wrapper for subsref
        %-----------------------------------------------------------------------

        function S = double (F)
            %DOUBLE returns the factorization as a matrix, A or inv(A)
            ij.type = '()' ;
            ij.subs = cell (1,0) ;
            S = subsref (F, ij) ;   % let factorize.subsref do all the work
        end

        %-----------------------------------------------------------------------
        % subsref: returns a matrix
        %-----------------------------------------------------------------------

        function C = subsref (F, ij)
            %SUBSREF A(i,j) or (i,j)th entry of inv(A) if F is inverted.
            % Otherwise, explicit entries in the inverse are computed.
            % This method also extracts the contents of F with F.whatever.
            switch (ij (1).type)
                case '.'
                    % F.A usage: extract one of the matrices from F
                    switch ij (1).subs
                        case 'A'
                            C = F.A ;
                        case 'Factors'
                            C = F.Factors ;
                        case 'is_inverse'
                            C = F.is_inverse ;
                        case 'is_ctrans'
                            C = F.is_ctrans ;
                        case 'kind'
                            C = F.kind ;
                        case 'alpha'
                            C = F.alpha ;
                        case 'A_cond'
                            C = F.A_cond ;
                        case 'A_condest'
                            C = F.A_condest ;
                        case 'A_rank'
                            C = F.A_rank ;
                        otherwise
                            error ('MATLAB:nonExistentField', ...
                            'Reference to non-existent field ''%s''.', ...
                            ij (1).subs) ;
                    end
                    % F.X(2,3) usage, return X(2,3), for component F.X
                    if (length (ij) > 1 && ~isempty (ij (2).subs))
                        C = subsref (C, ij (2)) ;
                    end
                case '()'
                    C = subsref_paren (F, ij) ;
                case '{}'
                    error ('MATLAB:cellRefFromNonCell', ...
                    'Cell contents reference from a non-cell array object.') ;
            end
        end

        %-----------------------------------------------------------------------
        % struct: extracts all contents of a factorization object
        %-----------------------------------------------------------------------

        function S = struct (F)
            %STRUCT convert factorization F into a struct.
            % S cannot be used for subsequent object methods here.
            S.A = F.A ;
            S.Factors = F.Factors ;
            S.is_inverse = F.is_inverse ;
            S.is_ctrans = F.is_ctrans ;
            S.alpha = F.alpha ;
            S.A_rank = F.A_rank ;
            S.A_cond = F.A_cond ;
            S.kind = F.kind ;
        end

        %-----------------------------------------------------------------------
        % disp: displays the contents of F
        %-----------------------------------------------------------------------

        function disp (F)
            %DISP displays a factorization object
            fprintf ('  class: %s\n', class (F)) ;
            fprintf ('  %s\n', F.kind) ;
            fprintf ('  A: [%dx%d double]\n', size (F.A)) ;
            fprintf ('  Factors:\n') ; disp (F.Factors) ;
            fprintf ('  is_inverse: %d\n', F.is_inverse) ;
            fprintf ('  is_ctrans: %d\n', F.is_ctrans) ;
            fprintf ('  alpha: %g', F.alpha) ;
            if (~isreal (F.alpha))
                fprintf (' + (%g)i', imag (F.alpha)) ;
            end
            fprintf ('\n') ;
            if (~isempty (F.A_rank))
                fprintf ('  A_rank: %d\n', F.A_rank) ;
            end
            if (~isempty (F.A_condest))
                fprintf ('  A_condest: %d\n', F.A_condest) ;
            end
            if (~isempty (F.A_cond))
                fprintf ('  A_cond: %d\n', F.A_cond) ;
            end
        end
    end

    %---------------------------------------------------------------------------
    % methods that are not user-callable
    %---------------------------------------------------------------------------

    methods (Access = protected)

        function [F, b, first_arg_is_F] = getargs (y, z)
            first_arg_is_F = isobject (y) ;
            if (first_arg_is_F)
                F = y ;             % first argument is a factorization object
                b = double (z) ;    % 2nd one coerced to be a matrix
            else
                b = y ;             % first argument is not an object
                F = z ;             % second one must be an object
            end
        end

        function F = scale_factor (F, use_beta_inverse, beta)
            %SCALE_FACTOR scales a factorization
            if (use_beta_inverse)
                % F = inv(alpha*A), so F*beta = inv((alpha/beta)*A)
                if (F.is_ctrans)
                    F.alpha = F.alpha / beta' ;
                else
                    F.alpha = F.alpha / beta ;
                end
            else
                % F = alpha*A, so F*beta = (alpha*beta)*A
                if (F.is_ctrans)
                    F.alpha = F.alpha * beta' ;
                else
                    F.alpha = F.alpha * beta ;
                end
            end
        end
    end
end


%-------------------------------------------------------------------------------
% subsref_paren: support function for subsref
%-------------------------------------------------------------------------------

function C = subsref_paren (F, ij)
%SUBSREF_PAREN C = subsref_paren(F,ij) implements C=F(i,j) and C=F(i)

    % F(2,3) usage, return A(2,3) or the (2,3) of inv(A).
    assert (length (ij) == 1, 'Improper index matrix reference.') ;
    A = F.A ;
    is_ctrans = F.is_ctrans ;
    if (is_ctrans && length (ij.subs) > 1)   % swap i and j
        ij.subs = ij.subs ([2 1]) ;
    end

    if (F.is_inverse)

        % requesting explicit entries of the inverse

        if (length (ij.subs) == 1)
            % for linear indexing of the inverse (C=F(i)), first
            % convert to double and then use builtin subsref
            C = subsref (double (F), ij) ;
        else
            % standard indexing, C = F(i,j)
            if (is_ctrans)
                [n, m] = size (A) ;
            else
                [m, n] = size (A) ;
            end
            if (length (ij.subs) == 2)
                ilen = length (ij.subs {1}) ;
                if (strcmp (ij.subs {1}, ':'))
                    ilen = n ;
                end
                jlen = length (ij.subs {2}) ;
                if (strcmp (ij.subs {2}, ':'))
                    jlen = m ;
                end
                j = ij ;
                j.subs {1} = ':' ;
                i = ij ;
                i.subs {2} = ':' ;
                if (jlen <= ilen)
                    % compute X=S(:,j) of S=inv(A) and return X(i,:)
                    C = subsref (mldivide (...
                        inverse (F), ...
                        subsref (identity (A, m), j), 1), i) ;
                else
                    % compute X=S(i,:) of S=inv(A) and return X(:,j)
                    C = subsref (mrdivide (...
                        subsref (identity (A, n), i), ...
                        inverse (F), 1), j) ;
                end
            else
                % the entire inverse has been explicitly computed
                C = mldivide (inverse (F), identity (A, m), 1) ;
            end
        end

    else

        % F is not inverted, so just return A(i,j)
        if (isempty (ij (1).subs))
            C = A ;
        else
            C = subsref (A, ij) ;
        end
        C = C * F.alpha ;
        if (is_ctrans)
            C = C' ;
        end
    end
end


%-------------------------------------------------------------------------------
% identity: return a full or sparse identity matrix
%-------------------------------------------------------------------------------

function I = identity (A, n)
%IDENTITY return a full or sparse identity matrix.  Not user-callable
    if (issparse (A))
        I = speye (n) ;
    else
        I = eye (n) ;
    end
end

%-------------------------------------------------------------------------------
% throw an error if inv(A) is being inadvertently computed 
%-------------------------------------------------------------------------------

function error_if_inverse (F, kind)
    % x = b\F or F/b where F=inverse(A) and b is not a scalar is unsupported.
    % It could be done by coercing F into an explicit matrix representation of
    % inv(A), via x = b\double(F) or double(A)/b, but this is the same as
    % b\inv(A) or inv(A)/b respectively.  That is dangerous, and thus it is
    % not done here automatically.
    if (F.is_inverse)
        if (kind == 1)
            s1 = 'B\F' ;
            s2 = 'B\double(F)' ;
        else
            s1 = 'F/B' ;
            s2 = 'double(F)/B' ;
        end
        error ('FACTORIZE:unsupported', ...
        ['%s where F=inverse(A) requires the explicit computation of the ' ...
         'inverse.\nThis is ill-advised, so it is never done automatically.'...
         '\nTo force it, use %s instead of %s.\n'], s1, s2, s1) ;
    end
end