Package org.openbel.framework.core.protonetwork

Source Code of org.openbel.framework.core.protonetwork.ProtoNetworkMerger

/**
* Copyright (C) 2012-2013 Selventa, Inc.
*
* This file is part of the OpenBEL Framework.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The OpenBEL Framework is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the OpenBEL Framework. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms under LGPL v3:
*
* This license does not authorize you and you are prohibited from using the
* name, trademarks, service marks, logos or similar indicia of Selventa, Inc.,
* or, in the discretion of other licensors or authors of the program, the
* name, trademarks, service marks, logos or similar indicia of such authors or
* licensors, in any marketing or advertising materials relating to your
* distribution of the program or any covered product. This restriction does
* not waive or limit your obligation to keep intact all copyright notices set
* forth in the program as delivered to you.
*
* If you distribute the program in whole or in part, or any modified version
* of the program, and you assume contractual liability to the recipient with
* respect to the program or modified version, then you will indemnify the
* authors and licensors of the program for any liabilities that these
* contractual assumptions directly impose on those licensors and authors.
*/
package org.openbel.framework.core.protonetwork;

import static org.openbel.framework.common.BELUtilities.hasItems;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.openbel.framework.common.InvalidArgument;
import org.openbel.framework.common.model.Statement;
import org.openbel.framework.common.model.Term;
import org.openbel.framework.common.protonetwork.model.AnnotationDefinitionTable;
import org.openbel.framework.common.protonetwork.model.AnnotationDefinitionTable.TableAnnotationDefinition;
import org.openbel.framework.common.protonetwork.model.AnnotationValueTable;
import org.openbel.framework.common.protonetwork.model.AnnotationValueTable.TableAnnotationValue;
import org.openbel.framework.common.protonetwork.model.DocumentTable;
import org.openbel.framework.common.protonetwork.model.DocumentTable.DocumentHeader;
import org.openbel.framework.common.protonetwork.model.NamespaceTable;
import org.openbel.framework.common.protonetwork.model.NamespaceTable.TableNamespace;
import org.openbel.framework.common.protonetwork.model.ParameterTable;
import org.openbel.framework.common.protonetwork.model.ParameterTable.TableParameter;
import org.openbel.framework.common.protonetwork.model.ProtoEdgeTable;
import org.openbel.framework.common.protonetwork.model.ProtoEdgeTable.TableProtoEdge;
import org.openbel.framework.common.protonetwork.model.ProtoNetwork;
import org.openbel.framework.common.protonetwork.model.ProtoNodeTable;
import org.openbel.framework.common.protonetwork.model.StatementAnnotationMapTable;
import org.openbel.framework.common.protonetwork.model.StatementAnnotationMapTable.AnnotationPair;
import org.openbel.framework.common.protonetwork.model.StatementTable;
import org.openbel.framework.common.protonetwork.model.StatementTable.TableStatement;
import org.openbel.framework.common.protonetwork.model.TermParameterMapTable;
import org.openbel.framework.common.protonetwork.model.TermTable;

/**
* ProtoNetworkMerger merges two {@link ProtoNetwork} objects together. This
* operation is mostly a union of the two proto networks, except for the term
* tables which treat each entry as unique. For example, given the two
* statements:
* <pre>
* geneAbundance(EG:207) :> rnaAbundance(EG:208)
* geneAbundance(EG:208) :> rnaAbundance(EG:310)
* </pre>
* the merged parameter will be:
* <pre>
* (union)
* 207
* 208
* 210
* </pre>
* whereas, the merged terms will be:
* <pre>
* (additive)
* geneAbundance(#)
* rnaAbundance(#)
* geneAbundance(#)
* rnaAbundance(#)
* </pre>
* The result of the merge will be in <tt>protoNetwork1</tt>.
*
* @author Anthony Bargnesi {@code <abargnesi@selventa.com>}
*/
public class ProtoNetworkMerger {

