Code covered by the BSD License  

Highlights from
Graph package

image thumbnail
from Graph package by Kyaw Tun
Graph package for molecular biologist

graph(varargin)
function g = graph(varargin)
% graph - graph object constructor
%
%   g = graph create a graph using GUI
%
%   g = graph(adj)      adjecency matrix, give symmetric matrix for
%   undirected graph
%   g = graph(adj, nodeLabels) adjecency matrix with node id
%   g = graph(adj, nodeLabels, graphName)
%
%   g = graph(elist)    edge list in cell array or numeric matrix
%   g = graph(elist, nodeLabels) edge list in cell array
%   g = graph(ig, nodelist) from a parent graph with node list
%
%   g = graph(nNode) generated Erdos-Reyni random graph with nNode number
%   of node and node probability of 1.5 times probability that gaint
%   components commence.
%   
%   g = graph(nNode, p) given that (p >= 0.0 and p <= 1.0) generated Erdos-Reyni
%   undirected random graph on n vertices with edge probability of of p. 
%
%   g = graph(nNode, k) generated Erdos-Reyni undirected random graph with
%   nNode number of nodes and k number of links.
%
%   g = graph('filename') create a graph as defined in file. Supported file
%   format are SIF, SBML, GML, DOT and node pair.
%   g = graph('sbml_file_name.xml') build reaction network from given sbml file
%       Matlab sbml tool box required.
%   g = graph('file_name.sif') build graph from given Simple Interaction
%   File (SIF). SIF is a tab delimited text file containing names of two
%   nodes with link name in between them.
%   g = graph('file_name.txt') file consists just pair of linking nodes
%   G = GRAPH(PG, NODEIDS) create a graph from parent graph PG of selected
%   nodes list NODEIDS. 
%
% Native graph data structure is link list and assume directed. The
% function edges give link list. Undirected graph is archieve by setting
% directed = 0, or set(g, 'directed', 0). Although native graph data
% structre assume directed, this package concern more on undirected graph.
%
%   Use set and get to change graph parameters, such as node color.
%
% Example:
%   g = graph(10); % randomly generate 10-node graph
%
% See also: adjacency, edges, node, set, get
%

%
% Kyaw Tun, RIKEN, Japan
% 2006 Oct
%

% TODO:
% 1) use adjacency matrix as basic data structure of graph
% 2) remove assumption about lazy initialization

% constants
UBQ_MAIN = 1; % take entities as ubiquiton if not main reactant or product
UBQ_UBQ = 2; % take ubiquiton as those over critical point in vertex degree distribution
UBQ_ARB = 3; % take ubiquiton as those over arbitratory threshold

ARB_TH = 17; % arbitratory threshould (this should be depend on network size)

% default variable
ubiquiton = UBQ_ARB;

% premative graph structure
g.nodes = [];
g.edges = [];
g.nodeSize = 0.015;
g.name = '';

% catched field 
g.directed = [];
g.modmat = [];
g.adj = [];

% sample named graphs
graphnames = {'group3', 'group4', 'karate', 'complete'};
graphs = {
[0 1 1 1 0 0 0 0 0 0 0 0;1 0 1 1 0 0 0 0 0 0 0 0;1 1 0 1 1 0 0 0 1 0 0 0;1 1 1 0 0 0 0 0 0 0 0 0;0 0 1 0 0 1 1 1 0 0 0 0;0 0 0 0 1 0 1 1 0 0 0 0;0 0 0 0 1 1 0 1 0 0 0 1;0 0 0 0 1 1 1 0 0 0 0 0;0 0 1 0 0 0 0 0 0 1 1 1;0 0 0 0 0 0 0 0 1 0 1 1;0 0 0 0 0 0 0 0 1 1 0 1;0 0 0 0 0 0 1 0 1 1 1 0]
[0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0;1 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0;0 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 1 2 1 1 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 1 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0;0 0 0 0 1 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 1 0;0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 1;0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0]
[0 1 1 1 1 1 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0;1 0 1 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0;1 1 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1;1 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1;0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1;1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 1;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0;0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0;0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1;0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1;1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 1;0 0 1 0 0 0 0 1 0 0 1 1 1 0 1 1 1 0 0 0 0 0 1 1 1 0]
};

