## \example kernel/dependency_graph.py
# When trying to understand what is going on in \imp, it can often be useful
# to view the dependency graph, that is, the graph showing how various
# entities relate to one another. In it, an arrow leads from an IMP::Container
# or IMP::Particle to an IMP::Restraint if the IMP::Restraint reads from that
# container or particle. Similarly, an arrow leads from an IMP::Container or
# IMP::Particle to an IMP::ScoreState if the score state reads from it, and
# an arrow leads from an IMP::ScoreState to an IMP::Container or IMP::Particle
# if the score state updates the particle.
#
# The resulting pruned dependency graph is:
# \dotgraph{ \dot
# digraph pruned_dependencies {
# 0[label="Nup85-12"];
# 1[label="Fragment [213-218)"];
# 2[label="Nup145C-4"];
# 3[label="Atom k distance restraint 2"];
# 4[label="Nup1202-8"];
# 5[label="Model 0"];
# 6[label="Fragment [239-244)"];
# 7[label="Nup1332-10"];
# 8[label="Nup1201-12"];
# 9[label="Connectivity particles"];
# 10[label="Nup84-14"];
# 11[label="Atom k distance restraint 3"];
# 12[label="MovedSingletonContainer2"];
# 13[label="Nup1333-16"];
# 14[label="Atom k distance restraint 5"];
# 15[label="Atom k distance restraint 4"];
# 16[label="normalize rigid bodies"];
# 17[label="Atom k distance restraint 0"];
# 18[label="ListSingletonContainer2"];
# 19[label="Chain A rigid body"];
# 20[label="ConnectivityRestraint 0"];
# 21[label="MovedSingletonContainer1"];
# 22[label="ListSingletonContainer1"];
# 23[label="HarmonicUpperBoundSphereDistancePairScore2 and ConnectingPairContainer"];
# 24[label="HarmonicUpperBoundSphereDistancePairScore1 and ConnectingPairContainer"];
# 25[label="Sec13"];
# 26[label="Hierarchy EV"];
# 27[label="Chain A rigid body rigid body positions"];
# 28[label="rigid bodies list"];
# 29[label="Atom k distance restraint 1"];
# 30[label="HarmonicUpperBoundSphereDistancePairScore0 and ConnectingPairContainer"];
# 31[label="ConnectingPairContainer"];
# 32[label="ConnectingPairContainer"];
# 33[label="Chain A rigid body"];
# 34[label="ListSingletonContainer0"];
# 35[label="ConnectivityRestraint 1"];
# 36[label="MovedSingletonContainer0"];
# 37[label="Chain A rigid body rigid body positions"];
# 38[label="ConnectingPairContainer"];
# 39[label="Seh1"];
# 40[label="Hierarchy EV particles"];
# 16->19 ;
# 16->33 ;
# 27->6 ;
# 27->39 ;
# 37->25 ;
# 37->1 ;
# 4->3 ;
# 2->3 ;
# 11->5 ;
# 17->5 ;
# 23->5 ;
# 14->5 ;
# 35->5 ;
# 15->5 ;
# 30->5 ;
# 24->5 ;
# 29->5 ;
# 26->5 ;
# 3->5 ;
# 20->5 ;
# 13->11 ;
# 10->11 ;
# 2->12 ;
# 18->12 ;
# 25->14 ;
# 2->14 ;
# 1->14 ;
# 33->14 ;
# 0->15 ;
# 6->15 ;
# 19->15 ;
# 39->15 ;
# 28->16 ;
# 8->17 ;
# 4->17 ;
# 13->20 ;
# 7->20 ;
# 22->21 ;
# 10->21 ;
# 2->23 ;
# 38->23 ;
# 31->24 ;
# 10->24 ;
# 0->26 ;
# 13->26 ;
# 8->26 ;
# 7->26 ;
# 4->26 ;
# 2->26 ;
# 6->26 ;
# 19->26 ;
# 10->26 ;
# 40->26 ;
# 1->26 ;
# 33->26 ;
# 19->27 ;
# 0->29 ;
# 2->29 ;
# 0->30 ;
# 32->30 ;
# 21->31 ;
# 22->31 ;
# 10->31 ;
# 0->32 ;
# 36->32 ;
# 34->32 ;
# 25->35 ;
# 2->35 ;
# 10->35 ;
# 1->35 ;
# 33->35 ;
# 0->36 ;
# 34->36 ;
# 33->37 ;
# 12->38 ;
# 2->38 ;
# 18->38 ;
# }
# \enddot
# }

import IMP.atom
import IMP.container
import sys

IMP.setup_from_argv(sys.argv, "Example of dependency graphs")