    /**
     * Merge the second proto network, <tt>protoNetwork2</tt>, into the first
     * proto network, <tt>protoNetwork1</tt>.  This will union each field of
     * the two proto networks, except terms, which will be combined with
     * possible overlap.
     *
     * @param protoNetwork1 {@link ProtoNetwork}, the first proto network,
     * which cannot be null
     * @param protoNetwork2 {@link ProtoNetwork}, the second proto network
     * which cannot be null
     * @throws InvalidArgument Thrown if either <tt>protoNetwork1</tt> or
     * <tt>protoNetwork2</tt> is null.
     */
    public void merge(ProtoNetwork protoNetwork1, ProtoNetwork protoNetwork2) {
        if (protoNetwork1 == null) {
            throw new InvalidArgument("protoNetwork1", protoNetwork1);
        }

        if (protoNetwork2 == null) {
            throw new InvalidArgument("protoNetwork2", protoNetwork2);
        }

        DocumentTable dt = protoNetwork2.getDocumentTable();

        for (DocumentHeader dh : dt.getDocumentHeaders()) {
            // add new document header
            int did = protoNetwork1.getDocumentTable().addDocumentHeader(dh);

            // union namespaces
            NamespaceTable nt = protoNetwork2.getNamespaceTable();
            for (TableNamespace ns : nt.getNamespaces()) {
                protoNetwork1.getNamespaceTable().addNamespace(ns, did);
            }

            // union annotation definition values
            Map<Integer, Integer> adremap = new HashMap<Integer, Integer>();
            AnnotationDefinitionTable adt =
                    protoNetwork2.getAnnotationDefinitionTable();
            for (TableAnnotationDefinition ad : adt.getAnnotationDefinitions()) {
                Integer oldDefinitionId = adt.getDefinitionIndex().get(ad);
                Integer newDefinitionId = protoNetwork1
                        .getAnnotationDefinitionTable()
                        .addAnnotationDefinition(ad, did);

                adremap.put(oldDefinitionId, newDefinitionId);
            }

            // union annotation values
            AnnotationValueTable avt = protoNetwork2.getAnnotationValueTable();
            for (TableAnnotationValue av : avt.getAnnotationValues()) {
                Integer newDefinitionId =
                        adremap.get(av.getAnnotationDefinitionId());

                protoNetwork1.getAnnotationValueTable().addAnnotationValue(
                        newDefinitionId, av.getAnnotationValue());
            }

            // union statements
            StatementTable statementTable = protoNetwork2.getStatementTable();
            List<TableStatement> statements = statementTable.getStatements();
            List<String> terms = protoNetwork2.getTermTable().getTermValues();
            Map<Integer, Integer> termMap = new HashMap<Integer, Integer>(
                    terms.size());
            for (int j = 0; j < statements.size(); j++) {
                mergeStatement(j, statements.get(j), protoNetwork1,
                        protoNetwork2, did, termMap);
            }
        }
    }

