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()
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
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)