1function C = incidence (A, varargin)
2%GRB.INCIDENCE graph incidence matrix.
3% C = GrB.incidence (A) is the graph incidence matrix of the square
4% matrix A.  C is GraphBLAS matrix of size n-by-e, if A is n-by-n with e
5% entries (not including diagonal entries).  The jth column of C has 2
6% entries: C(s,j) = -1 and C(t,j) = 1, where A(s,t) is an entry A.
7% Diagonal entries in A are ignored.
8%
9%   C = GrB.incidence (A, ..., 'directed') constructs a matrix C of size
10%       n-by-e where e = GrB.entries (GrB.offdiag (A)).  Any entry in the
11%       upper or lower trianglar part of A results in a unique column of
12%       C.  The diagonal is ignored.  This is the default.
13%
14%   C = GrB.incidence (A, ..., 'unsymmetric') is the same as 'directed'.
15%
16%   C = GrB.incidence (A, ..., 'undirected') assumes A is symmetric, and
17%       only creates columns of C based on entries in tril (A,-1).  The
18%       diagonal and upper triangular part of A are ignored.
19%
20%   C = GrB.incidence (A, ..., 'symmetric') is the same as 'undirected'.
21%
22%   C = GrB.incidence (A, ..., 'lower') is the same as 'undirected'.
23%
24%   C = GrB.incidence (A, ..., 'upper') is the same as 'undirected',
25%       except that only entries in triu (A,1) are used.
26%
27%   C = GrB.incidence (A, ..., type) constructs C with the type 'double',
28%       'single', 'int8', 'int16', 'int32', or 'int64'.  The default is
29%       'double'.  The type cannot be 'logical' or 'uint*' since C
30%       must contain -1's.
31%
32% Examples:
33%
34%   A = sprand (5, 5, 0.5)
35%   C = GrB.incidence (A)
36%
37% See also graph/incidence, digraph/incidence.
38
39% SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved.
40% SPDX-License-Identifier: GPL-3.0-or-later
41
42if (isobject (A))
43    A = A.opaque ;
44end
45
46[m, n] = gbsize (A) ;
47if (m ~= n)
48    error ('A must be square') ;
49end
50
51% get the string options
52kind = 'directed' ;
53type = 'double' ;
54for k = 1:nargin-1
55    arg = lower (varargin {k}) ;
56    switch arg
57        case { 'directed', 'undirected', 'symmetric', 'unsymmetric', ...
58            'lower', 'upper' }
59            kind = arg ;
60        case { 'double', 'single', 'int8', 'int16', 'int32', 'int64' }
61            type = arg ;
62        case { 'uint8', 'uint16', 'uint32', 'uint64', 'logical' }
63            error ('type must be signed') ;
64        otherwise
65            error ('unknown option') ;
66    end
67end
68
69switch (kind)
70
71    case { 'directed', 'unsymmetric' }
72
73        % create the incidence matrix of a directed graph, using all of A;
74        % except that diagonal entries are ignored.
75        A = gbselect ('offdiag', A, 0) ;
76
77    case { 'upper' }
78
79        % create the incidence matrix of an undirected graph, using only
80        % entries in the strictly upper triangular part of A.
81        A = gbselect ('triu', A, 1) ;
82
83    otherwise   % 'undirected', 'symmetric', or 'lower'
84
85        % create the incidence matrix of an undirected graph, using only
86        % entries in the strictly lower triangular part of A.
87        A = gbselect ('tril', A, -1) ;
88
89end
90
91% build the incidence matrix
92desc.base = 'zero-based' ;
93[I, J] = gbextracttuples (A, desc) ;
94e = length (I) ;
95I = [I ; J] ;
96J = (int64 (0) : int64 (e-1))' ;
97J = [J ; J] ;
98X = ones (e, 1, type) ;
99X = [-X ; X] ;
100C = GrB (gbbuild (I, J, X, n, e, desc)) ;
101
102