    /**
     * Merge the statement into the first {@link ProtoNetwork}
     * <tt>protoNetwork1</tt>.
     *
     * @param statementIndex <tt>int</tt>, the statement index
     * @param statement {@link TableStatement}, the table statement
     * to merge
     * @param protoNetwork1 {@link ProtoNetwork}, the first proto network to
     * merge into
     * @param protoNetwork2 {@link ProtoNetwork}, the second proto network to
     * merge from
     * @param documentId <tt>int</tt>, the new merged document index
     * @return {@link Integer}, the new merged statement index
     */
    private Integer mergeStatement(int statementIndex,
            TableStatement statement,
            ProtoNetwork protoNetwork1, ProtoNetwork protoNetwork2,
            int documentId, Map<Integer, Integer> termMap) {

        // always merge subject term since it's present in all statement types
        Integer newSubjectTermId = mergeTerm(statement.getSubjectTermId(),
                protoNetwork1, protoNetwork2,
                documentId, termMap);

        int newStatementIndex = 0;
        Statement stmtToMerge =
                protoNetwork2.getStatementTable().getIndexedStatements()
                        .get(statementIndex);
        if (statement.getRelationshipName() == null) {
            // merge definitional statement, no create of proto edge
            newStatementIndex =
                    protoNetwork1.getStatementTable().addStatement(
                            new TableStatement(
                                    newSubjectTermId, null, null), stmtToMerge,
                            documentId);
        } else if (statement.getObjectTermId() != null) {
            // merge simple statement
            Integer newObjectTermId = mergeTerm(
                    Integer.valueOf(statement.getObjectTermId()),
                    protoNetwork1, protoNetwork2, documentId, termMap);

            newStatementIndex = protoNetwork1.getStatementTable().addStatement(
                    new TableStatement(newSubjectTermId,
                            statement.getRelationshipName(), newObjectTermId),
                    stmtToMerge, documentId);
        } else {
            // merge nested statement, no creation of proto edge

            // merge nested subject
            Integer newNestedSubjectTermId = mergeTerm(
                    Integer.valueOf(statement.getNestedSubject()),
                    protoNetwork1, protoNetwork2, documentId, termMap);
            // merge nested object
            Integer newNestedObjectTermId = mergeTerm(
                    Integer.valueOf(statement.getNestedObject()),
                    protoNetwork1, protoNetwork2, documentId, termMap);

            newStatementIndex = protoNetwork1.getStatementTable().addStatement(
                    new TableStatement(newSubjectTermId,
                            statement.getRelationshipName(),
                            newNestedSubjectTermId,
                            statement.getNestedRelationship(),
                            newNestedObjectTermId),
                    stmtToMerge, documentId);
        }

        // remap edges for statement, if any exist
        ProtoEdgeTable et = protoNetwork2.getProtoEdgeTable();
        Map<Integer, Set<Integer>> stmtEdges = et.getStatementEdges();
        Set<Integer> edgeIndices = stmtEdges.get(statementIndex);
        if (hasItems(edgeIndices)) {
            remapEdges(protoNetwork1, protoNetwork2, documentId, termMap,
                    newStatementIndex, et.getProtoEdges(), edgeIndices);
        }

        // remap annotation definition + value
        Set<AnnotationPair> aps = protoNetwork2
                .getStatementAnnotationMapTable()
                .getStatementAnnotationPairsIndex().get(statementIndex);
        Set<AnnotationPair> newaps =
                new HashSet<StatementAnnotationMapTable.AnnotationPair>();

        if (hasItems(aps)) {
            for (AnnotationPair ap : aps) {
                // add original annotation definition to acquire new id
                TableAnnotationDefinition ad =
                        protoNetwork2
                                .getAnnotationDefinitionTable()
                                .getIndexDefinition().get(
                                        ap.getAnnotationDefinitionId());
                int newadid = protoNetwork1.getAnnotationDefinitionTable()
                        .addAnnotationDefinition(ad, documentId);

                // add original annotation value to acquire new id
                TableAnnotationValue av =
                        protoNetwork2.getAnnotationValueTable().getIndexValue()
                                .get(ap.getAnnotationValueId());
                int newavid = protoNetwork1.getAnnotationValueTable()
                        .addAnnotationValue(newadid,
                                av.getAnnotationValue());

                newaps.add(new AnnotationPair(newadid, newavid));
            }
        }

        // set new annotation pairs against new statement index
        protoNetwork1.getStatementAnnotationMapTable().addStatementAnnotation(
                newStatementIndex, newaps);

        return newStatementIndex;
    }