% temporary variables
nodeLabels = {};
positions = [];


if nargin == 0
    fig = figure('BackingStore', 'on', 'IntegerHandle', 'off', 'Name', 'Adjacency Matrix' ...
        ,'NumberTitle', 'off', 'MenuBar', 'none', 'DoubleBuffer','on');

    title(sprintf('Double click to create vertex. Single click to connect.\nRight click to delete. Enter to finish.'))
    xlim([0 10]);
    ylim([0 10]);
    nullgraph = graph([1 0; 1 0]);
    setappdata(fig, 'nullgraph', nullgraph);
    set(fig,'WindowButtonDownFcn', {@uigraph, 'down'});
    set(fig,'KeyPressFcn', {@uigraph, 'keypress'})
    set(fig,'CloseRequestFcn', {@closeuigraph, fig});
    uiwait(fig);
    adj = getappdata(fig,'Matrix');
    g = graph(adj);
    delete(fig);
    return;
elseif nargin >= 1
    if ischar(varargin{1}) && ismember(varargin{1}, graphnames)
        idx = strmatch(varargin{1}, graphnames, 'exact');
        if idx == length(graphnames)
            if nargin > 1
                n = varargin{2};
            else
                n = 4;
            end
            adj = ones(n,n);
            for k1 = 1:n, adj(k1, k1) = 0; end
            g = graph(adj);
            g.name = sprintf('%d complete graph', n);
        else
            g = graph(graphs{idx});
            g.name = graphnames{idx};
        end
        return
    end
    if nargin == 1 && ischar(varargin{1}) % a file name
        if length(varargin{1}) > 3 && isequal(varargin{1}(end-3:end), '.sif')
            % SIF file
            [n1, link, n2] = textread(varargin{1}, '%s%s%s', 'bufsize', 16777216);
            nodeLabels = union(n1, n2);
            n = length(nodeLabels);
            for k1 = 1:min(length(n1), length(n2))
                %kn1 = strmatch(n1{k1}, nodeLabels, 'exact');
                %kn2 = strmatch(n2{k1}, nodeLabels, 'exact');
                kn1 = find(strcmp(nodeLabels, n1{k1}));                
                kn2 = find(strcmp(nodeLabels, n2{k1}));
                g.edges(end+1, 1:2) = [kn1, kn2];
            end
            adj = sparse(n, n);
            for k = 1:size(g.edges,1)
                adj(g.edges(k,1),g.edges(k,2)) = 1;
            end
        elseif length(varargin{1}) > 3 && isequal(varargin{1}(end-3:end), '.gml')
            str = textread(varargin{1}, '%s', 'whitespace','', 'bufsize', 16777216); % read large file quickly
            str = str{:}; 
            % we have to smart when dealing with large string, don't touch
            % it, we use regular expression
            source = regexp(str, '\ssource\s(\d+)', 'tokens');
            target = regexp(str, '\starget\s(\d+)', 'tokens'); 
            id = regexp(str, '\sid\s(\d+)\s', 'tokens');
            ids = zeros(1,length(id));            
            n = length(id);
            for k = 1:n; ids(k) = str2double(id{k}{:}); end
            x = regexp(str, 'x\s((-)?\d+.\d+)', 'tokens');
            y = regexp(str, 'y\s((-)?\d+.\d+)', 'tokens');
            positions = zeros(n,2);
            try
                positions(:,1) = str2double([x{:}]);
                positions(:,2) = str2double([y{:}]);
                % positions(:,1) = positions(:,1) / (max(positions(:,1))+0.05);
                % positions(:,2) = positions(:,2) / (max(positions(:,2))+0.05);
                positions(:,2) = max(positions(:,2)) - positions(:,2);
            catch
                positions = [];
            end
            label =  regexp(str, '\slabel\s"(\w+)"', 'tokens');            
            nodeLabels = [label{1:n}]; % the rest may be label for edges
            for k = 1:length(source)
                g.edges(end+1, 1:2) = [find(ids==str2double(source{k}{:})), ...
                    find(ids==str2double(target{k}{:}))]; 
            end
            d = regexp(str, 'directed\s(\d)', 'tokens');
            if ~isempty(d)
                g.directed = str2double(d{1}{:});
            end
            if ~g.directed
                g.edges(end+1:end+size(g.edges,1), 1:2) = g.edges(:, [2 1]);
            end
        elseif length(varargin{1}) > 3 && isequal(varargin{1}(end-3:end), '.xml')
            % assume SBML file
            m = TranslateSBML(varargin{1});
            n = length(m.species);
            if isfield(m.species, 'id')
                labels = {m.species.id};
            else
                labels = {m.species.name};
            end
            % first we construct incident matrix
            % with row corresponding to metabolite and column corresponding
            % to reactions
            nr = length(m.reaction);
            ic = zeros(n, nr);
            dirs = [m.reaction.reversible];
            for k1 = 1:length(m.reaction)
                rs = [];
                ps = [];
                % % debug code
                % if isequal(m.reaction(k1).id, 'R03270')
                %    disp('reach');
                % end
                for k2 = 1:length(m.reaction(k1).reactant)
                    r = strmatch(m.reaction(k1).reactant(k2).species, labels, 'exact');
                    if isempty(r)
                        error('graph:sbml', ['Invalid reaction: ', m.reaction(k1).name]);
                    end
                    rs(end+1) = r(1);
                end
                for k3 = 1:length(m.reaction(k1).product)
                    p = strmatch(m.reaction(k1).product(k3).species, labels, 'exact');
                    if isempty(p)
                        error('graph:sbml', ['Invalid reaction: ', m.reaction(k1).name]);
                    end
                    ps(end+1) = p(1);
                end
                if isempty(ps) || isempty(rs)
                    warning('graph:sbml', ['Invalid reaction: ', m.reaction(k1).name, ' skipped']);
                    continue;
                end
                ic(rs,k1) = -1;
                ic(ps,k1) = 1;

            end
            % here we can build two kind of graph
            % enzyme graph (enzyme as node) or metabolite graph (metabolite
            % as node)            
            % for the time being, we will do only enzyme graph
            
            % Remove ubiquiton
            % before we do, there are ubiquiton like H20 it will link all
            % enzyme incorrectly, so we remove than, we take only one
            % reaction and one product for a reaction
            % There are several way to get rid of ubiquiton
            deg = sum(abs(ic),2);
            if ubiquiton == UBQ_MAIN
                % Method ONE: main product - main reactant
                icc = zeros(size(ic));
                for k1 = 1:size(ic,2)
                    rs = find(ic(:,k1)<0);
                    ps = find(ic(:,k1)>0);
                    [foo, mi] = min(deg(rs));
                    icc(rs(mi),k1) = -1;
                    [foo, mi] = min(deg(ps));
                    icc(ps(mi),k1) = 1;
                end
            elseif ubiquiton == UBQ_UBQ
                % Method TWO: remove motabolites more than critical
                % threashold
                [sdeg, ideg] = sort(deg);
                degdiff = diff(sdeg); 
                
                error('This method not implemented.');
            elseif ubiquiton == UBQ_ARB
                % Method THREE: remove motabolites more than arbitrary
                % threashold
                arb = ARB_TH;
                [sdeg, ideg] = sort(deg);
                ubq = ideg(end-arb:end);
                icc = ic;
                icc(ubq,:) = 0;
            else
                % Method THREE: No removal
            end
            
            % build enzyme (reaction) as node
            if isfield(m.reaction, 'id')
                nodeLabels = {m.reaction.id}; % level 1
            else
                nodeLabels = {m.reaction.name};
            end
            n = length(nodeLabels);
            for k1 = 1:size(icc,1)
                igs = find(icc(k1,:)<0);
                ogs = find(icc(k1,:)>0);
                for k2 = 1:length(igs)
                    for k3 = 1:length(ogs)
                        g.edges(end+1,1:2) = [igs(k2), ogs(k3)];
                    end
                end
            end         
        else
            % assume just pair or nodes
            [n1, n2] = textread(varargin{1}, '%s%s', 'commentstyle', 'matlab');
            nodeLabels = union(n1, n2);
            n = length(nodeLabels);
            for k1 = 1:min(length(n1), length(n2))
                %kn1 = strmatch(n1{k1}, nodeLabels, 'exact');
                %kn2 = strmatch(n2{k1}, nodeLabels, 'exact');
                kn1 = find(strcmp(nodeLabels, n1{k1}));                
                kn2 = find(strcmp(nodeLabels, n2{k1}));
                g.edges(end+1, 1:2) = [kn1, kn2];
            end          
        end
        g.name = getName(varargin{1});
    elseif isequal(class(varargin{1}), 'graph')
        % from a parent graph
        g = varargin{1};
        adj = adjacency(g);
        if nargin >= 2 && isnumeric(varargin{2})
            ig = varargin{2};
            if length(ig) ~= length(unique(ig))
                error('Node ids must be unique.');
            end
        elseif nargin >= 2 && isequal(varargin{2}, 'visited')
            ig = find([g.nodes.visited]);
        else
            ig = 1:length(adj);
        end
        nodeLabels = {g.nodes.label};
        name = g.name;
        colors = {g.nodes.color};

        g = graph(adj(ig, ig), nodeLabels(ig));
        g.name = [name, '->', num2str(length(ig))];
        g = set(g, 'color', colors(ig));
        return;
    elseif isnumeric(varargin{1}) && length(varargin{1}) == 1
        % generate graph randomly by Erdos-Reyni random graphs 
        n = varargin{1};
        mode = 'Erdos-Reyni';
        if nargin == 3
            mode = varargin{3};
        end
        switch mode
            case {'randperm', 'rp'}
                if nargin >= 2 && ~isempty(varargin{2})
                    k = varargin{2}(1);
                else
                    k = 0.01;
                end
                nk = ceil(n*k/2)*2;
                p = randperm(nk);
                elist = reshape(p,nk/2,2);
                g.edges = mod(elist,n)+1;
                g.directed = 0;
                g.name = sprintf('rp random graph %g', k);
            otherwise
                if nargin < 2
                    % a giant connected component should emerge when 
                    % p > 1/(n-1). 
                    p = 1.5 * 1/(n-1);
                else
                    p = varargin{2}(1);
                end
                if p < 0, error('Probability or number of edges cannot be negative.'); end
                if p <= 1.0
                    adj = rand(n,n) < p; % adjacency matrix
                else
                    % number of edges is given
                    adj = rand(n,n);
                    adj = triu(adj,1);
                    [i, j, v] = find(adj);
                    [y, si] = sort(v);
                    % take first p number of links
                    if p < length(si)
                        toDelete = si(p+1:end);
                        for kd = 1:length(toDelete)
                            adj(i(toDelete(kd)), j(toDelete(kd))) = 0;
                        end
                    end
                    adj = adj > 0.0;
                end                
                adj = triu(adj,1);
                adj = adj + adj'; % make undirected graph
                g = graph(adj);
                g.name = sprintf('Erdos-Reyni random graph %g', p);
                return;
        end
    elseif (isnumeric(varargin{1}) || islogical(varargin{1})) && ...
            size(varargin{1},1) == size(varargin{1},2)
        % adjacency matrix
        g.adj = sparse(varargin{1});
        n = length(g.adj);
        if size(g.adj,1) ~= size(g.adj,2)
            error('Adjacency matrix must be square.');
        end
        [e1, e2] = find(g.adj);
        g.edges = [e1, e2];
        if nargin > 1 
            if length(g.adj) ~= length(varargin{2}) 
                error('Label names must be same length as adjacency matrix');
            end
            if isnumeric(varargin{2})
                nodeLabels = cell(1,length(varargin{2}));
                for kv2 = 1:length(varargin{2});
                    nodeLabels{kv2} = num2str(varargin{2}(kv2));
                end
            else
                nodeLabels = varargin{2};
            end
        end
        if nargin > 2
            g.name = varargin{3};
        end
        if nargin > 3
            positions = varargin{4};
        end

    elseif iscell(varargin{1})
        % cell array of edges
        n = 0;
        for k = 1:length(varargin{1})
            g.edges(k,1:2) = varargin{1}{k};
            n = max([n, g.edges(k,1:2)]);
        end
    elseif isnumeric(varargin{1}) && size(varargin{1},2) == 2 && min(varargin{1}(:)) == 1 
        % edge list
        % support to be cell array of edges
        n = 0;
        for k = 1:size(varargin{1},1)
            g.edges(k,1:2) = varargin{1}(k,1:2);
            n = max([n, g.edges(k,1:2)]);
        end        
    elseif isnumeric(varargin{1}) 
        % assume incident matrix
        x = varargin{1};
        if min(x(:)) > 0
            error('Assumed incident matrix must contained negative value for link out.');
        end
        es = [];
        for kx = 1:size(x,2) % along column of edge
            ins = find(x(:,kx)>0);
            outs = find(x(:,kx)<0);
            for ki = ins
                for ko = outs
                    es(end+1,1:2) = [ki, ko];
                end
            end
        end
        g = graph(es);
        return;
    else
        error('Unreconginized input format.');
    end
