def test_topological_sort():
    """Test topological sort graph utility."""

    dummy_graph1 = {"A": ["B"], "B": ["C"], "C": ["D"], "D": ["E"], "E": []}

    sorted_order = graph.topological_sort(dummy_graph1)

    assert (sorted_order[0] == "A" and sorted_order[1] == "B"
            and sorted_order[2] == "C" and sorted_order[3] == "D"
            and sorted_order[4] == "E")

    dummy_graph2 = {
        "A": ["B", "C"],
        "B": ["D"],
        "C": ["D"],
        "D": [],
    }

    sorted_order = graph.topological_sort(dummy_graph2)

    has_order_1 = (sorted_order[0] == "A" and sorted_order[1] == "B"
                   and sorted_order[2] == "C" and sorted_order[3] == "D")

    has_order_2 = (sorted_order[0] == "A" and sorted_order[1] == "C"
                   and sorted_order[2] == "B" and sorted_order[3] == "D")

    assert (len(sorted_order) == len(dummy_graph2))
    assert has_order_1 or has_order_2
def test_density_dielectric_merging():

    substance = Mixture()
    substance.add_component('C', 1.0)

    density = Density(thermodynamic_state=ThermodynamicState(
        temperature=298 * unit.kelvin, pressure=1 * unit.atmosphere),
                      phase=PropertyPhase.Liquid,
                      substance=substance,
                      value=10 * unit.gram / unit.mole,
                      uncertainty=1 * unit.gram / unit.mole)

    dielectric = DielectricConstant(thermodynamic_state=ThermodynamicState(
        temperature=298 * unit.kelvin, pressure=1 * unit.atmosphere),
                                    phase=PropertyPhase.Liquid,
                                    substance=substance,
                                    value=10 * unit.gram / unit.mole,
                                    uncertainty=1 * unit.gram / unit.mole)

    density_schema = density.get_default_workflow_schema(
        'SimulationLayer', PropertyWorkflowOptions())
    dielectric_schema = dielectric.get_default_workflow_schema(
        'SimulationLayer', PropertyWorkflowOptions())

    density_metadata = Workflow.generate_default_metadata(
        density, get_data_filename('forcefield/smirnoff99Frosst.offxml'),
        PropertyEstimatorOptions())

    dielectric_metadata = Workflow.generate_default_metadata(
        density, get_data_filename('forcefield/smirnoff99Frosst.offxml'),
        PropertyEstimatorOptions())

    density_workflow = Workflow(density, density_metadata)
    density_workflow.schema = density_schema

    dielectric_workflow = Workflow(dielectric, dielectric_metadata)
    dielectric_workflow.schema = dielectric_schema

    workflow_graph = WorkflowGraph('')

    workflow_graph.add_workflow(density_workflow)
    workflow_graph.add_workflow(dielectric_workflow)

    merge_order_a = graph.topological_sort(density_workflow.dependants_graph)
    merge_order_b = graph.topological_sort(
        dielectric_workflow.dependants_graph)

    for protocol_id_A, protocol_id_B in zip(merge_order_a, merge_order_b):

        if protocol_id_A.find('extract_traj') < 0 and protocol_id_A.find(
                'extract_stats') < 0:

            assert density_workflow.protocols[protocol_id_A].schema.json() == \
                   dielectric_workflow.protocols[protocol_id_B].schema.json()

        else:

            assert density_workflow.protocols[protocol_id_A].schema.json() != \
                   dielectric_workflow.protocols[protocol_id_B].schema.json()