    /**
     * Remaps {@link TableProtoEdge proto edges} for a
     * {@link TableStatement statement}.  A new statement index is created from
     * a merge which requires the old {@link TableProtoEdge proto edges} to be
     * associated with it.
     *
     * @see https://github.com/OpenBEL/openbel-framework/issues/49
     * @param protoNetwork1 {@link ProtoNetwork}; merge into
     * @param protoNetwork2 {@link ProtoNetwork}; merge from
     * @param documentId {@code int}; bel document id
     * @param termMap {@link Map} of old term id to new proto node id
     * @param newStatementIndex {@code int} new merged statement id
     * @param edges {@link List}; merging statement's
     * {@link TableProtoEdge edges}
     * @param edgeIndices {@link Set}; set of old statement's edge indices
     */
    private void remapEdges(ProtoNetwork protoNetwork1,
            ProtoNetwork protoNetwork2, int documentId,
            Map<Integer, Integer> termMap, int newStatementIndex,
            List<TableProtoEdge> edges, Set<Integer> edgeIndices) {
        ProtoNodeTable nt = protoNetwork2.getProtoNodeTable();
        Map<Integer, Integer> nodeTermIndex = nt.getNodeTermIndex();

        TableProtoEdge[] remappedEdges = new TableProtoEdge[edgeIndices.size()];
        int i = 0;
        for (Integer edgeIndex : edgeIndices) {
            TableProtoEdge edge = edges.get(edgeIndex);
            int sourceBefore = edge.getSource();
            int targetBefore = edge.getTarget();

            Integer sourceTerm = nodeTermIndex.get(sourceBefore);
            Integer targetTerm = nodeTermIndex.get(targetBefore);
            Integer newSource = termMap.get(sourceTerm);
            if (newSource == null) {
                newSource = mergeTerm(sourceTerm, protoNetwork1, protoNetwork2,
                        documentId, termMap);
            }

            Integer newTarget = termMap.get(targetTerm);
            if (newTarget == null) {
                newTarget = mergeTerm(targetTerm, protoNetwork1, protoNetwork2,
                        documentId, termMap);
            }

            remappedEdges[i++] = new TableProtoEdge(newSource, edge.getRel(),
                    newTarget);
        }
        ProtoEdgeTable edgeTable = protoNetwork1.getProtoEdgeTable();
        edgeTable.addEdges(newStatementIndex, remappedEdges);
    }

    /**
     * Merge the term into the first {@link ProtoNetwork}
     * <tt>protoNetwork1</tt>.
     *
     * @param termId {@link Integer}, the original term index
     * @param protoNetwork1 {@link ProtoNetwork}, the first proto network
     * to merge into
     * @param protoNetwork2 {@link ProtoNetwork}, the second proto network
     * to merge from
     * @param documentId <tt>int</tt>, the new merged document index
     * @return {@link Integer}, the new merged term index
     */
    private Integer mergeTerm(Integer termId, ProtoNetwork protoNetwork1,
            ProtoNetwork protoNetwork2, int documentId,
            Map<Integer, Integer> termMap) {
        TermTable termTable = protoNetwork2.getTermTable();
        TermParameterMapTable mapTable =
                protoNetwork2.getTermParameterMapTable();
        ParameterTable parameterTable = protoNetwork2.getParameterTable();

        Term termToMerge = termTable.getIndexedTerms().get(termId);

        // tables for proto network we're merging into (1)
        final TermTable mtt = protoNetwork1.getTermTable();
        final ParameterTable mpt = protoNetwork1.getParameterTable();
        final TermParameterMapTable mtpmt =
                protoNetwork1.getTermParameterMapTable();
        final ProtoNodeTable mpnt = protoNetwork1.getProtoNodeTable();

        // if p1 contains an equivalent Term then return it's inndex without
        // adding a new one.
        Integer p1Index = mtt.getVisitedTerms().get(termToMerge);
        if (p1Index != null) {
            termMap.put(termId, p1Index);
            return p1Index;
        }

        List<Integer> newTermParameterIndices = new ArrayList<Integer>();
        List<Integer> termParameterIndices = mapTable.getTermParameterIndex()
                .get(termId);

        if (hasItems(termParameterIndices)) {
            for (Integer termParameterIndex : termParameterIndices) {
                TableParameter parameter = parameterTable.
                        getIndexTableParameter().get(termParameterIndex);
                newTermParameterIndices.add(mpt
                        .addTableParameter(parameter));
            }
        }

        Integer newTermIndex = mtt.addTerm(termToMerge);
        mtpmt.addTermParameterMapping(
                newTermIndex, newTermParameterIndices);

        Integer newNode = mpnt.addNode(newTermIndex, mtt.getTermValues().get(newTermIndex));

        termMap.put(termId, newNode);
        return newTermIndex;
    }
}
TOP

Related Classes of org.openbel.framework.core.protonetwork.ProtoNetworkMerger

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.