end


if isempty(nodeLabels) && (nargin >= 2 && iscell(varargin{2}))
    nodeLabels = varargin{2};
    if length(nodeLabels) ~= n
        error('Node label must be the same dimension as adjacency matrix');
    end
end
if isempty(nodeLabels)
    for k = 1:n
        nodeLabels{k} = num2str(k);
    end
end


if isempty(positions)
    r = 0.4;
    theta = 2*pi/n;    
    positions = [0.5 + r * cos(theta*([1:n]'-1)), ...
        0.5 + r * sin(theta*([1:n]'-1))];
end
for k = 1:n
    g.nodes(k).label = strtrim(nodeLabels{k});
    g.nodes(k).position = positions(k,:);
    % g.position(k,:) = positions(k,:);
    g.nodes(k).visited = 0;
    g.nodes(k).timeStamp = 0;
    g.nodes(k).color = 'w';
    g.nodes(k).groupid = -1;
end

if isempty(g.adj)
    g.adj = sparse(n, n);
    for k = 1:size(g.edges,1)
        g.adj(g.edges(k,1),g.edges(k,2)) = g.adj(g.edges(k,1),g.edges(k,2)) + 1;
    end
end

g = class(g, 'graph');

        
    
function s = getName(s)

sl = find(s==filesep);
if ~isempty(sl) && length(s) > sl(end)
    s = s(sl(end)+1:end);
end
dot = find(s=='.');
s = s(1:dot(end)-1);

function closeuigraph(varargin)

uiresume(varargin{3});


function uigraph(foo, foo2, action, varargin)
%
% ADJ_MATRIX_GUI
% Opens a figure.  Double click to create a vertex. Single click to 
% connect vertices.  Right click to delete vertices or edges.


if nargin == 0
    action = 'init';
end

switch action
case 'motion'
    line_h = getappdata(gcf,'motionline');
    pt = get(gca,'CurrentPoint');
    pt = pt(1,:);
    xdata = get(line_h,'XData');
    ydata = get(line_h,'YData');
    xdata(2) = pt(1);
    ydata(2) = pt(2);
    set(line_h,'XData',xdata,'YData',ydata)
case 'down'
    button = get(gcf,'SelectionType');
    switch button
    case 'normal'
        h = gco;
        fig = gcf;
        
        % First click
        if ~isappdata(fig, 'motionline')
            if isequal(get(h,'Type'),'text')
                pt = get(h,'Position');
                hold on
                line_h = plot(pt(1), pt(2),'b-.' ...
                                          ,'EraseMode','normal');
                setappdata(line_h,'startobj',h)    % Save start object
                hold off
                stack_text_on_top
                setappdata(fig,'motionline',line_h)
                set(fig,'WindowButtonMotionFcn', {@uigraph, 'motion'});
            end
        else
        % Second click
            line_h = getappdata(fig,'motionline');

            if isequal(get(gco,'Type'),'text')
                startobj = getappdata(line_h,'startobj');
                endobj = gco;
                startpt = get(startobj,'Position');
                endpt = get(endobj,'Position');
                set(line_h,'XData',[startpt(1) endpt(1)] ...
                          ,'YData',[startpt(2) endpt(2)]);
                I = round(str2double(get(startobj,'String')));
                J = round(str2double(get(endobj,'String')));
                Matrix = getappdata(gcf,'Matrix');
                Matrix(I,J) = Matrix(I,J)+1;
                Matrix(J,I) = Matrix(J,I)+1;
                setappdata(gcf,'Matrix',Matrix)
            else
                delete(line_h)
            end
            
            rmappdata(gcf,'motionline')
            set(fig,'WindowButtonMotionFcn', '');
        end
    case 'open'
        pt = get(gca,'CurrentPoint');
        pt = pt(1,:);
        hold on
        n = 1+length(findobj(get(gca,'Children'),'Type','text'));
        h = text(pt(1),pt(2),num2str(n) ...
                            ,'Color','r','FontWeight','bold');
        hold off
        if ~isappdata(gcf,'Matrix')
            setappdata(gcf,'Matrix',[])
        end
        Matrix = getappdata(gcf,'Matrix');
        Matrix(n,n) = 0;
        setappdata(gcf,'Matrix',Matrix)
    case 'alt'
        switch get(gco,'Type')
        case 'text'
            n = round(str2double(get(gco,'String')));
            pt = get(gco,'Position');
            handles = get(gca,'Children');
            for I=1:length(handles)
                h = handles(I);
                if isequal(get(h,'Type'),'text')
                    n2 = round(str2double(get(h,'String')));
                    if n2 > n
                        set(h,'String',n2-1)
                    end
                else
                    xdata = get(h,'XData');
                    ydata = get(h,'YData');
                    if (xdata(1) == pt(1) & ydata(1) == pt(2)) ...
                    |  (xdata(2) == pt(1) & ydata(2) == pt(2))
                        delete(h)
                    end
                end
            end
            if isappdata(gcf,'Matrix')
                Matrix = getappdata(gcf,'Matrix');
                Matrix(n,:) = [];
                Matrix(:,n) = [];
                setappdata(gcf,'Matrix',Matrix)
            end
            delete(gco)
        case 'line'
            xdata = get(gco,'XData');
            ydata = get(gco,'YData');
            txt_h = findobj(get(gca,'Children'),'Type','text');
            for K=1:length(txt_h)
                h = txt_h(K);
                pt = get(h,'Position');
                if (xdata(1) == pt(1) & ydata(1) == pt(2))
                    I = round(str2double(get(h,'String')));
                elseif (xdata(2) == pt(1) & ydata(2) == pt(2))
                    J = round(str2double(get(h,'String')));
                end
            end
            if isappdata(gcf,'Matrix')
                Matrix = getappdata(gcf,'Matrix');
                Matrix(I,J) = Matrix(I,J)-1;
                Matrix(J,I) = Matrix(J,I)-1;
                setappdata(gcf,'Matrix',Matrix)
            end
            delete(gco)
        end % End object switch
    end % End button switch
case 'keypress'
    ESC = 27;
    ENTER = 13';
    switch get(gcf,'CurrentCharacter')
    case ESC
        if isappdata(gcf,'motionline')
            line_h = getappdata(gcf,'motionline');
            delete(line_h)
                    
            rmappdata(gcf,'motionline')
        end
        set(gcf,'WindowButtonMotionFcn', '');
    case ENTER
        uiresume(gcf);
    end
case 'init'
    fig = figure('BackingStore', 'on', 'IntegerHandle', 'off', 'Name', 'Adjacency Matrix' ...
            ,'NumberTitle', 'off', 'MenuBar', 'none', 'DoubleBuffer','on');

    title('Double click to create vertex. Single click to connect. Right click to delete')
    set(fig,'WindowButtonDownFcn', {@uigraph, 'down'});
    set(fig,'KeyPressFcn', {@uigraph, 'keypress'})

otherwise
    error(['Unknown - ' action])
end % End action switch

function stack_text_on_top
    ax = gca;
    handles = get(gca,'Children');
    txt_h = findobj(handles,'Type','text');
    
    set(gca,'Children',[txt_h; setdiff(handles,txt_h)])


 

Contact us at files@mathworks.com