def create_representation():
    m = IMP.Model()
    all = IMP.atom.Hierarchy.setup_particle(IMP.Particle(m))
    all.set_name("the universe")
    rs = []

    def METHOD_NAME(name, ds):
        h = IMP.atom.METHOD_NAME(m, name, 10, ds)
        # leaves = IMP.atom.get_leaves(h)
        all.add_child(h)
        r = IMP.atom.create_connectivity_restraint(
            [IMP.atom.Selection(c) for c in h.get_children()], 1)
        if r:
            rs.append(r)

    def create_protein_from_pdbs(name, files):
        def create_from_pdb(file):
            with IMP.SetLogState(IMP.NONE):
                t = IMP.atom.read_pdb(
                    IMP.get_example_path("data/" + file), m,
                    IMP.atom.ATOMPDBSelector())
            # IMP.atom.show_molecular_hierarchy(t)
            c = IMP.atom.Chain(IMP.atom.get_by_type(t, IMP.atom.CHAIN_TYPE)[0])
            if c.get_number_of_children() == 0:
                IMP.atom.show_molecular_hierarchy(t)
            # there is no reason to use all atoms, just approximate the pdb
            # shape instead
            s = IMP.atom.create_simplified_along_backbone(c,
                                                          10.0 / 2.0)
            IMP.atom.destroy(t)
            # make the simplified structure rigid
            rb = IMP.atom.create_rigid_body(s)
            rb.set_coordinates_are_optimized(True)
            return s
        if len(files) > 1:
            p = IMP.Particle(m)
            h = IMP.atom.Hierarchy.setup_particle(p)
            h.set_name(name)
            for i, f in enumerate(files):
                c = create_from_pdb(f)
                h.add_child(c)
                c.set_name(name + " chain " + str(i))
            r = IMP.atom.create_connectivity_restraint(
                [IMP.atom.Selection(c) for c in h.get_children()], 1)
            if r:
                rs.append(r)
        else:
            h = create_from_pdb(files[0])
            h.set_name(name)
        all.add_child(h)
    METHOD_NAME("Nup85", 570)
    METHOD_NAME("Nup84", 460)
    METHOD_NAME("Nup145C", 442)
    METHOD_NAME("Nup120", [0, 500, 761])
    METHOD_NAME("Nup133", [0, 450, 778, 1160])
    create_protein_from_pdbs("Seh1", ["seh1.pdb"])
    create_protein_from_pdbs("Sec13", ["sec13.pdb"])
    return (m, rs, all)


def create_restraints(m, rs, all):
    def add_connectivity_restraint(s):
        r = IMP.atom.create_connectivity_restraint(s, 1)
        rs.append(r)

    def add_distance_restraint(s0, s1):
        r = IMP.atom.create_distance_restraint(s0, s1, 0, 1)
        rs.append(r)
    evr = IMP.atom.create_excluded_volume_restraint([all])
    rs.append(evr)
    s0 = IMP.atom.Selection(hierarchy=all, molecule="Nup145C",
                            residue_indexes=range(0, 423))
    s1 = IMP.atom.Selection(hierarchy=all, molecule="Nup84")
    s2 = IMP.atom.Selection(hierarchy=all, molecule="Sec13")
    add_connectivity_restraint([s0, s1, s2])
    add_distance_restraint(
        IMP.atom.Selection(hierarchy=all, molecule="Nup145C",
                           residue_indexes=range(0, 423)),
        IMP.atom.Selection(hierarchy=all, molecule="Nup85"))
    add_distance_restraint(
        IMP.atom.Selection(hierarchy=all, molecule="Nup145C",
                           residue_indexes=range(0, 423)),
        IMP.atom.Selection(hierarchy=all, molecule="Nup120",
                           residue_indexes=range(500, 762)))
    add_distance_restraint(IMP.atom.Selection(hierarchy=all, molecule="Nup84"),
                           IMP.atom.Selection(
                               hierarchy=all, molecule="Nup133",
                               residue_indexes=range(778, 1160)))
    add_distance_restraint(IMP.atom.Selection(hierarchy=all, molecule="Nup85"),
                           IMP.atom.Selection(hierarchy=all, molecule="Seh1"))
    add_distance_restraint(
        IMP.atom.Selection(hierarchy=all, molecule="Nup145C",
                           residue_indexes=range(0, 423)),
        IMP.atom.Selection(hierarchy=all, molecule="Sec13"))


# now do the actual work
(m, rs, all) = create_representation()
create_restraints(m, rs, all)

sf = IMP.core.RestraintsScoringFunction(rs)

# we can get the full dependency graph for the whole model with all
# the restraints but it is pretty complex
dg = IMP.get_dependency_graph(m)
IMP.show_graphviz(dg)

# better thing to do is to get the "pruned" graph
pdg = IMP.get_pruned_dependency_graph(m)

IMP.show_graphviz(pdg)