def test_cloned_schema_merging_simulation(registered_property_name,
                                          available_layer):
    """Tests that two, the exact the same, calculations get merged into one
    by the `WorkflowGraph`."""

    registered_property = registered_properties[registered_property_name]

    substance = Mixture()
    substance.add_component('C', 1.0)

    dummy_property = create_dummy_property(registered_property)

    workflow_schema = dummy_property.get_default_workflow_schema(
        available_layer, PropertyWorkflowOptions())

    if workflow_schema is None:
        return

    global_metadata = create_dummy_metadata(dummy_property, available_layer)

    workflow_a = Workflow(dummy_property, global_metadata)
    workflow_a.schema = workflow_schema

    workflow_b = Workflow(dummy_property, global_metadata)
    workflow_b.schema = workflow_schema

    workflow_graph = WorkflowGraph()

    workflow_graph.add_workflow(workflow_a)
    workflow_graph.add_workflow(workflow_b)

    ordered_dict_a = OrderedDict(sorted(workflow_a.dependants_graph.items()))
    ordered_dict_b = OrderedDict(sorted(workflow_b.dependants_graph.items()))

    merge_order_a = graph.topological_sort(ordered_dict_a)
    merge_order_b = graph.topological_sort(ordered_dict_b)

    assert len(workflow_graph._protocols_by_id) == len(workflow_a.protocols)

    for protocol_id in workflow_a.protocols:
        assert protocol_id in workflow_graph._protocols_by_id

    for protocol_id_A, protocol_id_B in zip(merge_order_a, merge_order_b):

        assert protocol_id_A == protocol_id_B

        assert workflow_a.protocols[protocol_id_A].schema.json() == \
               workflow_b.protocols[protocol_id_B].schema.json()
Beispiel #4
0
    def merge(self, other):
        """Merges another ProtocolGroup with this one. The id
        of this protocol will remain unchanged.

        It is assumed that can_merge has already returned that
        these protocol groups are compatible to be merged together.

        Parameters
        ----------
        other: ProtocolGroup
            The protocol to merge into this one.

        Returns
        -------
        Dict[str, str]
            A map between any original protocol ids and their new merged values.
        """

        merged_ids = super(ProtocolGroup, self).merge(other)

        other_execution_order = graph.topological_sort(other.dependants_graph)

        other_reduced_protocol_dependants = copy.deepcopy(
            other.dependants_graph)
        graph.apply_transitive_reduction(other_reduced_protocol_dependants)

        other_parent_protocol_ids = {}

        for protocol_id in other_execution_order:

            parent_ids = other_parent_protocol_ids.get(protocol_id) or []
            inserted_id = self._try_merge_protocol(protocol_id, other,
                                                   parent_ids, merged_ids,
                                                   [(other.id, self.id)])

            for dependant in other_reduced_protocol_dependants[protocol_id]:

                if dependant not in other_parent_protocol_ids:
                    other_parent_protocol_ids[dependant] = []

                other_parent_protocol_ids[dependant].append(inserted_id)

        self._execution_order = graph.topological_sort(self._dependants_graph)

        return merged_ids
Beispiel #5
0
    def add_protocols(self, *protocols):

        for protocol in protocols:

            if protocol.id in self._protocols:

                raise ValueError('The {} group already contains a protocol '
                                 'with id {}.'.format(self.id, protocol.id))

            self._protocols[protocol.id] = protocol
            self._dependants_graph[protocol.id] = []

        # Pull each of an individual protocols inputs up so that they
        # become a required input of the group.
        for protocol_id in self._protocols:

            protocol = self._protocols[protocol_id]

            for input_path in protocol.required_inputs:

                grouped_path = ProtocolPath.from_string(input_path.full_path)

                if grouped_path.start_protocol != protocol.id:
                    grouped_path.prepend_protocol_id(protocol.id)

                grouped_path.prepend_protocol_id(self.id)

                if grouped_path in self.required_inputs:
                    continue

                reference_values = protocol.get_value_references(input_path)

                if len(reference_values) == 0:
                    self.required_inputs.append(grouped_path)

                for source_path, reference_value in reference_values.items():

                    if reference_value.start_protocol not in self._protocols:

                        self.required_inputs.append(grouped_path)
                        continue

                    if protocol_id in self._dependants_graph[
                            reference_value.start_protocol]:
                        continue

                    self._dependants_graph[
                        reference_value.start_protocol].append(protocol_id)

        # Figure out the order in which grouped protocols should be executed.
        self._root_protocols = graph.find_root_nodes(self._dependants_graph)
        self._execution_order = graph.topological_sort(self._dependants_graph)